| name | shadcn-ui-setup |
| description | Install and configure Shadcn/ui component library with Radix UI primitives, Aceternity UI effects, set up components, and manage the component registry. Use when adding Shadcn/ui to a Next.js project or installing specific UI components for Phase 2. |
| allowed-tools | Bash, Write, Read, Edit, Context7 |
Shadcn/ui + Aceternity UI Setup and Component Management
⚠️ MANDATORY FIRST STEP
BEFORE RUNNING ANY SETUP COMMANDS: Use Context7 MCP to fetch latest documentation for:
shadcn-ui(initialization, components)aceternity-ui(effects, installation)radix-ui(primitives)
Quick reference for installing, configuring, and using Shadcn/ui components and Aceternity UI effects in Next.js projects.
What is Shadcn/ui?
Shadcn/ui is NOT a traditional component library. It's a collection of copy-paste components built on top of Radix UI primitives. When you add a component, the actual source code is copied into your project, giving you full control.
Benefits:
- Full control over component code
- No dependency bloat
- Customizable with Tailwind CSS
- Accessible by default (Radix UI)
- Type-safe with TypeScript
What is Aceternity UI?
Aceternity UI provides stunning visual effects for landing pages and marketing sections. Like Shadcn/ui, you copy the component code into your project.
Available Effects:
BackgroundBeams- Animated beam lines for hero sectionsTextGenerateEffect- Typewriter text animationMovingBorder- Animated gradient bordersSparklesCore- Particle sparkle effectsBackgroundGradient- Animated gradient backgroundsCardHoverEffect- 3D card hover animations
Quick Start
1. Initialize Shadcn/ui
cd frontend
# Interactive setup (recommended)
npx shadcn-ui@latest init
# You'll be prompted for:
# - TypeScript: Yes
# - Style: Default
# - Base color: Slate (or your preference)
# - Global CSS: src/styles/globals.css
# - CSS variables: Yes
# - Tailwind config: tailwind.config.ts
# - Import alias: @/components
# - React Server Components: Yes
This creates:
components.json- Configuration filesrc/components/ui/- Component directorysrc/lib/utils.ts- Utility functions (cn helper)- Updates
tailwind.config.tsandglobals.css
2. Setup Aceternity UI Directory
# Create Aceternity UI directory
mkdir -p src/components/aceternity
# Install required dependencies
npm install framer-motion tailwind-merge clsx
3. Configuration File
After init, components.json looks like:
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/styles/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
Installing Components
Core Components for Phase 2
# Essential form components
npx shadcn-ui@latest add button
npx shadcn-ui@latest add input
npx shadcn-ui@latest add textarea
npx shadcn-ui@latest add label
npx shadcn-ui@latest add checkbox
npx shadcn-ui@latest add form
# Layout components
npx shadcn-ui@latest add card
npx shadcn-ui@latest add separator
# Feedback components
npx shadcn-ui@latest add dialog
npx shadcn-ui@latest add toast
npx shadcn-ui@latest add alert
npx shadcn-ui@latest add skeleton
# Navigation
npx shadcn-ui@latest add dropdown-menu
npx shadcn-ui@latest add avatar
# Optional utility components
npx shadcn-ui@latest add scroll-area
npx shadcn-ui@latest add tooltip
npx shadcn-ui@latest add badge
Install Multiple at Once
npx shadcn-ui@latest add button input textarea label checkbox form card dialog toast
Component Usage Examples
Button Component
import { Button } from '@/components/ui/button'
export function ButtonExample() {
return (
<div className="flex gap-2">
<Button>Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>
<Button disabled>Disabled</Button>
</div>
)
}
Card Component
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@/components/ui/card'
import { Button } from '@/components/ui/button'
export function TaskCard({ task }: { task: Task }) {
return (
<Card>
<CardHeader>
<CardTitle>{task.title}</CardTitle>
<CardDescription>
Created {new Date(task.created_at).toLocaleDateString()}
</CardDescription>
</CardHeader>
<CardContent>
<p>{task.description}</p>
</CardContent>
<CardFooter className="flex justify-between">
<Button variant="outline">Edit</Button>
<Button variant="destructive">Delete</Button>
</CardFooter>
</Card>
)
}
Form with Input and Validation
'use client'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import * as z from 'zod'
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 { Button } from '@/components/ui/button'
const taskSchema = z.object({
title: z.string().min(1, 'Title is required').max(200),
description: z.string().max(1000).optional(),
})
type TaskFormValues = z.infer<typeof taskSchema>
export function TaskForm({ onSubmit }: { onSubmit: (data: TaskFormValues) => void }) {
const form = useForm<TaskFormValues>({
resolver: zodResolver(taskSchema),
defaultValues: {
title: '',
description: '',
},
})
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Title</FormLabel>
<FormControl>
<Input placeholder="Enter task title" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
render={({ field }) => (
<FormItem>
<FormLabel>Description</FormLabel>
<FormControl>
<Textarea placeholder="Enter task description" {...field} />
</FormControl>
<FormDescription>
Optional description for your task
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Create Task</Button>
</form>
</Form>
)
}
Dialog (Modal) Component
'use client'
import { useState } from 'react'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { TaskForm } from './task-form'
export function CreateTaskDialog() {
const [open, setOpen] = useState(false)
const handleSubmit = async (data: TaskFormValues) => {
// Create task
await createTask(data)
setOpen(false)
}
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button>Create Task</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Create New Task</DialogTitle>
<DialogDescription>
Add a new task to your todo list
</DialogDescription>
</DialogHeader>
<TaskForm onSubmit={handleSubmit} />
</DialogContent>
</Dialog>
)
}
Toast Notifications
First, set up the Toaster in your root layout:
// app/layout.tsx
import { Toaster } from '@/components/ui/toaster'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Toaster />
</body>
</html>
)
}
Then use the toast hook:
'use client'
import { useToast } from '@/hooks/use-toast'
import { Button } from '@/components/ui/button'
export function TaskActions() {
const { toast } = useToast()
const handleDelete = async () => {
try {
await deleteTask(taskId)
toast({
title: 'Task deleted',
description: 'Your task has been successfully deleted',
})
} catch (error) {
toast({
variant: 'destructive',
title: 'Error',
description: 'Failed to delete task. Please try again.',
})
}
}
return <Button variant="destructive" onClick={handleDelete}>Delete</Button>
}
Checkbox Component
'use client'
import { Checkbox } from '@/components/ui/checkbox'
import { Label } from '@/components/ui/label'
export function TaskItem({ task }: { task: Task }) {
const [isCompleted, setIsCompleted] = useState(task.completed)
const handleToggle = async (checked: boolean) => {
setIsCompleted(checked)
await toggleTask(task.id)
}
return (
<div className="flex items-center space-x-3">
<Checkbox
id={`task-${task.id}`}
checked={isCompleted}
onCheckedChange={handleToggle}
/>
<Label
htmlFor={`task-${task.id}`}
className={isCompleted ? 'line-through text-muted-foreground' : ''}
>
{task.title}
</Label>
</div>
)
}
Customizing Components
Modifying Existing Components
All component source code is in src/components/ui/. You can edit directly:
// src/components/ui/button.tsx
import * as React from 'react'
import { cva, type VariantProps } from 'class-variance-authority'
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
// Add custom variant
custom: 'bg-purple-500 text-white hover:bg-purple-600',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
// Add custom size
xl: 'h-14 rounded-md px-10 text-lg',
},
},
}
)
Creating Custom Theme Colors
Update tailwind.config.ts:
export default {
theme: {
extend: {
colors: {
// Custom color palette
brand: {
50: '#f0f9ff',
100: '#e0f2fe',
// ... more shades
900: '#0c4a6e',
},
},
},
},
}
Update globals.css:
@layer base {
:root {
--brand: 217 91% 60%;
--brand-foreground: 0 0% 100%;
}
}
Advanced Patterns
Composing Components
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Checkbox } from '@/components/ui/checkbox'
import { motion } from 'framer-motion'
export function AnimatedTaskCard({ task }: { task: Task }) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, x: -100 }}
>
<Card className="p-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Checkbox checked={task.completed} />
<span>{task.title}</span>
</div>
<Button variant="ghost" size="sm">Edit</Button>
</div>
</Card>
</motion.div>
)
}
Server Component Integration
// app/tasks/page.tsx (Server Component)
import { getTasks } from '@/lib/api'
import { TaskList } from '@/components/tasks/task-list'
export default async function TasksPage() {
const tasks = await getTasks()
return (
<div className="container py-8">
<h1 className="text-3xl font-bold mb-6">My Tasks</h1>
<TaskList initialTasks={tasks} />
</div>
)
}
Aceternity UI Components (Landing Page)
Aceternity UI provides stunning visual effects. Copy these components into src/components/aceternity/.
BackgroundBeams Component
// src/components/aceternity/background-beams.tsx
'use client'
import React from 'react'
import { motion } from 'framer-motion'
import { cn } from '@/lib/utils'
export function BackgroundBeams({ className }: { className?: string }) {
const paths = [
'M-380 -189C-380 -189 -312 216 152 343C616 470 684 875 684 875',
'M-373 -197C-373 -197 -305 208 159 335C623 462 691 867 691 867',
// Add more paths for effect
]
return (
<div className={cn('absolute inset-0 overflow-hidden', className)}>
<svg
className="pointer-events-none absolute inset-0 h-full w-full"
width="100%"
height="100%"
viewBox="0 0 696 316"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
{paths.map((path, index) => (
<motion.path
key={index}
d={path}
stroke="url(#beam-gradient)"
strokeOpacity="0.4"
strokeWidth="1"
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={{ duration: 2, delay: index * 0.2, repeat: Infinity }}
/>
))}
<defs>
<linearGradient id="beam-gradient" gradientUnits="userSpaceOnUse">
<stop stopColor="#18CCFC" stopOpacity="0" />
<stop offset="0.5" stopColor="#6344F5" />
<stop offset="1" stopColor="#AE48FF" stopOpacity="0" />
</linearGradient>
</defs>
</svg>
</div>
)
}
TextGenerateEffect Component
// src/components/aceternity/text-generate-effect.tsx
'use client'
import { useEffect } from 'react'
import { motion, stagger, useAnimate } from 'framer-motion'
import { cn } from '@/lib/utils'
interface TextGenerateEffectProps {
words: string
className?: string
}
export function TextGenerateEffect({ words, className }: TextGenerateEffectProps) {
const [scope, animate] = useAnimate()
const wordsArray = words.split(' ')
useEffect(() => {
animate(
'span',
{ opacity: 1, filter: 'blur(0px)' },
{ duration: 0.8, delay: stagger(0.1) }
)
}, [animate])
return (
<motion.div ref={scope} className={cn('font-bold', className)}>
{wordsArray.map((word, idx) => (
<motion.span
key={word + idx}
className="opacity-0"
style={{ filter: 'blur(10px)' }}
>
{word}{' '}
</motion.span>
))}
</motion.div>
)
}
MovingBorder Component
// src/components/aceternity/moving-border.tsx
'use client'
import React from 'react'
import { motion } from 'framer-motion'
import { cn } from '@/lib/utils'
interface MovingBorderProps {
children: React.ReactNode
className?: string
containerClassName?: string
borderRadius?: string
duration?: number
}
export function MovingBorder({
children,
className,
containerClassName,
borderRadius = '1rem',
duration = 2000,
}: MovingBorderProps) {
return (
<div
className={cn('relative overflow-hidden p-[1px]', containerClassName)}
style={{ borderRadius }}
>
<motion.div
className="absolute inset-0"
style={{
background: 'linear-gradient(90deg, #18CCFC, #6344F5, #AE48FF, #18CCFC)',
backgroundSize: '300% 100%',
}}
animate={{
backgroundPosition: ['0% 0%', '100% 0%', '0% 0%'],
}}
transition={{
duration: duration / 1000,
repeat: Infinity,
ease: 'linear',
}}
/>
<div
className={cn('relative bg-background', className)}
style={{ borderRadius }}
>
{children}
</div>
</div>
)
}
Landing Page Hero Example
// components/landing/hero-section.tsx
'use client'
import { BackgroundBeams } from '@/components/aceternity/background-beams'
import { TextGenerateEffect } from '@/components/aceternity/text-generate-effect'
import { MovingBorder } from '@/components/aceternity/moving-border'
import { Button } from '@/components/ui/button'
import Link from 'next/link'
export function HeroSection() {
return (
<section className="relative min-h-screen flex items-center justify-center bg-slate-950">
<BackgroundBeams />
<div className="relative z-10 text-center max-w-4xl mx-auto px-4">
<TextGenerateEffect
words="Organize your life, one task at a time"
className="text-4xl md:text-6xl text-white mb-6"
/>
<p className="text-lg text-gray-400 mb-8">
The simplest way to manage your tasks and boost productivity
</p>
<div className="flex gap-4 justify-center">
<MovingBorder containerClassName="rounded-full">
<Button size="lg" className="rounded-full" asChild>
<Link href="/signup">Get Started Free</Link>
</Button>
</MovingBorder>
<Button variant="outline" size="lg" className="rounded-full" asChild>
<Link href="#features">Learn More</Link>
</Button>
</div>
</div>
</section>
)
}
Component Reference for Phase 2
| Component | Use Case | Priority |
|---|---|---|
| Button | All actions (submit, cancel, delete) | High |
| Input | Text fields (title, email, password) | High |
| Textarea | Multiline text (description) | High |
| Checkbox | Task completion toggle | High |
| Label | Form field labels | High |
| Form | Integrated form handling | High |
| Card | Task display container | High |
| Dialog | Modals (create/edit task) | High |
| Toast | Success/error notifications | High |
| Avatar | User profile display | Medium |
| Dropdown Menu | User menu, task actions | Medium |
| Separator | Visual dividers | Low |
| Skeleton | Loading states | Low |
Troubleshooting
Component not found after installation:
# Reinstall component
npx shadcn-ui@latest add button --overwrite
Style conflicts:
- Check
globals.cssimports order - Verify Tailwind CSS variables are defined
- Clear
.nextcache and restart
TypeScript errors:
- Ensure
@/componentsalias is configured intsconfig.json - Run
npm installto install new peer dependencies
Best Practices
- Don't modify core variants - Create new variants instead
- Use the cn() utility - For conditional classes
- Keep components accessible - Shadcn/ui is accessible by default, maintain it
- Test on multiple screen sizes - Components are responsive
- Follow naming conventions - Use component names as-is
References
- Shadcn/ui: https://ui.shadcn.com/
- Radix UI: https://www.radix-ui.com/
- Tailwind CSS: https://tailwindcss.com/docs
- CVA (Class Variance Authority): https://cva.style/docs