VibeRescue · Blog

Lovable app works in preview but breaks after deploy: 6 fixes

October 15, 2026 · 8 min read

You built the app in Lovable, Bolt or Cursor. In the preview everything works: you can log in, data loads, every page opens. Then you publish it, or push it to Vercel or Netlify, and something is wrong. You might see a blank white page, a login button that does nothing, a dashboard that loads with no data, or a 404 when you refresh a page.

This is the most common complaint we see from people building with AI tools, and it is almost never "the AI wrote bad code". The preview gives your app things the live site doesn't have: injected keys, an approved domain, a dev server that forgives routing mistakes, and a proxy that hides cross-origin problems. Once you publish, all of that goes away.

This guide covers runtime failures, where the deploy succeeds but the live app misbehaves. If the build itself goes red, start with the build section below or Vercel Module not found.

When the build itself is red (not just the live site)

This guide is mostly about runtime failures after a green deploy. If Vercel or Netlify shows a red build log, fix the build first. The usual causes from Lovable exports are:

Once npm run build is green locally and on the host, come back here for blank pages, auth redirects and empty data.

Step 0: read the real error (2 minutes)

Don't ask the AI to "fix the deploy" yet. Open the live site in Chrome, press F12 (or Cmd+Option+I on a Mac), and reload the page.

Copy the first red error exactly. Nearly every cause below leaves a recognisable signature, and the table in each section tells you which one you're looking at.

1. Blank white page: missing environment variables

Signature: the page is completely white, and the console shows something like supabaseUrl is required, Invalid URL, or Cannot read properties of undefined.

In the Lovable or Bolt preview, the platform injects your Supabase URL and anon key for you. Your host knows nothing about them. Lovable and Bolt projects are Vite apps, and Vite only exposes variables whose names start with VITE_. It also bakes their values into the JavaScript at build time. So a variable you add after the build won't appear until you rebuild.

VITE_SUPABASE_URL=https://abcdefgh.supabase.co
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIs...
  1. Find the exact variable names your code uses: search the project for import.meta.env.
  2. Add each one in your host's settings (Vercel: Project → Settings → Environment Variables; Netlify: Site configuration → Environment variables). Tick Production and Preview.
  3. Trigger a new deploy. Re-running an old build won't pick up new values.

A white screen with no useful message often means the Supabase client crashed while the module loaded. A small guard makes the failure obvious next time:

// src/integrations/supabase/client.ts
const url = import.meta.env.VITE_SUPABASE_URL;
const key = import.meta.env.VITE_SUPABASE_ANON_KEY;
if (!url || !key) {
  throw new Error("Missing VITE_SUPABASE_URL or VITE_SUPABASE_ANON_KEY at build time");
}
export const supabase = createClient(url, key);

Some Lovable projects hard-code the URL and anon key straight into client.ts. In that case env vars aren't your problem, so move on to the next section. The anon key is meant to be public. The service_role key is not, and it must never appear in frontend code.

2. Login does nothing or bounces to localhost

Signature: after you sign in, or click the magic link, you land on http://localhost:3000 or localhost:5173, or you end up back on the login page.

Supabase only redirects to URLs you have approved. The preview domain was approved for you, but your new domain isn't. Fix it in three places:

  1. Supabase → Authentication → URL Configuration. Set Site URL to your live origin, for example https://myapp.com. Under Redirect URLs, add https://myapp.com/**, and keep your preview URL and localhost too if you still use them.
  2. Google Cloud Console (if you use Google sign-in): the OAuth client's authorised redirect URI should be your Supabase callback, https://<project>.supabase.co/auth/v1/callback, and your live domain should be listed as an authorised JavaScript origin.
  3. Your code. Search for redirectTo, emailRedirectTo, and localhost:
await supabase.auth.signInWithOAuth({
  provider: "google",
  options: { redirectTo: `${window.location.origin}/auth/callback` },
});

Test in an incognito window, because old sessions and cached OAuth state hide whether the fix worked. We go deeper on this in Supabase auth broken in a Bolt or Lovable app.

3. Refreshing a page gives a 404

Signature: the home page works and clicking around works, but reloading /dashboard or opening a shared link shows the host's 404 page.

A React single-page app has exactly one real file, index.html. React Router handles /dashboard in the browser. When you reload, the server looks for a file called /dashboard, can't find one, and returns a 404. The dev server rewrites these requests for you, but production hosts don't unless you tell them to.

// vercel.json (project root)
{ "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] }

# Netlify: public/_redirects
/*  /index.html  200

# Apache / cPanel hosting: .htaccess
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ /index.html [L]

Next.js apps don't need this. If a Next.js route 404s, the page file is missing or named wrong.

4. Data never loads: CORS and API URLs

Signature: the console says has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header, or the Network tab shows calls going to http://localhost:3001/api/....

Two separate mistakes look the same:

// Supabase Edge Function
const cors = {
  "Access-Control-Allow-Origin": "https://myapp.com",
  "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};
if (req.method === "OPTIONS") return new Response("ok", { headers: cors });

Restricted API keys fall into the same family. Google Maps, reCAPTCHA and similar keys are often locked to the preview domain, so add your real domain in each provider's console.

5. Logged in, but every list is empty

Signature: no errors at all, and queries return []. Or you see new row violates row-level security policy when you save.

Supabase Row Level Security (RLS) is working as designed here. Once RLS is on, a table with no policies returns nothing to the browser. In the preview you may have been using seeded data, a different project, or a policy the AI later removed. Add a policy for each operation, keyed on the signed-in user:

alter table public.projects enable row level security;

create policy "own rows: read" on public.projects
  for select to authenticated using (auth.uid() = user_id);

create policy "own rows: insert" on public.projects
  for insert to authenticated with check (auth.uid() = user_id);

Don't "fix" this by turning RLS off. That makes every row readable to anyone holding the public anon key, which means anyone who opens your site.

6. It works for you but not for your users

If your own browser works but friends report problems, suspect stale state. You are still signed in from the preview, or a service worker cached an old bundle. Test in incognito, on a phone over mobile data, and with a brand-new account. Also check the free-tier limits on Supabase auth emails: the built-in email sender is heavily rate-limited, so signups can fail quietly until you set up custom SMTP.

A post-publish checklist

How to prompt your AI tool so it doesn't make things worse

"It doesn't work after deploy" invites the model to guess, and guessing is how working code gets rewritten. Give it the evidence and set limits:

The live site at https://myapp.com shows this console error on load:
  Uncaught Error: supabaseUrl is required. (client.ts:6)
It works in preview. Do NOT change UI or refactor.
First explain the cause in 2 sentences, then propose the smallest fix.

If the answer is a hosting setting rather than code, as with causes 1, 2 and 3, no code change will help, and a model that keeps editing files is making things worse.

FAQ

Why does my Lovable app show a blank white page after publishing?

Usually the Supabase URL or anon key is missing in the production build. Vite only exposes VITE_ variables and inlines them at build time, so add them on your host and trigger a fresh deploy. The browser console will show an error such as "supabaseUrl is required".

Why does Supabase login redirect to localhost after deploy?

Your production domain is not in Supabase Authentication → URL Configuration (Site URL and Redirect URLs), or the code hard-codes a localhost redirectTo. Add the domain, use window.location.origin for redirects, and test in an incognito window.

Why do I get a 404 when I refresh a page on my deployed React app?

Single-page apps need the host to rewrite every route to index.html. Add a rewrite in vercel.json, a _redirects file on Netlify, or an .htaccess rule on Apache hosting.

Should I disable Row Level Security if my data does not load?

No. Disabling RLS exposes every row to anyone with your public anon key. Add select, insert, update and delete policies scoped to auth.uid() instead.

Not sure which of these it is?

Run the free Rescue Scan: paste a public GitHub repo and VibeRescue, an automated AI repair tool, classifies the problem (code bug, missing files, or missing secrets) before you pay anything. If it is something you can fix with this guide, we will tell you. If you want a fix run, it opens a pull request, with a full refund if the run can't fix it.

Run the free Rescue Scan →