Shadcn UI: A Front-End Developer’s Deep Dive into a Modern UI Toolkit

As a front-end dev, I’ve been diving into Shadcn UI—a minimalist toolkit that blends power and control. It’s a fresh take on UI libraries, perfect for modern workflows.
The Shadcn Philosophy: Less Is More
Shadcn UI isn’t here to hand you a bloated, opinionated design system. Instead, it’s a "bring-your-own-components" toolkit built on Tailwind CSS and Radix UI primitives. You get pre-styled, accessible components as a starting point—not a finish line. The source code gets copied into your project, meaning you own it entirely. No black-box dependencies, no runtime CSS-in-JS overhead—just lean, maintainable code.
What Sets It Apart?
Zero Runtime Styling: Unlike Chakra UI (Emotion) or MUI (JSS), Shadcn leans on Tailwind and CSS variables. No JavaScript bloat parsing styles at runtime.
Radix Under the Hood: Built on Radix UI’s headless components, it ensures accessibility and flexibility without dictating your UI.
Bundle Size: At ~45kb, it’s a featherweight compared to MUI’s 300kb+ or even Chakra’s 120kb.
TypeScript-First: Ships with tight TypeScript integration, which is a godsend for type safety.
Getting Started: Beyond the Basics
Let’s set it up in a Next.js project and push it further than a simple button. I’ll assume you’ve got a basic Next.js app with Tailwind already configured (if not, check the Tailwind docs).
Installation
Initialize Shadcn:
npx shadcn-ui@latest init
This scaffolds a components/ui directory and tweaks your tailwind.config.js to include CSS variables (optional but recommended). Add some components:
npx shadcn-ui@latest add button dropdown-menu dialog
Anatomy of a Component
Here’s what the generated button.tsx looks like (simplified):
import { cva, type VariantProps } from "class-variance-authority";
import { forwardRef } from "react";
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
outline: "border border-input hover:bg-accent",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, ...props }, ref) => {
return (
<button
className={buttonVariants({ variant, size, className })}
ref={ref}
{...props}
/>
);
}
);
Button.displayName = "Button";
export { Button, buttonVariants };
What’s happening here? CVA (Class Variance Authority): A utility for defining variant-based styles in a type-safe way. ForwardRef: Ensures the component can handle refs, which is clutch for focus management or animations. Tailwind-Driven: The base styles and variants use Tailwind classes, keeping things predictable.
Building Something Real
Let’s create a dropdown-triggered dialog—a common UI pattern that shows off Shadcn’s composability.
// app/components/Header.tsx
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
export default function Header() {
return (
<header className="p-4 border-b">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">Menu</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<Dialog>
<DialogTrigger asChild>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
Open Settings
</DropdownMenuItem>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Settings</DialogTitle>
<DialogDescription>
Customize your app preferences here.
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<label className="block">
Theme
<select className="mt-1 block w-full rounded-md border p-2">
<option>Light</option>
<option>Dark</option>
</select>
</label>
<Button>Save</Button>
</div>
</DialogContent>
</Dialog>
<DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Logout</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</header>
);
}
Read More:
https://nimajanbaz.dev/blog/shadcn-ui-a-front-end-developers-deep-dive-into-a-modern-ui-toolkit
Subscribe to my newsletter
Read articles from Nima Janbaz directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
