Migrating to useVyre
Step-by-step guides for moving your project to useVyre from another design system. Pick your starting point below.
General migration steps
These steps apply regardless of where you're coming from.
- Install useVyre — tokens package is always required, plus the framework package for React or Vue.
npm install @usevyre/tokens @usevyre/react
# or with all AI tooling bundled
npm install @usevyre/react-all npm install @usevyre/tokens @usevyre/vue
# or with all AI tooling bundled
npm install @usevyre/vue-all - Import styles at your app entry point — tokens CSS + component styles.
// main.tsx
import "@usevyre/tokens/css";
import "@usevyre/react/styles"; // main.ts
import "@usevyre/tokens/css";
import "@usevyre/vue/styles"; - Add AI context so your coding agent knows every valid prop and variant — no more hallucinated class names.
# Generate agent context file in one command
npx @usevyre/ai-context init --claude # → CLAUDE.md
npx @usevyre/ai-context init --cursor # → .cursor/rules
npx @usevyre/ai-context init --windsurf # → .windsurf/rules - Replace components gradually. useVyre can coexist with your old system during migration — you don't need to do it all at once. Start with high-traffic components like Button, Input, and Modal.
npx @usevyre/validate-ai-context src/ after
migration to catch any leftover invalid props before they reach production.
Migrate from...
shadcn/ui and useVyre share the same philosophy — components you own, copy-paste friendly, built on accessible primitives. The main difference: useVyre uses semantic props instead of Tailwind class strings, so your AI agent can't invent classes that don't exist.
Install
# Remove shadcn (optional — can coexist during migration)
npm uninstall @shadcn/ui
# Install useVyre
npm install @usevyre/tokens @usevyre/react Component mapping
| shadcn/ui | useVyre |
|---|---|
Button variant="destructive" | Button variant="danger" |
Button variant="outline" | Button variant="secondary" |
Button variant="default" | Button variant="primary" |
Alert variant="destructive" | Alert variant="danger" |
Dialog | Modal |
Sheet | Sheet ✓ same name |
Toaster (setup) | ToastProvider |
useToast | useToast ✓ same hook |
DropdownMenu | DropdownMenu ✓ same name |
Command | Command ✓ same name |
Skeleton | Skeleton ✓ same name |
Separator | Separator ✓ same name |
Tabs | Tabs ✓ same name |
Badge variant="secondary" | Badge variant="default" |
Button
shadcn/ui
// shadcn/ui
import { Button } from "@/components/ui/button";
<Button variant="destructive" size="sm">Delete</Button>
<Button variant="outline">Cancel</Button>
<Button variant="ghost" size="icon"><TrashIcon /></Button> useVyre
// useVyre
import { Button } from "@usevyre/react";
<Button variant="danger" size="sm">Delete</Button>
<Button variant="secondary">Cancel</Button>
<Button variant="ghost" size="icon" aria-label="Delete"><TrashIcon /></Button> Alert
shadcn/ui
// shadcn/ui
import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
<Alert variant="destructive">
<AlertTitle>Error</AlertTitle>
<AlertDescription>Something went wrong.</AlertDescription>
</Alert> useVyre
// useVyre — Alert is a single component with props
import { Alert } from "@usevyre/react";
<Alert variant="danger" title="Error">
Something went wrong.
</Alert> Dialog → Modal
shadcn/ui
// shadcn/ui
import {
Dialog, DialogContent, DialogHeader,
DialogTitle, DialogDescription, DialogFooter,
} from "@/components/ui/dialog";
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Confirm</DialogTitle>
<DialogDescription>Are you sure?</DialogDescription>
</DialogHeader>
<DialogFooter>...</DialogFooter>
</DialogContent>
</Dialog> useVyre
// useVyre — Modal with built-in title + onClose
import { Modal } from "@usevyre/react";
<Modal open={open} onClose={() => setOpen(false)} title="Confirm" size="sm">
<p>Are you sure?</p>
</Modal> Toast
shadcn/ui
// shadcn/ui — Toaster + useToast hook
import { useToast } from "@/components/ui/use-toast";
const { toast } = useToast();
toast({ title: "Saved!", variant: "default" }); useVyre
// useVyre — same hook pattern, different setup
import { useToast } from "@usevyre/react";
const { toast } = useToast();
toast({ title: "Saved!", variant: "success" });
// Required: wrap root with ToastProvider (same as shadcn's Toaster)
import { ToastProvider } from "@usevyre/react";
<ToastProvider><App /></ToastProvider> Tokens: Tailwind classes → CSS variables
shadcn/ui uses Tailwind utility classes that map to CSS variables. useVyre exposes those CSS variables directly — no Tailwind required.
shadcn/ui
/* shadcn/ui — Tailwind classes */
<div className="bg-primary text-primary-foreground rounded-md p-4"> useVyre
/* useVyre — CSS variables, no Tailwind required */
<div style={{
background: "var(--vyre-color-semantic-accent)",
color: "var(--vyre-color-semantic-on-accent)",
borderRadius: "var(--vyre-radius-md)",
padding: "var(--vyre-spacing-4)",
}}> MUI uses Emotion for styling and a JavaScript ThemeProvider for customization. useVyre replaces that with pure CSS variables — zero runtime, zero provider, theming via CSS overrides.
Install
npm uninstall @mui/material @emotion/react @emotion/styled
npm install @usevyre/tokens @usevyre/react Component mapping
| MUI | useVyre |
|---|---|
Button variant="contained" color="primary" | Button variant="primary" |
Button variant="contained" color="error" | Button variant="danger" |
Button variant="outlined" | Button variant="secondary" |
Button variant="text" | Button variant="ghost" |
Alert severity="error" | Alert variant="danger" |
Alert severity="warning" | Alert variant="warning" |
Alert severity="success" | Alert variant="success" |
Alert severity="info" | Alert variant="info" |
Dialog | Modal |
Drawer | Sheet |
Chip | Badge |
CircularProgress | Progress (use variant for color) |
LinearProgress | Progress |
Tooltip | Tooltip ✓ same name |
Skeleton | Skeleton ✓ same name |
Switch | Switch ✓ same name |
Slider | Slider ✓ same name |
Pagination | Pagination ✓ same name |
Snackbar | Toast via useToast() |
ThemeProvider | CSS variable overrides in :root |
Button
MUI
// MUI
import Button from "@mui/material/Button";
<Button variant="contained" color="error" size="small">Delete</Button>
<Button variant="outlined">Cancel</Button>
<Button variant="text">Learn more</Button> useVyre
// useVyre
import { Button } from "@usevyre/react";
<Button variant="danger" size="sm">Delete</Button>
<Button variant="secondary">Cancel</Button>
<Button variant="ghost">Learn more</Button> Alert
MUI
// MUI
import Alert from "@mui/material/Alert";
<Alert severity="error">Something went wrong.</Alert>
<Alert severity="warning" onClose={handleClose}>Low storage.</Alert> useVyre
// useVyre
import { Alert } from "@usevyre/react";
<Alert variant="danger">Something went wrong.</Alert>
<Alert variant="warning" onClose={handleClose}>Low storage.</Alert> Dialog → Modal
MUI
// MUI
import { Dialog, DialogTitle, DialogContent, DialogActions } from "@mui/material";
<Dialog open={open} onClose={handleClose} maxWidth="sm">
<DialogTitle>Confirm</DialogTitle>
<DialogContent>Are you sure?</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
</DialogActions>
</Dialog> useVyre
// useVyre
import { Modal, Button } from "@usevyre/react";
<Modal open={open} onClose={handleClose} title="Confirm" size="sm">
<p>Are you sure?</p>
<Button variant="secondary" onClick={handleClose}>Cancel</Button>
</Modal> Theming: ThemeProvider → CSS variables
useVyre needs no JavaScript provider for theming. Override semantic CSS variables in your stylesheet and every component updates automatically.
MUI
// MUI — ThemeProvider + createTheme
import { ThemeProvider, createTheme } from "@mui/material/styles";
const theme = createTheme({ palette: { primary: { main: "#7c3aed" } } });
<ThemeProvider theme={theme}><App /></ThemeProvider> useVyre
/* useVyre — override CSS variables directly, no provider needed */
:root {
--vyre-color-semantic-accent: #7c3aed;
--vyre-color-semantic-accent-hover: #6d28d9;
} Tailwind UI gives you unstyled markup with utility classes. useVyre trades the class-string approach for semantic component props — less to type, nothing for your AI agent to hallucinate.
Install
# useVyre doesn't require Tailwind — you can remove it,
# or keep it alongside useVyre during a gradual migration.
npm install @usevyre/tokens @usevyre/react Component mapping
| Tailwind UI / Headless UI | useVyre |
|---|---|
Custom className button | Button variant prop |
Dialog (Headless UI) | Modal |
Disclosure (Headless UI) | Accordion |
Listbox (Headless UI) | Select |
Combobox (Headless UI) | Command |
Switch (Headless UI) | Switch ✓ same name |
Popover (Headless UI) | Popover ✓ same name |
Tooltip (Headless UI) | Tooltip ✓ same name |
bg-primary text-white classes | var(--vyre-color-semantic-accent) |
rounded-lg | var(--vyre-radius-lg) |
shadow-md | var(--vyre-shadow-md) |
p-4 | var(--vyre-spacing-4) |
Button
Tailwind UI
// Tailwind UI — custom styled button
<button
className="rounded-md bg-violet-600 px-4 py-2 text-sm font-semibold
text-white hover:bg-violet-500 disabled:opacity-50"
>
Save changes
</button> useVyre
// useVyre — semantic props, no class lists
import { Button } from "@usevyre/react";
<Button variant="accent">Save changes</Button> Dialog (Headless UI) → Modal
Headless UI
// Headless UI
import { Dialog } from "@headlessui/react";
<Dialog open={open} onClose={setOpen}>
<Dialog.Panel className="...">
<Dialog.Title className="...">Confirm</Dialog.Title>
...
</Dialog.Panel>
</Dialog> useVyre
// useVyre — accessibility built in, no class config
import { Modal } from "@usevyre/react";
<Modal open={open} onClose={() => setOpen(false)} title="Confirm">
...
</Modal> Tokens: utility classes → CSS variables
useVyre's semantic tokens are CSS variables. They work in inline styles, plain CSS files, CSS Modules, or SCSS — no Tailwind config needed.
Tailwind UI
/* Tailwind — arbitrary color values or config */
<div className="bg-violet-600 text-white rounded-lg p-4 shadow-md"> useVyre
/* useVyre — semantic CSS variables, theme-aware */
<div style={{
background: "var(--vyre-color-semantic-accent)",
color: "var(--vyre-color-semantic-on-accent)",
borderRadius: "var(--vyre-radius-lg)",
padding: "var(--vyre-spacing-4)",
boxShadow: "var(--vyre-shadow-md)",
}}>
/* Or use a Card component */
import { Card, CardBody } from "@usevyre/react";
<Card variant="elevated"><CardBody>...</CardBody></Card> Validate after migration
Run the validator on your codebase to catch any invalid props or leftover patterns from the old system:
# Scan your source directory for invalid useVyre prop usage
npx @usevyre/validate-ai-context src/
# Output as JSON for CI integration
npx @usevyre/validate-ai-context src/ --json