Same UI (shadcn/ui + Tailwind)
Same framework (Next.js App Router)
Same TypeScript conventions
Zero redesign needed

Starting point vs full stack

v0 is a UI generator. It gives you components, not a product. Already is the production stack those components slot into. You're not migrating a running app — you're wiring polished UI into a production foundation.

This means there's no data to move, no users to preserve, no auth to untangle. You start with Already fully configured, then drop your v0 components in and connect them to real data.

What v0 generates (and what it doesn't)

v0 by Vercel is a UI generation tool — it creates high-quality shadcn/ui components and pages for Next.js App Router. What it doesn't give you is the full-stack production foundation: no auth, no billing, no multi-tenancy, no background jobs.

Already is exactly that foundation. It's Next.js 15 App Router with 16 production modules pre-wired. Your v0 components drop in without modification — they were built for the same stack.

What carries over without changes

  • All your UI components. v0 generates shadcn/ui with standard Tailwind. Copy them into Already's components/ directory — no class changes, no re-theming, no adapting.
  • Your page layouts. v0 uses Next.js App Router conventions. Already uses the same — your route layouts slot in directly.
  • Your TypeScript types. v0 follows standard TypeScript patterns. Already's codebase is fully typed and uses the same conventions. v0-generated interface definitions can seed your Drizzle schema declarations directly.
  • Next.js Image usage. v0 sometimes generates <Image /> from next/image. That works directly in Already — no changes needed.
  • Tailwind design tokens. v0's Tailwind classes are fully compatible with Already's Tailwind v4 setup.

Replacing mock data with real queries

v0 often generates components with inline mock data. Replacing it with real Server Component queries is the main wiring task.

// v0 pattern — mock data inline
const mockProjects = [
  { id: 1, name: "Website Redesign", status: "active" },
  { id: 2, name: "Mobile App", status: "draft" },
]
export default function ProjectList() {
  return <ul>{mockProjects.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}

// Already pattern — Server Component with Drizzle query
import { db } from '@/lib/db'
import { projects } from '@/db/schema'
import { withOrgScope } from '@/lib/org'

export default async function ProjectList() {
  const rows = await withOrgScope(db.select().from(projects))
  return <ul>{rows.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}

TypeScript interfaces → Drizzle schema

v0 generates TypeScript interfaces for its data shapes. These can seed your Drizzle schema declarations directly — the field names and types map across cleanly.

// v0 generated interface
interface Project {
  id: number
  name: string
  status: 'active' | 'draft'
  createdAt: Date
}

// Drizzle schema in db/schema/projects.ts
import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core'
export const projects = pgTable('projects', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  status: text('status', { enum: ['active', 'draft'] }).notNull(),
  createdAt: timestamp('created_at').defaultNow(),
})

When to keep vs remove "use client"

v0 marks many components "use client" even when they don't need to be — it's a safe default for a UI generator with no backend context. In Already, Server Components are preferred for anything that doesn't need browser APIs or interactivity.

  • Keep "use client" if the component uses: useState, useEffect, event handlers (onClick, onChange), browser APIs (window, localStorage), or third-party hooks.
  • Remove "use client" if the component only renders data passed as props, uses no React hooks, and has no event handlers. Make it async and fetch data directly.

What Already adds on top

  • Auth. Supabase Auth with email, OAuth, magic links, TOTP, and passkeys. Server Component middleware, RLS policies, and requireAuth() layout guards.
  • Billing. Stripe subscriptions with idempotent webhooks, customer portal, and plan-gating via requirePlan('pro').
  • Multi-tenancy. withOrgScope() query helpers, RLS isolation, invitation flow, and per-org billing — all pre-wired.
  • Background jobs, API keys, feature flags, i18n, AI integration — 16 modules total, all included.

Step-by-step integration

01
Clone Already and run pnpm setup

Buy a Solo or Team licence, clone the repository, create a Supabase project, and run pnpm setup. It validates env vars, runs DB migrations, and gets you to a running local app in about 20 minutes.

02
Copy your v0 components into components/

Drop your v0-generated components into Already's components/ directory. shadcn/ui and Tailwind v4 are already installed — nothing to add, nothing to configure. Your component imports will resolve immediately.

03
Audit "use client" directives

Go through your v0 components and remove "use client" from any that only display data without hooks or event handlers. Convert those to async Server Components. Keep it on components that use useState, useEffect, or browser event handlers.

04
Place pages into the right route groups

Move authenticated product pages under app/(app)/, auth flows under app/(auth)/, and marketing or public pages under app/(public)/. Layout guards at each group boundary handle auth checking automatically — no per-page getSession() calls.

05
Declare your schema and replace mock data

Use your v0 TypeScript interfaces as a starting point for Drizzle schema files in db/schema/. Then replace each component's mock data array with a Server Component query using withOrgScope(db.select().from(yourTable)).

06
Gate features behind billing plans

Run pnpm setup:stripe to create Stripe products. Wrap premium routes or server actions with await requirePlan('pro'). The webhook handler, upgrade flow, and customer portal are pre-built and tested.

Frequently asked

My v0 component uses mock API calls (fetch('/api/data')) — what now?

Replace the fetch with a Server Component that queries your Drizzle schema directly using withOrgScope(). The data flows in at render time — no client-side fetch needed. If you need a client-side API route for a mutation (form submit, button click), Already's app/api/ structure is ready to use.

I used v0 to generate a full app, not just components — does that change anything?

No. Whether you have individual components or full pages from v0, the process is the same. Move page files into the appropriate route group and connect them to Already's data layer. The component code is identical regardless of scope.

Do I need to keep using v0 after migrating?

Entirely up to you. Already's CLAUDE.md is pre-written so Claude Code can generate new components in the same shadcn/ui style. Many buyers use v0 to sketch a component and Claude Code to integrate it — they're fully compatible workflows.

Will v0's Tailwind classes conflict with Already's?

No. Already ships Tailwind CSS v4. v0 also targets Tailwind v4. All utility classes are compatible. Already's design tokens (--paper, --ink, --signal) live in custom properties you can optionally use for semantic theming on top of the standard Tailwind classes.