Key takeaways
- Technical SEO consultants for JavaScript sites start from one fact: Google crawls, renders and indexes in separate phases, and rendering can wait in a queue.
- Google can only crawl a link reliably when it is an
<a>element with anhref. Buttons,onclickhandlers andjavascript:links are the most common reason I find whole sections undiscovered. - Single-page apps often return a 200 status for “page not found” views. Google calls these soft 404s and tells you to prevent them.
- A
noindextag that JavaScript tries to remove may never be removed, because Google may skip rendering when it seesnoindex. - The test that settles most arguments is simple: compare the raw HTML with the rendered HTML in Search Console’s URL Inspection tool.
Technical SEO consultants for JavaScript sites spend most of their time on one question: does Google see the same page your users see? On a JavaScript framework site the answer is often “nearly”, and the gap is where traffic goes missing.
I have worked in SEO for more than 16 years, and JavaScript sites are where I find the most avoidable losses. The fixes are rarely exotic. Google documents almost every one of them in its JavaScript SEO basics guide. The hard part is knowing where to look on your own site and proving the problem to a development team.
Below is the checklist I work through, with the Google page behind each check.
How Google handles a JavaScript page
Google says it “processes JavaScript web apps in three main phases”: crawling, rendering and indexing. In practice:
- Crawling. Googlebot fetches the URL and reads the HTML the server returns. If robots.txt blocks the page or its scripts, the process stops there for those files: “Google Search won’t render JavaScript from blocked files or on blocked pages.”
- Rendering. “Googlebot queues all pages with a 200 HTTP status code for rendering.” Google says a page “may stay on this queue for a few seconds, but it can take longer than that.” Then “a headless Chromium renders the page and executes the JavaScript.”
- Indexing. Google “parses the rendered HTML for links again and queues the URLs it finds for crawling”, and uses the rendered HTML to index the page.
The important case is what Google calls the app shell model, “where the initial HTML does not contain the actual content and Google needs to execute JavaScript” before it can see anything useful. Every page built that way depends on the rendering step working. When I audit, my first question is how much of each key template exists in the server HTML and how much only appears after scripts run.
Rendering problems technical SEO consultants for JavaScript sites should catch
These are the problems I check on every JavaScript site, in roughly the order I find them. Each one comes from Google’s documentation, mostly its page on fixing Search-related JavaScript problems.
| Problem | What Google says | How I test it |
|---|---|---|
| Content only after a click or scroll | Lazy-loaded content must load “whenever it is visible in the viewport” | Look for the text in the rendered HTML in URL Inspection |
Links without href |
Google “can only crawl your link if it’s an <a> HTML element” with an href |
Search the rendered HTML for menu and pagination links |
Different content on # fragments |
“Don’t use URL fragments to load different content” | List the URLs a user can reach and check each loads on its own |
| Error views returning 200 | “Make sure to prevent soft 404 errors” | Request a made-up URL and read the status code |
noindex changed by script |
Google “may skip rendering and JavaScript execution” when it sees noindex |
Compare the robots tag in raw and rendered HTML |
| Canonical set by script | Set it “to the same value as the original HTML” | Compare the canonical in raw and rendered HTML |
| Scripts or APIs blocked in robots.txt | Google won’t render JavaScript from blocked files | Check the blocked resources in the live test |
| Stale scripts after a release | “WRS may ignore caching headers” | Check file names change with each build |
| Content that needs stored state or permissions | WRS “does not retain state across page loads” and Googlebot declines permission requests | Load the page in a fresh private window with permissions refused |
Links Google can follow, and the ones it can’t
This is the problem I find most often, and it is the one with the biggest effect, because a page Google never discovers can’t rank at all. Google’s page on crawlable links is short and precise.
Links that work: <a href="https://example.com">, <a href="/products/category/shoes"> and relative paths such as ./products/category/shoes. Adding an onclick handler to an <a> that already has a real href is fine. And “links are also crawlable when you use JavaScript to insert them into a page dynamically”, so a link built by your framework is fine as long as the final markup is a proper <a href>.
Links Google lists as not recommended: <a routerLink="products/category">, <span href="https://example.com">, <a onclick="goto('https://example.com')"> and href values that start with javascript:. Google says it “may still attempt to parse” some of these, but it “can’t reliably extract URLs from <a> elements that don’t have an href attribute.”
If your navigation only works when someone clicks it, Google may never see the pages behind it.
The two patterns I see most: category menus built from buttons, and “load more” pagination with no page URLs. Both look fine to a user.
Lazy content and infinite scroll
Google’s lazy-loading guidance has one rule that matters most: content should load when it is visible in the viewport, and the methods Google describes “don’t rely on user actions, such as scrolling or clicking, to load content.” Google Search doesn’t interact with your page like a person does. Tabs, accordions and “read more” panels that fetch their text only on click are the usual offenders.
The same page says not to lazy-load content that is “likely to be immediately visible when a user opens a page”, because it slows down what users see first.
For infinite scroll, Google wants paginated loading: each chunk gets its own URL, the URL shows the same content every time it loads (absolute numbers such as ?page=12, not relative ones such as ?date=yesterday), the pages link to each other in sequence, and the address bar updates through the History API as a new chunk becomes the main content. If your product lists only grow through scrolling, products beyond the first chunk may have no crawlable path at all.
Status codes, soft 404s and noindex in single-page apps
Single-page apps route on the client, so every URL usually gets the same 200 response from the server, including URLs that don’t exist. The app then draws a “not found” message. To Google that is a normal page with thin content: a soft 404. Google’s advice is to redirect with JavaScript to a URL where the server returns a 404 status, or to add a noindex robots meta tag to error views.
The noindex route has a trap. Google says that when it “encounters the noindex tag, it may skip rendering and JavaScript execution.” So if your server HTML ships with noindex and a script is meant to remove it on real pages, Google may never run that script. The page stays out of the index. Keep noindex out of the server HTML unless the page truly should stay out.
Canonicals follow the same logic. Google says “the best way to set the canonical URL is to use HTML”, and if you set it with JavaScript, use the same value as the original HTML and avoid a second canonical tag.
How I test a JavaScript site
Most disagreements between SEOs and developers end once both sides look at the same evidence. Google names the tools: the Rich Results Test and the URL Inspection tool in Search Console, where you can check “loaded resources, console output, and the rendered DOM”.
My JavaScript rendering checks, per key template
- Run a live test in URL Inspection and open the rendered HTML.
- Search the rendered HTML for the main heading, body copy, prices and internal links.
- Compare title, meta description, canonical and robots tag in the raw HTML and the rendered HTML.
- Check the page resources list for blocked or failed scripts and API calls.
- Read the console messages for errors.
- Request a URL that doesn’t exist and check the status code the server returns.
- Load the page with storage cleared and permissions refused.
- Confirm script file names change with each release (Google calls this content fingerprinting).
I test templates, not individual pages: if a template is broken, every page built on it is broken. On larger sites I add server logs, because they show which URLs and script files Googlebot actually requests. Our log file analysis service covers that part.
Choosing a rendering approach
Sooner or later the question becomes “should we change how the site renders?” Google’s position is clear. It says “server-side or pre-rendering is still a great idea” because it helps users and crawlers, and “not all bots can run JavaScript.” On dynamic rendering (serving a pre-rendered version only to bots), Google’s dynamic rendering page is blunt: it “is a workaround and not a recommended solution”, and Google recommends “server-side rendering, static rendering, or hydration” instead.
That does not mean every client-side app needs a rebuild. If the key templates pass the checks above, a client-rendered site can be indexed well. A rebuild is a migration in all but name, with all the risk that brings, so I only recommend one when testing shows content or links that Google can’t reach and the fixes inside the current setup are worse. If you are going that way, read our guide to what drives the cost of a site migration before you sign off the plan.
Not worth paying for
A “JavaScript SEO audit” that is only a crawler report with a score. You need someone to open the rendered HTML of your key templates, show the missing content or links, and write fixes your developers can test. If the deliverable has no raw-versus-rendered comparison, it hasn’t tested rendering.
What to ask before you hire for JavaScript SEO
The general questions for any technical consultant are in my piece on the first hour I spend on a new site. For JavaScript sites, add these:
- Which templates will you test, and will you show me raw and rendered HTML side by side?
- How will you check internal links, pagination and filters for crawlable
hrefvalues? - What happens to a URL that doesn’t exist on our site, and how will you test it?
- Will your fixes come as tickets our developers can reproduce and test?
- When would you advise against a rebuild?
Our technical SEO work starts with exactly these checks. I charge by the hour, with no fixed packages, so if only one template is broken you pay for one template’s worth of work.
FAQ
What is JavaScript SEO?
JavaScript SEO is the part of technical SEO that makes sure search engines can see content, links and metadata that a site produces with JavaScript. It covers rendering, crawlable links, status codes, lazy loading and how titles, canonicals and robots tags are set. Google’s JavaScript SEO basics page is the reference most of it rests on.
Does Google render JavaScript?
Yes. Google says it queues pages with a 200 status for rendering and uses a headless Chromium to execute the JavaScript. But rendering can wait in a queue, blocked scripts won’t run, and Google may skip rendering on pages with noindex, so “Google renders JavaScript” is not the same as “Google sees everything”.
Is client-side rendering bad for SEO?
Not on its own. A client-rendered page can be indexed if its content and links appear in the rendered HTML and its status codes are right. Google still recommends server-side or pre-rendering because it helps users and crawlers, and not all bots run JavaScript.
Do I need dynamic rendering for my JavaScript site?
Usually not. Google calls dynamic rendering a workaround and not a recommended solution, and points to server-side rendering, static rendering or hydration instead. If your key templates already pass rendering tests, fix the specific problems rather than adding a second rendering path.
How do I check rendering myself?
Use the URL Inspection tool in Search Console: run a live test, open the rendered HTML and search it for your main content and links. Then compare the title, canonical and robots tag with the raw HTML your server sends.
If you suspect Google sees less of your JavaScript site than your customers do, tell me which templates matter most and we will look at the rendered HTML together.
Eugen Platon · SEO consultant with 16+ years in SEO and digital marketing · free 10-minute first call