My pages looked fine in a browser. Google was indexing them. Then I read the HTML the way a crawler reads it, with no JavaScript:

curl -s https://your-site.com/some-page -A 'OAI-SearchBot/1.0' 

There was a header, an empty slot and a footer. The article came after the footer, inside <div hidden id="S:1">. A small inline script moves it into the slot when the page loads.

Googlebot runs that script. A crawler that doesn't run JavaScript gets a hidden block at the bottom of the page. I can't see inside every crawler to know what it does with that. I didn't want to bet my pages on it.

The cause

One wrapper I had written on purpose:

<Suspense fallback={null}> <Content params={params} /> </Suspense> 

I assumed a boundary that finishes at build time gets inlined. Not always. React has an option called progressiveChunkSize, 12,800 bytes by default. A boundary bigger than that gets outlined: the slot goes out first, the content later. The rule is about size, not about waiting.

You can prove it with nothing async at all. I rendered a plain Suspense boundary with React 19.3's renderToPipeableStream:

Boundary size Article hidden after the footer?
2,000 bytes No
12,000 bytes No
14,000 bytes Yes
40,000 bytes Yes
40,000 bytes, with progressiveChunkSize raised No

The limit counts HTML bytes, not words. Tailwind classes count. Even my shortest page, a list of five calculators, crossed it.

How to check

Find your <h1> in the raw HTML. If <div hidden id="S: comes right before it, you have this.

The fix

Stop wrapping the article. With Cache Components on, reading params outside Suspense fails the instant navigation check, so routes with a dynamic param also need:

export const instant = false; 

That turns off a check, not prerendering. My pages still answer x-nextjs-prerender: 1.

Most of my fallbacks were null, so I lost nothing there. A blank page was never instant in any way a person would value. On my docs I did give up a loading skeleton. I took that trade.

Before and after

On a calorie page on Kallo, the words outside the hidden block went from 107 to 779. Nothing is hidden now.

No test caught this. The types were fine, the page looked right, and Google was happy. It only shows up if you read the bytes.

Check your own site. It takes ten seconds.