UI Components
Pre-installed shadcn/ui components in Next Starter, including theming with CSS variables, Tailwind CSS v4, the cn() utility, and how to add new components with the shadcn CLI.
Overview
Next Starter uses shadcn/ui as its component library. shadcn/ui is not a dependency you install. It is a collection of components copied directly into your codebase, giving you full ownership and control over every component.
All components live in components/ui/ and are styled with Tailwind CSS v4 using CSS variables for theming.
Configuration
The components.json file at the project root controls how shadcn/ui is configured:
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}Key settings:
- style:
new-york, the more compact, opinionated shadcn variant - cssVariables:
true, so components inherit theme colors from CSS variables defined inapp/globals.css - rsc:
true, so components work with React Server Components by default
Included Components
The following components ship in components/ui/:
| Component | File | Purpose |
|---|---|---|
| Accordion | accordion.tsx | Collapsible content sections |
| Alert | alert.tsx | Inline status messages |
| Alert Dialog | alert-dialog.tsx | Confirmation dialogs |
| Avatar | avatar.tsx | User profile images with fallback |
| Badge | badge.tsx | Small status labels |
| Breadcrumb | breadcrumb.tsx | Navigation trail |
| Button | button.tsx | Primary interactive element |
| Card | card.tsx | Content container with header/footer |
| Data Table | data-table.tsx | Sortable, searchable table with server-side pagination |
| Dialog | dialog.tsx | Modal overlay |
| Dropdown Menu | dropdown-menu.tsx | Contextual action menus |
| Form | form.tsx | React Hook Form integration |
| Input | input.tsx | Text input field |
| Label | label.tsx | Accessible form labels |
| Navigation Menu | navigation-menu.tsx | Top-level navigation |
| Select | select.tsx | Dropdown selection |
| Separator | separator.tsx | Visual divider |
| Sheet | sheet.tsx | Slide-in panel |
| Sidebar | sidebar.tsx | Full sidebar primitive |
| Skeleton | skeleton.tsx | Loading placeholder |
| Sonner | sonner.tsx | Toast notification wrapper |
| Spinner | spinner.tsx | Loading indicator |
| Switch | switch.tsx | Toggle control |
| Table | table.tsx | Structured data display |
| Textarea | textarea.tsx | Multi-line text input |
| Toggle | toggle.tsx | On/off button |
| Toggle Group | toggle-group.tsx | Group of mutually exclusive toggles |
| Tooltip | tooltip.tsx | Hover hint overlay |
Adding New Components
To add a component not yet in the project, use the shadcn CLI:
pnpm dlx shadcn@latest add [component]For example, to add the Calendar component:
pnpm dlx shadcn@latest add calendarThis copies the component source into components/ui/calendar.tsx and installs any required dependencies. You own the file and can edit it freely.
Browse all available components at ui.shadcn.com/docs/components.
The cn() Utility
All components use the cn() utility from lib/utils.ts to combine Tailwind classes. It merges clsx and tailwind-merge so conflicting utility classes resolve correctly.
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}Use it whenever you need to apply conditional or merged classes:
import { cn } from "@/lib/utils";
function StatusBadge({ active }: { active: boolean }) {
return (
<span
className={cn(
"rounded-full px-2 py-1 text-xs font-medium",
active ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-600",
)}
>
{active ? "Active" : "Inactive"}
</span>
);
}tailwind-merge handles conflicts like px-2 px-4 (the last one wins) while clsx handles conditional class objects and arrays.
Theming via CSS Variables
Components reference CSS variables rather than hardcoded colors. This is what makes light/dark mode work automatically. The variables are defined in app/globals.css:
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--primary: oklch(0.5657 0.183562 254.571);
--primary-foreground: oklch(0.985 0 0);
--muted: oklch(0.98 0 0);
--muted-foreground: oklch(0.556 0 0);
/* ... */
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
/* ... */
}Tailwind CSS v4 maps these variables to utility classes automatically. When you use bg-primary or text-muted-foreground, the component reads from these variables, so your entire UI updates when the theme switches.
To change the brand color across the entire app, update --primary in globals.css. All buttons, links, and accents inherit it immediately.
Dark Mode
Dark mode is handled by next-themes. The ThemeProvider wraps the app in app/layout.tsx and defaults to the system preference. The ThemeToggle component appears in the sidebar, header, footer, and mobile navigation, switching between light and dark mode.
No additional configuration is needed in components. Tailwind's dark: variant and the CSS variable approach handle everything.
Using Components
Import any component directly from its file in components/ui/:
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
export function ExampleCard() {
return (
<Card>
<CardHeader>
<CardTitle>Example</CardTitle>
</CardHeader>
<CardContent className="flex flex-col gap-4">
<Input placeholder="Enter a value" />
<Button>Submit</Button>
</CardContent>
</Card>
);
}Components follow the shadcn/ui new-york style: slightly smaller default sizes, tighter spacing, and border-radius consistent with the neutral base color palette.
Dashboard Layout
How the dashboard sidebar, breadcrumbs, mobile header, and nested layouts work together in Next Starter, including auth gating, admin-only nav items, and responsive behavior.
Forms
How Next Starter handles forms. Zod 4 validation schemas, Server Actions with reCAPTCHA, and React Hook Form client components wired together with shadcn/ui primitives.