Subchapter 5.8
resources/migration-guide.mdMarkdown11 KBView on GitHub
This guide helps you migrate from other UI libraries to shadcn/ui.
Gradually replace components over time:
Pros: Low risk, can be done alongside feature work Cons: Temporary bundle size increase
Replace all components at once:
Pros: Clean cutover, no mixed UI Cons: High risk, requires dedicated time
If you need to support RTL languages (like Arabic or Hebrew) in an existing shadcn/ui project:
npx shadcn@latest migrate rtlThis CLI command transforms your components to use logical properties:
ml-4 -> ms-4 (margin-start)pl-4 -> ps-4 (padding-start)text-left -> text-startIt ensures your UI adapts correctly to layout direction without manual refactoring.
| MUI Component | shadcn/ui Equivalent | Notes |
|---|---|---|
| Button | Button | Similar API |
| TextField | Input + Label | Separate components |
| Select | Select | Different structure |
| Dialog | Dialog | Similar concept |
| Drawer | Sheet | Side panel |
| Card | Card | Very similar |
| Table | Table | Use with TanStack Table |
| Checkbox | Checkbox | Similar API |
| Switch | Switch | Similar API |
| Tabs | Tabs | Similar structure |
| Tooltip | Tooltip | Simpler API |
| Menu | Dropdown Menu | Different trigger model |
| Snackbar | Toast | Different implementation |
| Autocomplete | Combobox | Use with Command |
1. Import Structure
// MUI
import Button from '@mui/material/Button'
// shadcn/ui
import { Button } from '@/components/ui/button'2. Form Components
// MUI
<TextField
label="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
error={!!errors.email}
helperText={errors.email}
/>
// shadcn/ui
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
{errors.email && (
<p className="text-sm text-destructive">{errors.email}</p>
)}
</div>
// Or with Form component
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>3. Theming
// MUI
import { ThemeProvider, createTheme } from '@mui/material/styles'
const theme = createTheme({
palette: {
primary: { main: '#1976d2' },
},
})
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
// shadcn/ui
// Edit globals.css
:root {
--primary: 215 100% 50%;
}4. Styling Approach
// MUI (sx prop)
<Button sx={{ px: 4, py: 2, borderRadius: 2 }}>
Click me
</Button>
// shadcn/ui (Tailwind classes)
<Button className="px-4 py-2 rounded-lg">
Click me
</Button>Before (MUI):
import { TextField, Button, Box } from '@mui/material'
export function LoginForm() {
return (
<Box component="form" sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<TextField label="Email" type="email" required />
<TextField label="Password" type="password" required />
<Button variant="contained" type="submit">
Sign In
</Button>
</Box>
)
}After (shadcn/ui):
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Button } from '@/components/ui/button'
export function LoginForm() {
return (
<form className="flex flex-col gap-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" required />
</div>
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input id="password" type="password" required />
</div>
<Button type="submit">Sign In</Button>
</form>
)
}| Chakra UI | shadcn/ui | Notes |
|---|---|---|
| Button | Button | Similar variants |
| Input | Input | More basic |
| Select | Select | Different structure |
| Modal | Dialog | Similar concept |
| Drawer | Sheet | Very similar |
| Box | div | Use Tailwind classes |
| Flex | div | Use flex utilities |
| Stack | div | Use space-y-* classes |
| Text | p/span | Use typography classes |
| Heading | h1/h2/etc | Use typography classes |
| useToast | useToast | Different API |
| Menu | Dropdown Menu | Similar |
1. Layout Components
// Chakra UI
<Stack spacing={4} direction="column">
<Box>Item 1</Box>
<Box>Item 2</Box>
</Stack>
// shadcn/ui
<div className="flex flex-col space-y-4">
<div>Item 1</div>
<div>Item 2</div>
</div>2. Responsive Styles
// Chakra UI
<Box display={{ base: 'block', md: 'flex' }} />
// shadcn/ui
<div className="block md:flex" />3. Color Mode
// Chakra UI
import { useColorMode } from '@chakra-ui/react'
const { colorMode, toggleColorMode } = useColorMode()
// shadcn/ui (with next-themes)
import { useTheme } from 'next-themes'
const { theme, setTheme } = useTheme()| Ant Design | shadcn/ui | Notes |
|---|---|---|
| Button | Button | Similar |
| Input | Input | More basic |
| Form | Form | Different approach |
| Table | Table | Use TanStack Table |
| Modal | Dialog | Similar |
| Drawer | Sheet | Similar |
| Select | Select | Different API |
| DatePicker | Calendar + Popover | More manual |
| Menu | Navigation Menu | Different |
| message | Toast | Different API |
| notification | Toast | Similar concept |
1. Form Handling
// Ant Design
<Form
form={form}
onFinish={onSubmit}
>
<Form.Item name="email" rules={[{ required: true }]}>
<Input />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">Submit</Button>
</Form.Item>
</Form>
// shadcn/ui (with react-hook-form)
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>2. Notifications
// Ant Design
import { message } from 'antd'
message.success('Success!')
// shadcn/ui
import { useToast } from '@/components/ui/use-toast'
const { toast } = useToast()
toast({
title: "Success!",
description: "Operation completed.",
})| Bootstrap | shadcn/ui | Notes |
|---|---|---|
| btn | Button | Similar variants |
| form-control | Input | Similar |
| card | Card | Very similar structure |
| modal | Dialog | Different API |
| dropdown | Dropdown Menu | Similar concept |
| nav/navbar | Navigation Menu | Different |
| alert | Alert | Similar |
| badge | Badge | Similar |
| table | Table | Use with TanStack Table |
1. Class-Based vs Component-Based
// Bootstrap
<button className="btn btn-primary btn-lg">
Click me
</button>
// shadcn/ui
<Button variant="default" size="lg">
Click me
</Button>2. Cards
<!-- Bootstrap -->
<div class="card">
<div class="card-header">Title</div>
<div class="card-body">Content</div>
<div class="card-footer">Footer</div>
</div>
<!-- shadcn/ui -->
<Card>
<CardHeader>
<CardTitle>Title</CardTitle>
</CardHeader>
<CardContent>Content</CardContent>
<CardFooter>Footer</CardFooter>
</Card>shadcn/ui requires Tailwind. Ensure:
tailwind.config.js includes correct content pathsglobals.csstailwindcss-animate)Components use @/ imports. Configure:
tsconfig.json with path aliasesDon’t force shadcn/ui to work like your old library. Embrace the new patterns:
shadcn/ui form components are basic. For complex forms, use:
react-hook-form for form statezod for validationForm component for integrationWhile shadcn/ui is accessible by default, custom modifications can break this. Test with:
After migration: