DEV Community

Atlas Whoff
Atlas Whoff

Posted on

shadcn/ui: Building a Design System Without Writing a Design System

shadcn/ui: Building a Design System Without Writing a Design System

Component libraries trap you in their styling system. shadcn/ui copies components directly into your project — you own the code, Tailwind handles the styling, and customization is just editing a file.

What Makes It Different

Traditional Library shadcn/ui
Installed as npm package Copied into your src/
Styled by the library Styled with Tailwind in your code
Limited customization Full control, it's your file
Version lock-in No lock-in, it's your code

Setup

npx shadcn@latest init
# Choose: TypeScript, Tailwind, CSS variables
# Sets up components.json and globals.css
Enter fullscreen mode Exit fullscreen mode

Add Components

npx shadcn@latest add button
npx shadcn@latest add dialog
npx shadcn@latest add form
npx shadcn@latest add table
npx shadcn@latest add toast
Enter fullscreen mode Exit fullscreen mode

Each command copies the component to src/components/ui/.

Usage

import { Button } from '@/components/ui/button';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';

export function ConfirmDialog({ onConfirm }: { onConfirm: () => void }) {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button onClick={() => setOpen(true)}>Delete Account</Button>
      <Dialog open={open} onOpenChange={setOpen}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Are you sure?</DialogTitle>
          </DialogHeader>
          <p className='text-sm text-muted-foreground'>
            This action cannot be undone.
          </p>
          <div className='flex gap-2 justify-end'>
            <Button variant='outline' onClick={() => setOpen(false)}>Cancel</Button>
            <Button variant='destructive' onClick={onConfirm}>Delete</Button>
          </div>
        </DialogContent>
      </Dialog>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Form with Validation

import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';

const schema = z.object({ email: z.string().email() });

function EmailForm() {
  const form = useForm({ resolver: zodResolver(schema) });

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(console.log)}>
        <FormField control={form.control} name='email' render={({ field }) => (
          <FormItem>
            <FormLabel>Email</FormLabel>
            <FormControl><Input {...field} /></FormControl>
            <FormMessage />
          </FormItem>
        )} />
        <Button type='submit'>Subscribe</Button>
      </form>
    </Form>
  );
}
Enter fullscreen mode Exit fullscreen mode

Theming

/* globals.css — change these to retheme everything */
:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
  --primary: 222.2 47.4% 11.2%;
  --primary-foreground: 210 40% 98%;
  --destructive: 0 84.2% 60.2%;
}
Enter fullscreen mode Exit fullscreen mode

Available Components

Accordion, Alert, Avatar, Badge, Button, Calendar, Card, Checkbox, Command, DataTable, DatePicker, Dialog, Dropdown Menu, Form, Input, Label, Popover, Progress, Radio Group, Select, Separator, Sheet, Skeleton, Slider, Switch, Table, Tabs, Textarea, Toast, Toggle, Tooltip.

Full shadcn/ui component library ships in the AI SaaS Starter Kit — dashboard, landing page, and auth forms all built with shadcn. $99 at whoffagents.com.

Top comments (0)