[Crawl-Date: 2026-04-17]
[Source: DataJelly Visibility Layer]
[URL: https://datajelly.com/blog/script-shell-pages]
---
title: Script Shell Pages: When Your App Loads But Nothing Works | DataJelly
description: A 4KB HTML response with a single div and a script tag returns 200 OK and passes every monitor — while users see a blank page. Here's why script shell pages are the default failure mode of SPAs.
url: https://datajelly.com/blog/script-shell-pages
canonical: https://datajelly.com/blog/script-shell-pages
og_title: DataJelly - The Visibility Layer for Modern Apps
og_description: Rich social previews for Slack &amp; Twitter. AI-readable content for ChatGPT &amp; Perplexity. Zero-code setup.
og_image: https://datajelly.com/datajelly-og-image.png
twitter_card: summary_large_image
twitter_image: https://datajelly.com/datajelly-og-image.png
---

# Script Shell Pages: When Your App Loads But Nothing Works | DataJelly
> A 4KB HTML response with a single div and a script tag returns 200 OK and passes every monitor — while users see a blank page. Here's why script shell pages are the default failure mode of SPAs.

---

What a script shell looks like:

200 OK

HTTP status

4.2 KB

HTML size

<100

visible chars

all monitors green

This is the default failure mode of modern SPAs. It passes every system check.

## What's Actually Happening

A script shell page is HTML that contains JavaScript bootstrapping code but no usable content. Every SPA framework ships this by default — Vite, CRA, Lovable, most React setups.

A typical production response looks like this:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Your App</title>
    <link rel="stylesheet" href="/assets/index-9f3a1.css">
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/assets/index-9f3a1.js"></script>
  </body>
</html>

That response is often:

- • 2–6 KB total
- • Under 100 characters of visible text
- • Zero semantic elements (no `<h1>`, `<article>`, `<nav>`)
- • A single root `div` with no children

Everything depends on JavaScript executing correctly. There is no fallback. If the bundle 404s, if hydration throws, if the API returns 500 — users get a blank page and bots get an empty document.

## Browser vs HTML Reality

The disconnect between what your team sees and what bots see is the entire problem.
## What the browser shows

- • Full UI with navigation
- • Product data, pricing, testimonials
- • Working CTAs and forms
- • ~35 KB rendered DOM, 1,200+ words
### What the HTML actually contains

- • An empty container `<div id="root">`
- • Script and stylesheet references
- • No headings, no paragraphs, no links
- • ~4 KB total, <100 visible chars

Both are the same page. Both return 200. One is the page you built. The other is what every bot, every crawler, and every user with a failed JS load actually receives.

## Why It Looks Healthy

Every infrastructure signal you have will tell you the page is fine. None of them validate content.

Status code200 OK

TTFBFast (<100ms)

CDNServing cached HTML

Backend logsClean

Visible text in HTMLNone

Semantic elementsZero

Content discoverable by botsNo

ConversionsDown sharply

The system is healthy. The page is unusable. This is a page-level failure mode that infrastructure monitoring is structurally incapable of catching.

## Why Tools Miss This

Most teams monitor infrastructure, not output. Every standard tool has the same blind spot.
| Tool | What It Checks | Catches Empty HTML? |
| --- | --- | --- |
| Uptime monitoring | Confirms 200 OK, ignores body | No |
| Server logs | Backend behavior only | No |
| Synthetic tests | Often stop at DOM ready | No |
| Lighthouse | Runs with JS enabled and cached assets | No |
| Error tracking | Thrown JS exceptions | No |
| HTML output validation | Raw HTML size, content, structure | Yes |
| Bot-perspective fetch | What crawlers actually receive | Yes |
Your system can be 100% healthy by every standard metric while every page is unusable. None of these tools answer the question that actually matters: "Does this page contain content?"

## What We See in Production

We see this constantly, and it's almost always tied to deploys. Three patterns account for nearly every case.

1
## Missing JS bundle (most common)

A new build references `/assets/index-9f3a1.js`. The CDN still serves old assets at the edge. The file returns 404.

Result: HTML loads (4 KB), script fails to load, page never renders. Users see a blank screen. Bots see nothing. Revenue stops immediately.

GET /                         200 OK   4.2 KB
GET /assets/index-9f3a1.js    404 Not Found
// Page never mounts. No error in your logs.

2
### API-first rendering failure

The app waits for `/api/page-data` before rendering anything. The API returns 500 for ~5% of requests due to a downstream timeout.

Result: HTML loads, JS runs but never mounts content, page stays empty. This shows up as intermittent "ghost outages" — some users see content, others don't. Support gets reports you can't reproduce.

3
### Hydration crash after deploy

Server and client output diverge. React throws during hydration with "Text content does not match server-rendered HTML." Rendering halts.

Result: Partial UI or full blank, no recovery, still returns 200. See our companion post on [hydration crashes](https://datajelly.com/blog/hydration-crashes-silent-killer) for the full breakdown.

All three ship to production without alerts. All three return 200 OK. All three turn your site into a 4 KB empty response.

## How to Detect It

Stop guessing. Check the output directly.
## 1. Fetch raw HTML

Pull the page without executing JS. If your homepage HTML is 3–5 KB with no readable text, it's broken.

curl -s https://yoursite.com | wc -c
## Under 5KB → script shell

curl -s https://yoursite.com | grep -oP '(?<=>)[^<]+' | wc -w
## Under 100 words → bots see nothing

You should see headings, paragraphs, and links in that output. If you don't, bots won't either.
## 2. Compare source vs rendered

Open the page in a browser and copy a chunk of visible text. Then view source and search for it. If it's missing, your content is JS-only.

This is the simplest test that exists for content delivery, and almost no team runs it after every deploy.
## 3. Check HTML structure

Red flags in the raw HTML:

- • `<body>` contains only one `<div>`
- • No `<h1>`, `<article>`, or `<main>`
- • No meaningful text nodes
- • All content references resolve via JS at runtime
## 4. Force failure conditions

Block JS in DevTools or simulate a failed script load. If the page becomes blank, you've confirmed the dependency. Now you know what 5% of your users (and 100% of your bots) are seeing.

## Solutions

There's no single fix. The problem spans rendering strategy, monitoring, and deploy hygiene.
## Put content in the HTML

If your content only exists after JS runs, it is fragile by design. At minimum:

- • Core text content must exist in the HTML response
- • Navigation links must be present
- • Page structure (headings, semantic tags) must be complete

This is what [DataJelly Edge](https://datajelly.com/products/edge) does for SPAs that can't migrate to SSR — it generates real HTML for bots without rewriting the app.
## Validate HTML, not behavior

Stop relying on "it works in the browser." Validate:

- • HTML size (content pages should not be single-digit KB)
- • Text presence (raw, not rendered)
- • Structural elements (h1, main, nav, links)
## Monitor real pages

You need checks that fail when content disappears. Not "is the server up?" but:

- • "Does this page contain 500+ words of text?"
- • "Does the `<h1>` exist?"
- • "Did HTML size drop from 35 KB to 4 KB between deploys?"

That's the difference between catching a script shell regression and shipping it.

DataJelly Guard does exactly this

Guard inspects real HTML output, flags content loss, and catches script shell regressions before they hit users. Built for React, Vite, and Lovable apps where this failure mode is the default.

## Practical Checklist

Run this after every deploy. If any of these fail, investigate immediately — regardless of what your status dashboard says.

HTML response > 15 KB for content pages

Single-digit KB is a script shell

Visible text present in raw HTML

Not just JS-injected — actually in the response body

h1, main, and links exist in source

Bots index structure, not your rendered DOM

No dependency on API calls for initial render

If the API hiccups, the page should still show content

JS bundles return 200, not 404

Verify all asset hashes after deploy

Page still shows content with JS disabled

Open DevTools, disable JS, reload. What do you see?

No sudden HTML size drops between deploys

40 KB → 4 KB means something broke

Hydration completes without console warnings

Even one warning can kill an entire subtree

## Run These Tests Now

Guard automates all of this. Until it ships, run these manually — no signup required.

Each tool below tests a different layer of the script shell problem. If your page returns 200 but the HTML is empty, these will show you exactly where it breaks.

[Page Validator
Check whether your HTML contains the headings, links, and content bots need.](https://datajelly.com/seo-tools/page-validator) [HTTP Debug
Inspect raw HTTP responses by user agent — see what Googlebot actually receives.](https://datajelly.com/seo-tools/http-debug) [Visibility Test
Compare what bots see vs what users see — the core script shell check, done manually.](https://datajelly.com/visibility-test) [Page Speed Analyzer
Check Core Web Vitals and rendering performance that uptime tools miss.](https://datajelly.com/seo-tools/page-speed-analyzer) [Robots.txt Tester
Verify crawlers aren't blocked from the pages you think are live.](https://datajelly.com/seo-tools/robots-txt-tester)

The takeaway

Script shell pages are not rare bugs. They are the default failure mode of SPAs. A single missing script or failed API turns your site into a 4 KB empty response that still returns 200 OK.

Your monitoring will say everything is fine. Your users and search traffic will say otherwise. If you're not validating HTML output directly, you are shipping blind.
## Interested in Guard?

Guard is launching soon. If your React, Vite, or Lovable app has ever shipped a 4 KB shell to production without anyone noticing, we built this for you.

[Ask a Question](https://datajelly.com/contact) [Learn About Guard](https://datajelly.com/products/guard)

## FAQ
## What is a script shell page?
## Why does Google fail to index these pages?
## What HTML size indicates a problem?
## Why does the app look fine locally?
## Can this happen without errors in logs?
## What's the fastest way to confirm the issue?
## What's the most common root cause?
## Related Reading

[DataJelly Guard
Rendering monitoring for React, Vite, and Lovable apps](https://datajelly.com/products/guard) [Hydration Crashes: The Silent Killer
Companion Guard post on dead-button failures](https://datajelly.com/blog/hydration-crashes-silent-killer) [Why Your React App Shows a Blank Page in Production
How blank-page failures happen and why monitoring misses them](https://datajelly.com/blog/react-blank-page-production) [Your Site Returns 200 OK — But Is Completely Broken
When status codes lie about page health](https://datajelly.com/blog/site-returns-200-but-broken) [Your HTML Is Only 4KB
Why tiny HTML responses mean your content is invisible](https://datajelly.com/blog/html-only-4kb) [Page Validator
Check bot-readiness of any URL](https://datajelly.com/seo-tools/page-validator) [HTTP Debug Tool
Inspect raw HTTP responses by user agent](https://datajelly.com/seo-tools/http-debug) [Visibility Test
See what bots see on your pages](https://datajelly.com/visibility-test)

## Structured Data (JSON-LD)
```json
{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"What is a script shell page?","acceptedAnswer":{"@type":"Answer","text":"It\u0027s an HTML response that contains scripts and a root container but no actual content. The page only works if JavaScript executes successfully \u2014 and when it doesn\u0027t, you ship a 4KB blank document that still returns 200 OK."}},{"@type":"Question","name":"Why does Google fail to index these pages?","acceptedAnswer":{"@type":"Answer","text":"Because the HTML contains no content. Googlebot doesn\u0027t reliably execute JavaScript, especially on a render budget. If your content only exists after JS runs, bots index an empty page with a single div and some script tags."}},{"@type":"Question","name":"What HTML size indicates a problem?","acceptedAnswer":{"@type":"Answer","text":"Content pages under ~10 KB are a strong signal of a problem. Most real pages should be 20\u2013100 KB depending on content. If your homepage HTML is 4 KB with no readable text, it\u0027s broken \u2014 even if the browser shows a working UI."}},{"@type":"Question","name":"Why does the app look fine locally?","acceptedAnswer":{"@type":"Answer","text":"Local environments have working assets, fast APIs, and no CDN propagation lag. The failure modes that hit production \u2014 missing JS chunks, intermittent API errors, stale cached assets \u2014 don\u0027t reproduce on your laptop."}},{"@type":"Question","name":"Can this happen without errors in logs?","acceptedAnswer":{"@type":"Answer","text":"Yes. The backend can be perfectly healthy while the frontend fails to render. A 404 on a JS bundle from CDN doesn\u0027t show up in your application logs. A failed API call that prevents mount looks like normal user traffic."}},{"@type":"Question","name":"What\u0027s the fastest way to confirm the issue?","acceptedAnswer":{"@type":"Answer","text":"Fetch the raw HTML with curl. If you don\u0027t see readable headings, paragraphs, and links \u2014 just a div and scripts \u2014 the page is broken. Bots see what curl sees, not what your browser shows you."}},{"@type":"Question","name":"What\u0027s the most common root cause?","acceptedAnswer":{"@type":"Answer","text":"Missing or failed JavaScript bundles after deploy. CDN serves stale HTML referencing new asset hashes that don\u0027t exist yet. The HTML loads, the script 404s, the page never renders. This is the #1 trigger we see in production."}}]}
```


## Discovery & Navigation
> Semantic links for AI agent traversal.

* [DataJelly Edge](https://datajelly.com/products/edge)
* [DataJelly Guard](https://datajelly.com/products/guard)
* [Pricing](https://datajelly.com/pricing)
* [SEO Tools](https://datajelly.com/seo-tools)
* [Visibility Test](https://datajelly.com/visibility-test)
* [Dashboard](https://dashboard.datajelly.com/)
* [Blog](https://datajelly.com/blog)
* [Guides](https://datajelly.com/guides)
* [Getting Started](https://datajelly.com/guides/getting-started)
* [Prerendering](https://datajelly.com/prerendering)
* [SPA SEO Guide](https://datajelly.com/guides/spa-seo)
* [About Us](https://datajelly.com/about)
* [Contact](https://datajelly.com/contact)
* [Terms of Service](https://datajelly.com/terms)
* [Privacy Policy](https://datajelly.com/privacy)
