Question

JavaScript SEO: Rendering Issues and Solutions

7 min read

Short answer

Google can index content that JavaScript adds to a page, because it renders pages before indexing them. The problems come from three places: pages whose content or links appear only after a user action, signals in the original HTML that stop or contradict rendering (a noindex, an error status, a canonical that JavaScript later changes), and resources Google isn't allowed to fetch. Server-side rendering or static generation removes the dependency on rendering, and Google calls dynamic rendering "a workaround and not a recommended solution".

Sources

  1. 1developers.google.com
  2. 2developers.google.com
  3. 3developers.google.com
  4. 4developers.google.com
  5. 5developers.google.com

Ask next

How Google Processes JavaScript

Google’s JavaScript SEO basics describes three phases: crawling, rendering and indexing. The details that matter for diagnosis:

  • Robots.txt comes first. If a URL is disallowed, Googlebot skips it, and “Google Search won’t render JavaScript from blocked files or on blocked pages.”
  • Links in the raw HTML are found immediately. Googlebot parses the response for URLs “in the href attribute of HTML links” before rendering.
  • Rendering is queued. “Googlebot queues all pages with a 200 HTTP status code for rendering, unless a robots meta tag or header tells Google not to index the page.” The page “may stay on this queue for a few seconds, but it can take longer than that.”
  • Indexing uses the rendered page. After rendering, Googlebot parses the rendered HTML for links again, and “Google also uses the rendered HTML to index the page.”
  • Error pages may not render. “If the HTTP status code is non-200 (for example, on error pages with 404 status code), rendering might be skipped.”

So JavaScript content is not indexed in a second-class way; it depends on rendering succeeding. Google still recommends server-side or pre-rendering “because it makes your website faster for users and crawlers, and not all bots can run JavaScript.” Google’s dynamic rendering page adds that “Other search engines may choose to ignore JavaScript and won’t see JavaScript-generated content”, which is a separate reason to put core content in the HTML.

Signals That Must Be Right Before Rendering

Some signals are read from the original HTML and the HTTP response, and JavaScript can’t reliably repair them afterwards. Google clarified three of them in December 2025, as recorded in its documentation changelog.

Signal Google's guidance What it means in practice
<!–INLINECODE1–> "When Google encounters the noindex tag, it may skip rendering and JavaScript execution, which means using JavaScript to change or remove the robots meta tag from noindex may not work as expected." If a page should be indexed, don't ship <!–INLINECODE2–> in the original code and remove it with a script
Canonical Don't "use JavaScript to change the canonical URL to something else than the URL you specified as the canonical URL in the original HTML" Set it in the HTML, or only in JavaScript, never one value in each
Status code Rendering "might be skipped" on non-200 pages Return a real 404 for missing content instead of a 200 page that shows an error with JavaScript

Titles and meta descriptions are more flexible: Google says “You can use JavaScript to set or change the meta description as well as the <title> element.”

For single-page apps that route on the client and can’t return a 404 status, Google gives two ways to avoid soft 404s: “Use a JavaScript redirect to a URL for which the server responds with a 404 HTTP status code”, or add a noindex robots meta tag to error pages with JavaScript. Status codes in general are covered in HTTP status codes, and canonical rules in canonical tags.

Rendering Options Compared

Approach What the first HTML response contains Search risk Fits
Client-side rendering An app shell; content arrives after scripts run Depends on rendering succeeding; other crawlers may see nothing Logged-in apps, dashboards, tools that don't need to rank
Server-side rendering Full content, rendered per request Low; server cost and response time to manage Content that changes often and needs to rank
Static generation Full content, built ahead of time Low; stale pages if rebuilds lag behind updates Content that changes on publish, such as articles and documentation
Hydration Server-rendered HTML, then scripts make it interactive Low, if hydrated content matches the server HTML Interactive pages that also need to rank
Dynamic rendering A server-rendered copy for bots, client-rendered for users Google calls it "a workaround and not a recommended solution" A temporary bridge while moving to one of the options above

Google’s dynamic rendering page now describes the technique in the past tense, as “a workaround and not a long-term solution”, and recommends “server-side rendering, static rendering, or hydration as a solution.” It also says dynamic rendering “creates additional complexities and resource requirements.” If you have it in place, plan its replacement; don’t build new sites on it.

Client-side routing decides whether Google can find a JavaScript site’s pages at all. Google’s rules:

  • “Google can only discover your links if they are <a> HTML elements with an href attribute.” A navigation item that only has a click handler is not a link to Google. The full link rules are in Google’s crawlable links documentation and summarized in internal linking strategy.
  • “It’s fine to use JavaScript to inject links into the DOM, as long as such links follow the best practices for crawlable links.”
  • For single-page apps, “use the History API to implement routing between different views”, and “don’t use fragments to load different page content.” Google says that with fragment routes such as #/products, “Googlebot can’t reliably resolve the URLs”.

Lazy Loading and Infinite Scroll

Google’s lazy-loading guide says to load “all relevant content whenever it is visible in the viewport”, using browser built-in lazy loading for images and iframes, the IntersectionObserver API, or a library that loads data when it enters the viewport. The reason is stated plainly: these methods “don’t rely on user actions, such as scrolling or clicking, to load content, which is important as Google Search does not interact with your page.”

For infinite scroll the same guide asks for paginated loading: give each chunk “its own persistent, unique URL”, link the chunk URLs sequentially, and update the displayed URL with the History API when a new chunk becomes the primary visible element. The full pattern is in pagination and infinite scroll. Image-specific lazy loading is covered in image SEO.

Structured Data Generated With JavaScript

Generating JSON-LD with JavaScript is supported. Google’s JavaScript basics say you “can use JavaScript to generate the required JSON-LD and inject it into the page”, and its guide to generating structured data with JavaScript covers Google Tag Manager and custom scripts, tested with the Rich Results Test.

One exception is worth planning around. For Product markup, Google warns that “dynamically-generated markup can make Shopping crawls less frequent and less reliable, which can be an issue for fast-changing content like product availability and price.” Stores should put price and availability markup in the server-rendered HTML. Markup basics are in structured data fundamentals and product details in Product schema for e-commerce.

Resources, Caching and Web Components

  • Don’t block what the page needs. Scripts, stylesheets and API endpoints that build the content must be crawlable; see robots.txt syntax and common mistakes.
  • Fingerprint your files. Google says Googlebot “caches aggressively” and its rendering service “may ignore caching headers”, which can leave it with outdated JavaScript or CSS. Content fingerprints in filenames, such as main.2bb85551.js, force a new file name for every change.
  • Check shadow DOM content. Google “flattens the shadow DOM and light DOM content”, but “can only see content that’s visible in the rendered HTML.” Use slots so light DOM content appears in the rendered output.
  • Mind speed. Heavy scripts slow pages for users as well as crawlers; performance work is covered in Core Web Vitals.

Testing What Google Sees

  1. URL Inspection in Search Console. Run a live test and read the rendered HTML: is the main content there, are the links real <a href> elements, are the canonical and robots meta tags what you expect?
  2. Rich Results Test. It also shows rendered HTML, which helps on pages you don’t control in Search Console, and it validates JavaScript-generated structured data.
  3. Original HTML versus rendered DOM. View the page source to see what the server sends; inspect the DOM in browser developer tools to see what scripts produce. Anything important that exists only in the second depends on rendering.
  4. Error routes. Request a URL that shouldn’t exist and confirm it returns a 404 status or a noindex, not a 200 with “not found” text.
  5. Server logs. Confirm Googlebot requests the scripts and API endpoints the page needs; see log file analysis.

When content is missing from the rendered HTML, Google’s statement is unambiguous: “If the content isn’t visible in the rendered HTML, Google won’t be able to index it.”

Frameworks and Migrations

Framework defaults can change faster than SEO guides. One dated example: on February 14, 2025, the React team announced it was “deprecating Create React App for new apps,” and encouraged existing apps to migrate to a framework or a build tool such as Vite. Check your framework’s current documentation for its server rendering and static generation options rather than relying on what it did when your site was built.

When you migrate between rendering approaches, compare the rendered HTML of a sample of templates before and after: titles, canonicals, robots meta tags, structured data, internal links and status codes. A redesign that keeps every URL can still drop links or markup from the rendered output.

For each template, list what appears in the rendered DOM but not in the page source; everything on that list can be indexed by Google only if rendering succeeds, and other search engines may never see it.

Link profile

Cites 5 sources

  1. JavaScript SEO basics developers.google.com
  2. documentation changelog developers.google.com
  3. dynamic rendering page developers.google.com
  4. lazy-loading guide developers.google.com
  5. generating structured data with JavaScript developers.google.com