<!--
  Generated from docs/ — do not edit here; run `pnpm regen`.
  Canonical: https://cascivo.com/docs/migrating-from-shadcn.md
  registry v1.6.0 · generated 2026-10-02
-->

# Migrating from shadcn/ui to cascivo

cascivo's component and prop API is close to shadcn/ui, so most JSX ports over
with small renames. The two real differences are **variant names** and the
**CSS setup** (cascade layers + a theme, instead of Tailwind utility classes).
This page maps the deltas the way a migrator hits them.

## Component mapping

Every shadcn/ui component and its cascivo equivalent. Click a cascivo component
to open its doc page, which carries the exact `cascivo add` command, props, and
examples.

<!-- BEGIN shadcn-map (generated by scripts/migration/generate.ts) -->

_Generated from the [parity matrix](https://cascivo.com/docs/parity) — 58 of 59 shadcn/ui components have a cascivo equivalent. Each cascivo link carries the exact `cascivo add` command._

| shadcn/ui       | cascivo                                                                  | Notes                                                                   |
| --------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------- |
| Accordion       | [`accordion`](https://cascivo.com/docs/components/accordion)             |                                                                         |
| Alert           | [`alert`](https://cascivo.com/docs/components/alert)                     |                                                                         |
| Alert Dialog    | [`alert-dialog`](https://cascivo.com/docs/components/alert-dialog)       |                                                                         |
| Aspect Ratio    | [`aspect-ratio`](https://cascivo.com/docs/components/aspect-ratio)       |                                                                         |
| Avatar          | [`avatar`](https://cascivo.com/docs/components/avatar)                   |                                                                         |
| Badge           | [`badge`](https://cascivo.com/docs/components/badge)                     |                                                                         |
| Breadcrumb      | [`breadcrumb`](https://cascivo.com/docs/components/breadcrumb)           |                                                                         |
| Button          | [`button`](https://cascivo.com/docs/components/button)                   |                                                                         |
| Button Group    | [`button-group`](https://cascivo.com/docs/components/button-group)       |                                                                         |
| Calendar        | [`calendar`](https://cascivo.com/docs/components/calendar)               |                                                                         |
| Card            | [`card`](https://cascivo.com/docs/components/card)                       |                                                                         |
| Carousel        | [`carousel`](https://cascivo.com/docs/components/carousel)               |                                                                         |
| Chart           | `chart`                                                                  | chart family via @cascivo/charts                                        |
| Checkbox        | [`checkbox`](https://cascivo.com/docs/components/checkbox)               |                                                                         |
| Collapsible     | [`collapsible`](https://cascivo.com/docs/components/collapsible)         |                                                                         |
| Combobox        | [`combobox`](https://cascivo.com/docs/components/combobox)               |                                                                         |
| Command         | [`command-menu`](https://cascivo.com/docs/components/command-menu)       |                                                                         |
| Context Menu    | [`context-menu`](https://cascivo.com/docs/components/context-menu)       |                                                                         |
| Data Table      | [`data-table`](https://cascivo.com/docs/components/data-table)           |                                                                         |
| Date Picker     | [`date-picker`](https://cascivo.com/docs/components/date-picker)         |                                                                         |
| Dialog          | [`modal`](https://cascivo.com/docs/components/modal)                     |                                                                         |
| Direction       | _by convention_                                                          | RTL via CSS logical properties throughout; no JS provider               |
| Drawer          | [`sheet`](https://cascivo.com/docs/components/sheet)                     | sheet covers the panel; no mobile swipe gesture. drawer queued (v18-t6) |
| Dropdown Menu   | [`dropdown`](https://cascivo.com/docs/components/dropdown)               |                                                                         |
| Empty           | [`empty-state`](https://cascivo.com/docs/components/empty-state)         |                                                                         |
| Field           | [`field`](https://cascivo.com/docs/components/field)                     |                                                                         |
| Hover Card      | [`hover-card`](https://cascivo.com/docs/components/hover-card)           |                                                                         |
| Input           | [`input`](https://cascivo.com/docs/components/input)                     |                                                                         |
| Input Group     | [`input-group`](https://cascivo.com/docs/components/input-group)         |                                                                         |
| Input OTP       | [`otp-input`](https://cascivo.com/docs/components/otp-input)             |                                                                         |
| Item            | [`item`](https://cascivo.com/docs/components/item)                       |                                                                         |
| Kbd             | [`kbd`](https://cascivo.com/docs/components/kbd)                         |                                                                         |
| Label           | [`label`](https://cascivo.com/docs/components/label)                     |                                                                         |
| Menubar         | [`menubar`](https://cascivo.com/docs/components/menubar)                 |                                                                         |
| Native Select   | [`native-select`](https://cascivo.com/docs/components/native-select)     | Wraps the native control; cascivo `select` is the custom listbox.       |
| Navigation Menu | [`navigation-menu`](https://cascivo.com/docs/components/navigation-menu) |                                                                         |
| Pagination      | [`pagination`](https://cascivo.com/docs/components/pagination)           |                                                                         |
| Popover         | [`popover`](https://cascivo.com/docs/components/popover)                 |                                                                         |
| Progress        | [`progress-bar`](https://cascivo.com/docs/components/progress-bar)       | progress-bar + progress-circle                                          |
| Radio Group     | [`radio`](https://cascivo.com/docs/components/radio)                     |                                                                         |
| Resizable       | [`resizable`](https://cascivo.com/docs/components/resizable)             | a.k.a. splitter                                                         |
| Scroll Area     | [`scroll-area`](https://cascivo.com/docs/components/scroll-area)         |                                                                         |
| Select          | [`select`](https://cascivo.com/docs/components/select)                   |                                                                         |
| Separator       | [`separator`](https://cascivo.com/docs/components/separator)             |                                                                         |
| Sheet           | [`sheet`](https://cascivo.com/docs/components/sheet)                     |                                                                         |
| Sidebar         | [`side-nav`](https://cascivo.com/docs/components/side-nav)               |                                                                         |
| Skeleton        | [`skeleton`](https://cascivo.com/docs/components/skeleton)               |                                                                         |
| Slider          | [`slider`](https://cascivo.com/docs/components/slider)                   |                                                                         |
| Sonner          | [`toast`](https://cascivo.com/docs/components/toast)                     |                                                                         |
| Spinner         | [`spinner`](https://cascivo.com/docs/components/spinner)                 |                                                                         |
| Switch          | [`toggle`](https://cascivo.com/docs/components/toggle)                   |                                                                         |
| Table           | [`data-table`](https://cascivo.com/docs/components/data-table)           |                                                                         |
| Tabs            | [`tabs`](https://cascivo.com/docs/components/tabs)                       |                                                                         |
| Textarea        | [`textarea`](https://cascivo.com/docs/components/textarea)               |                                                                         |
| Toast           | [`toast`](https://cascivo.com/docs/components/toast)                     |                                                                         |
| Toggle          | [`toggle`](https://cascivo.com/docs/components/toggle)                   |                                                                         |
| Toggle Group    | [`toggle-group`](https://cascivo.com/docs/components/toggle-group)       |                                                                         |
| Tooltip         | [`tooltip`](https://cascivo.com/docs/components/tooltip)                 |                                                                         |
| Typography      | [`prose`](https://cascivo.com/docs/components/prose)                     | prose + text + heading                                                  |

<!-- END shadcn-map -->

## Layout names that invert the ecosystem convention

Two layout primitives are named against what Chakra, MUI and Radix taught you. Both are
deliberate, both are flagged in their TSDoc — but the TSDoc only helps once you have already
picked the component, and by then you have usually written the markup. So, before you pick:

| cascivo   | What you probably expect                                                                                                         | What it actually does                                                                                                                              |
| --------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `<Flex>`  | a **row** — CSS `flex-direction` defaults to `row`, and so do Chakra's `<Flex>`, MUI's `<Stack direction>`, and Radix's `<Flex>` | defaults to **`direction="vertical"`**. Pass `direction="horizontal"` for a row.                                                                   |
| `<Stack>` | a **spacing stack** — a column with a gap, which is what `<Stack>` means in Chakra and MUI                                       | an **overlap primitive**: children are layered on top of one another (think `position: relative` + absolutely-stacked children), not spaced apart. |

**What you want instead:**

| Goal                                  | Use                                        |
| ------------------------------------- | ------------------------------------------ |
| A row of items with a gap             | `<Flex direction="horizontal" gap={3}>`    |
| A column of items with a gap          | `<Flex gap={3}>` (vertical is the default) |
| Elements layered on top of each other | `<Stack>`                                  |

A 2026-07-28 adopter's summary: the JSDoc "caught us before runtime — but the names still
invert two of the strongest conventions in the ecosystem." The names are staying (renaming
them now would break every existing app for a naming preference); this table exists so the
inversion is findable while you are _choosing_ a component rather than while hovering one.

---

## CSS setup delta

shadcn relies on Tailwind: you copy a `globals.css` with `@tailwind` directives
and a big block of CSS variables, and style with utility classes. cascivo ships
real stylesheets — import the themes once and theme with a `data-theme`
attribute. Component CSS comes along with each component import (tree-shaken per
component by your bundler), so there's no component stylesheet to wire up.
There is **no Tailwind dependency and no `tailwind.config`** to port:

```tsx
// shadcn: Tailwind directives + utility classes in markup
// cascivo: one themes import, then plain components
import '@cascivo/themes/light-dark.css' // tokens once + base typography + light & dark
// component CSS (@layer cascivo.component) auto-included on import
```

```tsx
<main data-theme="light">
  <Button>Save</Button>
</main>
```

- Styles live in cascade layers (`cascivo.base < cascivo.theme < cascivo.component`).
  Your own **unlayered** CSS always wins — see `CSS-LAYERS-PITFALL.md`.
- Design tokens are `--cascivo-*` custom properties, enumerated in `TOKENS.md`
  (and `@cascivo/tokens/tokens.json` for tooling). No `tailwind.config` to mirror.

## Button variants

cascivo's Button variants are **not** shadcn's. There is **no `outline`**.

| shadcn variant | cascivo variant  | Notes                                     |
| -------------- | ---------------- | ----------------------------------------- |
| `default`      | `primary`        | the filled, primary action                |
| `secondary`    | `secondary`      | same name                                 |
| `outline`      | `secondary`      | no bordered-only variant — use secondary  |
| `ghost`        | `ghost`          | same name                                 |
| `destructive`  | `destructive`    | same name                                 |
| `link`         | `ghost` + `Link` | use the `Link` component for link styling |

```tsx
// shadcn
<Button variant="default">Save</Button>
<Button variant="outline">Cancel</Button>

// cascivo
<Button variant="primary">Save</Button>
<Button variant="secondary">Cancel</Button>
```

`size` (`sm | md | lg`), `loading`, and `disabled` carry over; Button spreads
`ButtonHTMLAttributes`.

## Form fields come with their own label/hint/error

shadcn composes `FormField` + `FormItem` + `FormLabel` + `FormMessage` around
each control. cascivo inputs (`Input`, `Textarea`, `Select`, …) accept
`label`, `hint`, and `error` directly, removing the wrapper boilerplate:

```tsx
// shadcn: ~6 wrapper components per field
// cascivo:
<Textarea label="Bio" hint="Markdown supported" error={errors.bio} />
```

For full forms, `Field` and the signal-based `createForm`/`useForm` store cover
validation without a resolver library.

## App shell / sidebar

Don't hand-roll the shell. `AppShell` wires `ShellHeader` + `SideNav` + content
into one sticky-header, full-height-nav, single-scroll-container layout, with the
header burger bound to the nav, an animated (and `prefers-reduced-motion`-aware)
show/hide, `inert`/focus handling, and a mobile drawer:

```tsx
<AppShell header={<ShellHeader brand={{ name: 'Acme' }} />} nav={<SideNav items={items} />}>
  <h1>Dashboard</h1>
</AppShell>
```

## Theming: CSS variables → tokens

shadcn's theme is one flat layer of semantic CSS variables (`--background`,
`--primary`, `--radius`) plus a `.dark` class that overrides them. cascivo uses a
three-tier token system — primitive → semantic → component — and scopes themes
with a `data-theme` attribute instead of a class, so a theme can apply to **any
subtree**, not just the document root.

| shadcn                                     | cascivo                                                        |
| ------------------------------------------ | -------------------------------------------------------------- |
| `--background` / `--foreground`            | `--cascivo-color-bg` / `--cascivo-color-text`                  |
| `--primary` / `--primary-foreground`       | `--cascivo-color-accent` / `--cascivo-color-accent-foreground` |
| `--muted` / `--muted-foreground`           | `--cascivo-color-surface` / `--cascivo-color-text-subtle`      |
| `--border`                                 | `--cascivo-color-border`                                       |
| `--radius`                                 | `--cascivo-radius-*` (control/surface/full)                    |
| `.dark { … }`                              | `[data-theme="dark"]` (or `warm`, plus 9 more)                 |
| `next-themes` `ThemeProvider` / `useTheme` | `@cascivo/react` `ThemeProvider` / `useTheme`                  |

```css
/* shadcn: override the flat variables under .dark */
.dark {
  --primary: 210 40% 98%;
}

/* cascivo: retint by scope — no rebuild, works on any element */
[data-theme='dark'] {
  --cascivo-color-accent: oklch(0.7 0.15 250);
}
```

The runtime switcher maps 1:1 in concept, but **the hook shape differs** — don't copy
next-themes' destructuring:

| next-themes                                                            | cascivo                                                                                 |
| ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `const { theme, setTheme } = useTheme()` (object; `theme` is a string) | `const [theme, setTheme] = useTheme()` (tuple; `theme` is a `string` — use it directly) |

cascivo's `useTheme()` returns a `[theme, setTheme]` tuple where `theme` is a plain **`string`**
(the current theme name — use it directly, e.g. `theme === 'dark'`), not a `{ theme, setTheme }`
object. The hook is signal-backed and calls `useSignals()` for you, so the component re-renders
on theme changes; for signal-native code use the `themeSignal()` export.
`themePreloadScript()` covers the SSR pre-paint script
you'd otherwise hand-write. See [THEMING.md](/docs/theming.md#switching-themes-at-runtime).

Full catalog in [TOKENS.md](/docs/tokens.md); brand a single component by overriding
its component-tier tokens (see [THEMING.md](/docs/theming.md)).

## Styling: `cn()` and utility classes → data attributes

There is no `cn()` / `clsx` / `tailwind-merge` in cascivo, because there are no
utility classes to merge. Variants and states are props that map to
`data-*` attributes the component's own CSS targets — so conditional styling is
data, not class-string concatenation:

```tsx
// shadcn: cn() composes utility classes for each state
<button className={cn('inline-flex …', variant === 'ghost' && 'bg-transparent', disabled && 'opacity-50')} />

// cascivo: props → data-attributes, styled in the component's CSS
<Button variant="ghost" disabled />   // renders data-variant="ghost" [disabled]
```

To extend a component you own (copied in via the CLI), edit its `.module.css`
directly — no `@apply`, no config.

## Forms: react-hook-form → createForm

shadcn wires forms with react-hook-form + a zod resolver + the `Form*` component
family. cascivo ships a signal-based store, `createForm` / `useForm`, with
built-in sync/async validation — no resolver library. `form.field(name)` returns
the `value`/`onChange`/`onBlur`/`error` to wire onto any control:

```tsx
// shadcn: useForm (RHF) + zodResolver + <FormField> render props
// cascivo:
const form = useForm({
  initialValues: { email: '' },
  validate: (v) => (v.email.includes('@') ? {} : { email: 'Invalid email' }),
})
const email = form.field('email')
<Form form={form} onValid={console.log}>
  <Input
    label="Email"
    value={email.value}
    onChange={(e) => email.onChange(e.currentTarget.value)}
    onBlur={email.onBlur}
    error={email.error}
  />
  <Button type="submit">Save</Button>
</Form>
```

## Charts: Recharts → @cascivo/charts

shadcn charts wrap Recharts (an SVG React chart lib you install as a dependency).
`@cascivo/charts` is built from scratch — its own scales and shapes, signal-driven,
**zero runtime dependencies**, CVD-safe palettes, and keyboard-navigable tooltips:

```tsx
// shadcn: <ChartContainer> around Recharts <AreaChart>/<Area>
// cascivo:
import { AreaChart } from '@cascivo/charts'
;<AreaChart series={series} x={(d) => d.date} y={(d) => d.value} />
```

The chart families map closely (area, bar, line, pie, radar, radial); cascivo adds
many Recharts doesn't ship (candlestick, sankey, treemap, sunburst, funnel, gauge,
boxplot, and more). See the [charts overview](https://cascivo.com/docs/charts).

## What else exists

Before hand-rolling a component, check the index — cascivo ships heavy ones that
are easy to miss: `DataTable` (sort/filter/paginate/select/expand), `CommandMenu`
(⌘K), `EmptyState`, `Stat`, `DataList`, `Combobox`, `MultiSelect`, and more. The
full categorized list is in the [`@cascivo/react` README](https://github.com/cascivo/cascivo/tree/main/packages/react#readme)
and at [cascivo.com](https://cascivo.com).
