---
schema_version: "1"
id: 006e8407465944db7d93b0a1401eb2dd0accf55e64d50b5ee219686a12cbb3eb
title: Component Differences with PWA Kit
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/commerce-cloud/sfnext/guides/sfnext-pwa-components.md
canonical_url: https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-pwa-components.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: 58eab052a765b93599a241d94c06632f73975ccfd64723ad566b78e5a4e24d8e
products:
  - Composable Storefront
  - Salesforce APIs
---
# Component Differences with PWA Kit

PWA Kit components are typically JavaScript + PropTypes with Chakra primitives and style props. Storefront Next components are TypeScript-first and generally composed from Radix/shadcn patterns with utility-class styling and CVA-based variants. The main shift is from runtime styling and theme objects to compile-time class composition and typed component contracts.

## Component Architecture

| Aspect            | PWA Kit                | Storefront Next            |
| ----------------- | ---------------------- | -------------------------- |
| Language          | JavaScript + PropTypes | TypeScript                 |
| Type Checking     | Runtime (PropTypes)    | Compile-time (TypeScript)  |
| Component Library | Chakra UI (runtime)    | shadcn/ui (copied source)  |
| Styling Approach  | CSS-in-JS (Emotion)    | Utility classes (Tailwind) |
| Variant System    | Theme config           | Class Variance Authority   |
| Primitives        | Chakra components      | Radix UI headless          |

## Styling Comparison

| Aspect           | PWA Kit (Chakra)                        | Storefront Next (Tailwind)              |
| ---------------- | --------------------------------------- | --------------------------------------- |
| Inline styles    | `<Box padding="4" bg="blue.500">`       | `<div className="p-4 bg-blue-500">`     |
| Responsive       | `display={{base: 'none', lg: 'block'}}` | `className="hidden lg:block"`           |
| Hover states     | `_hover={{bg: 'blue.600'}}`             | `className="hover:bg-blue-600"`         |
| Theme tokens     | `color="gray.500"`                      | `className="text-gray-500"`             |
| Component styles | `useMultiStyleConfig('Button')`         | `buttonVariants({ variant, size })`     |
| Style override   | Props spread: `{...styles.container}`   | Class merge: `cn(baseClass, className)` |

## Responsive Design Patterns

PWA Kit:

```jsx
// Object syntax for breakpoints
<Box display={{base: 'block', md: 'flex', lg: 'grid'}}>

// Array syntax (base, sm, md, lg, xl)
<Grid templateColumns={['1fr', '1fr', 'repeat(2, 1fr)', 'repeat(4, 1fr)']}>

// Hook-based
const [isDesktop] = useMediaQuery('(min-width: 992px)')
```

Storefront Next:

```tsx
// Breakpoint prefixes (mobile-first)
<div className="block md:flex lg:grid">

// Responsive grid
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4">

// JavaScript detection
const isDesktop = typeof window !== 'undefined' &&
    window.matchMedia('(min-width: 1024px)').matches;
```

## Component Variants

PWA Kit (Theme-based):

```jsx
// In theme/components/button.js
export default {
    baseStyle: {...},
    variants: {
        solid: {...},
        outline: {...},
    },
    sizes: {
        sm: {...},
        md: {...},
    }
}

// Usage
<Button variant="solid" size="md">Click</Button>
```

Storefront Next (CVA):

```tsx
// In component file
const buttonVariants = cva("base-classes", {
  variants: {
    variant: { solid: "...", outline: "..." },
    size: { sm: "...", md: "..." },
  },
});

// Usage
<Button variant="solid" size="md">
  Click
</Button>;
```

## Key Similarities

- Composition over inheritance: Both use functional components composed from primitives
- Props passthrough: Both spread `...rest` props to underlying elements
- Responsive-first: Both prioritize mobile-first responsive design
- Variant systems: Both support component variants for consistent styling
- Accessibility: Both build on accessible component foundations (Chakra/Radix)
- HOC patterns: Both use HOCs for cross-cutting concerns (auth, suspense)

## Conversion Considerations

Here are some tips for moving from Chakra to Tailwind.

- Style props vs. Utility classes: Convert `padding="4"` to `className="p-4"`.
- Theme tokens vs. Tailwind config: Map color/spacing scales.
- useMultiStyleConfig vs. CVA: Convert component style objects to variant functions.
- Responsive objects vs. breakpoint prefixes: Replace `{{base: 'x', lg: 'y'}}` with `"x lg:y"`.

## Component Library Conversion

| Chakra Component             | Storefront Next Equivalent                           | Notes                                                               |
| ---------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------- |
| _Layout Primitives_          |                                                      |                                                                     |
| `Box`                        | `<div>` with Tailwind                                | Use `className="..."` for styling                                   |
| `Flex`                       | `<div className="flex">`                             | Add `flex-row`, `flex-col`, `gap-*` as needed                       |
| `HStack`                     | `<div className="flex flex-row gap-*">`              | Horizontal stack                                                    |
| `VStack`                     | `<div className="flex flex-col gap-*">`              | Vertical stack                                                      |
| `Stack`                      | `<div className="flex gap-*">`                       | Use `flex-row` or `flex-col`                                        |
| `StackDivider`               | `<Separator>` (ui/separator)                         | Or use `divide-y`/`divide-x` classes                                |
| `Center`                     | `<div className="flex items-center justify-center">` |                                                                     |
| `Container`                  | `<div className="container mx-auto">`                |                                                                     |
| `Grid`                       | `<div className="grid">`                             | Add `grid-cols-*`, `gap-*`                                          |
| `GridItem`                   | `<div>`                                              | Use `col-span-*`, `row-span-*`                                      |
| `SimpleGrid`                 | `<div className="grid grid-cols-*">`                 |                                                                     |
| `Wrap`                       | `<div className="flex flex-wrap">`                   |                                                                     |
| `WrapItem`                   | `<div>`                                              | Direct child of flex-wrap                                           |
| `Spacer`                     | `<div className="flex-1">`                           | Or use `gap-*` on parent                                            |
| `AspectRatio`                | `<AspectRatio>` (ui/aspect-ratio)                    | Radix-based                                                         |
| `Divider`                    | `<Separator>` (ui/separator)                         | Radix-based                                                         |
| **Typography**               |                                                      |                                                                     |
| `Text`                       | `<p>`, `<span>` with Tailwind                        | Use `text-*`, `font-*` classes                                      |
| `Heading`                    | `<h1>`-`<h6>` with Tailwind                          | Use `text-*`, `font-bold`                                           |
| **Buttons & Links**          |                                                      |                                                                     |
| `Button`                     | `<Button>` (ui/button)                               | CVA variants: default, destructive, outline, secondary, ghost, link |
| `ButtonGroup`                | `<div className="flex gap-2">`                       | Group buttons manually                                              |
| `IconButton`                 | `<Button size="icon">`                               | Use icon size variant                                               |
| `Link`                       | React Router `<Link>`                                | Or `<a>` with Tailwind                                              |
| `CloseButton`                | `<Button size="icon" variant="ghost">`               | With X icon                                                         |
| **Form Controls**            |                                                      |                                                                     |
| `Input`                      | `<Input>` (ui/input)                                 |                                                                     |
| `InputGroup`                 | `<div className="relative">`                         | Position elements manually                                          |
| `InputLeftElement`           | `<div className="absolute left-3">`                  | Position inside relative parent                                     |
| `InputRightElement`          | `<div className="absolute right-3">`                 | Position inside relative parent                                     |
| `Select`                     | `<SelectNative>` (ui/select-native)                  | Native select element                                               |
| `Checkbox`                   | `<Checkbox>` (ui/checkbox)                           | Radix-based                                                         |
| `Radio`                      | `<RadioGroup>` (ui/radio-group)                      | Radix-based                                                         |
| `RadioGroup`                 | `<RadioGroup>` (ui/radio-group)                      | Radix-based                                                         |
| `FormControl`                | `<div>`                                              | Use `<Label>` + input + error message                               |
| `FormLabel`                  | `<Label>` (ui/label)                                 |                                                                     |
| `FormErrorMessage`           | `<p className="text-destructive text-sm">`           |                                                                     |
| `useNumberInput`             | Custom implementation                                | No direct equivalent                                                |
| **Feedback**                 |                                                      |                                                                     |
| `Alert`                      | `<Alert>` (ui/alert)                                 |                                                                     |
| `AlertIcon`                  | Icon inside `<Alert>`                                | Use Lucide icons                                                    |
| `AlertTitle`                 | `<AlertTitle>` (ui/alert)                            |                                                                     |
| `AlertDescription`           | `<AlertDescription>` (ui/alert)                      |                                                                     |
| `Spinner`                    | `<div className="animate-spin">`                     | Or a custom Spinner component                                       |
| `Skeleton`                   | `<Skeleton>` (ui/skeleton)                           |                                                                     |
| `useToast`                   | Custom toast implementation                          | Or use sonner/react-hot-toast                                       |
| **Overlay**                  |                                                      |                                                                     |
| `Modal`                      | `<Dialog>` (ui/dialog)                               | Radix-based                                                         |
| `ModalOverlay`               | Built into `<Dialog>`                                | Automatic overlay                                                   |
| `ModalContent`               | `<DialogContent>` (ui/dialog)                        |                                                                     |
| `ModalHeader`                | `<DialogHeader>` (ui/dialog)                         |                                                                     |
| `ModalBody`                  | `<div>` inside DialogContent                         |                                                                     |
| `ModalFooter`                | `<DialogFooter>` (ui/dialog)                         |                                                                     |
| `ModalCloseButton`           | Built into `<DialogContent>`                         | Optional `showCloseButton` prop                                     |
| `AlertDialog`                | `<AlertDialog>` (ui/alert-dialog)                    | Radix-based                                                         |
| `AlertDialogOverlay`         | Built into `<AlertDialog>`                           |                                                                     |
| `AlertDialogContent`         | `<AlertDialogContent>`                               |                                                                     |
| `AlertDialogHeader`          | `<AlertDialogHeader>`                                |                                                                     |
| `AlertDialogFooter`          | `<AlertDialogFooter>`                                |                                                                     |
| `AlertDialogBody`            | `<AlertDialogDescription>`                           |                                                                     |
| `Drawer`                     | `<Drawer>` (ui/drawer)                               | Vaul-based, or use `<Sheet>`                                        |
| `DrawerOverlay`              | Built into `<Drawer>`                                |                                                                     |
| `DrawerContent`              | `<DrawerContent>`                                    |                                                                     |
| `DrawerHeader`               | `<DrawerHeader>`                                     |                                                                     |
| `DrawerBody`                 | `<div>` inside DrawerContent                         |                                                                     |
| `DrawerFooter`               | `<DrawerFooter>`                                     |                                                                     |
| `DrawerCloseButton`          | `<DrawerClose>`                                      |                                                                     |
| `Popover`                    | `<Popover>` (ui/popover)                             | Radix-based                                                         |
| `PopoverTrigger`             | `<PopoverTrigger>`                                   |                                                                     |
| `PopoverContent`             | `<PopoverContent>`                                   |                                                                     |
| `PopoverHeader`              | `<div>` with styling                                 | No separate component                                               |
| `PopoverBody`                | `<div>` inside PopoverContent                        |                                                                     |
| `PopoverFooter`              | `<div>` with styling                                 | No separate component                                               |
| `PopoverArrow`               | CSS-based                                            | Tailwind arrow styling                                              |
| `PopoverCloseButton`         | `<Button>` with X icon                               |                                                                     |
| `Tooltip`                    | `<Tooltip>` (ui/tooltip)                             | Radix-based                                                         |
| `Portal`                     | React Portal or Radix Portal                         | Built into overlay components                                       |
| **Disclosure**               |                                                      |                                                                     |
| `Accordion`                  | `<Accordion>` (ui/accordion)                         | Radix-based                                                         |
| `AccordionItem`              | `<AccordionItem>`                                    |                                                                     |
| `AccordionButton`            | `<AccordionTrigger>`                                 |                                                                     |
| `AccordionPanel`             | `<AccordionContent>`                                 |                                                                     |
| `AccordionIcon`              | Built into `<AccordionTrigger>`                      | Chevron icon included                                               |
| `useDisclosure`              | React state or Radix open state                      | `const [open, setOpen] = useState(false)`                           |
| **Navigation**               |                                                      |                                                                     |
| `Breadcrumb`                 | `<Breadcrumb>` (ui/breadcrumb)                       |                                                                     |
| `BreadcrumbItem`             | `<BreadcrumbItem>`                                   |                                                                     |
| `BreadcrumbLink`             | `<BreadcrumbLink>`                                   |                                                                     |
| `Menu` (dropdown)            | `<DropdownMenu>` (ui/dropdown-menu)                  | Radix-based                                                         |
| Navigation Menu              | `<NavigationMenu>` (ui/navigation-menu)              | Radix-based                                                         |
| **Data Display**             |                                                      |                                                                     |
| `Badge`                      | `<Badge>` (ui/badge)                                 | CVA variants                                                        |
| `Image` / `Img`              | `<img>` with Tailwind                                | Use `object-cover`, `rounded-*`                                     |
| `Avatar`                     | `<Avatar>` (ui/avatar)                               | Radix-based                                                         |
| `List`                       | `<ul>` or `<ol>`                                     |                                                                     |
| `ListItem`                   | `<li>`                                               |                                                                     |
| `Card`                       | `<Card>` (ui/card)                                   | Composition pattern                                                 |
| **Other**                    |                                                      |                                                                     |
| `Icon`                       | Lucide React icons                                   | `import { IconName } from 'lucide-react'`                           |
| `VisuallyHidden`             | `<span className="sr-only">`                         | Screen reader only                                                  |
| `Fade`                       | CSS transitions                                      | `transition-opacity` with conditional `opacity-0`                   |
| **Hooks**                    |                                                      |                                                                     |
| `useMultiStyleConfig`        | CVA + `cn()`                                         | Define variants in component                                        |
| `useStyleConfig`             | CVA + `cn()`                                         |                                                                     |
| `useTheme`                   | Tailwind CSS variables                               | Access via `var(--color-*)`                                         |
| `useMediaQuery`              | `window.matchMedia()`                                | Or use Tailwind breakpoints                                         |
| `useBreakpoint`              | Tailwind breakpoints                                 | Responsive classes preferred                                        |
| `useBreakpointValue`         | Tailwind breakpoints                                 | Use responsive class syntax                                         |
| `useRadio` / `useRadioGroup` | Radix RadioGroup                                     | Built into component                                                |
| `useStyles`                  | Not needed                                           | Styles are class-based                                              |
| `createStylesContext`        | React Context                                        | Or pass classes as props                                            |
| `StylesProvider`             | React Context                                        | Or use Tailwind directly                                            |
| `extendTheme`                | `tailwind.config.js`                                 | Extend Tailwind theme                                               |
| `ChakraProvider`             | Not needed                                           | Tailwind is build-time                                              |

## TypeScript Adoption

- Convert PropTypes to TypeScript interfaces.
- Add explicit return types to functions.
- Use `React.ComponentProps<'element'>` for HTML element props.
- Replace `PropTypes.shape({...})` with `interface` definitions.
