Use cases

Lovable SEO and other vibe-coded sites (Bolt, v0): what breaks and the prompts that fix it

New Lovable apps render on the server since May 2026, so empty HTML is no longer the default problem. Duplicate titles, homepage canonicals, soft 404s and button links still are. Here's how to check and fix each.

By SEOFix TeamPublished Updated
2,025 words · 9 min readMarkdown
Lovable SEO and other vibe-coded sites (Bolt, v0): what breaks and the prompts that fix it

Lovable SEO advice from early 2026 is out of date. Lovable apps created from 13 May 2026 use TanStack Start with server-side rendering, so Google gets real HTML. Older React + Vite apps get pre-rendered HTML, but only verified search and AI crawlers receive it. What still goes wrong on Lovable, Bolt and v0 sites is the per-page detail: every route sharing one title, canonicals pointing at the home page, sitemaps with placeholder routes, missing pages that answer 200, and navigation built from buttons instead of links. Below is how to check each of these with curl and Search Console, and the prompt to give the builder to fix it.

First, find out what you're running

The fixes depend on the stack, and these tools generate different ones.

BuilderWhat it generatesHow to check
Lovable, new apps (from 13 May 2026)TanStack Start with SSRpackage.json lists @tanstack/react-start (Lovable docs)
Lovable, older appsReact + Vite single-page app, pre-rendered on request for verified crawlersNo @tanstack/react-start in package.json
BoltAny JavaScript framework, per its supported technologies pageLook for vite.config.ts (a Vite app) or next.config.* (Next.js)
v0Next.js App Router; "v0 defaults to Next.js", deployed to Vercelnext.config.* and an app/ folder

Lovable's Enterprise plans followed later: the changelog says new Enterprise apps got SSR by default on 22 June 2026. An older Lovable project can upgrade in place to TanStack Start.

How to see what crawlers see

Use two views, because they differ on older Lovable apps.

What Google sees. In Search Console, use URL Inspection, click Test live URL, then view the tested page. Google's help says this shows a screenshot of how Google-InspectionTool sees the page plus the rendered HTML. This is the ground truth for Google.

What everyone else sees. Fetch the raw HTML:

curl -s https://yoursite.com/pricing | grep -o '<title>[^<]*</title>'
curl -s https://yoursite.com/pricing | grep -c '<a href'

On a server-rendered app you'll see the page's own title and real links. On a plain single-page app you'll see the shell: one generic title and an empty <div id="root"></div>. That shell is what most AI crawlers get. Vercel's crawler study found that ChatGPT and Claude crawlers fetch JavaScript files but "they don't execute them".

On older Lovable apps the two views disagree on purpose. Lovable serves pre-rendered pages to Google, Bing, social-preview bots and several AI engines, but "third-party SEO scanners and other unverified agents see the regular single-page app". Its hosting docs warn that SEO tools may report your pages as empty while Google sees the full content. Trust URL Inspection for Google, and treat the curl result as what everything else gets.

1. Empty HTML (client-side rendering)

Who has it: older Lovable apps outside the verified-crawler path, exported Lovable Vite apps you host yourself, and Bolt projects built as a Vite single-page app.

Check: the curl above shows no page content, and the body is a single root div.

Fix: on Lovable, upgrade the project to TanStack Start. Once you export a Vite app and host it on Netlify, a CDN or Nginx, Lovable's docs describe it as static dist/ output with a fallback to index.html for client-side routes. Nothing in that setup pre-renders for crawlers, so Google has to render the JavaScript itself. Google does render JavaScript; it queues pages with a 200 status for rendering and renders them when resources allow.

Bot-only pre-rendering services are what Google calls dynamic rendering, and Google's own page says it "was a workaround and not a long-term solution". It recommends server-side rendering, static rendering or hydration instead. If you're starting a site that needs search traffic, start on a stack that renders on the server: a new Lovable app, v0, or a framework such as Next.js.

2. The same title on every route

This is the most common one, and it survives SSR because it's a code problem. The page looks right in the browser because client code sets document.title after load, but the server sends the default title from index.html or the root route. Lovable's own SEO review lists "Duplicate titles across routes" as a finding. SEOJuice reported checking a Lovable site where all 12 pages had the same title tag in Google's index.

Check:

for p in / /pricing /about /blog/first-post; do
  printf '%-22s ' "$p"
  curl -s "https://yoursite.com$p" | grep -o '<title>[^<]*</title>'
done

If every line prints the same title, you have it. Check <meta name="description"> the same way.

Prompt for Lovable (TanStack Start app):

Every route currently serves the same <title> and meta description in the server HTML.
Add a head() to every route file in src/routes so each page has its own title
(under 60 characters, most specific words first) and its own meta description
(under 160 characters). For dynamic routes like /blog/$slug, build them from the
loader data. Also set og:title, og:description and og:url per route.
Don't change page content or layout.

TanStack Start's SEO guide shows the pattern Lovable should produce: a head function on each route that reads loaderData. On a Next.js app (v0, or Bolt with Next.js), the equivalent is export const metadata or generateMetadata in each page.tsx.

3. Canonicals that point to the home page

When a builder adds a canonical tag once, in the root layout or index.html, every page inherits it. Every page then tells Google it's a copy of the home page. Lovable's review has a finding for exactly this: "Canonical URLs point to the homepage".

Check:

for p in / /pricing /blog/first-post; do
  curl -s "https://yoursite.com$p" | grep -o '<link rel="canonical"[^>]*>'
done

Each page should print its own absolute URL. If they all print https://yoursite.com/, fix it.

Prompt:

Remove the canonical link from the root layout / index.html. Add a canonical per route
instead, pointing to that route's own absolute URL on https://yoursite.com (no query
string, no trailing slash except on the home page). For /blog/$slug use the slug.
Show me the canonical each route renders when you're done.

4. Sitemap and robots.txt: missing, placeholder or blocking

Lovable's docs say plainly that "Sitemaps, robots.txt, metadata, and other SEO elements are not always generated up front". Its review lists the sitemap problems it looks for: placeholder routes, relative URLs, host mismatches and URLs out of sync with your routes. A single-page app adds one more trap: its catch-all route can answer /sitemap.xml with the app's HTML and a 200 status.

Check:

curl -sI https://yoursite.com/sitemap.xml | grep -i '^content-type'
curl -s https://yoursite.com/sitemap.xml | head -20
curl -s https://yoursite.com/robots.txt

You want application/xml (or text/xml), absolute URLs on your real domain (not a lovable.app subdomain), and only routes that exist. In robots.txt, look for Disallow lines that block /assets/ or your JavaScript and CSS. SEOJuice lists blocked assets among the common Lovable indexing problems, and Google can't render a page whose scripts it isn't allowed to fetch.

Prompt:

Generate /sitemap.xml from the real route list plus every published blog post, using
absolute URLs on https://yoursite.com. Serve it with Content-Type application/xml.
Add /robots.txt that allows everything and points to the sitemap. Don't include
routes that need login, and don't list placeholder or example routes.

TanStack Start can serve a dynamic sitemap from a server route and a static public/robots.txt. Next.js uses app/sitemap.ts and app/robots.ts. Then submit the sitemap in Search Console. Lovable has a Search Console connector for that, and notes that Google fetches the sitemap from your live site, so unpublished routes won't be submitted.

5. Missing pages that answer 200

A single-page app with a catch-all route answers every URL with the app shell and a 200. The app then shows "Page not found" in the browser, but the status says the page exists. Google calls these soft 404s and has a section on avoiding them in single-page apps.

Check:

curl -s -o /dev/null -w '%{http_code}\n' https://yoursite.com/this-page-does-not-exist
curl -s -o /dev/null -w '%{http_code}\n' https://yoursite.com/blog/no-such-post

Both should print 404. Test a dynamic route too: an app can return a proper 404 for unknown paths and still return 200 for /blog/<anything>.

Fix by stack:

  • Client-side app: Google gives two options: use a JavaScript redirect to a URL where the server responds with a 404, or add <meta name="robots" content="noindex"> to error pages.
  • TanStack Start: throw notFound() from the route's loader when the record doesn't exist (TanStack docs), then re-run the curl to confirm the status code your host returns.
  • Next.js: call notFound(). Next.js adds a noindex tag, and returns a 404 only if notFound() runs before streaming starts. If the response has already started streaming from a <Suspense> boundary, it keeps its 200 status. Do the lookup before any Suspense boundary.

Prompt:

Unknown URLs and blog slugs that don't exist must return HTTP 404, not 200.
In the /blog/$slug loader, throw notFound() when the post isn't found, and make sure
the root has a not-found component. Then tell me the exact curl command to verify
the status code on the published site.

Google can only discover links that are <a> elements with an href. Generated code often navigates with <button onClick={() => navigate('/pricing')}>, which works for people and is invisible to crawlers. Andrea Saez audited her own Lovable site and found 20 such cases across 11 public files, including the main homepage CTA. Her verdict: "My entire internal link structure was invisible to crawlers."

Check: search the code for onClick handlers that call navigate(, router.push( or set window.location. On the live page, compare the number of <a href in the raw HTML (curl ... | grep -c '<a href') with the number of links you can see.

Prompt:

Find every place where navigation to an internal page uses a button or onClick handler
(navigate, router.push, window.location). Replace each with the router's Link component
so it renders a real <a href>. Keep the same styling. List the files you changed.

7. Slow Largest Contentful Paint

Generated sites tend to ship one large JavaScript bundle and a full-width hero image. Google's guidance is to aim for a Largest Contentful Paint of 2.5 seconds or less. On a client-rendered page, the main content can't paint until the bundle has downloaded and run.

Check: run the home page and one inner page through PageSpeed Insights. Look at the field data first (real users) and the LCP element it names.

Prompt:

Improve LCP on the home page. Serve the hero image in WebP or AVIF at the size it's
displayed, give it explicit width and height, load it eagerly with fetchpriority="high",
and lazy-load images below the fold. Code-split routes so the home page doesn't load
the code for other pages. Don't change the design.

Custom domains

Publish on your own domain before you care about rankings. On Lovable, you pick one primary domain and the others redirect to it automatically; changing the custom domain later means verifying it in Search Console again. Only publicly published apps can be indexed, and branded workspace URLs are never indexable. On Bolt, custom domains are only available for Pro users. Settle the URL early. SEOJuice's Lovable guide warns that changing your published subdomain changes your URL, so Google treats it like a new site.

When to move to a framework with SSR

Stay in the builder if it already renders on the server (a new Lovable app, v0) and the checks above pass. Move, or upgrade, when:

  • the site is an older Lovable app or a Bolt Vite app and you need AI search tools and other non-verified crawlers to read it, not only Google;
  • you're exporting and self-hosting, which drops Lovable's crawler pre-rendering;
  • you need things the builder makes awkward: hundreds of programmatic pages, per-page structured data from a CMS, or redirects managed in code;
  • a developer will maintain the site with Claude Code, Cursor or Codex, and wants a normal repo with tests and previews.

For Lovable, the first step is the in-place upgrade to TanStack Start. For a Vite app from anywhere, moving routes into Next.js or TanStack Start is mostly mechanical: one route file per page, the metadata from section 2, the canonical from section 3, and the sitemap from section 4.

Where an audit tool fits

Running the curl checks on five routes catches most of this. On a site with hundreds of generated pages, you want them run on every URL and grouped by page type, so "/blog/[slug] has the home page canonical on 214 pages" is one task, not 214.

SEOFix audits the HTML your server returns, without running JavaScript, and samples rendered pages to flag content that only appears after JavaScript runs (JS_DEPENDENT_CONTENT); details are in JavaScript rendering checks. That makes it an unverified agent in Lovable's terms. On an older Lovable app it will report the shell that unverified crawlers get, not what Google gets, so compare its findings with URL Inspection before acting. Each issue comes with a fix prompt you can paste into Lovable or a coding agent (Fix prompts), and with the MCP connected, the agent can recheck the affected pages after you publish (Connect your agent).

FAQ

Is Lovable good for SEO now?

For new apps, the rendering problem is mostly solved: since May 2026 they render on the server. Ranking still depends on what Lovable's docs list as outside its control, and on the per-route details above, which you should check yourself.

Why does my SEO tool say my Lovable pages are empty?

On an older React + Vite Lovable app, pre-rendered pages go only to verified crawlers. Third-party SEO tools get the single-page app shell. Check what Google sees with URL Inspection's live test. If you want every crawler to get full HTML, upgrade to TanStack Start.

Do I need a pre-rendering service for Lovable?

Not on Lovable hosting: its docs say Lovable-hosted projects don't need one. If you export and self-host a Vite app, you lose Lovable's pre-rendering, and Google's advice is to prefer server-side or static rendering over bot-only pre-rendering.

How long does it take Google to index a Lovable site?

Lovable's docs say from a few hours to a few days, sometimes longer. Submitting the sitemap and requesting indexing for key pages in Search Console helps Google find them sooner.

Paste your URL on seofix.ai for a free 50-page preview, no account needed (what the preview covers).

ShareXLinkedIn
Fix technical SEO with OpenAI Codex (CLI and cloud)
Use cases

Fix technical SEO with OpenAI Codex (CLI and cloud)

Codex can fix SEO bugs in your templates if it gets crawl data and a way to recheck the live site. MCP setup in config.toml, an AGENTS.md section, sandbox modes, cloud vs local, and a Laravel canonical fix.

1,815 words · 8 min read