free extension · Chrome + Edge + Firefox · MIT

Does your server send content — or an empty shell?

Googlebot runs JavaScript later; many AI crawlers and link previews don't run it at all. One click compares the page's server HTML with what you see: SSR, CSR or hybrid, with the evidence.

$ analysis runs locally · no account · telemetry is opt-out

csr-vs-ssr — probe
// pick a page, hit analyze
← CSRSSR →

↑ simulated preview — the installed extension runs the real diff on the live page

The empty-shell problem

Your browser shows a page. The server may have sent a shell.

A client-rendered app answers the first request with a near-empty document and a script bundle. JavaScript then fetches the data and builds the page in the browser. You see the finished page; anything that reads only the HTML response sees the shell. A server-rendered page puts the content in that response.

● EMPTY SHELL

Client-rendered: the HTML response

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Acme Store</title>
  <script type="module" src="/assets/index.js"></script>
</head>
<body>
  <div id="root"></div>
</body>
</html>

No headings, text or links. The product title, description and prices exist only after index.js runs.

● SERVER-RENDERED

The same page, rendered on the server

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Trail Running Shoes · Acme Store</title>
  <meta property="og:image" content="/og/shoes.jpg">
</head>
<body>
  <div id="root">
    <h1>Trail Running Shoes</h1>
    <p>Light, grippy, waterproof.</p>
    <a href="/shoes/trail-2">Trail 2: $129</a>
  </div>
  <script type="module" src="/assets/index.js"></script>
</body>
</html>

The content is in the response. JavaScript can still hydrate it to make it interactive.

You can check this by hand with view-source: or curl. The extension does it for you on any page: it measures how much of the visible text was in the server HTML, and which regions JavaScript filled in.

Why it matters

Who only sees your server HTML?

Your visitors' browsers run JavaScript. Many of the programs that read your pages run it later, or not at all.

Search engines

Googlebot renders JavaScript, in a later pass

Google crawls the HTML first and queues the page for rendering in headless Chromium. Google says a page may stay on this queue for a few seconds, but it can take longer than that. Text that only appears after JavaScript waits for that pass and depends on your scripts running correctly in Google's renderer. Google still calls server-side or pre-rendering a great idea, because not all bots can run JavaScript.

Source: Google Search Central, JavaScript SEO basics

Link previews

Many unfurlers read only the HTML response

When a link is pasted into a chat app or a social network, a bot fetches the URL and reads the title, description and image from the meta tags. Many of these preview bots don't run JavaScript, so tags your app sets in the browser may never reach them. Slack, for example, documents that its link expander fetches as little of the page as it can to extract the meta tags.

Source: Slack, Slack Robots

AI crawlers

Many AI crawlers don't execute JavaScript

In Vercel and MERJ's December 2024 analysis of crawler traffic, none of the major AI crawlers they measured (from OpenAI, Anthropic, Meta, ByteDance and Perplexity) rendered JavaScript. ChatGPT's and Claude's crawlers downloaded script files but didn't run them. Gemini, which uses Googlebot's infrastructure, and Applebot did render pages.

Source: Vercel, The rise of the AI crawler

What the extension can and can't tell you. It re-fetches the page from your browser, with your cookies, so it shows the HTML your server sends to a browser. Some sites serve bots different HTML, and crawlers change over time, so read the result as a strong hint about what a reader without JavaScript gets, not as a replay of any particular bot.

How it works

Direct evidence, not guesswork.

Most detectors sniff for framework markers and call it a day. This one goes to the source: it re-fetches the page's server HTML and compares its text, word by word, with what the browser actually built.

$ 01Click the icon

On any page. The extension fetches the same URL again, which returns the server's HTML before any JavaScript runs, and reads the live page next to it. If the site refuses that second request, it falls back to a text measurement taken while the page was parsing, and lowers the confidence level.

$ 02Compare server HTML with the page

Both sides are measured the same way: script/style/noscript and hidden elements are left out, and visible words are counted. If the server sent only a small share of the visible words, that's strong client-rendered evidence.

$ 03Get one of three verdicts

15+ weighted signals (hydration markers, serialized state, timing, meta) refine the comparison into SSR, CSR or hybrid, with a High, Medium or Low confidence level and a “Why this verdict” path you can inspect.

● SSR

Server-Side Rendered

The content is already in the HTML the server sends. Readers that don't run JavaScript still get the full page, and slow devices see it sooner.

● CSR

Client-Side Rendered

The server sends a shell and a bundle; JavaScript builds everything after load. Fast for apps, risky for content that needs to rank.

● HYBRID

Islands / partial hydration

A static server-rendered shell with interactive islands hydrated on top — Astro, Next.js partial hydration, streamed Suspense boundaries.

What you get

A verdict plus the whole stack behind it.

%
Confidence level + evidence

High, Medium or Low, depending on how clearly the text could be measured. “Why this verdict” walks through the decision, and every signal is listed with its points and what it observed. No black box.

◎
Rendered where

Built at build time, served from a CDN cache, or rendered for this request — read from the document's own response headers, with the CDN, cache state, age and TTFB.

▚
Server vs JavaScript, by region

What share of the visible text came from the server and what JavaScript added, then which parts of the page each one built, character by character. #root gained 12,400 characters after the scripts ran.

⌗
DevTools panel

The whole report at full width in a “Rendering” panel, re-running on every navigation while you work.

⬡
Framework detection

Next.js (incl. App Router), Nuxt, React, Vue, Angular, SvelteKit, Astro, Gatsby, WordPress and more.

⚡
Core Web Vitals

LCP, CLS, INP and TTFB measured on the live page — plus Long Animation Frame stats.

◈
Tech stack

CSS framework, state management, build tool, hosting — Tailwind, Redux, Vite, Vercel…

🔍
SEO & a11y spot-check

Meta tags, Open Graph, structured data, alt-text coverage — the stuff crawlers and screen readers see.

⇄
SPA navigation aware

Tracks client-side route changes, soft navigations and the Navigation API — not just the first load.

⏱
Prerender-correct timing

Timing is rebased on activationStart and skipped for speculative loads, so prerendered pages don't read as impossibly fast.

▤
History & export

Recent analyses kept locally, exportable to JSON, CSV or Markdown — or copied as one line into a pull request.

⌨
One keystroke away

Ctrl/Cmd+Shift+Y, a right-click entry, or the toolbar icon — and the analysis starts the moment the popup opens.

Privacy

Local analysis. Honest telemetry.

Detection runs entirely in your browser. The extension can optionally send anonymous, aggregate stats — render type, framework, the site's origin only, never full URLs or page content — to power a public dashboard of rendering trends. One toggle in settings turns it off completely. The code is open source (MIT), so you can check.

local analysisorigin only, never URLsone-toggle opt-outopen source
FAQ

Questions people actually ask.

Why does my page look empty without JavaScript?

Because the server sent an empty shell. In a client-rendered app the HTML response is mostly a <div id="root"></div> and a script tag; the bundle then fetches data and builds the page. With JavaScript off, or for any reader that doesn't run it, nothing builds the page and the root stays empty. The usual fixes are server-side rendering, static generation at build time, or at least putting the title and meta tags in the server HTML. Run the extension before and after the change: the share of visible text from the server should go up.

What exactly leaves my browser?

Detection itself: nothing — the diff and scoring run locally. If telemetry is on (it is by default), the extension sends one anonymous record per analysis: render type, confidence, frameworks, Web Vitals, and the site's origin (e.g. example.com) — never the full URL, path, or page content. Response headers and the region breakdown are never sent at all; they are computed on your device, shown to you, and discarded. Disable telemetry in settings with one toggle.

Why not just look for hydration markers?

Because frameworks lie. A Next.js App Router page ships no identifiable element, and a CSR app can leave SSR-looking meta behind. The raw-vs-rendered text diff is the ground truth: either the bytes the server sent contain the content, or they don't. Markers and timing only refine that verdict — and since v3.7 they require raw-HTML evidence before they can vote.

SSR tells me nothing about caching. Can it?

That's exactly what the Delivery tab is for. “SSR” covers a page rendered once at build time and served from an S3 bucket, a page answered by a warm CDN cache, and a page the origin renders on every request — three very different systems. The extension reads the document's own response headers and says which one you're looking at, along with the CDN, the cache state, how old the copy is and how long the first byte took. It is reported separately and never allowed to vote on the SSR/CSR verdict: how a document travelled is not the same question as where it was rendered.

Does it work on SPAs and client-side navigations?

Yes. It detects soft navigations and the Navigation API, re-analyzing on route changes. Speculative loads (speculation-rules prerender, navigational prefetch) get their timing signals rebased or skipped so they don't skew the verdict.

Which browsers are supported?

Chrome, Microsoft Edge and Firefox 128+ (Manifest V3). Edge Add-ons ships the same package as the Chrome Web Store; the Firefox build is generated from the same source and published on addons.mozilla.org — detection features are identical across all three.

Is it free?

Yes — free and open source under the MIT license. Read the code, open an issue, or star it.

Two clicks from now

Stop guessing.
Read the bytes.