JavaScript site: what Google sees and AI crawlers do not
The client gets in touch because a new category page has not ranked for six weeks. In the browser, the text is there. In the source there is an empty <div id=“root”>. What gets expensive is not the mistake but the weeks in between: between suspicion and proof lie a ticket to development, a follow-up question and a deploy — and in the end it regularly turns out that Google rendered the page long ago and the real problem sits somewhere else.
How to go about it in JMX
- Crawl with stored HTML. Under Settings → Crawl, Store source has to be active. Otherwise there is nothing to compare against later; how many pages that affects is noted above the result.
- The GEO tab, Delivery area. The rule
GEO.RENDER.CLIENT_SIDEreports pages whose served HTML carries fewer than 50 words and at the same time contains a mount point such as#root,#appor#__next. Severity: error. - Start the Rendering tab. JMX loads the crawled pages in Chrome, Edge or Chromium on your machine; the application deliberately does not bring a browser with it. Under “Wait until”, network idle is the most complete and slowest setting, DOM loaded the fallback. Extra wait time and window size sit next to it; the expected duration is shown before the start.
- Read the four columns. Words added and Links added show what only exists after JavaScript. Needs JS marks pages where at least half the text comes into being in the browser. robots changed is the row nobody looks for: a
noindexthat only the browser sets. - Switch on screenshots. “Save a screenshot of every rendered page” writes a JPEG per page, full page or in the chosen viewport, in a folder per host. A click on the row shows the preview: so what is missing without JavaScript?
- Counter-check in Search Console. The URL inspection gives the index status from Google itself. Only that is authoritative.
What to watch out for
“Needs JS” is not a ranking finding. Google renders JavaScript. The finding belongs under GEO: GPTBot, ClaudeBot and PerplexityBot fetch a page as a plain HTTP document, and a page that gains 400 words in the comparison can sit at position 3 in Google and still never appear as a source in a single AI answer. Throw the two together and you justify an expensive rebuild with the wrong argument.
The limit of the method is stated in the view itself. Rendering happens with this machine’s browser, on your connection and with no time budget; Google renders with its own Chromium, on its own schedule and its own budget. That a page finishes here in two seconds does not mean Google renders it promptly. One render costs twenty to a hundred times a fetch, which is why the concurrency is deliberately separate from the crawl’s and much lower. And jmx-cli does not render: the nightly run checks the served HTML, rendering stays an operation in the interface.
Two special cases are worth knowing. Pages that keep loading forever — a chat widget, ads, a live ticker — run into the time limit under “network idle”; “DOM loaded” helps there. Sites built from web components need the preset switch Flatten shadow DOM, otherwise they would look as empty after rendering as before, and the comparison would dutifully report parity even though both sides of that calculation share the same blind spot.
If the comparison shows no difference, you have not wasted a run but closed a question: rendering is irrelevant for this site, and the poor placement has another cause that you start looking for somewhere else tomorrow.