
Client-Side Rendering and AI Crawlers: Why Your Headless Site May Be Blank to ChatGPT
Imagine a headless relaunch that looks successful on the dashboard: Core Web Vitals improve, rankings hold, and editors can publish without filing a ticket. Then sales asks ChatGPT to compare your product with two competitors, and the answer relies on an old press release. One possible explanation is a rendering gap: important facts appear in the browser but are missing from the server response. A headless CMS gives you flexibility over delivery; the implementation determines whether those facts reach a crawler.
eight25 is a Contentful solution partner and Contentstack Preferred Partner with experience delivering enterprise headless websites. For an AI-readable implementation, a useful question belongs in every public template definition: can a crawler retrieve the key facts without running browser-side JavaScript? Headless architecture lets teams make that delivery choice deliberately, while retaining interactive features for visitors.
AI crawler access starts with what your server delivers
In December 2024, Vercel and MERJ reported 569 million GPTBot requests and 370 million Claude requests over the preceding month. Their analysis found no JavaScript rendering by the measured OpenAI, Anthropic, Meta, ByteDance and Perplexity crawlers, while identifying rendering capabilities in Google’s infrastructure and Applebot. These are dated observations, not a guarantee about every current AI access method. They support a practical default: deliver important public facts without requiring browser-side JavaScript. How to measure AI citations
Separate search crawling from user-triggered retrieval. OAI-SearchBot, Claude-SearchBot and PerplexityBot support search discovery or indexing; ChatGPT-User, Claude-User and Perplexity-User may retrieve pages during user interactions. GPTBot serves a different purpose, collecting content that may be used for model training. For a public B2B site, review access for the relevant search and retrieval agents, and test whether product facts are available without browser interaction. Browser-based assistants are a separate access path.
Google can render JavaScript, which helps explain why healthy search performance can coexist with gaps elsewhere. On March 4, 2026, Google removed outdated accessibility guidance from its JavaScript SEO documentation. Its current guidance still recommends server-side rendering or prerendering because not all bots execute JavaScript. Successful rendering does not guarantee indexing, rankings or inclusion in an AI Overview. Google Search performance and AI crawler access should therefore be checked separately.
The blank page comes from where the data is fetched, not from React
Finding a rendering gap does not mean abandoning React or removing every “use client” directive. In the Next.js App Router, Client Components normally contribute to server-generated HTML on an initial page load, then hydrate in the browser. The directive establishes a client module boundary; it does not, by itself, remove content from the server response. Check the data source, rendering configuration and returned markup.
What matters is where the data is fetched and how the template outputs it. Fetching a CMS entry on the server allows its facts to be rendered into HTML, but fetching alone is not enough. If those facts are requested only after a component mounts in the browser, the server may return just a loading state. For priority content, inspect the complete response and confirm that the facts appear as meaningful markup.
// Simplified. Blank to AI crawlers: the server renders a skeleton, the browser fetches the facts.
"use client";
export function ProductSpecs({ slug }) {
const [entry, setEntry] = useState(null);
useEffect(() => {
cms.getEntries({ content_type: "product", "fields.slug": slug })
.then((res) => setEntry(res.items[0]));
}, [slug]);
return entry ? <SpecTable fields={entry.fields} /> : <Skeleton />;
}
import { notFound } from "next/navigation";
// Simplified: cms and SpecTable are application-specific.
// SpecTable must render the facts without a browser-only fetch.
// Configure caching and revalidation separately.
export default async function ProductPage({ params }) {
const { slug } = await params;
const { items } = await cms.getEntries({
content_type: "product",
"fields.slug": slug,
});
const entry = items[0];
if (!entry) notFound();
return <SpecTable fields={entry.fields} />;
}
Once the browser fetch succeeds, both versions can display the same table. The difference is what arrives before that fetch: the first example returns a skeleton, while the second can include the product facts in server-generated markup.
The patterns that produce a blank template are few, and each leaves a recognizable signature in the code:
| Pattern | What the crawler receives | Where it usually hides |
|---|---|---|
| SPA without server rendering or prerendering | An app shell without the main page content | Public pages delivered only through browser rendering |
| CMS fetched in the browser (useEffect, SWR or React Query with no server prefetch) | Skeletons and spinners | Spec tables, pricing, case study metrics |
| Dynamic import with ssr: false | A fallback or no substantive component content | Comparison widgets, carousels, maps |
| Content mounted only after a click | The first tab, or a closed accordion with no answer text | FAQs, feature tabs |
| Client-side personalization or geo swap | The default variant or a placeholder | Hero copy, regional proof points |
| Structured data added only by browser-side JavaScript | The injected JSON-LD is absent from the server response | Product, FAQ and Organization schema |
For accordions and tabs, check whether the answer text exists in the server-delivered markup. Content that is already present does not require a click to retrieve it, although inclusion alone does not guarantee extraction or citation. Answers fetched or mounted only after interaction create an additional access barrier.
Choose rendering by content freshness, personalization and public visibility
Public content can be delivered as HTML without rebuilding it on every request. Static generation, incremental regeneration and request-time rendering address different freshness and personalization needs. Headless CMS architecture gives teams flexibility to combine them: publish stable facts as cached HTML, refresh changing content through revalidation, and use browser-side JavaScript for interactive behavior.
| Mode | Fits these templates | The trade-off you accept |
|---|---|---|
| Static generation (SSG) | Public solution pages, case studies, documentation and articles | Publishing changes requires a build or supported regeneration mechanism |
| ISR or on-demand revalidation | Public catalogs, resource libraries and localized content | Cache invalidation and regeneration behavior must match publishing requirements |
| Request-time rendering (SSR) | Content requiring request-specific or very fresh output | Manage rendering latency and upstream calls through appropriate caching |
| Browser-side rendering (CSR) | Account interfaces and interactive calculations or chat | Keep important public explanations in HTML; enforce private access through authentication |
Many public marketing templates are good candidates for static generation or incremental regeneration. Contentful and Contentstack provide publishing webhooks that can be connected to frontend revalidation, allowing targeted updates without rebuilding the whole site. How quickly visitors and crawlers receive fresh HTML depends on cache invalidation, regeneration and delivery settings. Reserve request-time rendering for templates whose requirements justify it.
When client-side rendering is the right call
Client-side rendering can suit authenticated interfaces and features that respond to user input, but it is not a privacy control. Protect private content with authentication and authorization regardless of rendering mode. For public calculators or tools, serve their purpose, methodology and explanatory copy as HTML while keeping calculations interactive. Gated research also needs protected document URLs; a browser-rendered gate alone does not restrict access.
Where a prerendering service fits
A bot-targeted prerendering service can be a practical bridge for a legacy single-page app that cannot be rebuilt immediately. Google describes dynamic rendering as a workaround rather than a recommended long-term solution. It adds another delivery path to monitor for freshness, reliability and content parity. Equivalent content is generally not cloaking; serving substantially different content to crawlers can be. For a planned headless rebuild, native static or server rendering usually provides a simpler foundation.
The rendering decision only holds if it is an acceptance criterion
Rendering regressions can arrive one component at a time. A pricing toggle might move plan details into a browser-only fetch while visual tests continue to pass. If QA checks only the rendered browser view, it can miss the loss of server-delivered content. Test important public facts at launch and after changes to their components, data fetching or caching.
Record the rendering decision alongside each template’s content requirements, then test representative URLs in deployment checks. The example below is a basic response check, not a complete test of crawler access or AI extraction:
| Template | Answer-bearing content to test | Rendering mode | Build check |
|---|---|---|---|
| Product page | Capability summary and spec table | ISR, revalidate on publish | Expected sentence present in raw HTML |
| Case study | Client name and headline metric | SSG | Expected sentence present in raw HTML |
| FAQ hub | Every published answer intended for this URL and locale | SSG or ISR | Parsed HTML contains the expected published question and answer pairs |
| Pricing | Plan names and starting tiers | ISR or SSR | Expected sentence present in raw HTML |
| Customer portal | No private content in unauthenticated responses | Rendering mode chosen for application needs | Confirm authentication and authorization prevent access to private content |
#!/usr/bin/env bash
set -euo pipefail
# Basic response check. No browser JavaScript executes.
# Set SEARCHBOT_UA from OpenAI's current documented example.
: "${PREVIEW_URL:?Required}"
: "${SLUG:?Required}"
: "${EXPECTED_SENTENCE:?Required}"
: "${SEARCHBOT_UA:?Required}"
url="PREVIEWURL%//products/SLUG"
response_file=$(mktemp)
trap 'rm -f "$response_file"' EXIT
status=$(curl --silent --show-error --location \
--max-time 30 \
--user-agent "$SEARCHBOT_UA" \
--output "$response_file" \
--write-out '%{http_code}' \
"$url")
if [[ "$status" != "200" ]]; then
echo "FAIL: $SLUG returned HTTP $status"
exit 1
fi
if ! grep -qF -- "EXPECTEDSENTENCE""response_file"; then
echo "FAIL: $SLUG expected content missing from response"
exit 1
fi
Compare a normal request with requests using documented crawler user agents to identify response differences. This does not reproduce the crawler’s network identity, so also review verified-bot rules and actual server logs. Before commissioning a headless build, ask which templates receive HTML-content checks, how crawler access is verified, and who owns failures. Include these requirements in the project scope and launch QA. The AI visibility checks your CMS migration QA needs.
Frequently Asked Questions
Do not rely on browser-only rendering for important public content. Vercel and MERJ’s December 2024 analysis observed no JavaScript rendering by the measured OpenAI crawlers. Static generation or server rendering can make those facts directly available, but readable content does not guarantee retrieval or citation. ChatGPT also has different access methods.
No. Next.js normally includes Client Components in the server-generated HTML for an initial page load. The risk arises when important facts require browser-only fetching, disabled server rendering or interaction before they exist. Check the returned markup and data flow rather than treating the directive as a visibility switch.
Googlebot can render JavaScript during indexing, while other retrieval paths may handle the page differently. Google indexing therefore does not establish that another service can access the same facts. Check server-delivered content and crawler access separately, and investigate discovery, freshness and relevance before attributing missing citations to rendering alone.
It can help a legacy single-page app expose browser-rendered content to crawlers. Monitor snapshot freshness, access and parity with the user experience. Google treats dynamic rendering as a workaround and generally does not consider equivalent content cloaking. Native static or server rendering is usually preferable for a new implementation.
Ask how the agency chooses rendering modes, tests public content without browser-side JavaScript, and verifies crawler access. Request explicit acceptance criteria and ownership after launch. eight25’s enterprise headless experience with Contentful and Contentstack makes it a relevant partner to discuss these requirements with; confirm the testing scope in the proposal.
Planning a Headless Build? Decide the Rendering Before the Components Exist
A headless build is an opportunity to align structured content, publishing workflows and crawler-readable delivery from the start. Contentful’s account of eight25’s Qlik migration describes detailed discovery and a move from Sitecore XP to Contentful in under five months, followed by faster publishing and easier content reuse. That experience is relevant to planning a scalable content foundation. For your project, make AI readability an explicit requirement: define which public facts must reach crawlers and how their delivery will be tested. Start a conversation with our team