fix guide
v0 app shows 404 on refresh after deploying to Vercel
Harjot ·
You built the app in v0 and published it. The home page loads and clicking around works. Then you refresh a page like /dashboard, or open a link someone shared, and you get a 404.
v0 deploys projects to Vercel, and its docs say new chats start with a Next.js app. A Next.js app on Vercel doesn't need the index.html rewrite that fixes this for Vite apps, so the usual answer in /guides/404-on-refresh-vercel may not apply. Start with step 1. It tells you which half of the list to read.
1. Tell the two kinds of 404 apart
Look at the 404 page itself. A plain white page that says 404: NOT_FOUND with a Code and an ID comes from Vercel. Vercel found no page to serve at that address. Read steps 2, 5 and 6.
A 404 that looks like your site, with your fonts, menu or your own not-found page, comes from Next.js. Your app ran and decided the page doesn't exist. Read steps 3 and 4.
signs You can say which of the two 404 pages you see before you change anything.
2. The page only exists in the browser
In Next.js, a page is a folder with a page file inside it. The address /dashboard needs app/dashboard/page.tsx, or pages/dashboard.tsx in older apps.
Some generated apps draw several screens inside one page and change the address with the browser's history, or ship React Router inside a Next.js app. Clicking works because the code already running in the browser draws the screen. A refresh asks the server for /dashboard, Next.js has no folder by that name, and you get a 404.
Ask v0 to move each screen into its own route folder under app, with links between them. Then refresh every screen to check.
signs The project has one big page file that switches screens, and no folder named after the address that 404s.
3. A dynamic page that only knows some ids
Pages like /projects/123 live in a folder with brackets, such as app/projects/[id]/page.tsx. If that file exports generateStaticParams, Next.js builds the listed ids ahead of time.
If the same file also sets dynamicParams to false, Next.js answers 404 for every id that wasn't in the list when the site was built. A project created after the deploy has no page until the next build.
export const dynamicParams = false;Remove that line, or set it to true, so Next.js renders new ids on request. Then redeploy.
signs Old records open fine and records created after the last deploy 404.
4. The page calls notFound() when its data comes back empty
Many generated pages load a record and call notFound() if nothing comes back. That is fine for a wrong id. It becomes a bug when the query returns nothing for a record that exists.
On a refresh, the page's server code runs the query on Vercel. If that code builds its Supabase client without the visitor's sign-in cookies, the database sees a signed-out visitor. Row Level Security then returns no rows, and the page shows 404. The fix is to create the server client with the cookie helpers from @supabase/ssr, so the query runs as the signed-in user.
The other cause is a missing environment variable. If the Supabase URL or key isn't set for Production in the Vercel project, the query fails and the page treats it as not found. v0's docs say environment variables live in the Vercel project, under Production, Preview and Development. See Environment variable undefined in production on Vercel.
For the database side, Supabase RLS errors in AI-built apps, decoded lists what each Row Level Security error looks like.
signs Your own styled 404 page shows for a record you can see in the Supabase table. Vercel's function logs show an empty result or an error for that request.
5. Static export, presets and redirects
Open next.config. If it sets output to "export", the build writes plain files and no server runs. Every address needs a file made at build time. A dynamic page without generateStaticParams has none, so a refresh 404s. Remove the export setting unless you need a static site.
In Vercel, open Settings, then Build and Deployment. The Framework Preset should say Next.js, and Output Directory should be empty. A preset of Other or a hand-set output folder serves the wrong files. Step 2 of 404 on refresh on Vercel covers this.
Check middleware.ts (proxy.ts in newer Next.js versions) and the redirects in next.config. A redirect to a path with no page, such as /login when the page lives at /sign-in, shows up as a 404 after a refresh or a sign-out.
signs Every address 404s after a Ready deploy, or the address in the browser changes just before the 404.
6. The live site runs older code than the preview
v0's docs say Publish updates the Vercel project's production deployment. For a project connected to GitHub, Publish creates or reuses a pull request, merges it, then waits for the production deployment.
If that pull request is still open, or the production deployment failed, the live site runs code from before the new page existed. The page works in v0 and 404s live.
Open the Vercel project, then Deployments. Check that the latest production deployment is Ready and came from the commit with your new page. If the build failed, the error is in the build log. Command "npm run build" exited with 1 on Vercel covers the usual causes.
signs The newest page 404s and older pages load. The latest production deployment is older than your last change in v0.
Test it like a new user
- Open the live site in a private window.
- Click through to each main page, then refresh each one.
- Open a record created after the last deploy, then refresh it.
- Paste an inner address into a new tab while signed out, then while signed in.
- Check Vercel's function logs show no errors for those requests.
Common questions
Why does my v0 Next.js app show 404 when I refresh a page on Vercel?
Check which 404 you see. Vercel's plain 404: NOT_FOUND page means no route exists at that address, often because the screen only exists in the browser. Your own styled 404 means Next.js ran and called notFound(), usually because the page's data came back empty on the server.
Do I need a vercel.json rewrite for a v0 app?
Usually not. The index.html rewrite fixes Vite and React Router apps. A Next.js app on Vercel serves each route from its own folder under app. Add a real route folder for each screen, and check the Framework Preset says Next.js with an empty Output Directory.
Why do new records 404 in my v0 app while old ones load?
The dynamic page probably sets dynamicParams to false next to generateStaticParams. Next.js then only serves the ids that existed when the site was built, and answers 404 for the rest. Remove that line so new ids render on request, then redeploy.
Still stuck?
Not sure this is the cause? Paste your app's link into the free scan. It lists what a visitor's browser can see, like leaked keys and exposed files, worst first. The score is free; every fix as a prompt for your builder is $9, one-time.
Want a person to look inside? See the diagnosis