Lovable SEO: Why Google Can't Read Your App
Open your Lovable site, right-click, View Page Source. Not Inspect, the actual source. You’ll see something close to this:
<title>my-saas-landing</title>
<meta name="description" content="Lovable Generated Project" />
...
<div id="root"></div>
<script type="module" src="/assets/index-B7xk2.js"></script>
That’s your whole website as far as the first request is concerned. Every Lovable SEO complaint we get traces back to this one file. Lovable builds a React single-page app with Vite, the browser downloads an empty shell, and JavaScript paints everything after the fact. Your pricing page, your blog, your About page, all of them return that same HTML with the same title.
People will tell you this doesn’t matter anymore because Google runs JavaScript. Google does run JavaScript. It’s also not the only thing reading your site, and even Google does it on its own schedule.
What actually goes wrong
A founder came to us in the spring with a directory site built in Lovable, about 1,400 listing pages generated from a Supabase table. After three months, Search Console showed 31 of them indexed. The rest sat in “Discovered - currently not indexed” and “Crawled - currently not indexed”, and a chunk had been tagged “Duplicate without user-selected canonical”. Google had fetched the pages, seen the identical shell with the identical <title>, and decided they were copies of each other before it ever got round to rendering them.
That’s the pattern. Rendering is a second pass, it costs Google more, and a new domain with no links doesn’t get much of it. So a Lovable site’s SEO problems show up as:
- every route sharing the
index.htmltitle and description until JS runs, which is why people bolt onreact-helmet-asyncand still see the wrong title in search results for weeks - link previews on LinkedIn, Slack, WhatsApp, iMessage and X showing the same image and text for every URL, because none of those crawlers execute JavaScript at all
- AI crawlers like GPTBot and PerplexityBot mostly reading an empty div, so you don’t get cited in answers either
- no
sitemap.xmland norobots.txtunless you asked for them - soft 404s, because the SPA fallback answers
/anything-you-typewith a 200 and the React router shows “Page not found” afterwards
That last one is easy to miss. Search Console reports it as “Soft 404”, and from Google’s side your site looks like it has infinite pages, most of them junk.
Things that are worth doing inside Lovable
Honestly, a few fixes are cheap and you should do them today, even if you never touch the rendering problem.
Change the <title> and meta description in index.html to something that describes the business. Half the Lovable sites we audit still say “Lovable Generated Project” in the default description. Add a real og:image there too, so at least the home page previews properly.
Ask Lovable to generate a public/sitemap.xml and public/robots.txt. Keep the sitemap to pages that actually exist. Then submit it in Search Console.
Use react-helmet-async for per-page titles. It won’t fix link previews, but Google will pick them up once it renders.
Put real <a href> links between your pages. A surprising number of Lovable layouts navigate with onClick={() => navigate('/pricing')} on a div or a button. Googlebot does not click things. If there’s no href, the page is an orphan.
The real fix is serving HTML
Everything above is patching around the problem. The fix is making the server return the actual content on the first request. There are three ways to get there and we’ve done all of them.
Prerender at build time
For a marketing site with a known set of pages, this is the one we reach for first. At build time a headless browser loads each route and saves the rendered HTML as a static file. You keep the Lovable codebase, you keep editing it in Lovable, and the deploy pipeline adds one step. vite-plugin-prerender or a small Puppeteer script both work. The catch is that it only covers routes you list, so it’s a bad fit for those 1,400 directory pages unless you generate the route list from the database during the build, which we did for that client. It took about two days including fixing the components that assumed window existed.
Prerender at the edge
Services like Prerender.io sit in front of your site, spot crawler user agents and serve them a cached rendered copy. It works and it’s fast to set up. We don’t love it. You’re paying monthly to show bots a different response than users get, the cache goes stale, and when it breaks nobody notices because humans never see that version.
Split the marketing site out
If content is going to be your main growth channel (a blog, comparison pages, docs), move the public pages to something that renders on the server and leave the app in Lovable. This site is Astro, for example. The app lives on app.yourdomain.com, the marketing pages live on the root, and each one does the job it’s good at. It’s more work up front and it’s the setup we’d pick for anyone serious about organic search.
What we don’t recommend is migrating the whole app to Next.js just for SEO. The logged-in dashboard doesn’t need to rank. Rewriting it to get server rendering on pages Google can’t even reach behind a login is a lot of money for nothing.
How to check if it worked
Run curl -s https://yourdomain.com/pricing | grep -i "<title>" and see whether the title is the pricing page’s title or the generic one. Paste a URL into the LinkedIn Post Inspector and look at the preview. In Search Console, use URL Inspection, “Test live URL”, then “View tested page” and check the HTML tab, not the screenshot. The screenshot always looks fine.
If all three show the right content, Google is reading the same page your users see. After that, SEO is the normal grind of writing pages people search for, which is a much better problem to have.
If you’d rather not do the plumbing yourself, this is exactly what our Lovable SEO service fixes, usually as part of taking a Lovable app to production. Tell us what your site is built on and we’ll tell you which of the three fits.