Technical SEO

Technical SEO for JavaScript sites: crawling, rendering and indexing

A practical checklist to ensure that content, links, metadata and status codes in JavaScript applications are available to search engines.

Executive brief

Key takeaways

  • Critical content should be available without a required interaction.
  • Links need an anchor element with a resolvable href.
  • Status, canonical and robots directives should be coherent in the initial HTML.
  • Test source, rendered DOM and the search engine's inspection output.

JavaScript applications can appear in search engines, but the path between a URL and its content has more failure points. A crawler must access the page, receive an appropriate response, discover resources, execute code when required, build the content and evaluate the rendered version.

Google's JavaScript SEO guide describes crawling, rendering and indexing phases. Google uses Chromium, but rendering can enter a queue, blocked resources can prevent content and other bots may have different capabilities.

Understand what arrives in the initial HTML

Inspect the document response without executing JavaScript and look for:

  • title and meta description;
  • canonical and robots meta;
  • h1 and main content;
  • internal links;
  • structured data;
  • product or article information;
  • error states;
  • hreflang when applicable.

If the HTML contains only an empty container and scripts, understanding depends entirely on rendering. This may work for Google, but it increases complexity, latency and differences across crawlers.

Server rendering, static generation or a hybrid approach makes critical content available earlier to users and agents.

Use real URLs for each piece of content

Every discoverable page should have a stable, shareable URL. Filters, tabs and states only need their own URL when they represent useful, indexable content.

Avoid using fragments such as #/product to load different pages. Use the History API for client routing and keep the server able to respond directly to every public URL.

Test direct access, refresh, sharing and navigation without relying on an earlier session.

Make links crawlable

The most reliable pattern is:

<a href="/blog/core-web-vitals-campo-laboratorio">Field and lab data</a>

A div with onClick, a button that changes routes or a link without href may work for a person with JavaScript while failing discovery, keyboard navigation or opening in a new tab.

Use buttons for actions and links for navigation. Ensure the destination exists in rendered HTML and does not require scrolling or clicking to be created.

Return meaningful HTTP status codes

Single-page applications sometimes return 200 for every route, including removed products and errors. This creates soft 404s and makes interpretation harder.

The server should return 200 for available content, permanent or temporary redirects when appropriate, 404 for missing resources, 410 for intentional permanent removal, access-control codes for restricted content and 5xx for real server failures.

A “not found” message rendered inside a 200 response does not replace the status code.

Define stable metadata

Title, description, canonical and robots directives must represent the current URL. During client navigation, confirm that metadata updates correctly rather than inheriting values from the previous page.

Canonical URLs should be absolute and coherent across initial HTML, rendered output, sitemap and internal links. Do not use canonical as a universal solution for pagination or filters.

Avoid starting with noindex and removing it with JavaScript. Google warns that it may skip rendering after finding noindex, so the later removal may not work as expected.

Do not hide content behind interaction

Google does not scroll or click like a person to reveal content. Lazy-loading should load relevant content when it enters the viewport, not only after a button or gesture.

Main content, products in paginated lists and discovery links should not depend exclusively on infinite scroll without paginated URLs, “load more” without alternative links, hover, carousel gestures or an API call made only after interaction.

Google's lazy-loading guide recommends implementations that do not require user actions to expose relevant content.

Treat rendering and performance together

Sending all content through the client increases network and CPU work. Even when the crawler executes the code, users may face slow LCP and high INP.

For each component, ask whether it must be interactive, can render on the server, can load later, must hydrate, is on the critical path, appears on every route or depends on a third party.

Server Components, islands of interactivity and static generation are ways to reduce JavaScript. Architecture should follow data, personalization and product experience needs.

Keep content consistent

Crawler content should correspond to what users receive. Legitimate differences by device, location or login should preserve the same central intent.

Avoid complete server content that becomes empty after hydration, conflicting titles, canonical changes on the client, API errors that remove main content, permanent skeletons and structured data that describes missing information.

Hydration problems can leave the initial HTML correct and the final interface broken. Inspect both layers.

Test source and rendering

Use a repeatable process:

  1. request the URL directly and record status and HTML;
  2. disable JavaScript for diagnosis without assuming every bot behaves that way;
  3. render in a clean browser;
  4. compare content, links, metadata and schema;
  5. inspect console and network;
  6. test mobile and desktop;
  7. use URL inspection and rendered HTML from the search engine;
  8. validate good, empty, removed and API-error states.

A screenshot does not prove that links are crawlable. Correct HTML does not prove that hydration preserved the content.

Monitor deployments and regressions

Framework, routing, middleware and consent changes can affect the entire site. Automate checks for critical templates: status, title, canonical, robots, h1, main content, internal links, schema, hydration errors and sitemap inclusion.

Add the launch checklist to deployment. Test the published URL, not only local source.

Example finding and action

Observation: category pages return an empty shell; products and links appear only after a client API call. When the API fails, the page still returns 200 with no content. Action: render the heading, description, first product page and pagination on the server; return an appropriate error status when data is unavailable. Acceptance: content and links exist in initial HTML, client navigation still works and error states use coherent status codes.

JavaScript SEO does not require abandoning modern applications. It requires reducing unnecessary dependencies, using web platform patterns and verifying what each agent receives at every stage.

Direct answers

Frequently asked questions

Does Google execute JavaScript?

Yes. Google documentation describes crawling, rendering and indexing phases using Chromium. There are still queues and limitations, and other bots may not execute the application.

Does SSR guarantee SEO?

No. SSR improves initial content availability, but URLs, canonical, status, links, content and quality still need correct implementation.

Can JavaScript define a canonical?

Google can process a canonical inserted by JavaScript, but it recommends consistency and prefers defining it in HTML when possible.