---
title: "JavaScript SEO Audit: Compare Raw HTML vs. Rendered DOM"
description: "Google renders a lot of JavaScript, but you need proof it rendered your page, with your canonical tag and internal links, before the API call timed out. Here's how to diff raw HTML against the rendered DOM, spot the usual React, Vue, and Angular failures, and run the same check across 40,000 URLs."
image: "https://cdn.blackseoanalyzer.com/blog-images/how-to-audit-javascript-seo-without-guessing.jpg"
canonical: "https://www.blackseoanalyzer.com/en/blog/how-to-audit-javascript-seo-without-guessing"
language: "en"
---

# How to Audit JavaScript SEO Without Guessing

By [Seth Black](https://www.blackseoanalyzer.com/en/about) · Published November 02, 2026

![How to Audit JavaScript SEO Without Guessing](https://cdn.blackseoanalyzer.com/blog-images/how-to-audit-javascript-seo-without-guessing.jpg)

Most JavaScript SEO arguments I get pulled into end with someone saying “but Google renders JavaScript now.” Google renders a lot of it. The real question is whether it rendered *your* page, with *your* canonical tag, before the API call behind it timed out. You can answer that with evidence instead of a hunch.

My first production disaster came from exactly this. Firefox had just dropped, I switched to it, and I stopped testing in IE, which still owned nearly the whole browser market. One quick patch later, an e-commerce site went offline on launch weekend. Googlebot is basically IE: a user with unusual habits and a very short attention span.

## Get two copies of every page

A JavaScript audit compares the raw HTML your server returns with the rendered DOM after JavaScript runs. You can grab both for one URL in about ten seconds:

```bash
## Raw HTML, what a non-rendering crawler sees
curl -s https://example.com/pricing > raw.html

## Rendered DOM, after JavaScript runs
google-chrome --headless --dump-dom https://example.com/pricing > rendered.html

diff <(grep -o '<title>.*</title>' raw.html) <(grep -o '<title>.*</title>' rendered.html)
```

If the raw file is mostly an empty `<div id="root"></div>` and a pile of script tags, you’ve already learned something. This raw-versus-rendered gap is the core of [why JavaScript crawls break silently](https://www.blackseoanalyzer.com/en/blog/why-your-javascript-crawl-is-broken-and-how-to-fix-it) — the page looks fine in a browser while Google sees almost nothing.

## What to compare

Check these between the raw and rendered versions:

- **Title and meta description.** Shells often ship placeholder text that gets swapped out later.
- **Canonical and robots meta.** A canonical that only exists after hydration is a gamble. A `noindex` in the shell that JavaScript later removes is worse, because Google may act on the first one it sees.
- **H1 and body copy.** If the main content is missing from the raw HTML, you depend on rendering working every time.
- **Structured data.** JSON-LD injected by a tag manager sometimes never fires during a crawl.
- **Internal links.** These get their own section.

## Links that only show up after JavaScript

Google follows `<a href>` elements. It does not click buttons. I regularly find navigation built from `<div onClick={...}>` or router components that render an anchor with no `href`. Those links look perfect in a browser and do nothing for a crawler.

Count the links in each version. If the rendered DOM has 140 internal links and the raw HTML has 6, you have a crawl path problem. If the rendered DOM *also* has 6 because the rest sit behind a “Load more” button, rendering won’t save you. The same principle applies to [AI crawlers reading your JavaScript content](https://www.blackseoanalyzer.com/en/blog/can-ai-crawlers-see-javascript-content) — most of them never execute it at all.

## How rendering fails in React, Vue, and Angular

These are the three I see most often:

- **React:** hydration errors that blank out a section, or a client-side fetch that comes back after the renderer has already taken its snapshot.
- **Vue:** content wrapped in `v-if` that waits on API data, so the rendered DOM shows a spinner forever.
- **Angular:** an empty `<app-root>` on every route because server-side rendering was set up for the homepage and nowhere else.

If you’re procuring outside help for a framework-specific audit, the questions worth asking vendors are covered in [buying a technical SEO audit for React and Angular apps](https://www.blackseoanalyzer.com/en/blog/buying-technical-seo-audits-for-react-angular-apps).

## Doing this on 40,000 URLs

Hand-running curl and headless Chrome works until the site has more than a handful of routes you actually care about. bseoa runs an HTTP crawler and a headless Chrome crawler against the same URLs and diffs them per page:

```bash
black-seo-analyzer --url-to-begin-crawl https://example.com --spa --output-type html-folder --output-file ./audit
```

Crawl output streams in real time, so a broken route shows up while the crawl is still running. Every issue links to the exact URLs it affects, so you can give programmers a URL list grouped by failure type instead of a PDF that says “JavaScript issues found” and nothing else. If you prefer running audits from the command line rather than a browser tab, the [SEO audit CLI workflow for developers](https://www.blackseoanalyzer.com/en/blog/seo-audit-cli-for-developers) covers how to wire this into staging and CI pipelines.

[Start a free trial](https://www.blackseoanalyzer.com/free-trial) and run it on the section of your site you trust the most.

bseoa renders JavaScript the way Googlebot does, so React and Angular sites audit correctly. [See how](https://www.blackseoanalyzer.com/features) or [start a free trial](https://www.blackseoanalyzer.com/free-trial).

-Sethers

About the author

[Seth Black](https://www.blackseoanalyzer.com/en/about)

Seth is a software engineer and engineering leader who builds bseoa, a Rust-based technical SEO crawler with a GUI and CLI. He writes about the crawling, rendering and indexing problems he runs into on real sites.

[LinkedIn](https://www.linkedin.com/in/seth-black-tx/) · [GitHub](https://github.com/sethblack) · [YouTube](https://www.youtube.com/@SethBlack)

[Back to Blog](https://www.blackseoanalyzer.com/en/blog)

## Discover hundreds of SEO Issues in Seconds

Without Monthly Subscriptions

Comprehensive technical SEO analysis powered by ML and 16 specialized modules. Optional AI-powered insights from Claude, GPT-4, or Gemini. Get actionable insights in seconds, and never pay monthly fees again.

[Download Free Trial](https://www.blackseoanalyzer.com/en/free-trial)

I use AI to generate images for my posts and for general editing, updates, and ironically SEO purposes. I used to draw all of the images for my personal blog (taleas) myself, but as the volume of content I produce has increased, I've turned to AI tools to help create visuals that complement my writing. I go out of my way to generate images that look strange, and don't represent real people. If you ever want to chat about my use of AI, please reach out.

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://www.blackseoanalyzer.com/#organization","name":"Fiscus Technology, LLC","url":"https://www.blackseoanalyzer.com/","logo":{"@type":"ImageObject","url":"https://www.blackseoanalyzer.com/static/images/black-seo-analyzer.png"},"sameAs":["https://sethserver.com/","https://github.com/sethblack"],"founder":{"@id":"https://www.blackseoanalyzer.com/#seth"}},{"@type":"WebSite","@id":"https://www.blackseoanalyzer.com/#website","name":"bseoa","url":"https://www.blackseoanalyzer.com/","publisher":{"@id":"https://www.blackseoanalyzer.com/#organization"}},{"@type":"Person","@id":"https://www.blackseoanalyzer.com/#seth","name":"Seth Black","url":"https://www.blackseoanalyzer.com/about","jobTitle":"Creator of bseoa","sameAs":["https://www.linkedin.com/in/seth-black-tx/","https://github.com/sethblack","https://www.youtube.com/@SethBlack","https://sethserver.com/"]},{"@type":"BlogPosting","@id":"https://www.blackseoanalyzer.com/en/blog/how-to-audit-javascript-seo-without-guessing#article","headline":"How to Audit JavaScript SEO Without Guessing","name":"How to Audit JavaScript SEO Without Guessing","description":"Google renders a lot of JavaScript, but you need proof it rendered your page, with your canonical tag and internal links, before the API call timed out. Here's how to diff raw HTML against the rendered DOM, spot the usual React, Vue, and Angular failures, and run the same check across 40,000 URLs.","url":"https://www.blackseoanalyzer.com/en/blog/how-to-audit-javascript-seo-without-guessing","datePublished":"2026-11-02T00:32:24","dateModified":"2026-11-02T00:32:24","author":{"@id":"https://www.blackseoanalyzer.com/#seth"},"publisher":{"@id":"https://www.blackseoanalyzer.com/#organization"},"image":{"@type":"ImageObject","url":"https://cdn.blackseoanalyzer.com/blog-images/how-to-audit-javascript-seo-without-guessing.jpg","width":1200,"height":630},"mainEntityOfPage":{"@type":"WebPage","@id":"https://www.blackseoanalyzer.com/en/blog/how-to-audit-javascript-seo-without-guessing"},"isPartOf":{"@id":"https://www.blackseoanalyzer.com/#website"},"inLanguage":"en-US","isAccessibleForFree":true,"wordCount":736},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://www.blackseoanalyzer.com/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://www.blackseoanalyzer.com/en/blog"},{"@type":"ListItem","position":3,"name":"How to Audit JavaScript SEO Without Guessing","item":"https://www.blackseoanalyzer.com/en/blog/how-to-audit-javascript-seo-without-guessing"}]}]}
```
