afterbuild/ops
Resource

v0 to production: how to ship a v0 prototype.

v0 by Vercel is the best component generator available. But it generates UI — not a backend, not auth, not a database. Here's exactly how to add everything else and get to production.

By Hyder ShahFounder · Afterbuild LabsLast updated 2026-04-15

v0 is genuinely great at what it does. Type a prompt, get a polished React component back, iterate visually until the UI matches what you had in your head. For anyone who has ever fought with Tailwind classes, shadcn/ui variants, or the thousand tiny layout decisions a production component requires, v0 feels like cheating.

But there is a gap between a v0 prototype and a shipped product, and that gap is wider than most founders expect. The UI looks done. The app is not done. Everything the UI implies — a database, authentication, permissions, payments, email, a real deployment — is not there yet. This guide walks through exactly what you need to add, in what order, to turn a v0 prototype into an app your first hundred paying customers can actually use.

1. What v0 actually gives you

v0 generates shadcn/ui-compatible React components written in TypeScript. The code is clean by AI-generator standards — readable, idiomatic, mostly free of the weird anti-patterns that litter output from less focused tools. If you copy a v0 component into a Next.js project and wire the imports correctly, it will render, it will look good, and it will be responsive.

What it does notgive you is the application that the UI is implying. A v0 dashboard that shows a list of projects is displaying an empty array with a few hard coded placeholders. The “Create project” button is wired to a console.log or a commented out TODO. The auth page has form fields and a submit handler that does nothing. The settings page saves to local state that disappears on refresh.

This is not a bug. It is the product. v0 is a UI generator. It's the best one that exists. It is explicitly not trying to be a backend generator, a database designer, or a full-stack assistant. Treating it as one is where founders get stuck. The prototype problem is that the UI looks production ready, so the temptation is to ship it, but clicking the button does nothing, and there is no API endpoint for it to call.

Before you start extending a v0 prototype, get honest about what you are looking at. You have the visual layer. You have routing structure implied by the sidebar and top nav. You have component composition ready for a real codebase. You do not have state management that survives a refresh, a database schema, row-level security, authentication flows, payment processing, email, analytics, monitoring, tests, or a deploy pipeline. Every single one of those is your job.

2. Choose your backend stack

The first decision is the biggest: what is the server side of this app going to be? Because v0 outputs React components that assume no particular backend, you have real flexibility here. But practical considerations narrow the field quickly.

Recommended: Next.js App Router + Supabase

For founders who are not full-time backend engineers, this pairing is the fastest path to a production app. Next.js is a natural home for v0 output because v0 is a Vercel product — many of its generated patterns assume App Router conventions. You get server components, server actions, route handlers, and edge runtime in one coherent framework.

Supabase gives you a Postgres database, authentication, storage, edge functions, and realtime in one dashboard. You never touch infrastructure. You write SQL (or use the table editor), click to enable RLS, define policies, and you have a production database behind auth in an afternoon. For founders whose value is in the product, not the plumbing, Supabase is close to optimal.

Alternative: Next.js + Prisma + Neon

If you have backend experience and want more control over your data model, migrations, and query patterns, the Next.js + Prisma + Neon stack is a defensible choice. Prisma gives you typed queries and schema migrations. Neon gives you serverless Postgres with branching. You pay for the control with more setup: you still need to bolt on auth (NextAuth, Clerk, Lucia) because Neon is a database, not a platform.

What not to pick

Do not pick a stack you read about on Twitter yesterday. Do not pick three different backends so “you can use the best tool for each job.” At the prototype stage you need one boring stack that will still be boring a year from now. Next.js + Supabase is boring in the best way. So is Next.js + Prisma + Neon. Pick one, commit, move on.

3. Add routing with Next.js App Router

v0 exports components, not pages. Your first integration task is mapping those components into Next.js App Router pages. Create src/app/page.tsx for your landing page, src/app/dashboard/page.tsx for the main authenticated experience, src/app/login/page.tsx and src/app/signup/page.tsx for auth, and so on for every distinct route implied by your v0 designs.

Move the v0 component source files into src/components/ organised by feature: src/components/dashboard/, src/components/auth/, src/components/marketing/. Import them into your pages and render them there. Keep pages thin — a page file should compose components and fetch data, not contain layout logic.

Handle internal navigation with next/link. Do not use plain anchor tags for in-app navigation; you will lose the client-side transitions and the preloading that makes a Next.js app feel fast. Use useRouter for imperative navigation after form submissions.

Create src/app/layout.tsx for the root layout (fonts, html/body tags, globals import) and a nested src/app/(authenticated)/layout.tsx or similar for the logged in shell — sidebar, topbar, user menu. The route group pattern lets you apply an authenticated layout to every page inside without putting (authenticated) in the URL.

Finally, add a loading.tsx and error.tsx for every major segment. v0 rarely generates these, but they are the difference between an app that feels solid and one that flickers blank on every navigation.

4. Add Supabase database

Create a Supabase project at supabase.com. Pick a region close to your primary user base. Note the project URL and the anon (public) key — you will need them in a moment.

Define tables that match what your v0 UI is implying. If your dashboard shows a list of projects, create a projects table with at minimum id, user_id (references auth.users), name, created_at. If there is a tasks list under each project, create a tasks table with project_id and appropriate fields. Keep the schema simple at first — you can add columns later, but a bad foreign key design is expensive to undo.

Copy SUPABASE_URL and SUPABASE_ANON_KEY into .env.local. Prefix them with NEXT_PUBLIC_ because these specific values are safe to expose to the browser (they are protected by RLS). Never put the service role key into NEXT_PUBLIC_ variables — that key bypasses RLS and would give any visitor full database access.

Install @supabase/ssr and @supabase/supabase-js. Create a server client helper in src/lib/supabase/server.ts and a browser client helper in src/lib/supabase/client.ts. The server client reads cookies for the user session; the browser client is used in client components. Keeping these separate is not optional — mixing them is the source of half the session bugs in AI-built Next.js apps.

Enable RLS on every table, immediately.Do not wait until you have users. Do not wait until the app is in beta. Do not think “I'll turn it on before launch” — you will forget, because every other thing will be more urgent. Go to each table in the Supabase dashboard, click “Enable RLS,” and write policies: (SELECT) auth.uid() = user_id for the read policy, (INSERT) WITH CHECK (auth.uid() = user_id) for writes, (UPDATE) USING (auth.uid() = user_id), same for delete. If your schema has shared resources (teams, organizations), the policies get more complex — but the baseline rule is never skip RLS.

Connect your v0 list components to real Supabase queries. Where the v0 output has a hard coded array of sample data, replace it with a server component that queries Supabase. Where it has a form that did nothing, wire a server action that inserts into the database.

5. Add auth with Supabase Auth

With the Supabase clients in place, auth is mostly configuration. Install nothing extra — @supabase/ssr already includes what you need.

Create src/middleware.ts that calls supabase.auth.getUser() on every request to refresh the session cookie. Without this, the session will silently expire and users will be bounced back to login at random times. The Supabase docs have a canonical middleware pattern — copy it exactly. Do not improvise.

Build three server actions: signUp, signIn, signOut. Place them in src/app/(auth)/actions.ts. Each should call the corresponding Supabase Auth method, handle errors, and redirect on success. Wire your v0 login and signup forms to call these actions via the action prop on the form.

Configure OAuth providers if you want social login. Go to Supabase Auth → Providers, enable Google (or GitHub, or whichever), paste in the OAuth client ID and secret. Critically: set the redirect URL to your production domain, not localhost. The single most common launch-day bug on AI-built apps is OAuth that works locally but redirects to nowhere in production.

Configure the email templates in Supabase Auth → Email Templates. The default templates say “Supabase” on them. Rewrite them to say your product name. Change the redirect URL in the email templates to your production domain. Send yourself a test password reset email and click the link — if it bounces you to a localhost URL or a Vercel preview, you have a broken template.

Test the full loop: create a user, log in, refresh the page (session should persist), close the tab and reopen it (session should still persist), log out, confirm you are redirected. Run the password reset flow end to end. If any of these breaks, fix it before you move on to payments.

6. Add Stripe payments

If your app has a paid tier, Stripe is the next integration. Install stripe on the server and @stripe/stripe-js on the client. Create your product and price in the Stripe dashboard — keep it simple, one or two tiers, monthly and maybe annual.

Build a checkout session API route at src/app/api/checkout/route.ts. It should accept the authenticated user, look up or create their Stripe customer ID, create a checkout.sessions.create call with the correct price, pass a success_url and cancel_url, and return the session URL. The v0 “Upgrade” button fetches this route and redirects to the returned URL. Client-side, that is two or three lines.

Build the webhook handler at src/app/api/webhooks/stripe/route.ts. This is the part v0 will not write for you and the part you cannot skip. The handler must read the raw request body, verify the Stripe signature using your webhook secret, process the event, and return 200. Minimum events to handle:

Store subscription state in a subscriptionstable in Supabase with RLS. Stripe is the source of truth; your table mirrors it. Never calculate subscription status from “did they pay in the last 30 days?” — always read from the table Stripe updates via webhooks.

Test locally with the Stripe CLI: stripe listen --forward-to localhost:3000/api/webhooks/stripe. Then fire events: stripe trigger checkout.session.completed, stripe trigger invoice.payment_failed, and so on. Verify each produces the correct database state. If something is wrong, you want to know now, not after the first real customer pays.

7. Deploy to Vercel

Because v0 is a Vercel product, deployment is essentially zero friction. Push your repo to GitHub. Import it into Vercel. The framework preset auto detects as Next.js. The build runs.

Add environment variables in the Vercel project settings: NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY (server only, no NEXT_PUBLIC), STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY, and any others your app needs. Add them for all three environments: Production, Preview, and Development.

Attach your custom domain in Vercel → Settings → Domains. Follow the DNS instructions. Verify the SSL certificate provisions correctly (Vercel does this automatically, but give it a minute).

Now update the production side of everything. In Supabase Auth settings, add your production domain to the list of allowed redirect URLs. In Stripe, register your production webhook URL (https://yourdomain.com/api/webhooks/stripe) and copy the new webhook secret into Vercel env vars (it is different from your local test secret). Trigger a test event from Stripe and verify it lands.

Final launch check: log in on the production domain with a real email, go through signup, reach the dashboard, attempt a paid upgrade in Stripe test mode, verify the webhook fires and the subscription appears in your database, log out, close the browser, come back tomorrow and log in again. If all of that works, you have a v0 prototype that is now a working product.

See how rescue works →

FAQ
Does v0 generate a full-stack app?
No. v0 generates React components — UI layer only. There's no backend, no database, no auth, no API routes. It's the best component generator available, but you're responsible for everything else. The gap between a v0 prototype and a production app is the backend.
Can I use v0 with any backend?
Yes. v0 exports standard React/TypeScript that works with any backend. Next.js + Supabase is the fastest path. But you could use Next.js + Prisma + Neon, Remix + Supabase, or even a separate React app calling a custom API.
Why does everyone say v0 apps can't go to production?
Because they don't have backends. A v0 prototype looks production-ready — the UI is polished — but there's no data layer. When users try to sign up, nothing happens. The fix is not more v0 prompting; it's adding a backend.
How long does it take to go from v0 prototype to production?
A simple app with auth and a database: 1–2 weeks for a developer. A full app with Stripe, multiple user roles, and custom integrations: 3–5 weeks. We scope these fixed-fee — free audit first.
What's the biggest mistake when deploying a v0 app?
Not enabling RLS. Supabase makes it easy to create tables and skip the security step. Every v0 app we audit has at least one table with RLS disabled. Enable it before any real users touch the app.
Can Afterbuild Labs help ship a v0 app to production?
Yes — this is exactly what we do. v0 generates the UI layer; we add the backend, auth, payments, and deployment. Fixed-fee, free audit first.
Next step

Ship your v0 prototype to production

We add the backend, auth, Stripe, and deployment your v0 prototype is missing. Free audit in 48 hours, then a fixed-fee scope before any work starts.

Book free diagnostic →