Intermediate20 min

Learn how to properly integrate v0-generated components into your React and Next.js projects.

Integrating v0 Output

v0 generates beautiful components, but they need a home. This lesson covers how to properly integrate v0 output into your React or Next.js projects—handling dependencies, adapting code, and maintaining consistency.

Prerequisites for Integration

Project Setup

Before using v0 components, ensure your project has:

Required:

  • React 18+
  • TypeScript (strongly recommended)
  • Tailwind CSS configured

Recommended:

  • Next.js 14+ (App Router)
  • shadcn/ui installed

Installing shadcn/ui

If you haven't set up shadcn/ui:

Terminal
# In your Next.js project
npx shadcn@latest init

Follow the prompts:

  • Style: Default or New York
  • Base color: Choose your preference
  • CSS variables: Yes (recommended)

Understanding the Output

v0 generates code that assumes shadcn/ui is installed:

Terminal
import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"

These imports reference components in your components/ui folder.

Step-by-Step Integration

Step 1: Copy the v0 Code

Click the copy button in v0's code panel. You get something like:

Terminal
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader } from "@/components/ui/card"

export default function ProfileCard() {
  return (
    <Card>
      <CardHeader>
        <Avatar>
          <AvatarImage src="/placeholder.jpg" />
          <AvatarFallback>JD</AvatarFallback>
        </Avatar>
      </CardHeader>
      <CardContent>
        <h3 className="font-semibold">Jane Doe</h3>
        <p className="text-muted-foreground">Product Designer</p>
        <Button className="mt-4 w-full">Follow</Button>
      </CardContent>
    </Card>
  )
}

Step 2: Check for Missing Components

Identify which shadcn/ui components are needed:

Terminal
// This component uses:
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader } from "@/components/ui/card"

Step 3: Install Missing Components

Use the shadcn CLI to add them:

Terminal
npx shadcn@latest add avatar
npx shadcn@latest add button
npx shadcn@latest add card

Or install multiple at once:

Terminal
npx shadcn@latest add avatar button card

Step 4: Create the Component File

Save in your components folder:

Terminal
components/
├── ui/              # shadcn components
├── profile-card.tsx # Your v0 component
└── ...

Step 5: Adapt for Your Needs

Replace placeholders with real data:

Terminal
interface ProfileCardProps {
  name: string
  role: string
  avatarUrl?: string
  onFollow?: () => void
}

export function ProfileCard({
  name,
  role,
  avatarUrl,
  onFollow
}: ProfileCardProps) {
  return (
    <Card>
      <CardHeader>
        <Avatar>
          <AvatarImage src={avatarUrl} alt={name} />
          <AvatarFallback>{name.slice(0, 2).toUpperCase()}</AvatarFallback>
        </Avatar>
      </CardHeader>
      <CardContent>
        <h3 className="font-semibold">{name}</h3>
        <p className="text-muted-foreground">{role}</p>
        <Button className="mt-4 w-full" onClick={onFollow}>
          Follow
        </Button>
      </CardContent>
    </Card>
  )
}

Step 6: Use in Your App

Terminal
// app/page.tsx
import { ProfileCard } from '@/components/profile-card'

export default function HomePage() {
  return (
    <div className="p-8">
      <ProfileCard
        name="Jane Doe"
        role="Product Designer"
        avatarUrl="/images/jane.jpg"
        onFollow={() => console.log('Followed!')}
      />
    </div>
  )
}

Common Integration Patterns

Pattern 1: Direct Usage

For simple, static components:

Terminal
// Copy directly, minimal changes
export default function HeroSection() {
  // v0 code works as-is
}

Pattern 2: Props Extraction

For reusable components:

Terminal
// Extract all dynamic content as props
interface StatsCardProps {
  title: string
  value: string | number
  change?: number
  icon?: React.ReactNode
}

export function StatsCard({ title, value, change, icon }: StatsCardProps) {
  // Use props instead of hardcoded values
}

Pattern 3: Composition

For complex components:

Terminal
// Break into composable parts
export function DataTable({ columns, data, onRowClick }: DataTableProps) {
  return (
    <Table>
      <DataTableHeader columns={columns} />
      <DataTableBody data={data} columns={columns} onRowClick={onRowClick} />
      <DataTablePagination totalPages={10} />
    </Table>
  )
}

Pattern 4: Wrapper Components

For extending v0 output:

Terminal
// Wrap with additional functionality
import { BaseCard } from './base-card' // v0 component

export function ClickableCard({ onClick, ...props }: ClickableCardProps) {
  return (
    <div
      onClick={onClick}
      className="cursor-pointer transition-transform hover:scale-[1.02]"
    >
      <BaseCard {...props} />
    </div>
  )
}

Handling Dependencies

Checking Import Paths

v0 uses the @/ alias. Ensure your tsconfig.json has:

Terminal
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
      // or "./*" depending on your structure
    }
  }
}

Missing Icon Libraries

v0 often uses Lucide icons:

Terminal
npm install lucide-react

Usage:

Terminal
import { Home, Settings, User } from "lucide-react"

Utility Functions

v0 may reference a cn utility for class merging:

Terminal
// lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Install dependencies:

Terminal
npm install clsx tailwind-merge

Adapting for State Management

Adding Local State

Terminal
'use client' // Required for useState in App Router

import { useState } from 'react'

export function ToggleCard() {
  const [isActive, setIsActive] = useState(false)

  return (
    <Card className={isActive ? 'border-primary' : ''}>
      <CardContent>
        <Switch
          checked={isActive}
          onCheckedChange={setIsActive}
        />
      </CardContent>
    </Card>
  )
}

Connecting to Context

Terminal
'use client'

import { useUser } from '@/context/user-context'

export function UserMenu() {
  const { user, logout } = useUser()

  return (
    <DropdownMenu>
      <DropdownMenuTrigger>
        <Avatar>
          <AvatarImage src={user.avatar} />
        </Avatar>
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem onClick={logout}>
          Sign out
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

API Integration

Terminal
'use client'

import { useEffect, useState } from 'react'

interface User {
  id: string
  name: string
  email: string
}

export function UserList() {
  const [users, setUsers] = useState<User[]>([])
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    fetch('/api/users')
      .then(res => res.json())
      .then(data => {
        setUsers(data)
        setLoading(false)
      })
  }, [])

  if (loading) return <Skeleton /> // v0 loading state

  return (
    <div>
      {users.map(user => (
        <UserCard key={user.id} user={user} />
      ))}
    </div>
  )
}

File Organization

Recommended Structure

Terminal
src/
├── components/
   ├── ui/                    # shadcn/ui base components
      ├── button.tsx
      ├── card.tsx
      └── ...
   ├── features/              # Feature-specific components
      ├── auth/
         ├── login-form.tsx
         └── signup-form.tsx
      └── dashboard/
          ├── stats-cards.tsx
          └── recent-activity.tsx
   └── layouts/               # Layout components
       ├── header.tsx
       ├── sidebar.tsx
       └── footer.tsx
├── lib/
   └── utils.ts               # cn() and other utilities
└── app/
    └── ...

Naming Conventions

Terminal
# Component files
profile-card.tsx       # kebab-case file names
ProfileCard           # PascalCase component names

# Feature organization
features/auth/login-form.tsx
features/dashboard/stats-grid.tsx

Troubleshooting Common Issues

"Module not found" Errors

Check your import paths match your project structure:

Terminal
// v0 generates:
import { Button } from "@/components/ui/button"

// Might need to be:
import { Button } from "../../components/ui/button"
// or configure @ alias properly

Type Errors

v0 sometimes generates loose TypeScript. Fix with proper types:

Terminal
// Before
function handleClick(item) {
  // ...
}

// After
function handleClick(item: ItemType) {
  // ...
}

Style Conflicts

If Tailwind styles don't apply:

  1. Check Tailwind content paths in tailwind.config.js
  2. Ensure CSS import order is correct
  3. Check for conflicting styles

Server/Client Component Issues

In Next.js App Router, add 'use client' when using:

  • useState, useEffect, other hooks
  • Event handlers (onClick, onChange)
  • Browser APIs

Maintaining Consistency

Create a Component Pattern

Document how you adapt v0 components:

Terminal
// Component template
interface ComponentNameProps {
  // Always define explicit props
}

export function ComponentName(props: ComponentNameProps) {
  // Destructure props
  const { prop1, prop2 } = props

  // Component logic

  return (
    // JSX
  )
}

Consistent Export Style

Pick one and stick with it:

Terminal
// Named exports (recommended)
export function ProfileCard() { }

// Default exports
export default function ProfileCard() { }

Theme Alignment

Ensure v0 output matches your theme:

Terminal
// If v0 uses blue-500 but your theme uses indigo
// Global find/replace or configure in Tailwind theme

Summary

  • Install shadcn/ui components before using v0 output
  • Extract props to make components reusable
  • Add state and effects with 'use client' directive
  • Organize files by feature or component type
  • Fix types and handle client/server component rules
  • Maintain consistency across your codebase

Next Steps

You can now integrate v0 components seamlessly. Let's cover best practices for using v0 effectively in your workflow.

Mark this lesson as complete to track your progress