In short
- v0 generates Next.js App Router projects with Server Components, so pages are usually server-rendered HTML that Google can read.
- The common gaps are one title for every page, no canonical, no sitemap or robots.txt, and the vercel.app host serving duplicate pages.
- Redirect your vercel.app host to your domain with a host-scoped permanent redirect, and set metadataBase plus per-page canonicals.
- Watch for pages whose content moved into a client component with useEffect; curl each page type after big chats.
- v0's GitHub sync is two-way, so larger SEO work can happen in the repo with a coding agent and come back through Pull Changes.
A site built with v0 starts in a good place for Google. v0 generates a Next.js App Router project, uses Server Components, and deploys to Vercel, so pages usually arrive as real server-rendered HTML. The gaps are the parts nobody asks for in a chat: a title and description per page instead of one in the layout, a canonical, a sitemap and robots.txt, the vercel.app address competing with your domain, and the occasional page whose content was moved into a client component. Fix your domain and canonicals first, then per-page metadata, then the sitemap. Everything here is standard Next.js, so the fixes are short. For the platform-wide picture, see SEO for AI-built websites.
What v0 builds and how it renders
| Question | Answer | SEO meaning |
|---|---|---|
| Framework | Next.js by default (other frameworks possible, less reliable per v0's docs) | Server rendering and the Metadata API are available |
| Rendering | App Router with Server Components; Vercel says v0 sites are server-rendered by default | Content is in the HTML unless a page moves it into the browser |
| Hosting | Vercel, with a vercel.app URL for every deployment | One more host serving your pages |
| Previews | A working branch and preview deployment per chat | Vercel adds X-Robots-Tag: noindex to previews |
| SEO page in the docs | None; Vercel's blog describes metadata, Open Graph and JSON-LD support | Ask for SEO work explicitly |
The risk on v0 is not the framework, it is drift. A chat that adds a carousel, a filter or a data fetch can turn a page into "use client" with a useEffect, and the copy that used to be in the HTML now only appears in the browser. That is why the diagnosis below checks every page type, not just the home page.
The five-minute diagnosis
- Curl your production pages
Terminal for p in / /pricing /about /blog/first-post; do echo "== $p" curl -s "https://your-domain.com$p" | grep -oE '<title>[^<]*|<h1[^>]*>[^<]*|<link rel="canonical"[^>]*>' doneTerminal $ for p in / /pricing /blog/first-post; do …; done == / <title>v0 App <h1>Plan your week in minutes == /pricing <title>v0 App <h1>Pricing == /blog/first-post <title>v0 App (no h1, no canonical)
Illustrative output. The same title on every page and no canonical: metadata lives only in app/layout.tsx. The blog post's H1 is missing because the post loads in a client component. Good output has a different title per page, an H1 on every page, and a canonical on your domain. Next.js streams metadata on dynamic pages, so the title can appear late in the body for normal clients; Google reads it there.
- Check the vercel.app address
Terminal curl -sI https://your-project.vercel.app/pricing | grep -iE "^(HTTP|location|x-robots-tag)"If this returns
200with no noindex and no redirect, Google can index the same pages on two hosts. The redirect checker shows the chain. - Check robots.txt, the sitemap and 404s
Terminal curl -s https://your-domain.com/robots.txt curl -sI https://your-domain.com/sitemap.xml | head -1 curl -s -o /dev/null -w "%{http_code}\n" https://your-domain.com/blog/does-not-existYou want a robots.txt that names your sitemap, a
200sitemap and a404for the missing post. The free SEO checker covers one URL end to end.
Fixes, in order of impact
- 1One hostCustom domain; vercel.app redirects to it
- 2Server HTMLMain content in Server Components
- 3MetadatametadataBase, per-page title, description, canonical
- 4Sitemap + robotsapp/sitemap.ts and app/robots.ts
- 5Status codesnotFound() for missing records
1. Put the site on your domain, and only there
In v0, add a domain from Publish, Customize Domain (or the project's Settings, Domains); DNS and redirects are managed in the Vercel dashboard. Then redirect the vercel.app host. Vercel's guide recommends a host-scoped permanent redirect in vercel.json:
{
"redirects": [
{
"source": "/(.*)",
"has": [{ "type": "host", "value": "your-project.vercel.app" }],
"destination": "https://your-domain.com/$1",
"permanent": true
}
]
}Previews stay safe on their own: Vercel adds X-Robots-Tag: noindex to every preview deployment, unless you assign a custom domain (such as staging.your-domain.com) to a non-production branch. Noindex those yourself.
2. Keep the main content in Server Components
If a page lost its H1 or copy in the curl check, ask v0 to move the data back to the server:
On /blog/[slug], fetch the post in the page (a Server Component) and pass it to the
client components as props. Keep "use client" only on the interactive parts (the like
button and the share menu). The post title must be the page's only <h1>. Don't change
the design.3. Give every page its own metadata
v0 often sets one metadata object in app/layout.tsx and stops there. Set metadataBase and a title template in the layout, then a title, description and canonical per page (generateMetadata for dynamic routes). Never put the canonical in the layout, or every page inherits the homepage's.
export const metadata: Metadata = {
metadataBase: new URL("https://your-domain.com"), // never the vercel.app or a preview URL
title: { default: "Acme", template: "%s | Acme" },
description: "Plan your week in minutes. Free for teams of up to five.",
};export const metadata: Metadata = {
title: "Pricing",
description: "Acme is free for up to five people. Paid plans add unlimited projects, SSO and priority support.",
alternates: { canonical: "/pricing" },
};Preview titles and descriptions with the SERP preview. Our Next.js SEO checklist covers metadata merging, Open Graph images and hreflang in more depth.
4. Add a sitemap and robots.txt
Add app/sitemap.ts that returns every public route as an absolute URL on
https://your-domain.com (blog posts from the same data source the blog uses, with
lastModified from the post's real update date). Add app/robots.ts that allows everything
except /api/ and points to the sitemap. Don't disallow /_next/.Check both after deploying with the robots.txt tester and sitemap validator.
5. Return real 404s
Pages for records that don't exist should call notFound(). It only sets a 404 status if it runs before streaming starts, so if the route has a loading.tsx, check that the record exists before any Suspense boundary. Otherwise the page returns 200 with a noindex tag.
Working on the code outside v0
Larger SEO changes (a sitemap from a CMS, redirects for renamed URLs, structured data across templates) are easier in a repo with a coding agent. v0's GitHub sync is two-way: once connected, the repository is the source of truth. Each chat works on its own branch with its own preview deployment, and Publish opens or reuses a pull request into your base branch. Pull Changes brings commits made outside v0 back in.
- Connect GitHub in v0 and clone the repo
Branch protection on the repo still applies to v0's pull requests.
- Brief your agent with checks it can run
Prompt for a coding agent This Next.js App Router project was generated by v0 and deploys to Vercel. 1. Add metadataBase (https://your-domain.com), a title template, and per-page title, description and alternates.canonical. Use generateMetadata on dynamic routes. 2. Add app/sitemap.ts and app/robots.ts from the real routes. 3. Move any main content fetched in useEffect into Server Components. 4. Add the host-scoped vercel.app redirect to vercel.json. Done when curl on the preview deploy shows a unique <title>, an <h1> and a canonical on each route, and /blog/does-not-exist returns 404. - Check the preview, merge, then pull in v0
Curl the preview URL before merging. After the merge, use Pull Changes in v0 so the next chat starts from the fixed code.
What you can and can't control
| Item | Where | Notes |
|---|---|---|
| Title, description, canonical, Open Graph | Next.js Metadata API in each page.tsx | Full control |
| robots.txt and sitemap | app/robots.ts, app/sitemap.ts | Not created unless you ask |
| Path redirects | next.config or vercel.json | Permanent redirects are 308s |
| Domain redirects | Vercel dashboard or vercel.json | Dashboard redirects may be temporary; check the status |
| 404 status | notFound() | Before any Suspense boundary |
| Preview noindex | Vercel default | Except custom domains on non-production branches |
Because v0 output is Next.js, npx serpkin maps its routes to files exactly: it sets up Claude Code, Codex or Cursor in the cloned repo, registers the site, runs a first scan and shows the first mission with the files to change. Small metadata fixes come bundled as one "Fix the basics" mission. Since v0 gives every chat a preview deployment, npx serpkin check <preview-url> is a natural gate before Publish: it fails if a preview drops a title, canonical or sitemap.
Questions
Is v0 good for SEO?
Its output is a good starting point: Next.js with Server Components, rendered on the server and hosted on Vercel. You still need per-page titles, descriptions and canonicals, a sitemap, robots.txt and one canonical host.
Does v0 use server-side rendering?
Yes, by default. v0 builds Next.js App Router projects with Server Components, and Vercel says v0 sites return full HTML at request time. Content fetched inside a client component with useEffect is the exception.
How do I add a sitemap to a v0 site?
Ask v0 to create app/sitemap.ts that returns every public URL on your domain, and app/robots.ts that points to it. Next.js serves them at /sitemap.xml and /robots.txt.
Will Google index my vercel.app URL?
It can, because production vercel.app URLs serve the same pages without noindex. Add a host-scoped permanent redirect from the vercel.app host to your domain and use absolute canonicals on your domain.
Sources
- v0 docs: full-stack apps (Next.js default, Server Components) (checked October 29, 2026)
- Vercel blog: how v0 builds SEO-optimized sites by default (checked October 29, 2026)
- v0 docs: custom domains (checked October 29, 2026)
- v0 docs: deployments (previews, domain settings in Vercel) (checked October 29, 2026)
- v0 docs: GitHub sync (checked October 29, 2026)
- v0 docs: FAQs (export, two-way sync) (checked October 29, 2026)
- Vercel: are preview deployments indexed? (checked October 29, 2026)
- Vercel: avoiding duplicate content with vercel.app URLs (checked October 29, 2026)
- Next.js docs: generateMetadata (streaming metadata, alternates) (checked October 29, 2026)
- Next.js docs: notFound (checked October 29, 2026)
- Next.js docs: sitemap.ts (checked October 29, 2026)