The Copy-Paste Component Library Is a Strange New Species
shadcn/ui is a strange new species — not a library, not a framework. Here's what it gets right, what it gives up, and when to choose ownership over dependency.
shadcn/ui is not a component library in the traditional sense. You do not install it. You run npx shadcn@latest add button and it writes source code into your repository. The component is now yours — not a versioned dependency, not a black box, not something you need to wait on a maintainer to fix. This is either liberating or terrifying depending on how your team works, and the right answer is not obvious.
Why it exists
The version dependency hell in React UI libraries is real. Upgrading a major version of a component library often means breaking changes to APIs, class names, behavior, and sometimes peer dependencies. Teams end up pinned to old versions because the cost of upgrading is too high. Or they maintain a fork that drifts from upstream. Neither outcome is good.
shadcn/ui sidesteps this entirely. When you copy the code, you own it. There is no upstream to track. There is no breaking-changes.md to read before running npm install. The component does what it does, forever, until you change it.
This is built on Radix UI primitives (for accessibility and behavior) and Tailwind CSS (for styling). The copy-paste layer is thin — it is mostly the styling decisions that shadcn makes that you would have made yourself. The Radix dependency is still there in your package.json. Only the top layer is owned.
What it gets right
Modification is first-class. Every other component library makes modification second-class. You either override styles with specificity battles, extend components through props, or abandon the library and write your own. shadcn assumes you will modify. The component code is in your repo, in your editor, with your linter running on it.
You can read it. When something breaks, you open the file. You do not dig through node_modules. You do not read a minified bundle. The code is right there, 50-100 lines, with your own comments if you added them.
The defaults are good. The choices shadcn makes — Tailwind utility classes, Radix for behavior, cn() utility for conditional classes, a consistent className prop — are all defensible. You can disagree with them, but they are not arbitrary.
What it gives up
No automatic updates. When Radix ships a bug fix, you get it (it is still a dependency). When shadcn ships an improved version of a component, you do not. You have to re-run the add command, diff the output, and manually reconcile with your modifications. In practice, most teams never do this.
Maintenance burden scales with your footprint. If you copy 30 components, you own 30 components. Accessibility regressions, API improvements, browser compatibility fixes — all of these are now your problem. A small team using 30 shadcn components is effectively maintaining a component library.
Fork divergence is silent. There is no tooling that tells you "you are 8 months behind the upstream button component." Your version works, so you do not notice. Until you go to add a new component that assumes a newer version of a utility, and things subtly break.
When this model is the right call
Copy-paste is the right approach when your design system diverges significantly from defaults. If you know your Button needs 12 custom variants, a specific focus ring, animation on hover, and a loading state with a spinner — just own it from day one. Starting with a versioned library and fighting the API is worse.
It is also the right call for prototypes and marketing sites that will not be maintained long-term. The setup cost is lower, the time-to-ship is faster, and the maintenance concern is someone else's future problem.
When a versioned dependency is better
If your team does not have a dedicated design engineer, a versioned dependency is almost always better. The maintenance cost of owned components is invisible until it hits. A library with a changelog, semantic versioning, and a community of bug reporters is worth the occasional upgrade friction.
Large teams with strict accessibility requirements should think carefully. A versioned library like React Aria or a commercial library with an accessibility support contract gives you a legal and technical paper trail. "We use shadcn/ui" means "we own this accessibility behavior" — which is fine if you know what you're doing, and risky if you don't.
The honest take
shadcn/ui is excellent for what it is: a collection of well-considered starting points for components you will customize. The copy-paste model is not a workaround for versioning problems — it is a deliberate design decision that trades upgrade convenience for code ownership. Know what you are opting into. If your team reviews the component code before shipping it, owns the accessibility testing, and has a plan for divergence, it is a good trade. If components get copied and forgotten, it accumulates debt silently.