shadcn/ui: the day I stopped fighting my UI library

4 min read
shadcn-ui/ui

I have a confession. For years I picked UI libraries the way you pick a barber in a new city: read two reviews, walk in, hope for the best. If the buttons looked wrong, I blamed myself. If the theme system felt like reading someone else’s diary, I assumed I wasn’t smart enough.

Then one afternoon I saw a tweet about shadcn/ui. The pitch was so simple I almost scrolled past: the components live in your repo, not in node_modules. You copy them. You own them. That’s it.

I thought: that can’t possibly work. A week later I was using it in production.

The moment everything clicked

The first time you run the CLI, it feels almost anticlimactic.

npx shadcn@latest init
npx shadcn@latest add button card dialog

Files appear in your project. Not compiled bundles. Not minified garbage. Actual TypeScript files with Tailwind classes you can read at 2 AM without squinting. I opened the Button component, understood it in thirty seconds, changed the border radius, and moved on with my life.

That never happened to me with Chakra. With Chakra I’d spend an hour in the theme docs, convince myself I understood the extendTheme API, then watch my changes get silently overridden by some internal style precedence I never signed up for.

Why people actually star this thing

A hundred thousand stars is a weird number. People don’t star a README. They star relief.

Here’s what I think they’re relieved about:

The old worldWhat shadcn/ui does differently
Upgrade the library, pray nothing breaksYou decide when to change your own files
Theming requires a PhD in the library’s mental modelTokens sit right next to the component
Debugging means reading compiled source mapsBreakpoints land in your code

The trick isn’t “zero dependencies.” It’s fewer secrets. Fewer places where your app’s look and feel is someone else’s compiled decision that you can’t inspect without a decompiler.

import { Button } from "@/components/ui/button";

export function Submit() {
  return <Button variant="outline">Ship it</Button>;
}

That import path is your project. Not a package. Not a prayer.

The part nobody talks about

There is a cost. You now own this code. When Radix pushes a breaking change to the accessibility primitives underneath, nobody sends you a PR. You update it yourself, or you don’t. For a solo developer that’s fine. For a team of twenty, you need someone to pay attention.

I’ve seen teams copy components once and never touch them again for a year. The code works, but it slowly drifts from upstream improvements. That’s a tradeoff, not a flaw. But you should know it going in.

The other thing: the ecosystem around shadcn/ui is getting loud. Starter kits, paid templates, “shadcn-compatible” badge on every new library. The noise is a compliment — people only argue about tools worth stealing — but it also means you’ll wade through a lot of marketing to find what’s genuinely useful.

The honest summary

shadcn/ui didn’t make me a better designer. It made me stop fighting my tools. The gap between “I want this button to look different” and “this button looks different” went from forty-five minutes of theme archaeology to three minutes of editing a file I own.

If you’ve ever stared at a ThemeProvider wrapper wondering why your border color won’t change, you already know why this repo has six figures of stars. It’s not magic. It’s the opposite of magic. It’s your code, in your repo, doing what you told it to do.

That shouldn’t feel revolutionary. But it does.


shadcn-ui/ui · MIT · 110k stars · docs