Lovable SSR Explained: TanStack, Pre-rendering and Crawlers
Lovable SSR explained: what server-side rendering is, whether Lovable's TanStack Start really does it, why it is often not working on dynamic pages, and what each crawler gets.
Server-side rendering means the server builds the finished HTML for a page and sends it in the first response. The browser receives a complete document and then attaches JavaScript to it. Client-side rendering means the server sends a near-empty shell plus a JavaScript bundle, and the browser builds the page itself. A crawler is not a browser, so the difference decides what a crawler can read.
Lovable has both, depending on when your project was created. Projects on the TanStack Start stack are server-rendered. Projects on the older Vite stack are not, and get an automatically pre-rendered snapshot for search engines instead. Which one you are on is not shown anywhere in the builder, and it changes everything downstream.
This page explains how server-side rendering works, whether what Lovable ships qualifies, and what each crawler actually receives today. For the on-page fix list, see Fix Lovable SEO.
How server-side rendering works on Lovable, one request at a time
Take a request for /pricing.
Under server-side rendering, the server runs your components, fetches whatever data they need, and returns the finished page:
<!-- What the server returns --><body><h1>Pricing</h1><div class="plan"><h2>Starter</h2><p>$19 per month</p></div><script src="/assets/app.js"></script></body>
The text is there before any JavaScript runs. The bundle loads afterwards and takes over the page, a step called hydration, which turns the static HTML into a working app.
Under client-side rendering, the server returns the shell:
<!-- What the server returns --><body><div id="root"></div><script src="/assets/app.js"></script></body>
Then the browser downloads the bundle, runs it, calls your API, and paints the page. Everything a reader sees arrives in that last step.
For a person on a fast connection the two feel similar. For a crawler they are not similar at all. A crawler that reads only the first response sees a full page in the first case and an empty container in the second.
Google does run JavaScript, in a second pass that is queued separately and rationed by cost. That pass can come minutes later, weeks later, or not at all. Most AI crawlers do not run JavaScript in any pass.
Server-side rendering is not pre-rendering
The two words get swapped in the discussion around Lovable's announcement, so it is worth being exact about what each one hands a crawler.
Server-side rendering builds HTML on the server, per request. In the modern form TanStack Start uses, the server returns the initial HTML, usually the template with some basic data injected, and the rest of the page loads its data once that HTML lands in the browser. Fast, cheap, and good architecture. The catch is that whatever loads after the initial response does not exist as far as crawlers are concerned.
Pre-rendering is a layer on top of whatever you already run. A real browser loads the page ahead of time, runs the scripts, waits for the network and the DOM to stop changing, and the completed HTML is stored and served to crawlers. Humans keep getting your normal app.
Lovable uses both. Server-side rendering gives crawlers the route shell on TanStack projects. Publish-time pre-rendering gives crawlers the static pages Lovable can see in your code on Vite projects. Neither covers a page whose content arrives from a database, a CMS, or an API at the moment of the request.
The full comparison of the four rendering approaches is in SSG vs SSR vs Pre-rendering.
Is Lovable's TanStack SSR real server-side rendering?
Partly, and the part matters.
TanStack Start sets rendering per route, not per site. Each route file carries a flag:
ssr: truerenders the route on the server, HTML and data together.ssr: falserenders nothing on the server. That route is client-side rendered again, exactly like the old stack.ssr: "data-only"runs the route's data loader on the server but does not render the components to HTML there.
Lovable's generator sets these flags while it builds your project. It does not ask you, and it does not surface the choice in chat. So a project described as server-rendered can contain routes that are not.
You can check every route at once from the Lovable code view or a local checkout:
grep -rn "ssr:" src/routes/
Any route that comes back ssr: false is client-side rendered. Any route that comes back ssr: "data-only" returns a shell with data attached, not finished HTML. Routes with no flag use the project default, which is server rendering.
Two failure modes follow from this and both show up in Search Console rather than in the builder. A hydration mismatch, where the server HTML and the browser's first render disagree, can blank out content after the page loads. And a not-found route that returns HTTP 200 with a "page not found" body registers as a soft 404, which Google treats as a quality problem across the site.
Which Lovable stack is your project on, Vite or TanStack?
Two checks, neither takes 30 seconds.
File check, the reliable one. Open /src. If router.tsx and routeTree.gen.ts are there, you are on TanStack Start. If instead you see App.tsx and main.tsx with a react-router-dom import, you are on the Vite stack.
View source, the fast one. Open your live site, right-click, View Source. Not Inspect Element, which shows you the rendered result and tells you nothing. Search the source for your hero copy and your pricing copy. Missing strings are rendered client-side, whichever stack you are on.
The second check is the one that matters, because it tests the live response rather than the code.
Why Lovable SSR is not working on your database pages
Stack alone does not settle it. How a page gets its data settles it.
Under TanStack server rendering, data fetched in a route loader runs on the server and lands in the HTML. Data fetched in useEffect or a useQuery hook runs in the browser, after the response, and never reaches a crawler that does not execute JavaScript.
This is where server rendering on Lovable stops short of what people assume it does. Lovable Cloud is Supabase underneath, and the code Lovable generates talks to Supabase directly from the browser. The page component is rendered on the server with nothing in it, and the actual rows arrive afterwards from a call the browser makes. Unless you explicitly instruct Lovable to fetch in a route loader, that is the default shape.
The result is the empty shell problem again, in different clothing. Your layout, headings and navigation are in the HTML. Your products, posts, listings and profiles are not. A static marketing page is fine, because its content is in the code. Anything reading from a database, an external system or a CMS is not.
You can confirm it on your own site in under a minute. Open the page, open your browser's developer tools, and watch the Network tab while it loads. A request to a Supabase endpoint that returns your page's content means that content arrived after the HTML did, and a crawler that does not run JavaScript never saw it. The same applies to a WordPress REST endpoint, Sanity, Contentful or Airtable. What matters is not which service holds the data; it is whether the fetch happens before the response or after it.
Under a runtime pre-render layer, the question does not arise. A browser loads the page, waits for the data to settle, and stores the result, so whatever a visitor would see is what the crawler gets.
| Page type | Source of content | In the first response? |
|---|---|---|
| Homepage, about, pricing, features | In the project as code | Yes on TanStack. On Vite, could not verify |
| Blog index with a hardcoded list | In the project as code | Same as above |
| Blog post fetched from a CMS | Sanity, Contentful, Notion, Supabase | Only if fetched in a route loader |
| Directory or listing from a database | Supabase, Lovable Cloud, Postgres, REST | Only if fetched in a route loader |
| User-generated content pages | Any database | Usually no |
| Catalog with API-driven product data | Any e-commerce API | Only if fetched in a route loader |
| Anything behind a login | Server, per user | No, and it should not be |
What Googlebot, GPTBot and the social scrapers receive today
The table below is what the four setups hand each crawler. Sources are named, because some of these cannot be reproduced from outside.
| Crawler | Vite, client-rendered | Vite + Lovable's auto-snapshot | TanStack, ssr: true route |
Runtime pre-render |
|---|---|---|---|---|
| Googlebot | Shell first, JavaScript pass later | Could not verify | Initial HTML only | Rendered HTML |
| Bingbot | Shell | Could not verify | Initial HTML only | Rendered HTML |
| GPTBot | Shell | Could not verify | Initial HTML only | Rendered HTML |
| ClaudeBot | Shell | Could not verify | Initial HTML only | Rendered HTML |
| PerplexityBot | Shell | Could not verify | Initial HTML only | Rendered HTML |
| facebookexternalhit | Shell | Could not verify | Initial HTML only | Rendered HTML |
| LinkedInBot | Shell | Title only, wrong image | Initial HTML only | Rendered HTML |
| Shell | No card | Initial HTML only | Rendered HTML | |
| Slack | Shell | Root index.html for every URL | Initial HTML only | Rendered HTML |
| iMessage | Shell | Root index.html for every URL | Initial HTML only | Rendered HTML |
Where those columns come from.
We tested this across more than 50 live Lovable sites, two ways on each. We asked ChatGPT, Claude and Gemini to read the pages after the update and report a fact that only appears on the page. And we ran the same URLs through Encited's crawler simulator as Googlebot, GPTBot, ClaudeBot and the social scrapers.
On the automatic pre-rendering column, we could not verify it was being applied at all. Across every site we checked, the models could not read the pages and the simulator received no page content. That is not the same as proving the feature does nothing. Lovable publishes no documentation on how the snapshot is built, which requests receive it, or how to check, and you cannot inspect your own. So the honest answer for that column is that we have no evidence it is in effect, and no way to get any.
The TanStack column is a narrower failure and easier to miss. Server rendering does run, and the initial HTML arrives. What does not arrive, consistently, is the data. Anything coming from Lovable Cloud, Supabase or a remote API is fetched from the browser after that first response, so it is absent from the HTML a crawler reads. Your layout indexes. Your products, posts and listings do not. For a site whose search traffic lives on exactly those pages, that is the whole problem, in a shape that looks like it has been solved.
You can repeat the simulator half of this against your own URL in under a minute with the crawler simulator.
How to test Lovable SSR on your own pages
Do not take the table above on trust. Test your own URL, and pick the most common page type on your site rather than the homepage. A blog post if you run a blog. A listing if you run a directory. Something that pulls its data at runtime.
Ask an AI assistant to read the page. Not search for it, read it. Paste the URL and ask for a fact that appears only in the dynamic part of the page, such as a price or a review count. A vague or wrong answer means the model did not get your content.
Watch the Network tab. Open the page with developer tools open. If a request to Supabase or your CMS returns the content you care about, that content was not in the HTML and a crawler without JavaScript did not get it.
Run it through a crawler simulator. It shows the response each bot receives, without a terminal.
See what Googlebot, GPTBot, ClaudeBot and PerplexityBot receive
Paste any URL from your Lovable site and compare the raw response against what a visitor sees.
Or curl it. This is the truest test, with nothing in between:
curl -s -L -A "Googlebot" https://yourdomain.com/important-page \| grep -i "expected headline or product name"
No match means that page is not reaching crawlers as HTML. Run the same check against a static page such as your homepage. The difference between the two tells you which part of the stack is doing the work.
Which setup do you need, and what it costs
Work down this list and stop at the first line that matches you.
New project on TanStack, every SEO route ssr: true, data in loaders. Google is covered. Check social previews and AI crawlers separately, because those depend on tags and on route coverage rather than on the stack.
New project that came out on Vite. The rollout was never uniform, so this is common. You need a pre-render layer, or /migrate to convert the project and spend the credits.
Older project, created before April 2026. Same two options. Lovable's auto-snapshot covers Google on static routes and nothing else.
Any project that needs AI crawler visibility or working link previews. A pre-render layer, whatever the stack. The auto-snapshot does not reach those crawlers by design, and a TanStack route only covers them when it is server-rendered and its data is in a loader.
Any project whose search traffic comes from dynamic routes. Blog posts, directory entries, generated landing pages. These are the routes publish-time tooling cannot reach, and usually the ones built to earn traffic in the first place.
A runtime pre-render layer covers every route on a domain regardless of stack. Encited is one option and sets up through DNS in five to ten minutes.
The Lovable SEO update: what shipped in May 2026
Four features, announced together on 14 May 2026.
TanStack Start server-side rendering for new projects, covered above. Existing projects do not get it automatically. Lovable's FAQ is explicit: existing projects get pre-rendering, and full server rendering is only for new TanStack projects. The /migrate command converts an existing project and charges credits.
Native pre-rendering for older Vite projects, running at publish time against the static routes Lovable can see in your code. On launch night, users on r/lovable and X reported Googlebot-style requests still hitting empty shells on static pages. Whether it works for your project is a curl away.
An in-builder SEO and AI search review, free on every plan, which audits performance, metadata, headings, alt text, canonicals, Open Graph tags, robots and sitemap, and offers one-click fixes that consume build credits. It inspects the static routes Lovable can see at publish time, so it will confirm your homepage has the right meta tags and will not tell you whether /blog/[slug] returns empty HTML to Googlebot. Auditing that needs a crawler hitting your live URLs from outside the builder, such as the free SEO Spider.
A Semrush connector, for keyword research inside Lovable chat. No Semrush account needed, because Lovable handles authentication. Each invocation costs 10 Lovable credits, measured in our own testing; Lovable's page calls them regular build credits without naming a number. The launch promo waived the cost through 15 August 2026, and the per-call cost applies after that. It changes nothing about what crawlers receive.
Three of those four run at publish time against static routes. That is the line worth remembering: Lovable renders what it can see in your code when you publish, and anything fetched at the moment of the request falls outside it. Lovable's announcement also claims AI search optimization through structured markdown, semantic HTML and structured data, and the same fine print applies.
For how AI crawlers behave on React sites generally, see How to Get Your Pages Indexed by ChatGPT, Perplexity, and Other AI Search Engines.
Frequently asked questions
Is TanStack Start real server-side rendering?
Yes, on routes where it is turned on. TanStack Start sets rendering per route. A route marked ssr: true is rendered on the server and arrives as finished HTML. A route marked ssr: false is client-side rendered, and ssr: "data-only" runs the loader on the server without rendering components to HTML. Lovable's generator sets these flags without asking, so check them with grep -rn "ssr:" src/routes/ rather than assuming.
Does Lovable's pre-rendering work for blog posts pulled from a CMS?
No. It runs at publish time, so it only covers pages that exist as static routes in your project at that moment. Posts fetched from Sanity, Contentful, Notion or Supabase at runtime are not covered.
Do ChatGPT and Perplexity see Lovable's pre-rendered pages?
We could not verify that it reaches them. We tested more than 50 live Lovable sites after the update, asking ChatGPT, Claude and Gemini to read the pages and running the same URLs through a crawler simulator, and the responses carried no page content. Lovable documents nothing about how the snapshot works or how to check it, and you cannot inspect your own, so there is no way to confirm it is in effect. On a TanStack route the initial HTML does arrive, but data from Lovable Cloud, Supabase or a remote API is fetched in the browser afterwards, so it is not in what the crawler reads.
How do I know if I have the TanStack Start template?
Check /src for router.tsx and routeTree.gen.ts, or open your live site and use View Source. Visible content in the raw HTML means at least partial server rendering. Only <div id="root"> and a bundle import means the Vite stack. You can also run the URL through the Crawler Simulator.
Does Lovable's SEO review cover my dynamic pages?
No. It inspects the static routes Lovable can see at publish time. Auditing dynamic routes needs a crawler hitting your live URLs from outside the builder, such as the free SEO Spider.
Is the Semrush connector worth using?
For light keyword research it is convenient and needs no Semrush account. Since the promo ended on 15 August 2026, each invocation costs 10 Lovable credits in our testing, which adds up for ongoing research. The same data is in dedicated SEO tools or Google Search Console.
Do I need a pre-render layer on top of what Lovable ships?
If your site is marketing pages only, with no database or CMS content, and a crawler test confirms complete HTML, Lovable's own tooling may be enough. If you are on the Vite stack and cannot verify rendered HTML, or you have a CMS blog, a directory, a catalog, user-generated content, or anything fetched at runtime, those routes need a layer that runs at request time. The DNS setup for Lovable takes 5 to 10 minutes.
What changed compared to the April 2026 rollout?
April was unannounced, partial and inconsistent across accounts. The May announcement made it official, opened migration for existing projects, and added the three adjacent features. The architecture did not change, and the partial-rollout caveat still holds: new projects still come out on the Vite stack sometimes.


