Three ways to stop shipping an empty page

The default output of most AI builders is an HTML file with almost nothing in it and a script that fills the page in afterwards. Here are the three ways out, in order of how much work each one is.

Run the free check

A single-page app can be made readable in three ways: pre-rendering, which generates static HTML for each route at build time and is the cheapest option for a small app; server-side rendering, which builds the HTML per request and suits apps with changing data; and static generation through a framework that does it by default. For an app with a fixed set of pages, pre-rendering at build time is almost always the right answer.

What the problem actually is

When a browser requests your page, the server sends an HTML file. In a typical single-page app that file contains a nearly empty container and a link to a JavaScript bundle. The browser downloads the bundle, runs it, and the script builds the page.

A human never notices, because this happens in a fraction of a second. A crawler that does not execute JavaScript, or that executes it later and separately, receives the empty file.

Before assuming this affects you, check. Several platforms now solve it. Lovable serves pre-rendered HTML to verified crawlers, and Base44 serves real HTML. If you are on one of those, an empty view-source can be entirely normal and your app may be fine.

How to test it properly

Two checks, and the second is the one that settles it.

  • Fetch the page without a browser and read what comes back. On a command line, curl the URL and look for your actual headline text in the response. If it is there, you are serving real HTML.
  • Use the URL Inspection tool in Google Search Console, test the live URL, and read the rendered HTML Google fetched. This is definitive, because it is Google showing you what Google received.
  • Do not rely on a third-party SEO scanner, especially on a platform that serves pre-rendered HTML only to verified crawlers. The scanner is not on that list and will report an empty page truthfully about a version Google never sees.

Option one: pre-render at build time

The cheapest fix for a small app, and the right answer in most cases.

At the end of your build, a script visits each of your routes in a headless browser, waits for the page to finish rendering, and writes the resulting HTML to a file. Your host then serves those files directly. Crawlers and humans both receive complete HTML, and the app still behaves as a single-page app once the script loads.

This suits an app with a known, fixed set of pages, which describes most vibe-coded products. It costs a build step and some build time, and it requires that you list your routes somewhere the script can read.

The limitation is content that changes between builds. A page whose content comes from a database will be frozen at whatever it said when you last deployed, so pre-rendering suits marketing pages, guides and documentation rather than a live dashboard.

Option two: server-side rendering

The server builds the HTML for each request and sends a complete page every time. Everybody gets real content, including data that changed a second ago.

This is the more capable option and the more expensive one. It needs a running server rather than static file hosting, which means more to operate, more to pay for, and more that can break. It also changes how your code is written, because anything that assumes a browser now has to survive running on a server.

It is the right choice when your public pages show data that genuinely changes, such as listings, profiles or anything user-generated that you want indexed. It is overkill for a marketing site with eight fixed pages.

Option three: use a framework that does it already

If you are early enough to choose, or willing to migrate, several frameworks handle this by default and remove the decision entirely.

Lovable's own documentation states that apps created from 13 May 2026 use TanStack Start with server-side rendering, where every request returns fully rendered HTML to everyone rather than only to verified crawlers.

Next.js renders on the server by default in the App Router, which is why it produces the best starting position of the common stacks, and also why one misplaced directive can undo it. That specific trap is covered on its own page in this section.

Migrating an existing working app to a new framework is a real cost and is rarely worth it purely for this. If you are starting something new, it is worth knowing that this choice is made at the beginning and is expensive to revisit.

Choosing between them

A short decision rule that covers most situations.

If your public pages are fixed between deployments, pre-render at build time. It is the least work, the least to operate, and it is enough.

If your public pages show data that changes and you need that data indexed, use server-side rendering and accept the operational cost.

If your public pages show data that changes but you do not need it indexed, pre-render the marketing pages and leave the application behind a login, where crawlers were never going anyway. This last case is more common than people realise and it saves a great deal of unnecessary work.

Confirming it worked

After deploying, fetch the page again without a browser and look for your headline in the response. That is the whole test.

Then use URL Inspection in Search Console on the live URL and confirm the rendered HTML contains your content. Give Google some time to recrawl before expecting the index to reflect it, since fixing the rendering does not retroactively change what was already indexed.

One thing worth checking that people forget: test a deep route, not just the homepage. It is common for the homepage to be handled and interior routes to be missed, which is the version of this bug that hides longest.

Part of a larger guide

This page is one part of The fixes. The other parts:

Questions people ask

How do I make a single-page app readable by search engines?
Three ways: pre-render each route to static HTML at build time, which is cheapest and suits an app with fixed pages; server-side render, which builds HTML per request and suits changing data; or use a framework that renders on the server by default. For most vibe-coded apps, build-time pre-rendering is enough.
How do I test whether my app serves real HTML?
Fetch the page without a browser and look for your actual headline text in the response, then confirm with the URL Inspection tool in Search Console on the live URL. Do not rely on a third-party scanner, since some platforms serve pre-rendered HTML only to verified crawlers and a scanner is not on that list.
Is pre-rendering good enough, or do I need server-side rendering?
Pre-rendering is enough when your public pages are fixed between deployments, which describes most vibe-coded apps. Server-side rendering is worth its operational cost only when your public pages show data that genuinely changes and you need that changing data indexed.
My homepage renders fine but interior pages do not. Why?
This is the most common version of the bug and the one that hides longest, because the page people check first works. Always test a deep route rather than only the homepage, since it is easy for a setup to handle the entry point and miss everything else.
Should I migrate frameworks to fix this?
Rarely, if you have a working app. Migration is a real cost and pre-rendering solves the problem for far less effort. The framework choice matters most when you are starting something new, because it is made at the beginning and is expensive to revisit later.
Will fixing rendering immediately fix my rankings?
No. It makes your pages readable from now on, but it does not retroactively change what was already indexed, and Google needs to recrawl before the index reflects the change. Fix it, submit the sitemap, and expect the improvement to appear over the following weeks.

See exactly what a crawler receives

The free Get Found Check fetches your pages the way a crawler does and shows you what came back, so you do not have to guess whether the rendering is working.

Run the free check Have Licheo do it for you