Fix.
Migrate a Lovable app from Vite to Next.js and Supabase
How to extract a Lovable web app from client-side Vite into Next.js App Router, migrate Supabase auth to server cookies, and deploy standalone.
AI app builders like Lovable get prototypes running remarkably fast. Within a few hours of natural language prompting, you have an interactive interface with styled tables, working login forms, and mock workflows.
The friction begins when real paying users arrive. Lovable code exports as a client-side Single Page Application (SPA) powered by Vite and react-router-dom. When you need server-side rendering for search engine discovery, cryptographic webhook validation for Stripe, or secure cookie-based session management, a client-only bundle hits a wall.
Migrating your prototype from client-side Vite to Next.js App Router with an independent Supabase project gives you complete code ownership, zero vendor lock-in, and an architecture ready for production traffic.
Here is the technical migration sequence our studio uses to decouple Lovable prototypes into maintainable standalone repositories.
Step 1: Export the codebase and database backup
Before modifying any source files, separate your code from the managed cloud instance:
- GitHub synchronization: Inside Lovable, connect your GitHub account and synchronize the project to a private repository. Clone this repository locally.
- Export the database: In the Lovable dashboard, open your project stack settings, navigate to Lovable Cloud, and initiate a database export. You will receive a PostgreSQL custom-format archive (
.backupfile) containing your table schemas, triggers, Row-Level Security policies, and user auth records. - Provision an independent Supabase project: Create a new project in your own Supabase account. Restore the
.backupfile usingpg_restorevia the Supabase CLI:
supabase db restore --db-url "postgresql://postgres:[YOUR-PASSWORD]@[YOUR-HOST]:5432/postgres" < export.backup
Restoring the full dump preserves hashed user passwords, ensuring existing customers do not experience broken login sessions.
Step 2: Transition from Vite to Next.js App Router
Lovable exports rely on client-side routing packages like react-router-dom. In Next.js App Router, routing is determined by the filesystem structure under app/:
- Initialize the Next.js shell: Create a Next.js project with TypeScript and Tailwind CSS (
npx create-next-app@latest). Copy over your custom components fromsrc/components/and utility functions fromsrc/lib/. - Restructure client views: Replace
<Routes>and<Route path="/dashboard" element={<Dashboard />} />declarations with page files:src/pages/Index.tsxbecomesapp/page.tsxsrc/pages/Dashboard.tsxbecomesapp/dashboard/page.tsxsrc/pages/Auth.tsxbecomesapp/(auth)/login/page.tsx
- Replace client links: Change all
import { Link } from "react-router-dom"references toimport Link from "next/link". - Environment variables: Update variable prefixes. Vite uses
import.meta.env.VITE_*, which exposes values to the browser. In Next.js, rename public keys toNEXT_PUBLIC_*and keep sensitive backend keys strictly server-side:
# Public keys (browser accessible)
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
# Private secrets (server route handlers only)
SUPABASE_SERVICE_ROLE_KEY=your-secret-service-role-key
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
Step 3: Replace client-only auth with server cookies
Lovable prototypes typically initialize Supabase in the browser using @supabase/supabase-js and store JWT session tokens in browser localStorage.
localStorage has two critical vulnerabilities in production:
- It is readable by any client-side JavaScript, leaving sessions exposed to cross-site scripting (XSS).
- It cannot be read by Next.js Server Components, middleware, or edge functions before page rendering, causing layout shifts and auth redirects.
Migrate to @supabase/ssr, which manages authentication through HttpOnly server cookies:
// lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr';
import { cookies } from 'next/headers';
export async function createClient() {
const cookieStore = await cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll();
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
);
} catch {
// Handled in middleware for Server Components
}
},
},
}
);
}
With cookie-based auth in place, you can enforce route protection directly in middleware.ts, preventing unauthenticated visitors from downloading dashboard assets.
Step 4: Secure payment webhooks and API routes
In Lovable apps, payment flows frequently rely on redirect parameters (e.g. checking ?success=true in the URL) to grant credits or upgraded user roles. A visitor who inspects the frontend code can navigate directly to the success URL and bypass billing.
Move all subscription mutations into a Next.js API route handler that verifies cryptographic signatures directly with Stripe:
// app/api/webhooks/stripe/route.ts
import { headers } from 'next/headers';
import { NextResponse } from 'next/server';
import Stripe from 'stripe';
import { createClient } from '@supabase/supabase-js';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const supabaseAdmin = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);
export async function POST(req: Request) {
const body = await req.text();
const signature = (await headers()).get('stripe-signature');
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body,
signature!,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err) {
return NextResponse.json({ error: 'Webhook signature verification failed' }, { status: 400 });
}
if (event.type === 'checkout.session.completed') {
const session = event.data.object as Stripe.Checkout.Session;
const userId = session.metadata?.userId;
// Mutate subscription records using server credentials only
await supabaseAdmin
.from('profiles')
.update({ subscription_tier: 'pro', updated_at: new Date().toISOString() })
.eq('id', userId);
}
return NextResponse.json({ received: true });
}
Notice that SUPABASE_SERVICE_ROLE_KEY is loaded only inside this server-side route handler. It never ships to the browser bundle.
Step 5: Preparing for native mobile packaging
Once your web application runs on a clean Next.js architecture with dedicated database tables and server-side authentication, converting it to a native mobile app becomes straightforward.
Using Capacitor, you can wrap your production web views into native iOS and Android packages that share the same backend, database, and auth cookies. Because the underlying code is clean and modular, passing Apple App Store review (and satisfying Guideline 4.2 Minimum Functionality) is predictable. Our studio successfully used this exact path to ship SlabMath: Predict & Profit to the iOS App Store.
Summary
Migrating from a visual AI builder to a standalone codebase requires five clear steps:
- Export your source code and extract your PostgreSQL database dump.
- Port client-side
react-routerviews into Next.js App Router. - Replace browser
localStorageauth with@supabase/ssrserver cookies. - Move sensitive mutations and Stripe webhooks into verified backend route handlers.
- Deploy to independent infrastructure you control.
If your prototype has paying customers and you want an experienced engineer to handle the migration, explore our vibe coded to standalone migration service or request our free production diagnosis for written findings on your live app within 48 hours.
More guides on fix
- Hardening Supabase and Node backends for production8 min read
- How to fix infinite recursion in Supabase RLS6 min read
- How to migrate a vibe-coded app to a standalone codebase9 min read
- The vibe coded MVP audit checklist8 min read
- The production web app penetration testing checklist7 min read
- Taking over a codebase someone else built6 min read
- Lovable Cloud to Supabase migration, what it actually involves7 min read
- What it actually costs to fix a vibe-coded app6 min read
- Is your Lovable app ready for real users? Ten checks8 min read
- Supabase RLS policies that actually work7 min read
- Your Supabase service role key is in your frontend. Do this now5 min read
- Check whether your Supabase data is public in five minutes5 min read
- Why your Lovable app shows a blank page after deploy6 min read