
Building frontends these days can feel a bit overwhelming with options like Material UI, Chakra UI, Ant Design and countless others, it’s easy to assume that choosing a polished component library will automatically speed up your development. Install a package, pick a theme, and you’re sorted. In practice, you often end up fighting the library instead of building the thing you actually care about. That’s where shadcn/ui feels different. This post looks at why shadcn/ui fits so well with Next.js 15 and Tailwind CSS 4, and why I prefer its copy-and-own model for iterating and building frontends quickly for side projects and POC's.
The first surprise with shadcn/ui is that you do not install it as a normal dependency. There is no single package in node_modules that controls all your UI. Instead you run a CLI command and copy actual component source files into your project.
npx shadcn-ui@latest init
npx shadcn-ui@latest add button card dialog
After that the components live in your repository. They are written in TypeScript, styled with Tailwind and structured around Radix primitives. If you want to change a Dialog, you open dialog.tsx and edit it like any other component.
This small shift changes the relationship between you and your UI kit. You are not working around a black box. You own the code.
Shadcn/ui leans on two tools that already have strong adoption in modern React projects:
Radix gives you headless primitives that handle keyboard support, ARIA attributes and focus management. Tailwind gives you utility classes that keep styling close to the markup. Shadcn brings these together into ready-made components.
You get:
When you are working on a product for a local business in Sandton or a student portal for a university, that clarity saves time. A junior developer can inspect a Button component, see the Tailwind classes and adapt them without hunting through a theme object.
Traditional libraries give you a theme file and a list of props for customisation. You can only change what the library exposes. When design requirements shift, you either stack overrides on top of overrides, or you fork the library.
With shadcn/ui:
If the design system at your company in Gauteng changes button radiuses, you update one file and it is done. There is no need to wait for a library update or to dig through GitHub issues to see if someone else asked for the same thing.
You also only include the components you actually use. If your project never needs a Combobox, it never enters the bundle.
Recent releases of shadcn/ui added more structural components that sit above the low-level primitives. These are not just pretty wrappers. They encode patterns that show up in many apps:
Instead of rewriting the same layout and ARIA attributes for every form field, you rely on a Field component that already captures good practice.
Next.js 15 and Tailwind CSS 4 give shadcn/ui a solid foundation.
@theme directive so your colour and spacing decisions live in CSS, not just in a JavaScript config.A typical stack looks like this:
You might define brand tokens like this:
@theme {
--color-primary: #1d4ed8;
--color-primary-foreground: #ffffff;
}
Then connect them to shadcn/ui by adjusting Tailwind classes in your Button and Card components. If a client in Durban wants a different palette, you update the tokens and a few classes instead of rewriting a theme object deep inside a library.
Shadcn/ui tends to work well when:
Here is a minimal layout using shadcn/ui components in a Next.js project:
import { Button } from "@/components/ui/button";
import { Card, CardHeader, CardContent } from "@/components/ui/card";
export default function DashboardPage() {
return (
<main className="min-h-screen bg-background text-foreground">
<section className="mx-auto flex max-w-5xl flex-col gap-6 px-4 py-10">
<header className="flex items-center justify-between">
<h1 className="text-2xl font-semibold">
Client Accounts
</h1>
<Button>
Add client
</Button>
</header>
<Card>
<CardHeader>
<h2 className="text-lg font-medium">
Overview
</h2>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">
Here you can see current balances, trends and account status for your clients.
</p>
</CardContent>
</Card>
</section>
</main>
);
}
The code is ordinary React. The styling is plain Tailwind. The components are yours.
If you find yourself wrestling with your component library instead of shipping features, try shadcn/ui on your next Next.js and Tailwind project. Copy a few components in, connect them to your theme tokens and see how it feels to truly own your UI.