Design systems

Migrating from MUI to shadcn/ui: order of work and real costs

Migrating from MUI to shadcn/ui has a fixed order and a cost counted in files, not components: tokens, base components, layout, then advanced components.

Key points

  • The order is tokens, base components, layout, advanced components: inverting it means rewriting twice.
  • The cost is measured in files that import `@mui/material`, not in the number of available components.
  • The catalogue indexes 153 MUI components and 61 shadcn components as of 1 November 2026: comparisons start from records.
  • shadcn/ui is not versioned and does not cover MUI X data grids: that is the limitation to state before starting.

Migrating from MUI to shadcn/ui follows a fixed order: tokens, base components, layout, advanced components. Each phase closes on one real screen and is measured before the next starts; the cost is not the number of components, it is the number of files that import MUI.

MUI is not a component list: it brings a theme, the `sx` prop, a grid and a style system. shadcn/ui is not a package: it brings code you copy into the project. The migration changes who owns the styles, not just the syntax.

The order of work, in five steps

  • 1. Inventory: count the files that import `@mui/material` and the distinct components. Without that number, every estimate is an opinion.
  • 2. Tokens: move colour, spacing and radii into CSS variables before touching a component. Tokens are the only layer that survives the migration.
  • 3. Base components: Button, Input, Label and Dialog first. They cover most screens and expose the theme problems early.
  • 4. Layout: replace Grid and Stack with CSS Grid and flex where the layout is simple. Keep the abstraction only where it pays for itself.
  • 5. Advanced components: tables, date pickers and autocomplete last. They cost more and have fewer equivalents.

Why tokens come before components

If you migrate components before tokens, you rewrite every button twice: once to make it work, once to make it match the design. Tokens are the only part that does not change when the component changes.

From `sx` to semantic tokens

The `sx` prop wins because it is fast, but it ties colour to the file. With CSS variables the component asks for `--ui-bg` and does not know which brand uses it. The mapping is written once and checked with a contrast test.

From Grid to native layout

The MUI Grid is an abstraction over flex and grid. If the layout is a marketing page, native CSS Grid is shorter. If it is a dashboard with twelve widgets, the abstraction may still pay off: measure it on one screen before deciding.

The real costs, item by item

ItemWhat you rewriteSignal in the codeCost
TokensColour, spacing, radii, typographyColours written inside `sx`Once, before anything else
BaseButton, Input, Label, DialogImports from `@mui/material`Per file, mechanical
LayoutGrid, Stack, Box`sx={{ display: ... }}`, `Grid item`High when the layout is nested
AdvancedTable, Autocomplete, DatePickerMUI X packagesHigh: few equivalents
VerificationFocus, keyboard, screen readerExisting tests and checklistsRequired, not optional

The first component to migrate

Start with a component that uses three things: state, focus and an icon. A Button with an icon and a disabled state is a good test; a Dialog is the second. If both pass without touching the layout, the rest is repetition.

# 1. file che importano MUI: è il numero che decide il costo
rg -l "@mui/material|@mui/system" src | wc -l

# 2. componenti importati, ordinati per frequenza
rg -o 'from "@mui/material/([A-Za-z]+)"' -r '$1' src | sort | uniq -c | sort -rn

# 3. sostituisci un componente su un ramo, non tutto il sito
npx shadcn@latest init
npx shadcn@latest add button dialog input label

The inventory command counts files, not components: that is the number that sets the cost. If it returns twelve, the migration is one review; if it returns two hundred, split it by area and put it on a plan.

When to stop

Stop if the product is Material by choice, the team knows the API and the screens already agree. Stop also if you lean on MUI X: shadcn/ui has no equivalent of the Data Grid, and rewriting it costs more than the theme you are leaving.

Named limitation: shadcn/ui is not a versioned package. The code is yours, but accessibility and compatibility updates are yours to track. MUI, with all its flaws, sends you a changelog.

The starting records are /components/lib/mui with 153 components and /components/lib/shadcn with 61; buttons live in /components/cat/button. The comparison between approaches is alternatives to MUI, measured, the criteria are in how to choose a UI library, the bundle cost in UI library bundle size and the copy-paste model in copy-paste or headless. Licence is the easy part: shadcn/ui is MIT, Radix is MIT, Tailwind is MIT; the MUI core is MIT and MUI X is commercial.

Frequently asked questions

How much does migrating from MUI to shadcn/ui cost?

It costs the number of files that import MUI multiplied by the time to rebuild one screen; there is no single price. Count the files with the inventory command first, then decide.

Which component comes first?

Tokens first, then Button, Input and Dialog. Tokens stop you rewriting the same component twice, and the two base components expose focus and theme problems early.

Can I migrate one page at a time?

Yes, but only after the tokens are in place. Two themes in parallel double the style bugs and make it impossible to tell which system won.

Is shadcn/ui free?

Yes, the code ships under the MIT licence and lands in your repository. The cost is not the licence but the maintenance: you track the updates.

What does shadcn/ui not cover?

Advanced data grids and complex components. MUI X offers them under a commercial licence, and for those the migration costs more than the theme you are leaving.