Tailwind v4 + shadcn/ui in 2026: the design system that works
Tailwind v4 for utilities, shadcn/ui for components. Why this combo is the SaaS default in 2026 and how to wire it well.
Israel Palma
3 min read
In 2026 the "component library or utilities?" debate is over. The answer is **Tailwind v4 for
utilities + shadcn/ui for components**. Not a trade-off: two pieces that fit together.
This guide explains why this combo became the default and how to wire it well into a Next.js SaaS.
## Tailwind v4: what's new that matters
Tailwind v4 (2025) brought strong changes:
- **Rust engine** (Lightning CSS): builds ~10x faster
- **Config in CSS** (no more `tailwind.config.js` except edge cases)
- **Native CSS variables** for design tokens
- **`@theme`** to declare your palette directly in CSS
Example `globals.css`:
```css
@import 'tailwindcss';
@theme {
--color-brand: #d62828;
--font-display: 'Inter', sans-serif;
--radius-card: 0.75rem;
}
```
Anywhere:
```tsx
```
No config, no extra imports.
## shadcn/ui: not a library, not a boilerplate
shadcn/ui is a weird and great system. It's not a library you install (npm install). It's
**components you copy** into your repo.
```bash
bunx shadcn@latest add button
bunx shadcn@latest add dialog
```
The command creates `src/shared/components/ui/button.tsx` in your repo. From there, it's yours: you
modify it, extend it, delete it. No update breaks because there's no package to version.
Pros:
- You have the source code, you control it
- No library lock-in
- Zero extra runtime weight
- Trivial customization
Con: if a component updates, you decide manually whether to merge the changes.
## Both in a SaaS
My typical flow in a new project:
1. **Tailwind v4** installed and configured (5 minutes)
2. **shadcn/ui** init: `bunx shadcn@latest init`
3. **Add 5-10 core components**: Button, Input, Dialog, Tabs, Select, Card, DataTable, Form
4. **Done**: I have the base design system
For domain-specific components (a BlogPostCard, a ProjectKanbanColumn), I write them myself with
Tailwind directly. For everything "platform" (buttons, inputs, modals), shadcn/ui.
## Pattern: extend, don't copy
If you need Button variations, don't create PrimaryButton, SecondaryButton, DangerButton by copying
code. Extend:
```tsx
import { Button } from '@/shared/components/ui/button';
export function DangerButton(props: React.ComponentProps) {
return ;
}
```
Or add a variant to `button.tsx` with `cva`. Single source of truth.
## Dark mode
Tailwind v4 + shadcn/ui ship dark mode built in. With `next-themes`:
```tsx
import { ThemeProvider } from 'next-themes';
{children}
;
```
And every shadcn component respects `dark:` automatically. No extra CSS.
## Common mistakes
**1. Mixing shadcn with MUI / Chakra**: if you have a half-Material-UI codebase, don't add shadcn on
top. Pick one and migrate. Two systems duplicate everything.
**2. Over-customizing `button.tsx`**: if you write 800 lines of variants, "code my way" becomes
"code I'm afraid to touch". Keep it small.
**3. Not using CSS variables for tokens**: if your brand uses `#D62828` hardcoded in 47 places,
rebrand day is misery. Define it in `@theme`.
**4. Forgetting the Tailwind Prettier plugin**: without it, long class strings become hell.
`prettier-plugin-tailwindcss` sorts classes automatically.
## When NOT to use shadcn/ui
- Big team with an in-prod design system already — keep yours
- App with exotic design (complex animations, non-standard layout) — shadcn is pragmatic, not
creative
- You need very specific components (complex calendars, charts) — use a specialized library for
those
For 90% of indie SaaS, shadcn/ui covers the base.
## Bottom line
Tailwind v4 + shadcn/ui is the combo with the best speed / control / maintenance ratio for SaaS
in 2026. Setup in an afternoon, code under your control, coherent design system.
If your boilerplate still rolls Bootstrap or MUI, look at shadcn. The DX gap from day 1 is real.
Enjoyed this article?
Subscribe for more tutorials and tips on building products with AI