Back to results
SkillHub ClubBuild UIFrontend
moai-library-shadcn
Moai Lib Shadcn Ui - Professional implementation guide
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Stars
2
Hot score
79
Updated
March 20, 2026
Overall rating
C1.4
Composite score
1.4
Best-practice grade
C62.8
Install command
npx @skill-hub/cli install junseokandylee-rallyapp-moai-library-shadcn
libraryshadcnenterprisedevelopmentui
Repository
junseokandylee/RallyApp
Skill path: .claude/skills/moai-library-shadcn
Moai Lib Shadcn Ui - Professional implementation guide
Open repositoryBest for
Primary workflow: Build UI.
Technical facets: Frontend.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: junseokandylee.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install moai-library-shadcn into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/junseokandylee/RallyApp before adding moai-library-shadcn to shared team environments
- Use moai-library-shadcn for library workflows
Works across
Claude CodeCodex CLIGemini CLIOpenCode
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: moai-library-shadcn
aliases: [moai-library-shadcn]
category: library
description: Moai Lib Shadcn Ui - Professional implementation guide
version: 2.0.0
modularized: true
tags:
- library
- shadcn
- enterprise
- development
- ui
updated: 2025-11-27
status: active
---
## Quick Reference (30 seconds)
# Enterprise shadcn/ui Component Library Expert
Comprehensive shadcn/ui expertise with AI-powered design system architecture, Context7 integration, and intelligent component orchestration for modern React applications.
Core Capabilities:
- AI-Powered Component Architecture using Context7 MCP
- Intelligent Design System with automated theme customization
- Advanced Component Orchestration with accessibility and performance
- Enterprise UI Framework with zero-configuration design tokens
- Predictive Component Analytics with usage insights
When to Use:
- shadcn/ui component library discussions
- React component architecture planning
- Tailwind CSS integration and design tokens
- Accessibility implementation
- Design system customization
Module Organization:
- Core Concepts: This file (shadcn/ui overview, architecture, ecosystem)
- Components: [shadcn Components](modules/shadcn-components.md) (component library, advanced patterns)
- Theming: [shadcn Theming](modules/shadcn-theming.md) (theme system, customization)
- Advanced Patterns: [Advanced Patterns](modules/advanced-patterns.md) (complex implementations)
- Optimization: [Optimization](modules/optimization.md) (performance tuning)
---
## Implementation Guide
### shadcn/ui Overview
What is shadcn/ui:
shadcn/ui is a collection of re-usable components built with Radix UI and Tailwind CSS. Unlike traditional component libraries, it's not an npm package but rather a collection of components you copy into your project.
Key Benefits:
- Full control and ownership of components
- Zero dependencies (only Radix UI primitives)
- Complete customization with Tailwind CSS
- TypeScript-first with excellent type safety
- Built-in accessibility with WCAG 2.1 AA compliance
Architecture Philosophy:
```
shadcn/ui Components
Radix UI Primitives (unstyled, accessible)
Tailwind CSS (utility-first styling)
TypeScript (type safety)
Your Customization (full control)
```
### Core Component Categories
1. Form Components:
- Input, Select, Checkbox, Radio, Textarea
- Form validation with react-hook-form + Zod
- Accessibility with proper ARIA labels
2. Display Components:
- Card, Dialog, Sheet, Drawer, Popover
- Responsive design patterns
- Dark mode support
3. Navigation Components:
- Navigation Menu, Breadcrumb, Tabs, Pagination
- Keyboard navigation support
- Focus management
4. Data Components:
- Table, Calendar, DatePicker, Charts
- Virtual scrolling for large datasets
- TanStack Table integration
5. Feedback Components:
- Alert, Toast, Progress, Badge, Avatar
- Loading states and skeletons
- Error boundaries
### Installation & Setup
Step 1: Initialize shadcn/ui:
```bash
npx shadcn-ui@latest init
```
Step 2: Configure components.json:
```json
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "app/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui"
}
}
```
Step 3: Add Components:
```bash
npx shadcn-ui@latest add button
npx shadcn-ui@latest add form
npx shadcn-ui@latest add dialog
```
### Foundation Technologies (November 2025)
React 19:
- Server Components support
- Concurrent rendering features
- Automatic batching improvements
- Streaming SSR enhancements
TypeScript 5.5:
- Full type safety across components
- Improved inference for generics
- Better error messages
- Enhanced developer experience
Tailwind CSS 3.4:
- JIT (Just-In-Time) compilation
- CSS variable support
- Dark mode variants
- Container queries
Radix UI:
- Unstyled, accessible primitives
- Keyboard navigation
- Focus management
- ARIA attributes
Integration Stack:
- React Hook Form: Form state management
- Zod: Schema validation
- class-variance-authority: Variant management
- Framer Motion: Animation library
- Lucide React: Icon library
### AI-Powered Architecture Design
```python
# AI-powered shadcn/ui architecture optimization with Context7
class ShadcnUIArchitectOptimizer:
def __init__(self):
self.context7_client = Context7Client()
self.component_analyzer = ComponentAnalyzer()
self.theme_optimizer = ThemeOptimizer()
async def design_optimal_shadcn_architecture(self,
requirements: DesignSystemRequirements) -> ShadcnUIArchitecture:
"""Design optimal shadcn/ui architecture using AI analysis."""
# Get latest shadcn/ui and React documentation via Context7
shadcn_docs = await self.context7_client.get_library_docs(
context7_library_id='/shadcn-ui/docs',
topic="component library design system theming accessibility 2025",
tokens=3000
)
react_docs = await self.context7_client.get_library_docs(
context7_library_id='/react/docs',
topic="hooks server-components performance optimization 2025",
tokens=2000
)
# Optimize component selection
component_selection = self.component_analyzer.optimize_component_selection(
requirements.ui_components,
requirements.user_needs,
shadcn_docs
)
# Optimize theme configuration
theme_configuration = self.theme_optimizer.optimize_theme_system(
requirements.brand_guidelines,
requirements.accessibility_requirements,
shadcn_docs
)
return ShadcnUIArchitecture(
component_library=component_selection,
theme_system=theme_configuration,
accessibility_compliance=self._ensure_accessibility_compliance(
requirements.accessibility_requirements
),
performance_optimization=self._optimize_component_performance(
component_selection
),
integration_patterns=self._design_integration_patterns(
requirements.framework_stack
),
customization_strategy=self._plan_customization_strategy(
requirements.customization_needs
)
)
```
### Best Practices
Requirements:
- Use CSS variables for theme customization
- Implement proper TypeScript types
- Follow accessibility guidelines (WCAG 2.1 AA)
- Use Radix UI primitives for complex interactions
- Test components with React Testing Library
- Optimize bundle size with tree-shaking
- Implement responsive design patterns
Critical Implementation Standards:
[HARD] Use CSS variables exclusively for color values
WHY: Enables dynamic theming, supports dark mode transitions, and maintains design system consistency across all components
IMPACT: Without CSS variables, theme changes require code modifications, dark mode fails, and brand customization becomes unmaintainable
[HARD] Include accessibility attributes on all interactive elements
WHY: Ensures WCAG 2.1 AA compliance, screen reader compatibility, and inclusive user experience for users with disabilities
IMPACT: Missing accessibility attributes excludes users with disabilities, violates legal compliance requirements, and reduces application usability by 15-20% of potential users
[HARD] Implement keyboard navigation for all interactive components
WHY: Provides essential navigation method for keyboard users, supports assistive technologies, and improves overall user experience efficiency
IMPACT: Without keyboard navigation, power users cannot efficiently use the application, accessibility compliance fails, and user productivity decreases by 30-40% for keyboard-dependent workflows
[SOFT] Provide loading states for asynchronous operations
WHY: Communicates operation progress to users, reduces perceived latency, and improves user confidence in application responsiveness
IMPACT: Missing loading states create user uncertainty, increase perceived application slowness by 2-3x, and lead to duplicate action submissions
[HARD] Implement error boundaries around component trees
WHY: Prevents entire application crashes from isolated component failures, enables graceful error recovery, and maintains application stability
IMPACT: Without error boundaries, single component failures crash the entire application, user data loss occurs, and recovery requires full page refresh
[HARD] Apply Tailwind CSS classes instead of inline styles
WHY: Maintains consistency with design system, enables JIT compilation benefits, supports responsive design variants, and improves bundle size optimization
IMPACT: Inline styles bypass Tailwind optimization, increase CSS bundle size by 40-60%, prevent design token usage, and break theme customization
[SOFT] Implement dark mode support across all components
WHY: Provides user preference respect, reduces eye strain in low-light environments, and aligns with modern UI expectations
IMPACT: Missing dark mode support limits usability in 60% of usage contexts, increases user eye strain, and reduces user satisfaction scores by 20-30%
### Performance Optimization
Bundle Size:
- Tree-shaking removes unused components
- Code splitting for large components
- Lazy loading with React.lazy
- Dynamic imports for heavy dependencies
Runtime Performance:
- React.memo for expensive components
- useMemo/useCallback for computations
- Virtual scrolling for large lists
- Debouncing user interactions
Accessibility:
- ARIA attributes for all interactive elements
- Keyboard navigation support
- Focus management
- Screen reader testing
---
## Advanced Patterns
### Component Composition
Composable Pattern:
```typescript
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
export function DashboardCard({ title, children }) {
return (
<Card>
<CardHeader>
<CardTitle>{title}</CardTitle>
</CardHeader>
<CardContent>
{children}
</CardContent>
</Card>
);
}
```
### Form Validation
Zod + React Hook Form:
```typescript
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
const formSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
type FormValues = z.infer<typeof formSchema>;
export function LoginForm() {
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
});
return (
<form onSubmit={form.handleSubmit(onSubmit)}>
{/* Form fields */}
</form>
);
}
```
---
## Works Well With
- [shadcn Components](modules/shadcn-components.md) - Advanced component patterns and implementation
- [shadcn Theming](modules/shadcn-theming.md) - Theme system and customization strategies
- `moai-domain-uiux` - Design system architecture and principles
- `moai-lang-typescript` - TypeScript best practices
- `code-frontend` - Frontend development patterns
---
## Context7 Integration
Related Libraries:
- [shadcn/ui](/shadcn-ui/ui): Re-usable components built with Radix UI and Tailwind
- [Radix UI](/radix-ui/primitives): Unstyled, accessible component primitives
- [Tailwind CSS](/tailwindlabs/tailwindcss): Utility-first CSS framework
Official Documentation:
- [shadcn/ui Documentation](https://ui.shadcn.com/docs)
- [API Reference](https://ui.shadcn.com/docs/components)
- [Radix UI Documentation](https://www.radix-ui.com/)
- [Tailwind CSS Documentation](https://tailwindcss.com/)
Latest Versions (November 2025):
- React 19
- TypeScript 5.5
- Tailwind CSS 3.4
- Radix UI Latest
---
Last Updated: 2025-11-21
Status: Production Ready
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### modules/shadcn-components.md
```markdown
name: moai-library-shadcn-components
description: Advanced shadcn/ui component patterns and implementations
## Advanced Component Patterns
### Complex Data Table Implementation
```typescript
// Complex data table with shadcn/ui components
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import {
ColumnDef,
flexRender,
getCoreRowModel,
getPaginationRowModel,
useReactTable,
} from "@tanstack/react-table";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
searchKey?: string;
filterableColumns?: string[];
}
export function DataTable<TData, TValue>({
columns,
data,
searchKey,
filterableColumns = [],
}: DataTableProps<TData, TValue>) {
const [globalFilter, setGlobalFilter] = useState("");
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onGlobalFilterChange: setGlobalFilter,
onColumnFiltersChange: setColumnFilters,
state: {
globalFilter,
columnFilters,
},
});
return (
<div className="space-y-4">
{/* Search and Filters */}
<div className="flex items-center gap-4">
<Input
placeholder={`Filter ${searchKey}...`}
value={(globalFilter ?? "") as string}
onChange={(event) => setGlobalFilter(String(event.target.value))}
className="max-w-sm"
/>
{/* Column Filters */}
{filterableColumns.map((column) => (
<DataTableColumnFilter
key={column}
column={table.getColumn(column)}
title={column}
/>
))}
</div>
{/* Data Table */}
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
{/* Pagination */}
<div className="flex items-center justify-end space-x-2 py-4">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
);
}
```
### Multi-Step Form Implementation
```typescript
// Advanced form with multi-step validation
import { useState } from "react";
import { useForm, FormProvider } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import { Progress } from "@/components/ui/progress";
const multiStepSchema = z.object({
// Step 1: Personal Information
personalInfo: z.object({
firstName: z.string().min(2),
lastName: z.string().min(2),
email: z.string().email(),
phone: z.string().optional(),
}),
// Step 2: Address
address: z.object({
street: z.string().min(5),
city: z.string().min(2),
state: z.string().min(2),
zipCode: z.string().regex(/^\d{5}(-\d{4})?$/),
country: z.string().min(2),
}),
// Step 3: Preferences
preferences: z.object({
newsletter: z.boolean(),
notifications: z.boolean(),
theme: z.enum(["light", "dark", "system"]),
}),
});
type MultiStepFormValues = z.infer<typeof multiStepSchema>;
export function MultiStepForm() {
const [currentStep, setCurrentStep] = useState(0);
const [isSubmitting, setIsSubmitting] = useState(false);
const methods = useForm<MultiStepFormValues>({
resolver: zodResolver(multiStepSchema),
defaultValues: {
personalInfo: {
firstName: "",
lastName: "",
email: "",
phone: "",
},
address: {
street: "",
city: "",
state: "",
zipCode: "",
country: "",
},
preferences: {
newsletter: false,
notifications: true,
theme: "system",
},
},
});
const steps = [
{ title: "Personal Info", component: PersonalInfoStep },
{ title: "Address", component: AddressStep },
{ title: "Preferences", component: PreferencesStep },
];
const handleNext = async () => {
const currentStepName = ["personalInfo", "address", "preferences"][currentStep];
const isValid = await methods.trigger(currentStepName as any);
if (isValid && currentStep < steps.length - 1) {
setCurrentStep(currentStep + 1);
}
};
const handlePrevious = () => {
if (currentStep > 0) {
setCurrentStep(currentStep - 1);
}
};
const onSubmit = async (data: MultiStepFormValues) => {
setIsSubmitting(true);
try {
console.log("Form submitted:", data);
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (error) {
console.error("Submission error:", error);
} finally {
setIsSubmitting(false);
}
};
const progress = ((currentStep + 1) / steps.length) * 100;
const CurrentStepComponent = steps[currentStep].component;
return (
<div className="w-full max-w-2xl mx-auto p-6">
<div className="mb-8">
<h1 className="text-2xl font-bold mb-4">Multi-Step Form</h1>
<Progress value={progress} className="mb-2" />
<p className="text-sm text-muted-foreground">
Step {currentStep + 1} of {steps.length}: {steps[currentStep].title}
</p>
</div>
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
<div className="mb-8">
<CurrentStepComponent />
</div>
<div className="flex justify-between">
<Button
type="button"
variant="outline"
onClick={handlePrevious}
disabled={currentStep === 0}
>
Previous
</Button>
{currentStep === steps.length - 1 ? (
<Button type="submit" disabled={isSubmitting}>
{isSubmitting ? "Submitting..." : "Submit"}
</Button>
) : (
<Button type="button" onClick={handleNext}>
Next
</Button>
)}
</div>
</form>
</FormProvider>
</div>
);
}
```
### Enhanced Button Component
```typescript
// Enhanced button component with loading state and accessibility
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
loading?: boolean;
loadingText?: string;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, loading, loadingText, children, disabled, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
disabled={disabled || loading}
aria-disabled={disabled || loading}
aria-describedby={loading ? "loading-description" : undefined}
{...props}
>
{loading && (
<div className="mr-2 h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
)}
{loading && loadingText ? (
<span id="loading-description" className="sr-only">
{loadingText}
</span>
) : null}
{loading ? loadingText || children : children}
</Comp>
);
}
);
Button.displayName = "Button";
export { Button, buttonVariants };
```
### Performance Optimization
```typescript
// Performance optimization for shadcn/ui components
export class ComponentPerformanceOptimizer {
// Lazy loading components with React.lazy
static createLazyComponent(importFn: () => Promise<{ default: React.ComponentType<any> }>) {
return React.lazy(importFn);
}
// Component memoization with React.memo
static createMemoizedComponent<P extends object>(
Component: React.ComponentType<P>,
areEqual?: (prevProps: P, nextProps: P) => boolean
) {
return React.memo(Component, areEqual);
}
// Hook for performance monitoring
static usePerformanceMonitor(componentName: string) {
const [renderCount, setRenderCount] = useState(0);
const [renderTime, setRenderTime] = useState(0);
useEffect(() => {
const startTime = performance.now();
setRenderCount(prev => prev + 1);
return () => {
const endTime = performance.now();
setRenderTime(endTime - startTime);
if (process.env.NODE_ENV === 'development') {
console.log(
`${componentName} render #${renderCount}: ${renderTime.toFixed(2)}ms`
);
}
};
});
return { renderCount, renderTime };
}
// Bundle size optimization
static optimizeBundleSize() {
return {
treeShaking: {
enabled: true,
description: "Remove unused components and utilities",
},
codeSplitting: {
enabled: true,
description: "Split components into separate chunks",
},
compression: {
enabled: true,
description: "Compress bundle with gzip/brotli",
},
};
}
// Runtime performance optimization
static optimizeRuntimePerformance() {
return {
virtualScrolling: {
enabled: true,
description: "Virtual scrolling for large data sets",
},
memoization: {
enabled: true,
description: "Memoize expensive computations",
},
debouncing: {
enabled: true,
description: "Debounce user interactions",
},
};
}
}
```
---
Last Updated: 2025-11-26
Related: [Main Skill](../SKILL.md), [shadcn Theming](shadcn-theming.md)
```
### modules/shadcn-theming.md
```markdown
name: moai-library-shadcn-theming
description: shadcn/ui theme system and design token customization
## Theme System Implementation
### Advanced Theme Provider
```typescript
// Advanced theme system with CSS variables and dark mode
import { createContext, useContext, useEffect, useState } from "react";
type Theme = "dark" | "light" | "system";
interface ThemeProviderProps {
children: React.ReactNode;
defaultTheme?: Theme;
storageKey?: string;
attribute?: string;
enableSystem?: boolean;
}
interface ThemeProviderState {
theme: Theme;
setTheme: (theme: Theme) => void;
}
const ThemeProviderContext = createContext<ThemeProviderState | undefined>(undefined);
export function ThemeProvider({
children,
defaultTheme = "system",
storageKey = "ui-theme",
attribute = "class",
enableSystem = true,
...props
}: ThemeProviderProps) {
const [theme, setTheme] = useState<Theme>(() => {
if (typeof window !== "undefined") {
return (localStorage.getItem(storageKey) as Theme) || defaultTheme;
}
return defaultTheme;
});
useEffect(() => {
const root = window.document.documentElement;
root.classList.remove("light", "dark");
if (theme === "system" && enableSystem) {
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
: "light";
root.classList.add(systemTheme);
return;
}
root.classList.add(theme);
}, [theme, enableSystem, attribute]);
const value = {
theme,
setTheme: (theme: Theme) => {
localStorage.setItem(storageKey, theme);
setTheme(theme);
},
};
return (
<ThemeProviderContext.Provider {...props} value={value}>
{children}
</ThemeProviderContext.Provider>
);
}
export const useTheme = () => {
const context = useContext(ThemeProviderContext);
if (context === undefined)
throw new Error("useTheme must be used within a ThemeProvider");
return context;
};
```
### Design Token Configuration
```typescript
// Theme configuration with design tokens
export const theme = {
light: {
background: "hsl(0 0% 100%)",
foreground: "hsl(240 10% 3.9%)",
card: "hsl(0 0% 100%)",
cardForeground: "hsl(240 10% 3.9%)",
popover: "hsl(0 0% 100%)",
popoverForeground: "hsl(240 10% 3.9%)",
primary: "hsl(240 9% 10%)",
primaryForeground: "hsl(0 0% 98%)",
secondary: "hsl(240 4.8% 95.9%)",
secondaryForeground: "hsl(240 3.8% 46.1%)",
muted: "hsl(240 4.8% 95.9%)",
mutedForeground: "hsl(240 3.8% 46.1%)",
accent: "hsl(240 4.8% 95.9%)",
accentForeground: "hsl(240 5.9% 10%)",
destructive: "hsl(0 72.22% 50.59%)",
destructiveForeground: "hsl(0 0% 98%)",
border: "hsl(240 5.9% 90%)",
input: "hsl(240 5.9% 90%)",
ring: "hsl(240 5.9% 10%)",
},
dark: {
background: "hsl(240 10% 3.9%)",
foreground: "hsl(0 0% 98%)",
card: "hsl(240 10% 3.9%)",
cardForeground: "hsl(0 0% 98%)",
popover: "hsl(240 10% 3.9%)",
popoverForeground: "hsl(0 0% 98%)",
primary: "hsl(0 0% 98%)",
primaryForeground: "hsl(240 9% 10%)",
secondary: "hsl(240 3.7% 15.9%)",
secondaryForeground: "hsl(0 0% 98%)",
muted: "hsl(240 3.7% 15.9%)",
mutedForeground: "hsl(240 5% 64.9%)",
accent: "hsl(240 3.7% 15.9%)",
accentForeground: "hsl(0 0% 98%)",
destructive: "hsl(0 62.8% 30.6%)",
destructiveForeground: "hsl(0 0% 98%)",
border: "hsl(240 3.7% 15.9%)",
input: "hsl(240 3.7% 15.9%)",
ring: "hsl(240 4.9% 83.9%)",
},
};
// Apply theme CSS variables
export function applyThemeCSS() {
const root = document.documentElement;
Object.entries(theme.light).forEach(([key, value]) => {
root.style.setProperty(`--${key}`, value);
});
}
```
### CSS Variables in globals.css
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--primary: 240 9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 3.8% 46.1%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 72.22% 50.59%;
--destructive-foreground: 0 0% 98%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--ring: 240 5.9% 10%;
--radius: 0.5rem;
}
.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
```
### Custom Brand Theme
```typescript
// Create custom brand theme
export function createBrandTheme(brandColors: {
primary: string;
secondary: string;
accent: string;
}) {
return {
light: {
...theme.light,
primary: brandColors.primary,
secondary: brandColors.secondary,
accent: brandColors.accent,
},
dark: {
...theme.dark,
primary: brandColors.primary,
secondary: brandColors.secondary,
accent: brandColors.accent,
},
};
}
// Apply custom brand theme
export function applyBrandTheme(brandTheme: typeof theme) {
const root = document.documentElement;
const currentTheme = root.classList.contains("dark") ? "dark" : "light";
Object.entries(brandTheme[currentTheme]).forEach(([key, value]) => {
root.style.setProperty(`--${key}`, value);
});
}
```
### Theme Toggle Component
```typescript
// Theme toggle component
import { Moon, Sun } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useTheme } from "@/components/theme-provider";
export function ThemeToggle() {
const { setTheme } = useTheme();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}>
Light
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>
Dark
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>
System
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
```
### Tailwind Configuration
```javascript
// tailwind.config.js
/ @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: [
'./pages//*.{ts,tsx}',
'./components//*.{ts,tsx}',
'./app//*.{ts,tsx}',
'./src//*.{ts,tsx}',
],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [require("tailwindcss-animate")],
}
```
---
Last Updated: 2025-11-26
Related: [Main Skill](../SKILL.md), [Component Architecture](component-architecture.md)
```
### modules/advanced-patterns.md
```markdown
# Advanced shadcn/ui Component Patterns
## Architecture Patterns
### Complex Component Composition
shadcn/ui excels at composing complex UI from simple, reusable components. The architecture follows a composition-over-inheritance approach.
Pattern: Compound Components
```typescript
// Compound component pattern with shadcn/ui
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import React, { createContext, useContext } from "react"
// Create a context for form state
interface FormContextType {
data: Record<string, any>
setData: (key: string, value: any) => void
errors: Record<string, string>
}
const FormContext = createContext<FormContextType | undefined>(undefined)
export function Form({ children, onSubmit }: { children: React.ReactNode; onSubmit: (data: any) => void }) {
const [data, setData] = React.useState({})
const [errors, setErrors] = React.useState({})
return (
<FormContext.Provider value={{ data, setData: (k, v) => setData(prev => ({ ...prev, [k]: v })), errors }}>
<form
onSubmit={(e) => {
e.preventDefault()
onSubmit(data)
}}
>
{children}
</form>
</FormContext.Provider>
)
}
export function FormField({ label, name, type = "text" }: { label: string; name: string; type?: string }) {
const context = useContext(FormContext)
if (!context) throw new Error("FormField must be used inside Form")
return (
<div className="mb-4">
<label htmlFor={name} className="block text-sm font-medium mb-1">
{label}
</label>
<Input
id={name}
type={type}
value={context.data[name] || ""}
onChange={(e) => context.setData(name, e.target.value)}
/>
</div>
)
}
// Usage
export function MyForm() {
return (
<Form onSubmit={(data) => console.log(data)}>
<Card>
<CardHeader>
<CardTitle>Contact Form</CardTitle>
<CardDescription>Enter your details</CardDescription>
</CardHeader>
<CardContent>
<FormField label="Name" name="name" />
<FormField label="Email" name="email" type="email" />
<Button type="submit">Submit</Button>
</CardContent>
</Card>
</Form>
)
}
```
### Design Token Integration
shadcn/ui uses CSS variables for theming. Components automatically adapt to theme changes.
Pattern: Theme-Aware Components
```typescript
// Using design tokens in custom components
export function StatusBadge({ status }: { status: "success" | "error" | "warning" }) {
const statusConfig = {
success: "bg-green-500/20 text-green-700 dark:text-green-400",
error: "bg-red-500/20 text-red-700 dark:text-red-400",
warning: "bg-yellow-500/20 text-yellow-700 dark:text-yellow-400",
}
return (
<span className={`px-3 py-1 rounded-md text-sm font-medium ${statusConfig[status]}`}>
{status.charAt(0).toUpperCase() + status.slice(1)}
</span>
)
}
// Theme switching with context
import { useEffect, useState } from "react"
export function ThemeSwitcher() {
const [theme, setTheme] = useState("light")
useEffect(() => {
const html = document.documentElement
if (theme === "dark") {
html.classList.add("dark")
} else {
html.classList.remove("dark")
}
}, [theme])
return (
<Button
variant="outline"
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
>
{theme === "light" ? "" : ""}
</Button>
)
}
```
## Advanced Component Patterns
### Form Composition with Validation
shadcn/ui Button, Input, and Form components integrate well with validation libraries like react-hook-form.
```typescript
import { useForm } from "react-hook-form"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
interface FormData {
username: string
email: string
}
export function AdvancedForm() {
const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
defaultValues: { username: "", email: "" }
})
const onSubmit = (data: FormData) => {
console.log("Form submitted:", data)
}
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div>
<Input
{...register("username", { required: "Username is required" })}
placeholder="Enter username"
/>
{errors.username && <p className="text-red-500 text-sm">{errors.username.message}</p>}
</div>
<div>
<Input
{...register("email", { required: "Email is required", pattern: { value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, message: "Invalid email" } })}
placeholder="Enter email"
type="email"
/>
{errors.email && <p className="text-red-500 text-sm">{errors.email.message}</p>}
</div>
<Button type="submit">Submit</Button>
</form>
)
}
```
### Complex Data Table with Sorting and Filtering
```typescript
import { DataTable } from "@/components/ui/data-table"
import { Button } from "@/components/ui/button"
import { useReactTable, getCoreRowModel, getSortedRowModel, getFilteredRowModel } from "@tanstack/react-table"
export function UserDataTable({ users }: { users: User[] }) {
const [sorting, setSorting] = React.useState([])
const [columnFilters, setColumnFilters] = React.useState([])
const table = useReactTable({
data: users,
columns: [
{
accessorKey: "name",
header: "Name",
},
{
accessorKey: "email",
header: "Email",
},
],
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
state: {
sorting,
columnFilters,
},
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
})
return (
<div>
<Button onClick={() => setColumnFilters([{ id: "name", value: "john" }])}>
Filter by John
</Button>
<DataTable table={table} />
</div>
)
}
```
## Component Composition Strategies
### Building Component Trees
shadcn/ui components can be nested and composed to create complex layouts.
Strategy: Hierarchical Component Structure
```typescript
// Parent wrapper component
export function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<div className="grid grid-cols-4 gap-4 p-6">
<aside className="col-span-1">
<Navigation />
</aside>
<main className="col-span-3">
{children}
</main>
</div>
)
}
// Navigation subcomponent
function Navigation() {
return (
<nav className="space-y-2">
<Button variant="ghost" className="w-full justify-start">Dashboard</Button>
<Button variant="ghost" className="w-full justify-start">Settings</Button>
<Button variant="ghost" className="w-full justify-start">Profile</Button>
</nav>
)
}
// Usage
export function Dashboard() {
return (
<DashboardLayout>
<Card>
<CardHeader>
<CardTitle>Welcome</CardTitle>
</CardHeader>
<CardContent>
Dashboard content here
</CardContent>
</Card>
</DashboardLayout>
)
}
```
## Design System Scaling
### Managing Component Variants
shadcn/ui components support variants for different use cases.
Pattern: Variant Management
```typescript
// Using Button variants for consistent interface
export function ActionButtons() {
return (
<div className="flex gap-2">
<Button variant="default">Primary Action</Button>
<Button variant="secondary">Secondary Action</Button>
<Button variant="outline">Outline Action</Button>
<Button variant="destructive">Delete</Button>
<Button variant="ghost">Ghost Action</Button>
</div>
)
}
// Creating custom size variants
export function SizedButtons() {
return (
<div className="flex gap-2 items-center">
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
</div>
)
}
```
## Responsive Design Patterns
### Mobile-First Approach with Tailwind
shadcn/ui uses Tailwind CSS which supports responsive breakpoints.
```typescript
export function ResponsiveCard() {
return (
<Card className="w-full sm:max-w-sm md:max-w-md lg:max-w-lg">
<CardHeader className="p-4 sm:p-6">
<CardTitle className="text-lg sm:text-xl md:text-2xl">
Responsive Card
</CardTitle>
</CardHeader>
<CardContent className="p-4 sm:p-6">
<p className="text-sm sm:text-base md:text-lg">
Content adapts to screen size
</p>
</CardContent>
</Card>
)
}
```
## Performance Patterns
### Memoization and Lazy Loading
shadcn/ui components benefit from React.memo for preventing unnecessary re-renders.
```typescript
import React from "react"
import { Card, CardContent } from "@/components/ui/card"
interface UserCardProps {
user: User
}
export const UserCard = React.memo(function UserCard({ user }: UserCardProps) {
return (
<Card>
<CardContent>
<p>{user.name}</p>
<p className="text-gray-500">{user.email}</p>
</CardContent>
</Card>
)
})
```
## Accessibility Standards
### ARIA Integration
shadcn/ui components come with built-in accessibility features.
```typescript
export function AccessibleDialog() {
const [open, setOpen] = React.useState(false)
return (
<>
<Button onClick={() => setOpen(true)} aria-label="Open dialog">
Open
</Button>
{open && (
<div role="dialog" aria-labelledby="dialog-title" aria-modal="true">
<h2 id="dialog-title">Dialog Title</h2>
<p>Dialog content here</p>
<Button onClick={() => setOpen(false)}>Close</Button>
</div>
)}
</>
)
}
```
---
Version: 4.0.0
Last Updated: 2025-11-22
Status: Production Ready
```
### modules/optimization.md
```markdown
# shadcn/ui Performance Optimization
## Bundle Size Optimization
### Code Splitting Strategy
Pattern: Lazy Loading Components
```typescript
import React from "react"
// Lazy load heavy components
const DataTableComponent = React.lazy(() =>
import("@/components/data-table").then(mod => ({ default: mod.DataTable }))
)
export function Dashboard() {
return (
<React.Suspense fallback={<p>Loading...</p>}>
<DataTableComponent />
</React.Suspense>
)
}
```
### CSS File Size Optimization
shadcn/ui uses Tailwind CSS. Optimize CSS output by:
1. Purging unused styles in `tailwind.config.ts`:
```typescript
export default {
content: [
"./app//*.{js,ts,jsx,tsx}",
"./components//*.{js,ts,jsx,tsx}",
],
// Only include styles used in templates
}
```
2. Tree-shaking unused components - Import only what you need:
```typescript
// GOOD: Import specific components
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
// AVOID: Importing entire component library
import * as UI from "@/components/ui"
```
### Component Import Optimization
```typescript
// Optimized imports
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
function Form() {
return (
<>
<Input placeholder="Name" />
<Button>Submit</Button>
</>
)
}
```
## Rendering Optimization
### React.memo for Components
```typescript
import React from "react"
import { Card } from "@/components/ui/card"
interface ItemProps {
id: string
title: string
description: string
}
// Prevent re-renders when props haven't changed
export const ListItem = React.memo(function ListItem({ id, title, description }: ItemProps) {
return (
<Card className="p-4">
<h3>{title}</h3>
<p>{description}</p>
</Card>
)
}, (prevProps, nextProps) => {
// Custom comparison
return prevProps.id === nextProps.id &&
prevProps.title === nextProps.title &&
prevProps.description === nextProps.description
})
```
### useMemo for Expensive Computations
```typescript
import React, { useMemo } from "react"
import { Button } from "@/components/ui/button"
interface DataTableProps {
rows: any[]
filter: string
}
export function DataTable({ rows, filter }: DataTableProps) {
// Memoize filtered results
const filteredRows = useMemo(() => {
return rows.filter(row => row.name.includes(filter))
}, [rows, filter])
return (
<div>
{filteredRows.map(row => (
<div key={row.id}>{row.name}</div>
))}
</div>
)
}
```
### useCallback for Event Handlers
```typescript
import React, { useCallback } from "react"
import { Button } from "@/components/ui/button"
export function Form() {
const handleSubmit = useCallback((e: React.FormEvent) => {
e.preventDefault()
// Expensive operation
}, [])
return (
<form onSubmit={handleSubmit}>
<Button type="submit">Submit</Button>
</form>
)
}
```
## Token Efficiency
### CSS Variable Optimization
```typescript
// Efficient design token usage
const tokenConfig = {
colors: {
primary: "hsl(var(--primary))",
secondary: "hsl(var(--secondary))",
},
spacing: {
xs: "var(--spacing-xs)",
sm: "var(--spacing-sm)",
},
}
// Use in components
export function Button() {
return (
<button className="bg-[var(--primary)] px-[var(--spacing-sm)]">
Click me
</button>
)
}
```
## Rendering Optimization
### Virtual Scrolling for Large Lists
```typescript
import React, { useMemo } from "react"
import { FixedSizeList } from "react-window"
import { Card } from "@/components/ui/card"
interface Item {
id: string
name: string
}
export function VirtualList({ items }: { items: Item[] }) {
const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => (
<div style={style}>
<Card className="p-2">
<p>{items[index].name}</p>
</Card>
</div>
)
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={80}
width="100%"
>
{Row}
</FixedSizeList>
)
}
```
### Pagination Pattern
```typescript
import React from "react"
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
export function PaginatedList() {
const [page, setPage] = React.useState(1)
const itemsPerPage = 10
const startIndex = (page - 1) * itemsPerPage
const endIndex = startIndex + itemsPerPage
return (
<div>
<div className="space-y-2">
{/* Render only items for current page */}
</div>
<div className="flex gap-2">
<Button onClick={() => setPage(page - 1)} disabled={page === 1}>
Previous
</Button>
<span>Page {page}</span>
<Button onClick={() => setPage(page + 1)}>
Next
</Button>
</div>
</div>
)
}
```
## Network Optimization
### Image Optimization with Next.js
```typescript
import Image from "next/image"
export function OptimizedImage() {
return (
<Image
src="/image.jpg"
alt="Optimized image"
width={400}
height={300}
priority={false}
loading="lazy"
/>
)
}
```
## Best Practices Summary
1. Lazy Load Components - Use React.lazy for non-critical components
2. Memoize Selectively - Don't over-memoize; profile first
3. Tree-shake Imports - Import specific components, not entire libraries
4. Optimize CSS - Configure Tailwind content properly
5. Virtual Scroll Large Lists - Use react-window for 1000+ items
6. Cache Computations - Use useMemo and useCallback judiciously
7. Monitor Bundle - Use bundle analyzer to identify bottlenecks
---
Version: 4.0.0
Last Updated: 2025-11-22
Token Focus: CSS file size, JavaScript bundle size, rendering performance
```