Rendering & JavaScript SEO
Googlebot does not scroll or click, and it wipes state between page loads. Rendering decides everything for JS sites: SSR/SSG is a requirement, not an option. The WRS pipeline, the noindex-in-HTML trap, lazy loading, and framework guidance.
The WRS pipeline (three phases, sequential)
- Crawl — parses raw HTML, queues
<a href>links. Only200proceeds. - Render — queued for WRS (evergreen headless Chromium); JS runs, DOM mutates, rendered HTML produced. Queue delay usually short, not guaranteed.
- Index — rendered HTML indexed; new links fed back.
WRS behaviors that trip people up:
- State is wiped between page loads —
localStorage/sessionStorage/cookies do NOT persist across navigations. Never depend on them for content or routing. - WRS caches resources aggressively and may ignore your cache headers. Fix with content fingerprinting (
main.2bb85551.js) so updated assets get new filenames. - Not all browser APIs supported (WebGL, WebSockets, WebRTC, Camera). Feature-detect and provide fallbacks.
- Client-side analytics undercount Googlebot — use Crawl Stats and URL Inspection instead.
JavaScript SEO rules
- Titles & meta per route: every route must set a unique
<title>and meta description (JS can setdocument.title; WRS captures it). - Routing — use the History API, never hash fragments. Google discovers links only via real
<a href>;onclick-only orhref="#"is invisible.
window.history.pushState({}, '', hrefUrl); // DO — real crawlable URL
// DON'T: fragment routing (#/products) — AJAX-crawling scheme is dead
- Don't block JS/CSS in robots.txt — if disallowed, WRS cannot render and content vanishes.
- Soft 404s in SPAs: redirect missing content to a real-404 URL, OR inject
noindexwhen content is missing. - Canonical/robots injection: prefer server-set
rel="canonical"; JS injection is fragile (easily produces conflicting tags). ⚠️ You cannot reliably removenoindexwith JS — Google may skip rendering when it seesnoindexin raw HTML. If you want a page indexed, never putnoindexin the original page code. - Web Components: WRS flattens light + shadow DOM; use
<slot>; verify via URL Inspection → Rendered HTML. - Use meaningful server-level status codes; non-
200may skip rendering.
Dynamic rendering — DEPRECATED
⚠️ Google deprecated dynamic rendering ("a workaround, not a long-term solution"). Use instead: SSR (HTML per request — Next.js app/getServerSideProps, Astro SSR), static rendering/prerendering (build time — Next.js SSG, Astro default static), and hydration (ship HTML then attach JS — the standard Next.js/Astro model). This is where accessibility and crawlability are jointly won or lost.
Lazy loading
Core rule: Googlebot does not scroll or click. All content must load when it enters the viewport or already be present. Approved: native loading="lazy", IntersectionObserver, viewport-detection libs that don't need interaction. DON'T lazy-load primary content.
const io = new IntersectionObserver((entries, obs) => {
entries.forEach(e => { if (e.isIntersecting) { e.target.src = e.target.dataset.src; obs.unobserve(e.target); } });
});
document.querySelectorAll('img[data-src]').forEach(img => io.observe(img));
next/image note: lazy-loads by default; for above-the-fold LCP images set priority (loading="eager" + fetchpriority="high"). Testing: URL Inspection → Rendered HTML (confirm src populated); or drive Puppeteer with a tall viewport and assert content in the DOM.
Framework guidance (Next.js & Astro)
- Prefer SSR/SSG output so headings, landmarks, alt text, body copy, canonical, and per-route title/meta exist in the initial HTML.
- Bing has weaker JS rendering than Google — serving crawlable HTML/SSR matters more for Bing; don't assume BingBot runs SPA JS.
- Do NOT publish parallel Markdown mirrors or
llms.txtas an AI-SEO tactic (SOTR ep. 111): Markdown strips navigation, internal links, hierarchy, and accessibility signals; indexing already reduces HTML to clean text. Serve clean, valid HTML.
Related on Crawlinx
- JavaScript SEO: How Google Renders JS Pages
- Next.js SEO: Rendering, Metadata & Render-Diffs
- Mobile-First Indexing: How to Pass Parity Checks
- Soft 404s: What They Are and How to Fix Them
- content.thin
Sources
Audit your own site — free
156 checks, internal PageRank, render-diff. No signup, results in ~30s.