Next Starter Logo
Components

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 in app/globals.css
  • rsc: true, so components work with React Server Components by default

Included Components

The following components ship in components/ui/:

ComponentFilePurpose
Accordionaccordion.tsxCollapsible content sections
Alertalert.tsxInline status messages
Alert Dialogalert-dialog.tsxConfirmation dialogs
Avataravatar.tsxUser profile images with fallback
Badgebadge.tsxSmall status labels
Breadcrumbbreadcrumb.tsxNavigation trail
Buttonbutton.tsxPrimary interactive element
Cardcard.tsxContent container with header/footer
Data Tabledata-table.tsxSortable, searchable table with server-side pagination
Dialogdialog.tsxModal overlay
Dropdown Menudropdown-menu.tsxContextual action menus
Formform.tsxReact Hook Form integration
Inputinput.tsxText input field
Labellabel.tsxAccessible form labels
Navigation Menunavigation-menu.tsxTop-level navigation
Selectselect.tsxDropdown selection
Separatorseparator.tsxVisual divider
Sheetsheet.tsxSlide-in panel
Sidebarsidebar.tsxFull sidebar primitive
Skeletonskeleton.tsxLoading placeholder
Sonnersonner.tsxToast notification wrapper
Spinnerspinner.tsxLoading indicator
Switchswitch.tsxToggle control
Tabletable.tsxStructured data display
Textareatextarea.tsxMulti-line text input
Toggletoggle.tsxOn/off button
Toggle Grouptoggle-group.tsxGroup of mutually exclusive toggles
Tooltiptooltip.tsxHover 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 calendar

This 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.

On this page