shadcn/ui Setup
Install, configure, and theme shadcn/ui - copy-paste components built on Radix UI and Tailwind CSS.
Search across all documentation pages
Install, configure, and theme shadcn/ui - copy-paste components built on Radix UI and Tailwind CSS.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Quick-reference recipe card - copy-paste ready.
# Initialize shadcn in your project
npx shadcn@latest init
# Add specific components
npx shadcn@latest add button
npx shadcn@latest add input
npx shadcn@latest add card
# Add multiple at once
npx shadcn@latest add button input card dialog form// components.json - generated by init
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"tailwind": {
"config": "",
"css": "app/globals.css",
"baseColor": "zinc",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}// lib/utils.ts - the cn utility
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}When to reach for this: At the start of any Next.js or React project where you want polished, accessible UI components with full control over the source code.
/* app/globals.css - shadcn theme with CSS variables */
@import "tailwindcss";
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
}
@layer base {
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--radius: 0.625rem;
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--primary: oklch(0.985 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.269 0 0);
--input: oklch(0.269 0 0);
--ring: oklch(0.439 0 0);
}
}// app/layout.tsx
import { ThemeProvider } from "next-themes";
import "@/app/globals.css";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body className="bg-background text-foreground antialiased">
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
{children}
</ThemeProvider>
</body>
</html>
);
}// Quick demo using shadcn components
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
export function LoginCard() {
return (
<Card className="w-[350px]">
<CardHeader>
<CardTitle>Login</CardTitle>
<CardDescription>Enter your credentials to continue.</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="you@example.com" />
</div>
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input id="password" type="password" />
</div>
<Button className="w-full">Sign in</Button>
</CardContent>
</Card>
);
}What this demonstrates:
ThemeProvider setup from next-themescn() utility for merging class namescomponents/ui/ and are fully editablecn() utility merges class names with clsx and resolves Tailwind conflicts with tailwind-mergeCustom theme colors:
/* Use the shadcn theme generator at ui.shadcn.com/themes */
:root {
--primary: oklch(0.5 0.2 260); /* custom blue */
--destructive: oklch(0.5 0.2 25); /* custom red */
}Adding a component with dependencies:
# Some components auto-install their dependencies
npx shadcn@latest add dialog
# This installs @radix-ui/react-dialog and the Dialog componentPath aliases setup:
// tsconfig.json
{
"compilerOptions": {
"paths": {
"@/*": ["./*"]
}
}
}// cn utility is fully typed
import { cn } from "@/lib/utils";
cn("bg-red-500", undefined, false, "text-white");
// returns "bg-red-500 text-white"
// Component props extend Radix types
// e.g., Button extends React.ButtonHTMLAttributes<HTMLButtonElement>
import { Button, type ButtonProps } from "@/components/ui/button";
function MyButton(props: ButtonProps) {
return <Button variant="outline" {...props} />;
}Not a package - it is source code - npm install shadcn is wrong. Fix: Use npx shadcn@latest add <component> to copy components into your project.
Tailwind v4 compatibility - shadcn/ui supports Tailwind v4. Make sure to run the latest npx shadcn@latest init which generates the v4-compatible CSS.
CSS variables must be defined - If you skip the CSS variable setup, components render with no colors. Fix: Ensure your globals.css includes the full :root and .dark variable blocks.
cn() import path - Components import from @/lib/utils. If your alias differs, update components.json and run init again.
Component updates - Since components are copied, they do not auto-update. Fix: Re-run npx shadcn@latest add <component> to get the latest version (but this overwrites your changes).
| Alternative | Use When | Don't Use When |
|---|---|---|
| Radix UI (bare) | You want primitives without opinions on styling | You want pre-styled, ready-to-use components |
| Headless UI | You use Tailwind but prefer a different primitive library | You want the full shadcn ecosystem |
| MUI / Chakra UI | You want a fully packaged component library | You want full control over component source code |
| Mantine | You want a batteries-included React component library | You use Tailwind for styling |
No. shadcn/ui copies component source code into your project. Use npx shadcn@latest add <component>, not npm install shadcn.
components.json with project settingslib/utils.ts with the cn() utilityglobals.css for themingcn() combines clsx (conditional class merging) with tailwind-merge (Tailwind conflict resolution). It ensures className overrides work correctly on shadcn components.
In components/ui/ by default. Each component is a fully editable source file, not a node_modules dependency.
Edit the CSS variables in globals.css under :root (light) and .dark (dark). Use the theme generator at ui.shadcn.com/themes for quick presets.
CSS variables are defined twice -- once in :root for light mode and once in .dark for dark mode. Use next-themes with attribute="class" to toggle the .dark class.
npx shadcn@latest add button input card dialog formThe CSS variables are missing. Ensure globals.css includes the full :root and .dark variable blocks generated by npx shadcn@latest init.
Re-running npx shadcn@latest add <component> overwrites the file. Back up your customizations before updating, or use version control.
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}ClassValue accepts strings, arrays, objects, undefined, null, and booleans.
import { Button, type ButtonProps } from "@/components/ui/button";
function MyButton(props: ButtonProps) {
return <Button variant="outline" {...props} />;
}Component props extend their underlying Radix or HTML element types.
Reviewed by Chris St. John·Last updated Jul 16, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥