A browser extension (Chrome and Firefox 128+) that detects whether a webpage uses Server-Side Rendering (SSR) or Client-Side Rendering (CSR).
- Chrome / Edge / Brave — install from the Chrome Web Store → "Add to Chrome".
- Firefox (128+) — install from addons.mozilla.org → "Add to Firefox".
- Clone the repository and
cd extension - Open Chrome →
chrome://extensions - Enable Developer Mode (top-right toggle)
- Click Load unpacked → select this
extension/directory
- Run
npm run build:firefox(generates the Gecko variant indist/firefox/) - Open
about:debugging#/runtime/this-firefox - Click Load Temporary Add-on… → select
dist/firefox/manifest.json
Firefox loads the copy in dist/firefox/, not your live src/ edits — re-run npm run build:firefox and reload the add-on after each change. Firefox 128 is the floor because the analyzer relies on a world: "MAIN" content script.
- 🎯 Accurate Detection: Analyzes 15+ indicators including DOM structure, framework markers, performance metrics
- 🚀 Framework Recognition: Detects Next.js, Nuxt, Gatsby, Remix, SvelteKit, Astro, React, Vue, Angular
- 🏷️ Badge on Icon: Shows SSR/CSR/MIX result directly on extension icon (green/red/amber)
- 📊 Detailed Analysis: Confidence score, performance metrics, detection indicators
- 📈 Core Web Vitals: LCP, CLS, FID, TTFB measurements
- 📱 Device Context: Device type, screen size, connection quality
- 📄 Page Classification: Auto-detect page type (blog, ecommerce, docs, app, etc.)
- 🎨 CSS Framework Detection: Tailwind, Bootstrap, MUI, Chakra UI
- ⚡ State Management: Redux, MobX, Recoil, Apollo
- 🔧 Build Tools: Webpack, Vite, Parcel
- 🌐 Hosting Detection: Vercel, Netlify, GitHub Pages, Cloudflare
- 🔍 SEO Analysis: Meta tags, Open Graph, heading structure
- ♿ Accessibility Score: Alt text coverage, ARIA landmarks
- 💧 Hydration Tracking: Hydration errors and timing
- 🧭 Navigation Analysis: SPA vs MPA detection, route tracking
- 🌙 Dark Mode: Beautiful dark theme with system preference detection
- 📤 Export Results: Download as JSON, CSV, or Markdown
- 📜 History: Stores recent analyses with configurable limit
- ⚙️ Settings: Customize notifications, history limit, theme
- Navigate to any website
- Click the extension icon in the browser toolbar
- Click "Analyze Page"
- View results:
- Rendering type (SSR/CSR/Hybrid)
- Confidence score (30-95%)
- Detected frameworks
- Performance metrics
- Detection indicators
The extension badge shows the result at a glance:
- 🟢 SSR - Server-Side Rendered
- 🔴 CSR - Client-Side Rendered
- 🟡 MIX - Hybrid/Mixed Rendering
The extension uses a weighted scoring system analyzing:
| Indicator | SSR Points | CSR Points |
|---|---|---|
| Raw HTML vs Rendered mismatch | - | +40 |
| Raw HTML matches rendered | +30 | - |
| Framework hydration markers (raw-HTML evidence required since v3.7.0) | +30 | - |
Serialized data (__NEXT_DATA__) |
+25 | - |
| Fast DOM + slow FCP pattern | - | +25 |
| SPA root container (#root/#app) | - | +20 |
| Rich initial content | +20 | - |
| Minimal text content | - | +30 |
| CSR framework scripts | - | +25 |
| Noscript JS warning | - | +15 |
Key Detection Method (v3.2.0+): The extension fetches the page's raw HTML and compares it to the rendered DOM. If raw HTML is much smaller than rendered content, it's likely CSR (JavaScript loaded the content).
Accuracy hardening (v3.7.0): both sides of the comparison strip script/style text before measuring; framework hydration markers only count toward SSR when found in the raw HTML; when the server sent almost none of the visible text, post-JS "SSR-looking" signals are capped; and when the raw fetch is blocked, confidence is capped instead of guessing confidently. Detection changes are validated against a 22-site ground-truth suite: node scripts/validate-detection.mjs (requires playwright).
Classification:
- ≥75% SSR score → "Server-Side Rendered (SSR)"
- ≤25% SSR score → "Client-Side Rendered (CSR)"
- 60-74% → "Likely SSR with Hydration"
- 26-40% → "Likely CSR/SPA"
- 41-59% → "Hybrid/Mixed Rendering"
extension/
├── manifest.json # Extension config (Manifest V3) — Chrome canonical; Firefox variant generated
├── popup.html/js # Extension popup UI
├── options.html/js # Settings page
├── welcome.html/js # First-install onboarding page
├── background.js # Service worker (Chrome) / event page (Firefox)
├── scripts/
│ ├── build-bundle.js # Builds the src/ bundles (npm run build)
│ ├── build-firefox.js # Generates the Firefox/Gecko package in dist/firefox/ (npm run build:firefox)
│ └── validate-detection.mjs # 22-site ground-truth validation harness
├── src/
│ ├── analyzer-bundle.js # Bundled detection code (injected into pages)
│ ├── telemetry-bundle.js # Bundled collectors (injected only when Share Data is on)
│ ├── core/
│ │ ├── config.js # Scoring weights configuration
│ │ ├── analyzer.js # Main analysis orchestration
│ │ └── scoring.js # Classification logic
│ ├── detectors/ # SSR/CSR detection signals
│ │ ├── content-detector.js # DOM/content analysis
│ │ ├── framework-detector.js # Framework detection
│ │ ├── meta-detector.js # Meta tags analysis
│ │ ├── performance-detector.js # Timing metrics
│ │ ├── comparison-detector.js # Raw HTML vs rendered DOM
│ │ ├── csr-pattern-detector.js # SPA/CSR patterns
│ │ └── hybrid-detector.js # Islands/partial hydration
│ ├── collectors/ # Telemetry only (v3.6.0 split) — never affect the verdict
│ │ ├── performance-collector.js # Core Web Vitals (v3.3.0+)
│ │ ├── page-type-detector.js # Page classification (v3.3.0+)
│ │ ├── device-detector.js # Device info (v3.3.0+)
│ │ ├── tech-stack-detector.js # CSS/state/build tools (v3.4.0+)
│ │ ├── seo-detector.js # SEO & accessibility (v3.4.0+)
│ │ ├── hydration-detector.js # Hydration tracking (v3.5.0+)
│ │ └── navigation-detector.js # SPA navigation (v3.5.0+)
│ └── ui/
│ └── results-renderer.js # Results HTML generation
├── _locales/ # i18n name/description (en, ja, ko, fr, de, es, pt_BR, pl)
├── icon*.png # Extension icons
├── promo-images/ # Store marketing assets (Chrome tiles + Firefox promo; HTML→PNG)
├── store-listing.md # Chrome Web Store listing copy (+ store-listings.md translations)
├── amo-listing.md # Firefox/AMO listing copy + submission-form answers
├── privacy-policy.md # Privacy policy
└── CHANGELOG.md # Version history
- Edit source files in
src/directory - Rebuild the bundles:
npm run build(ornpm run build:watchwhile developing) - Run the unit tests:
npm run test:run - Reload the extension:
- Chrome —
chrome://extensions→ click the refresh icon - Firefox — re-run
npm run build:firefox, then reload the add-on inabout:debugging
- Chrome —
- For detection changes, run the ground-truth harness:
node scripts/validate-detection.mjs
Edit src/detectors/framework-detector.js:
// Add to frameworkMarkers object
newframework: document.querySelector('[data-newframework]') !== null
// Or add to staticGenerators object
newgenerator: document.querySelector('meta[name="generator"][content*="NewGen"]') !== nullThen rebuild the bundles: npm run build.
Releases are tag-driven (.github/workflows/release.yml). Bump manifest.json
CHANGELOG.md, merge tomain, thengit tag vX.Y.Z && git push origin vX.Y.Z. One tag builds both store packages — Chrome (csr-ssr-detector-vX.Y.Z.zip) and Firefox (csr-ssr-detector-firefox-vX.Y.Z.zip) — and attaches them to a GitHub Release. Upload the Chrome zip to the Web Store dashboard and the Firefox zip to AMO (seeamo-listing.mdfor the reviewer build notes). Full flow and gotchas: the rootCLAUDE.md"Creating Extension Release".
Manual fallback (build the zips locally):
# Chrome
zip -r ../csr-ssr-detector-vX.Y.Z.zip \
manifest.json popup.html popup.js options.html options.js \
background.js welcome.html welcome.js icon*.png src/ _locales/ \
-x "*/__tests__/*"
# Firefox → dist/csr-ssr-detector-firefox-vX.Y.Z.zip
npm run build:firefox -- --zip| Setting | Default | Description |
|---|---|---|
| Theme | Auto | Light/Dark/System preference |
| History Limit | 10 | Number of analyses to store (5-unlimited) |
| Notifications | On | Desktop notifications after analysis |
| Share Data | On | Anonymous telemetry (opt-out) |
- All analysis happens locally in your browser
- No data is sent unless "Share Data" is enabled (on by default, opt-out in settings); when disabled, the telemetry collectors are never even injected (v3.6.0+)
- When enabled, only the domain (never the full URL) is shared, along with the render verdict, confidence, detected frameworks, and technical metrics (Core Web Vitals, device/browser type, tech stack, SEO/accessibility flags, hydration and navigation stats)
- See privacy-policy.md for details
- Fork the repository
- Make changes in a feature branch
- Test on various sites (Next.js, React SPAs, static sites)
- Submit a pull request
-
npm run test:runpasses (105+ unit tests) -
node scripts/validate-detection.mjs— 22-site ground-truth suite (aim: ≥20/22) - Manual spot-check: a Next.js site, a pure CSR SPA, a static site, a hybrid/islands site
See CHANGELOG.md for version history.
MIT License - see LICENSE