Make v0 output a maintainable Next.js repo, not a throwaway demo
Turn v0-generated UI into a long-lived Next.js app: opinionated repo structure, design system cleanup, real data, a11y, tests and CI on Vercel.
What “from v0 UI to a production Next.js repository” actually means
If screens are already being sketched in v0, a key decision is to treat v0 as a generator of UI and scaffolding, and a GitHub-backed Next.js repository as the long‑lived source of truth. That repo should own design, data, auth, testing and deployments. v0 then remains a fast collaborator, rather than the environment that production is shipped from.
This article lays out a concrete workflow:
- Use v0 to generate Next.js App Router UI (React, TypeScript, Tailwind, shadcn/ui).
- Connect a GitHub repo and promote that UI into a stable structure with clear ownership boundaries.
- Refactor into a design system, wire real data and auth, harden accessibility, and add tests.
- Let Vercel (or alternatives) deploy only what passes CI gates, even when changes start in v0.
For deeper product context on v0 itself, including how its editor works and where it is strong and weak as a UI builder, see the dedicated v0 by Vercel review and the broader AI coding tools shortlist on this site.
Baseline: how v0 structures projects and repositories today
Vercel positions v0 as a way to “build full-stack web apps with AI” that generates production‑grade React/Next.js UI and can connect directly to GitHub so code lives in a real repository, as described on the main v0 site v0 by Vercel - Build Full-Stack Web Apps with AI. Documentation and partner guides describe v0-generated apps as standard Next.js App Router projects using React and TypeScript, commonly styled with Tailwind CSS and often incorporating shadcn/ui components, which can be deployed to Vercel or other hosts such as Netlify Deploy Your v0 App to Netlify.
v0’s public homepage positions it as an AI tool for building full-stack web apps and highlights direct GitHub sync, grounding the discussion of v0’s repository-centred workflow.
For GitHub-backed projects, the v0 changelog explains that v0 can work directly against a repository and branch. Each publish action raises or updates a pull request, and merging into the base branch triggers preview and production deployments when connected to Vercel Changelog | v0. That unified branch‑and‑publish flow is attractive, but it also means v0 can, in theory, touch any part of an app if it is not constrained.
v0 plans, limits and why they matter to workflow
The v0 pricing page sets out four plans in USD (all prices excluding VAT, GST and other applicable taxes) v0 by Vercel – Plans and Pricing:
| Plan |
Price (per user / month) |
Included monthly credits |
Daily login credits |
Key limits |
| Free |
$0 |
$5 |
– |
7 messages/day; deploy apps to Vercel; Design Mode; GitHub sync |
| Plus |
$30 |
$30 |
$2/day per user |
Access to all models; $30 of monthly credits per user; $2 of free daily login credits per user; additional team‑shared credits; centralised billing and team collaboration |
| Business |
$100 |
$30 |
$2/day per user |
Access to all models; $30 of monthly credits per user; $2 of free daily login credits per user; training opt‑out by default; shared extra credits; centralised billing and collaboration |
| Enterprise |
Custom |
Custom |
Custom |
Access to all models; your data is never used for training; SAML SSO; role‑based access control; priority access for better performance and no queues; support SLAs |
As of the latest public pricing documentation referenced here, the pricing page lists v0’s first‑party models (currently named Mini, Pro, Max, Max Fast) with on‑demand token pricing per million input, output and cache tokens v0 by Vercel – Plans and Pricing. Exact dollar amounts and model names may change over time and should always be confirmed against the live pricing page before budgeting.
These numbers matter because the proposed workflow is UI‑heavy: frequent generations, speculative redesigns and refactors. The repo architecture does not change hosting cost, but it does influence how much rework is sent through v0. A clean boundary between v0‑editable UI and human‑owned domain code keeps higher‑priced models away from business logic.
Designing a production Next.js repository around v0 output
Several migration guides and agency write‑ups treat v0 as primarily a front‑end/UI generator and note that backend, data models, auth and deployment need additional engineering work to reach production Migrate from v0 to production Next.js · FullCode · Yo! No Code. This section turns that into an explicit repository architecture. If you are designing this alongside IDE agents, it pairs well with the broader AI development workflow from prompt to production described elsewhere on this site.
Step 1: decide on repo shape
For a v0‑centric app, a single Next.js app is often preferable to a large polyglot monorepo for the first iteration. A common, maintainable layout is:
/app – Next.js App Router routes, layouts and server components.
/components – shared React components.
/features – feature‑oriented slices combining UI and domain wiring.
/lib – data access, helpers, API clients.
/tests – unit/integration tests.
/e2e – Playwright/Cypress end‑to‑end tests.
Next.js supports both /app and /src/app. v0‑generated projects are commonly structured with /app at the root. To minimise friction when syncing, it is usually simpler to keep that convention and avoid relocating /app unless there is a strong reason.
Step 2: define ownership boundaries in the tree
To make v0 safe in a long‑lived repo, define two worlds:
- v0 world (AI‑editable) – presentational components and page shells.
- human‑only world – domain logic, data, auth, critical configuration.
An opinionated split:
/app/(v0) – public‑facing routes largely designed in v0.
/app/(secure) – authenticated routes where v0 can propose UI in PRs but cannot merge changes without human review.
/components/v0 – raw v0 components and shadcn clones before cleanup.
/components/ui – curated design system primitives, not directly edited by v0 once stabilised.
/features/* – human‑owned feature slices combining /components/ui with data and state.
/lib/db, /lib/auth, /lib/payments – human‑only; never generated or edited by v0.
This can be enforced by convention and tooling:
- Document “v0 may touch only
/app/(v0) and /components/v0” in CONTRIBUTING.md.
- Add a simple script in CI that fails if PR diffs from v0 include changes under
/lib or /features.
- Use CODEOWNERS to require senior review for protected directories.
The result is that v0 remains fast for UI, while everything that carries business risk sits behind human review.
Component ownership and design-system cleanup
Specialist agencies note that v0’s output is often good enough visually but lacks the structure of a long‑term design system Migrate from v0 to production Next.js · FullCode · Yo! No Code. The goal is to turn a one‑off set of components into stable primitives and patterns. For teams already betting on AI-first stacks, it also helps to align this with your broader tool choice from the best AI development stack for 2026 guide.
Step 1: audit the initial v0 drop
After the first v0 export, a practical next step is to walk the tree and classify:
- Primitives – buttons, inputs, selects, cards, modals, tooltips.
- Layout – page shells, grids, sidebars, navbars.
- Feature components – e.g.
InvoicesTable, ProfileForm, ChatPanel.
- Throwaways – demo sections, duplicated variants, unused experiments.
Move primitives into /components/ui, layouts into /components/layout, feature components into /features/<feature>/components, and delete throwaways.
Step 2: extract a coherent design system layer
v0 often uses Tailwind plus shadcn/ui. shadcn/ui itself encourages cloning and customising components. To avoid drift:
- Choose a small set of theme tokens (spacing scale, radii, typography, colours) and encode them in Tailwind config.
- Normalise all v0 classes to use that scale (
p-3.5 becomes p-4 if that matches the system; arbitrary values like px-[17px] should be removed).
- Wrap shadcn primitives into custom
<Button>, <Input>, <Dialog> components, exported from /components/ui.
- Ban direct use of raw shadcn imports in feature code via ESLint rules.
Once this is done, lock down /components/ui with CODEOWNERS so only named maintainers can modify primitives. v0 contributors should consume them via imports, not clone new ones.
Step 3: keep future v0 imports in line
Future v0 generations will still arrive with their own Tailwind class combinations. To keep them compatible:
- Use lint rules to warn on arbitrary Tailwind values and non‑token colours.
- Run codemods periodically to map common class combinations to utility components or layout wrappers.
- Explicitly include in v0 prompts: “Use existing components from
components/ui. Do not introduce new colours or font sizes.”
This keeps the v0 world bounded while still allowing UI iteration.
Data wiring: from placeholder UI to real backends
At least one migration guide notes that v0 UIs typically ship with mock data, limited validation and no secure auth or error handling until a developer adds them From V0 to Production | Turn V0 UI Into a Real App. The workflow below uses, as an opinionated example, a typed Postgres backend (Supabase or Neon) plus an ORM such as Prisma or Drizzle.
Step 1: choose your backend and data layer
One example pattern outlined by Afterbuild Labs pairs:
For readers choosing between Supabase and Firebase specifically, there is a separate deep dive comparing Supabase vs Firebase for AI-built apps on this site. That article covers trade‑offs on SQL vs NoSQL, RLS, triggers and auth in more depth, and complements the broader Supabase review for AI builders.
In this workflow:
/lib/db exposes a typed client (e.g. Prisma) and model functions.
/lib/auth exposes “current user”, role info, and helpers to enforce policies.
- Server actions and API routes use
/lib/db and /lib/auth rather than direct ORM calls scattered across components.
Step 2: replace mock data with typed queries
Typical v0 patterns include static arrays in components or fetches from placeholder URLs. For each feature:
- Move data fetching into a server component or server action under
/app or /features.
- Define TypeScript types in
/lib/types or derive them from the ORM schema.
- Thread typed props into v0‑derived presentational components, stripping out
fetch calls from the UI layer.
This separation lets v0 keep iterating on presentational components without touching data access logic.
Step 3: wire auth and authorisation
Supabase and similar backends offer row‑level security; Neon pairs well with custom auth. A common pattern is:
- Use a top‑level layout under
/app/(secure) that loads the current session and either renders the secure tree or redirects to sign‑in.
- Gate server actions with role checks from
/lib/auth and database policies rather than trusting the client.
- Keep v0’s involvement here to UI components for forms and dashboards; avoid letting it generate auth logic.
For teams that want detailed guidance on choosing and wiring Supabase in particular, the site’s Supabase review for AI builders goes into recommended defaults, auth patterns and cost profile.
Step 4: handle error and loading states
v0‑generated screens often under‑specify error cases and loading flows. In a production repo:
- Use Next.js
error.tsx and loading.tsx files at route segments to centralise handling.
- Add skeleton components in
/components/ui for consistent loading visuals.
- Standardise error patterns (e.g. toast plus inline message) and expose them as primitives; ask v0 to use these instead of inventing new ones.
Accessibility pass on v0-generated interfaces
Accessibility issues in AI‑generated UI are predictable: missing labels, bad heading hierarchy, non‑focusable elements. v0’s stack (React + shadcn/ui) can be accessible, but it requires disciplined implementation.
Step 1: identify common gaps
Common issues in early v0 drops include:
- Use of
<div> for interactive elements instead of buttons or links.
- Missing
aria-label on icon buttons and interactive icons.
- Improper heading nesting (
h1 reused multiple times, skipped levels).
- Dialogs and drawers without proper focus trapping or labelling.
- Form fields without associated
<label> elements.
Step 2: harden shadcn-based components centrally
Because shadcn/ui components are cloned into the codebase, accessibility fixes can be applied once in the primitive:
- Ensure
<Dialog>, <Drawer> and other overlays use accessible patterns and ARIA attributes.
- Standardise how icon‑only buttons express labels (e.g.
aria-label prop on an <IconButton> wrapper).
- Wrap form controls into composite components that always render a label and description.
Once primitives are correct, downstream v0‑generated feature components inherit that behaviour as long as they use /components/ui.
Step 3: add automated accessibility checks to CI
A practical minimum for a v0‑derived app:
- ESLint plugins for JSX a11y rules to catch missing labels and wrong roles at lint time.
- Storybook with a11y addon for design‑system components, if Storybook is in use.
- Playwright or Cypress tests with axe‑core integration on key routes.
These checks can be wired into the GitHub pipeline so that any PR, including those initiated by v0, must pass basic a11y checks before merge.
Testing strategy for a v0-derived Next.js codebase
The aim is to keep tests stable even if v0 continues to refactor the UI. That suggests a focus on domain flows rather than snapshots of specific components.
Unit and integration tests
- Unit‑test utilities and pure functions in
/lib with a framework like Vitest or Jest.
- Integration‑test feature flows in
/features (e.g. form submission and validation) using React Testing Library to treat v0 components as black boxes.
- Avoid snapshot tests of full component trees; they will be brittle under v0‑driven UI changes.
E2E tests on critical paths
Use Playwright or Cypress for:
- Authentication flows and session handling.
- Core CRUD operations (e.g. create invoice, pay invoice, see payment status).
- Payment flows (Stripe checkout), if relevant.
- Key dashboards and reporting pages.
These tests can be hooked into the same GitHub CI that runs on all PRs. For v0‑initiated PRs, any regression in a core flow then blocks automatic publish.
GitHub, CI/CD, and deployment with Vercel (and alternatives)
v0 integrates deeply with GitHub and Vercel. The changelog notes that GitHub‑backed v0 projects use a unified branch‑and‑publish flow where publishing creates or reuses a pull request, merges into the base branch and deploys to production, with preview deployments for each change Changelog | v0.
Step 1: connect v0 to GitHub with Git as source of truth
The decision rule is that GitHub remains authoritative while v0 acts as a client that proposes changes via PRs. In practice:
- Create a GitHub repo with the structure outlined earlier.
- Connect v0 to this repo and a specific branch (e.g.
v0-main or develop).
- Ensure branch protection on
main: required reviews, required status checks, no direct pushes.
This way, even if v0 has a button to “publish”, that publish results in a PR into main which must pass tests and review.
Step 2: layer CI on top of v0’s publish flow
Add a CI pipeline that runs on pull requests from any source:
pnpm lint or npm run lint for ESLint and a11y rules.
pnpm test for unit/integration tests.
pnpm test:e2e on a limited subset of critical specs for PRs, and the full suite on main merges.
- Type‑checking (e.g.
tsc --noEmit).
If you are also experimenting with branch‑only AI workflows, this pattern pairs cleanly with the Codex + Vercel preview‑to‑production workflow and the broader AI coding agents in CI/CD pipelines guide.
Step 3: configure Vercel (or alternatives)
When connecting the repo to Vercel:
- Use one project per Next.js app.
- Define environment variables for preview and production (API keys, database URLs, feature flags).
- Ensure that the production environment only builds from
main; preview environments build from PR branches.
For smaller side projects and MVPs, there is a separate guide on this site for deploying side projects on Vercel safely, including sensible defaults and how to avoid surprise bills.
Netlify’s guidance on deploying v0 apps shows that v0’s output is a standard Next.js project that can be hosted on other providers such as Netlify Deploy Your v0 App to Netlify. If deploying elsewhere, the same GitHub‑driven CI pattern can be replicated; only the final deploy step changes.
Operating the system: costs, limits, and safe use of v0 in production
The structural decisions above do not change Vercel or database bills, but they influence how many iterations run through v0 and which plan/model tier is required.
Plan selection by usage pattern
Based on v0’s pricing page v0 by Vercel – Plans and Pricing, three scenarios cover many teams:
- Solo builder, light usage – Free plan at $0/month with included credits and a daily message cap. Most refactoring and data wiring can happen locally with tools like Cursor or Claude Code. v0 is used for initial UI passes and occasional redesigns. For a deeper look at Cursor’s strengths and limits, see the dedicated Cursor review and the follow‑up on Cursor rules for real projects.
- Small product team, heavy UI iteration – Plus with per‑user monthly pricing and bundled credits. Each active UI owner has a seat. Cheaper models can be used for small tweaks, reserving higher‑tier models for complex flows.
- Larger/compliance‑sensitive team – Business with per‑user pricing, training opt‑out and governance features, or Enterprise with SAML SSO and RBAC. Seats are typically limited to people actively designing UI; backend engineers can use local agents.
Token costs and model choice
The v0 pricing page lists on‑demand token prices for each first‑party model tier v0 by Vercel – Plans and Pricing. Because those prices and model names are periodically updated, the live pricing table should be treated as the authoritative source.
A simple budget heuristic aligned with v0’s tiered model approach:
- Default to the lowest‑priced model tier for layout tweaks and text changes.
- Use mid‑tier models when requesting substantial multi‑screen flows or cross‑page refactors.
- Reserve the most expensive, latency‑optimised tiers for complex reasoning tasks or where response time is critical.
Because credits are denominated in USD, not tokens, the right model depends on how many generations a team triggers per feature. High‑churn designs lean towards cheaper models and tighter prompts; stable design systems can accommodate occasional expensive passes.
Guardrails for safe, cost‑effective v0 use
- Limit v0 seats to people actively responsible for UI.
- Set team‑wide monthly spend caps on additional credits in the v0 billing settings.
- Use local agents (Cursor, Claude Code, GitHub Copilot) for refactoring and data wiring once the code is in GitHub.
- Keep the v0 API v2 at arm’s length for now; the v0 API documentation explicitly labels API v2 as beta and “not yet ready for production” Overview | v0 Docs (API v2). Treat generated code, not the API, as the production surface until that status changes.
For teams interested in IDE/agent trade‑offs, this site has a deeper comparison of Cursor vs Claude Code vs editor and terminal agents, and separate guidance on using AI coding agents in PR‑only workflows in the AI coding agent PR workflow guide.
Putting it together: a step-by-step v0-to-Next.js migration plan
This section summarises the workflow into an ordered checklist, assuming a v0 project with a handful of screens (e.g. dashboard, detail page, settings) and a Next.js + Vercel stack.
Phase 1: seed the UI with v0
- In v0, generate the initial screens for core flows.
- Connect v0 to a new GitHub repo and sync the project.
- Inspect the generated file tree (App Router, components, shadcn/ui files).
Phase 2: establish repo structure and boundaries
- Reorganise into the structure described earlier:
/app, /components/ui, /components/v0, /features, /lib.
- Move raw v0 components into
/components/v0 and route shells under /app/(v0).
- Create CODEOWNERS to protect
/lib, /features and /components/ui.
- Add
CONTRIBUTING.md stating where v0 can and cannot write.
Phase 3: build the design system and accessibility baseline
- Consolidate Tailwind config and design tokens.
- Refactor shadcn components into
/components/ui with stable APIs.
- Fix accessibility issues in primitives (focus handling, ARIA, labels).
- Add ESLint with JSX a11y rules.
Phase 4: wire data, auth and error handling
- Set up Supabase or Neon with a Postgres schema and auth.
- Introduce
/lib/db (ORM client) and /lib/auth.
- Replace mock data in v0 components with typed props sourced from server components or actions.
- Add error and loading components to Next.js route segments.
Phase 5: layer in tests and CI/CD
- Add unit and integration tests for
/lib and /features.
- Add E2E tests for core flows.
- Configure GitHub Actions (or another CI) to run lint, typecheck and tests on PRs.
- Connect the repo to Vercel (or another host) for preview and production deploys.
Phase 6: define ongoing v0 usage rules
- Allow v0‑initiated changes primarily in
/app/(v0) and /components/v0.
- Require human review on any PR that modifies protected areas.
- Use local agents like Cursor or Claude Code for refactors, migrations and data/auth work.
- Regularly prune unused v0 components and keep the design system authoritative.
When this workflow is overkill (and when it is mandatory)
The decision to adopt this full workflow depends on the app’s lifespan, stack choices and compliance needs.
The v0 pricing page shows the four main plans, their monthly per-user pricing and included credits, anchoring the article’s guidance on cost and usage-heavy workflows.
- Short‑lived marketing experiments or simple internal tools – Shipping directly from v0’s managed project, optionally with GitHub sync but without a full test suite or strict design system, may be acceptable. The overhead of hardening may not pay off.
- Teams standardised on other AI‑first builders (Lovable, Bolt) – If another builder already provides both UI and backend plus its own GitHub and deployment workflows, introducing v0 may fragment the stack. In this case, v0 can be kept as a one‑off UI generator or omitted. For a deeper look at those stacks, see the Lovable review and the comparison of Lovable vs Bolt.
- Compliance‑heavy organisations – v0 Business or Enterprise, with SAML SSO and RBAC, plus a locked‑down GitHub/Vercel pipeline, becomes close to mandatory. The repo‑first architecture described here is aligned with that requirement.
- Teams with minimal engineering capacity – If maintaining a Next.js repo, tests and deployments is not realistic, using v0 as a UI‑as‑a‑service and accepting reduced control might be preferable, at the cost of long‑term flexibility.
- Non‑Vercel infra and custom runtimes – When deploying into more bespoke infrastructure, v0 can be treated as a one‑time UI generator. The project then moves fully into local development and IDE agents. The value comes from owning the code and pipeline end‑to‑end.
For founders who want idea‑to‑production without running any backend or Next.js repo at all, tools like Lovable or Bolt are often a better fit. This site has a dedicated comparison of Lovable vs Bolt that goes deeper into that decision, plus a separate breakdown of Lovable pricing and credits if you are modelling cost against v0.
For teams already comfortable with GitHub‑centric workflows and IDE agents, a separate article here covers an AI development workflow from prompt to production, combining Codex, Cursor and CI/CD without adding a v0 layer.
The thread running through all of this is consistent: treat v0 as a fast UI generator plugged into a GitHub repository, not as the production environment itself. The Next.js repo, with its design system, data layer, auth, accessibility and tests, is where the real product lives.