Coding

Shadcn Ui

Try it

Use when building UI with shadcn/ui components, Tailwind CSS layouts, form patterns with react-hook-form and zod, theming, dark mode, sidebar layouts, mobile navigation, or any shadcn component question.

What it does

Comprehensive guide for building production UIs with shadcn/ui, Tailwind CSS, react-hook-form, and zod.

The skill document

shadcn/ui Expert

Comprehensive guide for building production UIs with shadcn/ui, Tailwind CSS, react-hook-form, and zod.

Core Concepts

shadcn/ui is not a component library — it's a collection of copy-paste components built on Radix UI primitives. You own the code. Components are added to your project, not installed as dependencies.

Installation

# Initialize shadcn/ui in a Next.js project
npx shadcn@latest init

# Add individual components
npx shadcn@latest add button
npx shadcn@latest add card
npx shadcn@latest add dialog
npx shadcn@latest add form
npx shadcn@latest add input
npx shadcn@latest add select
npx shadcn@latest add table
npx shadcn@latest add toast
npx shadcn@latest add dropdown-menu
npx shadcn@latest add sheet
npx shadcn@latest add tabs
npx shadcn@latest add sidebar

# Add multiple at once
npx shadcn@latest add button card input label textarea select checkbox

Component Categories & When to Use

Layout & Navigation

ComponentUse When
sidebarApp-level navigation with collapsible sections
navigation-menuTop-level site navigation with dropdowns
breadcrumbShowing page hierarchy/location
tabsSwitching between related views in same context
separatorVisual divider between content sections
sheetSlide-out panel (mobile nav, filters, detail views)
resizableAdjustable panel layouts

Forms & Input

ComponentUse When
formAny form with validation (wraps react-hook-form)
inputText, email, password, number inputs
textareaMulti-line text input
selectChoosing from a list (native-like)
comboboxSearchable select (uses command + popover)
checkboxBoolean or multi-select toggles
radio-groupSingle selection from small set
switchOn/off toggle (settings, preferences)
sliderNumeric range selection
date-pickerDate selection (uses calendar + popover)
togglePressed/unpressed state (toolbar buttons)

Feedback & Overlay

ComponentUse When
dialogModal confirmation, forms, or detail views
alert-dialogDestructive action confirmation ("Are you sure?")
sheetSide panel for forms, filters, mobile nav
toastBrief non-blocking notifications (via sonner)
alertInline status messages (info, warning, error)
tooltipHover hints for icons/buttons
popoverRich content on click (color pickers, date pickers)
hover-cardPreview content on hover (user profiles, links)
skeletonLoading placeholders
progressTask completion indicators

Data Display

ComponentUse When
tableTabular data display
data-tableTables with sorting, filtering, pagination (uses @tanstack/react-table)
cardContent containers with header, body, footer
badgeStatus labels, tags, counts
avatarUser profile images
accordionCollapsible FAQ or settings sections
carouselImage/content slideshows
scroll-areaCustom scrollable containers

Actions

ComponentUse When
buttonPrimary actions, form submissions
dropdown-menuContext menus, action menus
context-menuRight-click menus
menubarApplication menu bars
commandCommand palette / search (⌘K)

Form Patterns (react-hook-form + zod)

Complete Form Example

npx shadcn@latest add form input select textarea checkbox button
'use client'

import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
import { Button } from '@/components/ui/button'
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from '@/components/ui/form'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select'
import { Checkbox } from '@/components/ui/checkbox'
import { toast } from 'sonner'

const formSchema = z.object({
  name: z.string().min(2, 'Name must be at least 2 characters'),
  email: z.string().email('Invalid email address'),
  role: z.enum(['admin', 'user', 'editor'], { required_error: 'Select a role' }),
  bio: z.string().max(500).optional(),
  notifications: z.boolean().default(false),
})

type FormValues = z.infer

export function UserForm() {
  const form = useForm({
    resolver: zodResolver(formSchema),
    defaultValues: {
      name: '',
      email: '',
      bio: '',
      notifications: false,
    },
  })

  async function onSubmit(values: FormValues) {
    try {
      await createUser(values)
      toast.success('User created successfully')
      form.reset()
    } catch (error) {
      toast.error('Failed to create user')
    }
  }

  return (
    
      
         (
            
              Name
              
                
              
              
            
          )}
        />

         (
            
              Email
              
                
              
              
            
          )}
        />

         (
            
              Role
              
                
                  
                    
                  
                
                
                  Admin
                  Editor
                  User
                
              
              
            
          )}
        />

         (
            
              Bio
              
                
              
              Max 500 characters
              
            
          )}
        />

         (
            
              
                
              
              
                Email notifications
                Receive emails about account activity
              
            
          )}
        />

        
          {form.formState.isSubmitting ? 'Creating...' : 'Create User'}
        
      
    
  )
}

Form with Server Action

'use client'

import { useFormState } from 'react-dom'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'

export function ContactForm() {
  const form = useForm({
    resolver: zodResolver(schema),
  })

  async function onSubmit(values: FormValues) {
    const formData = new FormData()
    Object.entries(values).forEach(([key, value]) => formData.append(key, String(value)))
    await submitContact(formData)
  }

  return (
    
      
        {/* fields */}
      
    
  )
}

Theming & Dark Mode

Setup with next-themes

npm install next-themes
npx shadcn@latest add dropdown-menu
// app/providers.tsx
'use client'
import { ThemeProvider } from 'next-themes'

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    
      {children}
    
  )
}
// components/theme-toggle.tsx
'use client'
import { Moon, Sun } from 'lucide-react'
import { useTheme } from 'next-themes'
import { Button } from '@/components/ui/button'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'

export function ThemeToggle() {
  const { setTheme } = useTheme()
  return (
    
      
        
          
          
          Toggle theme
        
      
      
         setTheme('light')}>Light
         setTheme('dark')}>Dark
         setTheme('system')}>System
      
    
  )
}

Custom Colors in globals.css

@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 222.2 84% 4.9%;
    --primary: 222.2 47.4% 11.2%;
    --primary-foreground: 210 40% 98%;
    --secondary: 210 40% 96.1%;
    --secondary-foreground: 222.2 47.4% 11.2%;
    --muted: 210 40% 96.1%;
    --muted-foreground: 215.4 16.3% 46.9%;
    --accent: 210 40% 96.1%;
    --accent-foreground: 222.2 47.4% 11.2%;
    --destructive: 0 84.2% 60.2%;
    --destructive-foreground: 210 40% 98%;
    --border: 214.3 31.8% 91.4%;
    --ring: 222.2 84% 4.9%;
    --radius: 0.5rem;
  }

  .dark {
    --background: 222.2 84% 4.9%;
    --foreground: 210 40% 98%;
    --primary: 210 40% 98%;
    --primary-foreground: 222.2 47.4% 11.2%;
    /* ... etc */
  }
}

Common Layouts

App Shell with Sidebar

import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'
import { AppSidebar } from '@/components/app-sidebar'

export default function DashboardLayout({ children }: { children: React.ReactNode }) {
  return (
    
      
      
        
          
          Dashboard
        
        {children}
      
    
  )
}

Responsive Header with Mobile Nav

import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'
import { Button } from '@/components/ui/button'
import { Menu } from 'lucide-react'

export function Header() {
  return (
    
      
        
          
          
            Dashboard
            Settings
          
        

        {/* Mobile hamburger */}
        
          
            
              
            
          
          
            
              Dashboard
              Settings
            
          
        

        
          
          
        
      
    
  )
}

Card Grid

import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'

export function StatsGrid({ stats }: { stats: Stat[] }) {
  return (
    
      {stats.map((stat) => (
        
          
            {stat.label}
            
          
          
            {stat.value}
            {stat.description}
          
        
      ))}
    
  )
}

Tailwind CSS Patterns

Common Utility Patterns

// Centering


// Container with max-width


// Responsive grid


// Sticky header


// Truncated text
Very long text...

// Line clamp
Multi-line truncation...

// Aspect ratio


// Animations
    {/* Loading skeleton */}
     {/* Spinner */}

Button Variants

Default
Secondary
Outline
Ghost
Link
Delete
Small
Large

Disabled
As Link

Toast Notifications

npx shadcn@latest add sonner
// app/layout.tsx
import { Toaster } from '@/components/ui/sonner'

export default function RootLayout({ children }) {
  return (
    {children}
  )
}

// Usage anywhere
import { toast } from 'sonner'

toast.success('User created')
toast.error('Something went wrong')
toast.info('New update available')
toast.warning('This action cannot be undone')
toast.promise(asyncAction(), {
  loading: 'Creating...',
  success: 'Created!',
  error: 'Failed to create',
})

Command Palette (⌘K)

'use client'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/navigation'
import {
  CommandDialog,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from '@/components/ui/command'

export function CommandPalette() {
  const [open, setOpen] = useState(false)
  const router = useRouter()

  useEffect(() => {
    const down = (e: KeyboardEvent) => {
      if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
        e.preventDefault()
        setOpen((open) => !open)
      }
    }
    document.addEventListener('keydown', down)
    return () => document.removeEventListener('keydown', down)
  }, [])

  return (
    
      
      
        No results found.
        
           { router.push('/dashboard'); setOpen(false) }}>
            Dashboard
          
           { router.push('/settings'); setOpen(false) }}>
            Settings
          
        
      
    
  )
}

Related skills

Generate and edit Draw.io, Mermaid, and Excalidraw diagrams from natural language using a structured JSON spec.

by nssa.io1.0k installs47 stars

Find why your productivity system keeps failing, then apply the smallest fix — capacity math, bottleneck routing, durable local notes.

by Iván855 installs69 stars

Join a video meeting as an AI bot with voice, avatar, and screenshare across four operating modes.

by johnpatternai22 installs8 stars

Stores durable facts in a categorized, plain-markdown vault on disk, alongside your agent's built-in memory.

by Iván559 installs18 stars

Run Git operations — commits, branches, merges, rebases, conflict resolution, and recovery — with safety rules enforced.

by Iván535 installs31 stars

More from jgarrison929

Browse all skills

Use when reviewing code for security vulnerabilities, implementing authentication flows, auditing OWASP Top 10, configuring CORS/CSP headers, handling secrets, input validation, SQL injection prevention, XSS protection, or any security-related code review.

by jgarrison929871 installs47 stars

Use when building Next.js 14/15 applications with the App Router. Invoke for routing, layouts, Server Components, Client Components, Server Actions, Route Handlers, authentication, middleware, data fetching, caching, revalidation, streaming, Suspense, loading states, error boundaries, dynamic routes, parallel routes, intercepting routes, or any Next.js architecture question.

by jgarrison929354 installs12 stars

Use when designing database schemas, writing migrations, optimizing SQL queries, fixing N+1 problems, creating indexes, setting up PostgreSQL, configuring EF Core, implementing caching, partitioning tables, or any database performance question.

by jgarrison929442 installs7 stars

Use when building .NET 8/9 applications, ASP.NET Core APIs, Entity Framework Core, MediatR CQRS, modular monolith architecture, FluentValidation, Result pattern, JWT authentication, or any C# backend development question.

by jgarrison929102 installs1 stars