Direct answer
JavaScript is not inherently bad for SEO. The risk appears when essential content, links, metadata, or status depends on code that a crawler, user, or agent cannot reliably execute. Server-rendered semantic HTML is the most robust baseline because it gives browsers, crawlers, assistive technology, and agents useful information before optional enhancement runs.
To diagnose a JavaScript page, compare the HTTP response, rendered DOM, network requests, mobile layout, keyboard flow, and accessibility tree. Check the actual status and canonical before judging the framework. A client-rendered page can be indexed. An SSR page can still fail through wrong status codes, hydration errors, hidden mobile content, or broken links.
Do not ask “is React good for SEO?” Ask “what can each observer retrieve and operate in the real deployed state?”
What you will be able to do
By the end, you will be able to:
- distinguish server-side rendering, static generation, hydration, client rendering, and prerendering;
- compare source HTML with rendered DOM;
- diagnose failed dependencies, soft 404s, late metadata, and non-crawlable navigation;
- verify mobile content, metadata, and structured-data parity;
- understand screenshot, HTML, DOM, and accessibility-tree agent observations;
- choose the smallest complete rendering fix instead of migrating frameworks blindly.
How Google processes JavaScript
Google's JavaScript SEO basics describes three broad phases:
- crawling;
- rendering;
- indexing.
A successful 200 page can enter a rendering queue. Rendering can reveal content and links that were not present in the initial response.
This capability is not a guarantee that every dependency succeeds, that every crawler executes JavaScript, or that content loaded only after a click will be found.
The rendering patterns
Static site generation, or SSG
Pages are produced as HTML during a build.
Good fit:
- editorial pages;
- documentation;
- routes that change less often than deployments.
Trade-off:
- large inventories can make builds expensive;
- fresh data may require revalidation or client enhancement.
Server-side rendering, or SSR
The server generates HTML for each request or cached request.
Good fit:
- dynamic public routes;
- personalized shells with public core content;
- frequently updated indexable pages.
Trade-off:
- server latency and availability;
- cache complexity;
- risk of tenant or authorization leakage if cache keys are wrong.
Hydration
JavaScript attaches behavior to server-rendered HTML.
Good fit:
- interactive controls on an already meaningful document.
Trade-off:
- hydration mismatch;
- duplicate work;
- delayed interaction;
- large script cost.
Client-side rendering, or CSR
The initial response often contains an application shell. JavaScript fetches data and builds the page.
Good fit:
- authenticated applications;
- highly interactive tools;
- experiences where search indexing is not required.
Trade-off:
- public content depends on script, API, auth, and render success;
- error routes can return the wrong status;
- non-Google crawlers may not execute the same code.
Prerendering or dynamic rendering
A separate system produces rendered HTML snapshots.
This can be a tactical compatibility layer, but it adds invalidation, parity, cloaking, and operational complexity. Prefer one truthful experience when possible.
Rendering-pattern decision table
| Rendering pattern | Response HTML content | JavaScript dependency | Search risk | Agent and accessibility risk | Performance trade-off | Best test | Suitable use |
|---|---|---|---|---|---|---|---|
| SSG | Complete at build | Low for meaning | Low when current | Low with semantic markup | Fast delivery, build cost | Fetch HTML plus browser QA | Lessons and docs |
| SSR | Complete per request | Optional enhancement | Low when server stable | Low with semantic markup | Server and cache cost | Anonymous fetch, logs, browser QA | Dynamic public pages |
| SSR plus hydration | Complete then interactive | Medium | Hydration can alter state | Focus and duplicate-DOM risk | Script and main-thread cost | Response, DOM, keyboard, performance trace | Interactive public pages |
| CSR | App shell initially | High | Render and dependency risk | Empty or inoperable states | Light HTML, heavier client work | Response, rendered DOM, failed requests | Authenticated apps |
| Prerender | Snapshot | Build or cache dependency | Staleness and parity risk | Snapshot may omit interaction | Extra infrastructure | Bot and user parity diff | Temporary compatibility need |
| Progressive enhancement | Meaningful HTML plus optional JS | Low for core task | Low | Usually robust | Requires disciplined design | Disable JS and complete core task | Forms, navigation, calculators |
No row guarantees ranking. The table helps choose robustness for the task.
Source HTML versus rendered DOM
The HTTP response is what arrives before page scripts run. The rendered DOM is the browser's processed document after scripts and changes.
Check both for:
- H1 and primary answer;
- title and description;
- canonical;
- robots directives;
- structured data;
- internal links;
- product or article content;
- error state;
- language and alternates.
If the response is empty but the rendered DOM is complete, Google may process it, but you have created a render dependency. If both are complete but users cannot interact, the problem is not indexing. It is experience and accessibility.
Crawlable links
Use real anchors:
<a href="/academy/core-web-vitals-inp/">Core Web Vitals lesson</a>
A button with an onclick router call may be operable to one browser but does not provide the same discoverable URL relationship.
Use the History API for client routing and ensure every meaningful state has:
- a stable URL;
- a server response;
- a direct reload path;
- the correct status;
- shareable navigation.
Fragment-only routes such as /#/lesson are a weak default for public canonical content.
Meaningful status codes in single-page applications
A router can display “not found” while the server returns 200 for every route. This is a soft-404 pattern.
The server or rendering platform should return:
200for a real page;- direct
3xxfor a move; 404or410for an absent route;- truthful temporary errors for availability failures.
Client copy cannot repair the HTTP state after a crawler has received the response.
Metadata and structured data timing
Google documents that JavaScript can add or modify metadata and JSON-LD during rendering. The final rendered data must match visible content.
That does not make late injection the safest architecture. Generate stable title, canonical, language, robots, and core structured data from server route data when practical.
Common failures:
- source canonical points to A and rendered canonical points to B;
- a default title is indexed before late replacement;
- JSON-LD describes content that failed to render;
- client route transition keeps metadata from the previous page.
Lazy loading without hiding meaning
Lazy load below-the-fold media to reduce initial work, but do not require a user click or scroll event for essential indexable text. Use browser-supported image loading patterns and stable dimensions.
Test:
- fresh load without interaction;
- slow network;
- failed script;
- mobile viewport;
- reduced motion;
- keyboard navigation.
Mobile-first indexing means content parity
Google's mobile-first indexing best practices says the mobile version is used for indexing and ranking.
The mobile version should preserve:
- primary content;
- meaningful headings;
- metadata;
- structured data;
- image quality and alt text;
- crawlable links;
- robots access to necessary resources.
Responsive presentation can change. Hiding the entire evidence table behind a broken interaction changes the accessible content.
Do not put essential structured data only on desktop. Do not shorten the mobile page into a thin summary while the desktop page contains the evidence.
Agent-readable interfaces
The web.dev article Build agent-friendly websites, published in April 2026, explains that browser agents can observe interfaces through combinations of:
- screenshots;
- raw HTML or DOM;
- the accessibility tree.
Treat this as interface design guidance, not a Google ranking factor.
A robust action exposes:
- a clear accessible name;
- semantic role;
- current state;
- stable location;
- predictable result;
- error feedback;
- keyboard operation.
For example, a button labeled only with an icon may be visually familiar to a human and unnamed in the accessibility tree. A custom dropdown can look complete in a screenshot while remaining impossible to operate with a keyboard or agent.
Semantic HTML creates a shared baseline. It does not grant permission to expose private actions to automated agents. Authorization, rate limits, confirmation, idempotency, and audit logs remain necessary for consequential operations.
A complete rendering audit
1. Define the public task
Write what anonymous users, crawlers, and assistive technology should be able to read and do.
2. Fetch the response
Record final status, headers, response HTML, canonical, robots, title, H1, links, and structured data.
3. Render in a clean browser
Use no logged-in session. Record console and network failures, rendered DOM, visible content, and final metadata.
4. Compare response and rendered states
Classify differences as:
- intentional enhancement;
- acceptable late content;
- critical meaning dependency;
- conflict;
- failure.
5. Test Google’s current view
Use URL Inspection for indexed HTML and a live test for the current fetch. Remember that these are samples and processed states.
6. Test mobile parity
At a 375px viewport, compare copy, links, metadata, structured data, media, and operability.
7. Test accessibility
Navigate by keyboard. Inspect headings, landmarks, names, roles, states, focus order, error announcements, and table access.
8. Test failure states
Block the content API, disable JavaScript, load a missing route, and simulate slow connections. The correct response depends on the page's public purpose.
9. Fix the first material failure
Do not migrate from React to another framework when the actual bug is a 401 on a public content endpoint.
Worked example: the invisible lesson
The route /academy/rendering/ has four defects.
Defect 1: app-shell response
The response contains only:
<div id="app"></div>
<script src="/assets/app.js"></script>
The lesson arrives from /api/lesson/rendering.
Defect 2: public API requires a session
The editor is logged in, so the page looks complete. A clean browser receives 401. The rendered DOM stays empty.
Smallest complete fix: serve public lesson data without account authorization and keep private features on a separate authenticated endpoint. Better still, render the public lesson in the initial response.
Defect 3: router errors return 200
/academy/not-a-real-lesson/ displays an error message but returns 200.
Fix: make the server route return 404 and preserve helpful navigation.
Defect 4: mobile evidence disappears
The evidence table is placed inside a custom collapsed panel. Its toggle is not keyboard operable and the content is removed from the accessibility tree.
Fix: use a responsive table wrapper or an accessible disclosure with a real button, name, state, focus behavior, and content available on mobile.
Metadata conflict
The response canonical points to /academy/rendering-old/; JavaScript changes it after hydration.
Fix: generate the canonical once from server route data and keep client transitions consistent.
Why schema and llms.txt do not fix this
Markup cannot make absent visible content appear. A special text file does not repair a failed API, wrong HTTP status, or inaccessible interface.
Why this can still fail
After rendering is fixed, the lesson may remain excluded as a duplicate or fail its user task. Rendering creates reliable observability, not guaranteed indexing or ranking.
SEOryon response-versus-rendered checklist
Download the Response vs Rendered DOM Checklist.
Use only public or synthetic inputs. Do not build a server-side tool that fetches arbitrary private URLs, internal hosts, metadata endpoints, or tenant pages. That would create a serious server-side request forgery risk.
For each check, compare:
- response HTML;
- rendered DOM;
- mobile state;
- accessibility tree;
- expected state;
- failure class;
- smallest complete fix.
Common mistakes
“Google can render JavaScript, so architecture does not matter”
Rendering capability does not remove failed dependencies, queueing, status, or parity risk.
“CSR never ranks”
That is too absolute. Diagnose the actual deployed content and system.
“SSR guarantees SEO”
SSR can serve thin content, broken canonicals, 200 errors, or inaccessible interactions.
Buttons used as links
Use anchors for navigation and buttons for actions.
Primary content after interaction
Do not require a click or scroll to create essential public meaning.
Bot-specific substantive content
Serving materially different content to crawlers and users can create cloaking and operational risk. Build one truthful baseline.
Exercise: three SPA fixtures
Fixture A
The response returns 200 and an app shell. Render succeeds. All lesson navigation uses buttons with router handlers and no href.
Fixture B
The response and rendered page contain the lesson. The canonical is correct in source, but route transitions retain the previous page's JSON-LD and title.
Fixture C
Desktop contains the full lesson and schema. Mobile CSS removes the worked example and sources. The accessibility tree also omits them.
For each fixture, name the defect, evidence, user or search impact, and smallest complete fix.
Answer key
- Fixture A: non-crawlable navigation relationship. Render semantic anchors with real URLs, then enhance navigation.
- Fixture B: client metadata lifecycle defect. Update and remove route-specific metadata from one canonical data source on every transition, and test direct loads.
- Fixture C: mobile content and structured-data parity failure. Preserve substantive content and schema on mobile, then use responsive presentation rather than removal.
A passing answer uses observed evidence and does not recommend a full framework migration without showing why it is necessary.
Final checklist
- The public task is defined for anonymous users.
- Response status and headers match the visible state.
- Primary content exists in response HTML or a reliable render.
- Critical content does not depend on authentication.
- Source and rendered title, canonical, robots, and language agree.
- JSON-LD matches visible content.
- Navigation uses crawlable anchors.
- Every public route works on direct reload.
- Missing routes return meaningful errors.
- Essential content does not require scroll or click.
- Mobile preserves content, metadata, links, and schema.
- Keyboard navigation and accessibility-tree state are usable.
- Network and script failure states are tested.
- No bot receives materially different substantive content.
- Private URL inspection tools cannot fetch arbitrary internal resources.
Frequently asked questions
Is SSR better than CSR for SEO?
SSR provides a more robust public-content baseline, but the correct choice depends on the task. Test response, render, status, links, and performance rather than choosing by acronym.
Does Google execute JavaScript?
Google documents JavaScript rendering. That does not mean every crawler renders, every dependency succeeds, or interaction-only content is discovered.
Can I inject JSON-LD with JavaScript?
Google documents that rendered JSON-LD can be processed when it is valid and matches visible content. Server-generated stable data is often easier to govern.
Does mobile-first indexing mean mobile-only rankings?
Google primarily uses the mobile version for indexing and ranking. Desktop still matters to users, but substantive mobile parity is essential.
Is agent-friendly design a ranking factor?
The web.dev article is design guidance, not documented ranking-factor evidence. Semantic, accessible interfaces are valuable for people and diverse agents regardless.
Sources and methodology
Community research showed that developers continue to ask whether SPA and CSR sites can be indexed as effectively as SSR sites, and whether framework choice alone explains competitor performance. Those questions shaped the evidence-first diagnostic. Community opinions were not used as authority.
- Google Search Central, JavaScript SEO basics, updated 4 March 2026 and checked 28 July 2026. Official crawling, rendering, status, routing, metadata, and JSON-LD guidance.
- Google Search Central, Mobile-first indexing best practices, updated 10 December 2025 and checked 28 July 2026. Official mobile content and metadata parity guidance.
- Google Search Console, URL Inspection tool, checked 28 July 2026. Official indexed and live inspection context.
- web.dev, Build agent-friendly websites, published 1 April 2026 and checked 28 July 2026. Design guidance about screenshot, HTML, and accessibility-tree observation. It is not a ranking-factor source.
Previous: Indexing, Canonicals, Redirects, and HTTP Status
Next: Core Web Vitals and Page-Experience Diagnostics