Website Markup Using Shadcn/UI
Shadcn/UI is not an npm package, but a collection of components that can be copied, based on Radix UI Primitives and Tailwind CSS. Components are added to the project via CLI — the source code ends up directly in the repository and can be modified without forks. This fundamentally distinguishes Shadcn/UI from traditional libraries.
Initialization
npx shadcn@latest init
The CLI asks about style (default/new-york), base color, and utilities alias. The configuration is saved to components.json:
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/styles/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}
Adding components:
npx shadcn@latest add button card dialog form input select table tabs
Each command creates a file in src/components/ui/.
Color System via CSS Variables
/* src/styles/globals.css */
@import "tailwindcss";
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222 47% 11%;
--card: 0 0% 100%;
--card-foreground: 222 47% 11%;
--popover: 0 0% 100%;
--popover-foreground: 222 47% 11%;
--primary: 221 83% 53%; /* #2563eb */
--primary-foreground: 0 0% 100%;
--secondary: 210 40% 96%;
--secondary-foreground: 222 47% 11%;
--muted: 210 40% 96%;
--muted-foreground: 215 16% 47%;
--accent: 210 40% 96%;
--accent-foreground: 222 47% 11%;
--destructive: 0 84% 60%;
--destructive-foreground: 0 0% 100%;
--border: 214 32% 91%;
--input: 214 32% 91%;
--ring: 221 83% 53%;
--radius: 0.5rem;
}
.dark {
--background: 222 47% 7%;
--foreground: 210 40% 98%;
--card: 222 47% 11%;
--card-foreground: 210 40% 98%;
--popover: 222 47% 11%;
--popover-foreground: 210 40% 98%;
--primary: 217 91% 60%;
--primary-foreground: 0 0% 100%;
--secondary: 217 33% 17%;
--secondary-foreground: 210 40% 98%;
--muted: 217 33% 17%;
--muted-foreground: 215 20% 65%;
--accent: 217 33% 17%;
--accent-foreground: 210 40% 98%;
--destructive: 0 63% 31%;
--destructive-foreground: 210 40% 98%;
--border: 217 33% 17%;
--input: 217 33% 17%;
--ring: 217 91% 60%;
}
}
Out-of-the-Box Components: Usage Examples
Dialog with Form
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
const ContactDialog = () => (
<Dialog>
<DialogTrigger asChild>
<Button>Contact Us</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Get in Touch</DialogTitle>
<DialogDescription>
Fill out the form and we'll respond within 24 hours.
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-4 py-4">
<div className="flex flex-col gap-2">
<Label htmlFor="name">Name</Label>
<Input id="name" placeholder="John Doe" />
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="[email protected]" />
</div>
</div>
<DialogFooter>
<Button type="submit" className="w-full">Send</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
Form with React Hook Form + Zod
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
const formSchema = z.object({
email: z.string().email('Invalid email'),
name: z.string().min(2, 'Minimum 2 characters'),
subject: z.string().min(5, 'Minimum 5 characters'),
});
type FormValues = z.infer<typeof formSchema>;
const LeadForm = () => {
const form = useForm<FormValues>({ resolver: zodResolver(formSchema) });
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-5">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input placeholder="John Doe" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input type="email" placeholder="[email protected]" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" className="w-full">
Submit
</Button>
</form>
</Form>
);
};
Cards with Tabs
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
const PricingTabs = () => (
<Tabs defaultValue="monthly" className="mx-auto max-w-2xl">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="monthly">Monthly</TabsTrigger>
<TabsTrigger value="annual">Annual</TabsTrigger>
</TabsList>
<TabsContent value="monthly">
<Card>
<CardHeader>
<CardTitle>Monthly Subscription</CardTitle>
<CardDescription>Pay each month, cancel anytime.</CardDescription>
</CardHeader>
<CardContent>
{/* Plan content */}
</CardContent>
</Card>
</TabsContent>
<TabsContent value="annual">
<Card>
<CardHeader>
<CardTitle>Annual Subscription</CardTitle>
<CardDescription>Save 20% compared to monthly plans.</CardDescription>
</CardHeader>
<CardContent>
{/* Plan content */}
</CardContent>
</Card>
</TabsContent>
</Tabs>
);
Component Customization
Since the component code is in the project, you can modify it directly:
// src/components/ui/button.tsx — add a variant
const buttonVariants = cva(
'inline-flex items-center justify-center ...',
{
variants: {
variant: {
default: '...',
// Add a new variant
gradient: 'bg-gradient-to-r from-brand-500 to-violet-600 text-white hover:opacity-90',
},
},
}
);
Updating Components
# Check for updates
npx shadcn@latest diff
# Update a specific component
npx shadcn@latest add button --overwrite
Timeline
Shadcn/UI initialization and basic theme setup: 1–2 hours. Adding and customizing needed components: 2–4 hours. Landing page with Shadcn/UI + Tailwind + React: 1–2 days. Shadcn/UI is well-suited for SaaS products, admin panels, and any projects where you need professional UI with full control over component code.







