The Nightly Build.
Expert Tools

shadcn/ui gives custom UI a source code boundary

The strongest starting point is not the library with the prettiest demo; it is the layer your team can change without fighting its API.

The prettiest component is the one your team can still change

The familiar component-library failure is not that the button looks bad. It is that the button looks almost right. A product designer asks for a different focus ring, a denser menu, a confirmation dialog with one extra region, or a DOM order the library did not anticipate. The team wraps the imported component, adds a prop, then adds a prop to the wrapper. Soon the public API of someone else's package is the architecture of the product.

That is why the best tooling for beautiful, custom UI is not a single winner. It is a deliberate boundary between three things: the visual surface your team owns, the difficult interaction behavior it borrows, and the place where every state can be inspected. shadcn/ui's documentation states the ownership idea more plainly than most marketing pages: “This is not a component library. It is how you build your component library.”1

The distinction matters in September 2026 because shadcn/ui is now a source-distribution layer with several possible behavior foundations. New projects default to Base UI, while Radix remains supported and React Aria is a first-class base.23 The recommendation is therefore less “pick this component catalog” and more “decide which part of a component you are willing to own.”

shadcn/ui starts with a local visual surface

A conventional package gives you a component through an import boundary. shadcn/ui gives you a component by writing source into the application. The difference is practical: the file in src/components/ui is no longer an implementation detail hidden behind a version number. It is a local part of the product, available to a code review, a design-system refactor, or a small, opinionated change to the markup.1

a starting point, then a local componentSHELL
pnpm dlx shadcn@latest init
pnpm dlx shadcn@latest add button dialog

# The visual surface now belongs to the app.
# Edit src/components/ui/button.tsx when the product needs a new rule.

pnpm dlx shadcn@latest diff button
The CLI distributes source and can show upstream changes; it is not a replacement for reviewing those changes.4

This is a better default for a bespoke product than a wrapper around a closed visual vocabulary. It is also a transfer of work. The shadcn/ui CLI documentation is explicit that copied components need manual updates. Before applying one, commit the local version, inspect the diff, and decide whether upstream's fix still fits the component your team has changed.4 Source ownership removes a package's visual boundary by moving merge decisions into the application.

The behavior layer is the decision that matters

Once the visual files are local, the next question is what not to reimplement. Menus, popovers, dialogs, comboboxes and date controls carry keyboard navigation, focus management, ARIA relationships, touch behavior and—in some cases—internationalization. A headless or unstyled foundation can supply that difficult behavior while leaving CSS, layout and component composition in your hands. Base UI documents exactly that boundary: no bundled CSS, WAI-ARIA patterns, and open component APIs that let an application add or remove parts.5

The useful comparison is the ownership boundary, not a leaderboard of defaults.15678
Starting point It owns Use it when The cost
shadcn/ui Source-distributed visual components You want the UI files in the product repository Your team owns edits, drift and merges
Base UI Unstyled React behavior You want CSS and DOM control without starting interaction logic from zero You must design and maintain the visual layer
Radix Low-level React primitives You have an existing Radix vocabulary or want incremental adoption Its parts and composition model become your local convention
React Aria Unstyled components, hooks and behavior Internationalization and assistive-technology behavior are central The composition model asks for more deliberate DOM and styling work
Ark UI / Zag Headless state-machine logic with adapters The same behavior must travel across framework boundaries Adapters and a lower-level mental model add surface area
MUI / Mantine / Chakra Styled or prop-driven application components Breadth and time-to-product beat a blank visual surface You work inside an established styling vocabulary

Base UI is the sensible shadcn/ui default for a new React project that wants to own its styles. It comes from the maintainers behind Radix, Material UI and Floating UI, and its open APIs expose the component nodes rather than sealing them behind a finished skin.5 That does not make it a visual system. It gives the team a sturdy place to make one.

Radix is still the right answer when a team already has Radix primitives in production or values its familiar parts, focus behavior and incremental adoption. The July change in shadcn/ui is not a reason to rewrite a working system: Radix remains supported, and the primitive library itself is still designed to be accessible, unstyled and customizable.26

React Aria deserves the deliberate choice when the interface has serious internationalization requirements or when moving between a ready component and a lower-level hook is valuable. Its documentation emphasizes unstyled components and hooks, built-in interaction behavior, and a roughly one-to-one relationship between components and DOM elements so the developer can control order and layout.37 Ark UI and Zag take the same separation further: the behavior is modeled as framework-agnostic state-machine logic, and framework adapters render the result.89

None of those choices makes an application automatically accessible. A library can supply roles, focus mechanics and interaction patterns; the product still has to provide labels, sensible order, contrast, content, tests and a complete state model. “Headless” means you own the surface. It does not mean the surface is finished.

Styled libraries are valid starting points

There is a temptation to treat a styled library as a failure of taste. That is the wrong comparison. Tailwind is a styling system, not a component library: its utility classes keep structure and styling together, constrain values to a chosen design system, and make state and responsive variants local to the source.10 Pairing those constraints with source-owned components is powerful, but it still asks a team to make the decisions that turn tokens into a coherent product.

Tailwind's Catalyst makes the same trade in a more curated package of source. Its team describes the model with the useful phrase “Your components, not ours”: copy the pieces into the app, then make them yours.11 The appeal is not that a source file is automatically beautiful. The appeal is that a good default can disappear behind the system you build from it. If the starting code remains recognizable as a library six months later, the team has adopted a visual dependency, not a design language of its own.

Catalyst is the polished answer for a team already committed to Tailwind and willing to start from a strong visual point of view. Its documentation positions it as a React UI kit to copy into the project, with Headless UI, Motion and clsx in the surrounding toolchain.1112 That makes it a good accelerator for an admin surface or a product that needs a convincing first pass quickly. It also makes its assumptions part of the project: Tailwind's theme, its component composition, and the maintenance of code you have copied.

MUI is the more comprehensive speed-and-breadth option. Its Material UI package supplies a large, production-oriented React catalog with a deep customization system.13 Mantine advertises more than 120 customizable components and 70 hooks, which is a persuasive reason to use it when the product needs many controls before it needs a bespoke visual language.14 Chakra makes styling props, compositions, CSS variables and dark mode a first-class workflow.15

These are not lesser choices. They are choices to optimize for a different constraint: shipping a broad application surface now, then customizing inside a library's vocabulary. Brad Frost's warning is useful here: “atomic design is not rigid dogma.”16 Do not turn “source-owned” or “headless” into a doctrine either. If a pre-styled system lets the team ship the product, and its theming boundary is honest enough for the work, it may be the most responsible foundation.

Storybook is the feedback loop your fork needs

A copied component becomes a system only when the team can see and exercise its states. Storybook is useful here not because it styles anything, but because it provides an isolated workshop for components and pages, including hard-to-reach states and edge cases. Its documentation also treats testing and documentation as part of the same surface, and says it can be adopted incrementally.17

This is the UI version of a broader engineering habit. Mitchell Hashimoto's rule is short: “The goal is to always give yourself a good demo.”18 For a dialog, the demo is not only the happy path. It is the long title, the validation error, the empty result, the keyboard path, the narrow viewport, the dark theme and the loading state. Put those into stories before the team copies the component into three product flows.

The same loop keeps the ownership decision honest. If a Base UI or Radix primitive is hard to render in a story, the problem may be your composition rather than the primitive. If a MUI or Mantine component cannot express the product's states without an escape hatch, the library boundary may be too tight. If a shadcn file has accumulated six local variants with no explanation, the source is yours—but the system is not yet designed.

Start with stories for the components that have behavior, not for every atom on day one. The goal is a small, living catalogue where a visual change can be inspected beside its focus, disabled, error and responsive states. That catalogue is what makes future customization cheaper than the first customization.

Sources

  1. shadcn/ui · Introduction
  2. shadcn/ui · Base UI as the default
  3. shadcn/ui · React Aria base
  4. shadcn/ui · New CLI and update workflow
  5. Base UI · About Base UI
  6. Radix · Primitives introduction
  7. Adobe · React Aria getting started
  8. Ark UI · About
  9. Zag · Headless component machines
  10. Tailwind CSS · Styling with utility classes
  11. Tailwind CSS · Introducing Catalyst
  12. Catalyst · Getting started
  13. MUI · Material UI overview
  14. Mantine · React components and hooks
  15. Chakra UI · Styling overview
  16. Brad Frost · Extending Atomic Design
  17. Storybook · Build, test and document components
  18. Mitchell Hashimoto · Building large technical projects