Getting Started with shadcn/ui: A Practical Guide
Alex Mercer
- 4 minutes read - 720 wordsIf you have built a React app in the last couple of years, you have probably run into shadcn/ui. It is one of those tools that looks deceptively simple at first and then quietly changes how you think about component libraries. This post is a short, practical guide to getting it running in a real project.
It is not a component library (really)
The first thing worth understanding is what shadcn/ui actually is, because it does not work like the libraries you may be used to. There is no big package that lives in node_modules and that you import a <Button> from. Instead, when you ask for a component, the CLI copies the actual source code into your project. The button you end up importing is your code:
import { Button } from "@/components/ui/button";That sounds like a small detail, but it is the whole point. You own every line. When you need a variant the maintainers never imagined, you just edit the file. There is no fighting against a closed API and no waiting for an upstream release.
Prerequisites
You need Node.js installed and a React framework to drop components into. shadcn/ui supports Next.js, Vite, TanStack Start, React Router, Astro, and Laravel. Styling is built on Tailwind CSS v4, which the setup step configures for you, so you do not need to wire it up by hand.
Step 1: Initialize
Run init from the root of your project. The CLI package is simply shadcn now (older tutorials reference shadcn-ui, which was renamed; if you see “command not found”, that is why).
npx shadcn@latest initThis installs the dependencies, adds a small cn utility for merging class names, sets up your CSS variables, and writes a components.json file that records your choices. You will be asked for a base color (neutral, gray, zinc, stone, or slate). If you would rather skip the prompts, npx shadcn@latest init -d uses sensible defaults.
If you do not have a project yet, the CLI can scaffold one for you with a template:
npx shadcn@latest init -t nextStep 2: Add components
Now pull in whatever you need. Components are added by name, and you can list several at once:
npx shadcn@latest add button card dialogEach one lands in components/ui/ as plain, readable source. Before committing to anything, it is worth previewing what a command will write. The --dry-run flag shows you the plan without touching a single file:
npx shadcn@latest add button --dry-runStep 3: Use it
Import the component the same way you would any local module and render it:
import { Button } from "@/components/ui/button";
export default function Page() {
return (
<div className="p-8">
<Button>Click me</Button>
<Button variant="outline">Or this</Button>
</div>
);
}Because the code is yours, opening components/ui/button.tsx and reading exactly how those variants are defined takes about thirty seconds. If you want a new one, add it there.
Theming
Theming runs through CSS variables defined during init, so changing your palette rarely means touching component files. Beyond the base color, recent versions ship presets, which bundle a whole design system (colors, fonts, radius) behind a single code. You can apply one when you initialize:
npx shadcn@latest init --preset novaYou can also choose your underlying primitives. shadcn/ui builds on either Radix UI or Base UI, selectable with --base radix or --base base, depending on which accessibility primitives you prefer.
A few CLI commands worth knowing
The CLI does more than init and add. A handful of commands save real time:
npx shadcn@latest searchfinds components across registries.npx shadcn@latest view <component>prints the source so you can read before adding.npx shadcn@latest docs <component>pulls docs, examples, and API links.npx shadcn@latest inforeports your framework, version, CSS variables, and which components are installed.
That last one is genuinely useful when you come back to a project months later and cannot remember what you set up.
Wrapping up
The mental shift with shadcn/ui is going from “install a dependency” to “add starter code you fully control.” It takes one project to get used to, and after that the idea of components you cannot open and edit starts to feel strange. Start with init, add a button, read its source, and you will have the whole model in your head within an afternoon.