# How to Extract Static HTML from Running Web Applications

> Learn to extract static HTML from running web apps with stitch. This skill uses Puppeteer to capture rendered pages, inline CSS and images, and remove dev scripts for self-contained files.

- Repository: [Google Labs Code/stitch-skills](https://github.com/google-labs-code/stitch-skills)
- Tags: how-to-guide
- Published: 2026-07-12

---

**The `stitch::extract-static-html` skill captures fully-rendered web pages by launching a headless Chromium browser via Puppeteer, inlining all CSS and images as base64 data-URIs, and removing development-only scripts to produce a self-contained HTML file.**

The `stitch::extract-static-html` skill in the `google-labs-code/stitch-skills` repository provides a robust solution to extract static HTML from running web applications. Unlike simple "view source" operations, this tool renders JavaScript-heavy frameworks like React, Vue, and Angular, then materializes runtime-generated styles and embeds all external assets directly into the HTML. This produces a portable snapshot that works independently of the original server or build environment.

## How the Snapshot Pipeline Works

The core logic resides in [`plugins/stitch-design/skills/extract-static-html/scripts/snapshot.ts`](https://github.com/google-labs-code/stitch-skills/blob/main/plugins/stitch-design/skills/extract-static-html/scripts/snapshot.ts), which orchestrates a multi-step transformation pipeline entirely within a headless browser context.

### Browser Launch and Navigation

The process begins with argument parsing via `parseArgs()` (lines 72-119) and validation through `validateOpts()` (lines 165-226), which checks for required fields, valid URLs, and writable output directories. The script then launches a sandboxed headless Chromium instance using `puppeteer.launch()` (lines 271-281).

Navigation to the target URL occurs via `page.goto()` (lines 295-309), which implements a fallback strategy from `networkidle0` to `networkidle2` to handle slow-loading applications. For interactive scenarios, the script supports pre-capture clicks—executing `page.$(selector).click()` and waiting an additional 2 seconds (lines 317-446)—useful for modal dialogs or dynamic content.

### CSS Materialization and Inlining

Before asset extraction, the pipeline handles runtime-generated styles. The **CSS-in-JS materialization** step (lines 842-876) populates empty `<style>` tags by extracting rules from the CSSOM, ensuring styled-components and emotion-based styles survive serialization.

External stylesheets undergo **stylesheet inlining** (lines 884-944), where each `<link rel="stylesheet">` is fetched, relative `url()` references are resolved against the base URL, and the link is replaced with an inline `<style>` tag containing the resolved CSS.

### Asset Inlining and Base64 Conversion

The `__snapshot` helper library injected into the page context provides `toDataUri()`, which fetches resources and converts them to base64 data-URIs. This process handles:
- Standard `<img>` tags and `srcset` attributes
- `<source srcset>` elements in `<picture>` tags
- Background images from inline styles
- SVG `<image>` elements
- Video poster frames and favicons

Concurrency is controlled via `processInBatches()` to prevent overwhelming the browser. Additionally, the script parses CSS with `extractCssUrls()`—a character-by-character parser rather than regex—to inline `url()` references (lines 1064-1120), optionally including fonts when `--inline-fonts` is specified.

### Cleanup and Output Generation

The final processing phase removes development artifacts. The script strips all `<script>` tags and known development overlays (Vite, Next.js, CRA) between lines 1246-1294. It also removes fixed-position elements that appear out of view (lines 639-677) and any user-specified selectors (lines 679-694).

The serialized DOM is extracted via `document.documentElement.outerHTML` (lines 1296-1308) and written to the `--output` path. Optional JSON reporting provides asset counts, file sizes, and duration metrics for CI/CD integration (lines 1310-1330).

## Command-Line Usage Examples

Basic capture of a locally running Vite application:

```bash
npx tsx plugins/stitch-design/skills/extract-static-html/scripts/snapshot.ts \
  --url http://localhost:5173 \
  --output .stitch/home.html \
  --wait 2000

```

Advanced capture with dark mode, viewport specification, and interactive element triggering:

```bash
npx tsx plugins/stitch-design/skills/extract-static-html/scripts/snapshot.ts \
  --url http://localhost:3000/dashboard \
  --output .stitch/dashboard.html \
  --html-class dark \
  --data-theme dark \
  --remove-fixed \
  --viewport 1440x900 \
  --click "#open-settings" \
  --json

```

## Static Fallback and Post-Processing

For scenarios requiring the Static Fallback method using [`MockPage.jsx`](https://github.com/google-labs-code/stitch-skills/blob/main/MockPage.jsx), the repository includes [`post_process.ts`](https://github.com/google-labs-code/stitch-skills/blob/main/post_process.ts) to finalize asset inlining after the initial extraction:

```bash
npx tsx plugins/stitch-design/skills/extract-static-html/scripts/post_process.ts \
  .stitch/Page.html --base-dir path/to/app

```

The alternative extraction method is implemented in [`extract_inline_html.ts`](https://github.com/google-labs-code/stitch-skills/blob/main/extract_inline_html.ts), which supports the MockPage workflow for specific framework interactions where direct browser manipulation is preferred.

## Summary

- **Browser-based rendering**: Uses Puppeteer to execute JavaScript and render dynamic content before extraction
- **Self-contained output**: Converts all external assets (CSS, images, fonts) to base64 data-URIs embedded in the final HTML
- **Framework agnostic**: Works with React, Vue, Svelte, Angular, and plain HTML applications
- **Development-ready**: Automatically removes dev overlays and scripts while preserving production-essential styling
- **Configurable pipeline**: Supports viewport customization, element clicking, class injection, and selective removal of fixed elements

## Frequently Asked Questions

### Does this work with single-page applications (SPAs) using client-side routing?

Yes. The `stitch::extract-static-html` skill executes all JavaScript during the Puppeteer navigation phase, allowing SPAs to fully hydrate before capture. The `networkidle0` to `networkidle2` fallback ensures asynchronous routes and lazy-loaded components complete rendering before the snapshot occurs, as implemented in lines 295-309 of [`snapshot.ts`](https://github.com/google-labs-code/stitch-skills/blob/main/snapshot.ts).

### How does the tool handle CSS-in-JS libraries like styled-components?

The pipeline specifically materializes CSS-in-JS styles by reading the CSSOM for empty `<style>` tags and writing the computed rules back into the DOM (lines 842-876 in [`snapshot.ts`](https://github.com/google-labs-code/stitch-skills/blob/main/snapshot.ts)). This ensures runtime-generated styles from styled-components, emotion, or similar libraries are preserved in the static output rather than lost during serialization.

### Can I exclude specific elements from the final HTML?

Yes. Use the `--remove-fixed` flag to automatically strip `position: fixed` and `position: sticky` elements that appear outside the viewport (lines 639-677). Additionally, pass `--remove-selector` with any valid CSS selector to eliminate specific elements such as cookie banners or navigation headers (lines 679-694) before the HTML is written to disk.

### What is the difference between the main snapshot.ts and the Static Fallback method?

The [`snapshot.ts`](https://github.com/google-labs-code/stitch-skills/blob/main/snapshot.ts) script performs live browser rendering via Puppeteer and is the recommended approach for most applications. The Static Fallback method, implemented in [`extract_inline_html.ts`](https://github.com/google-labs-code/stitch-skills/blob/main/extract_inline_html.ts), uses a [`MockPage.jsx`](https://github.com/google-labs-code/stitch-skills/blob/main/MockPage.jsx) component for specific framework scenarios where direct browser access is limited. The [`post_process.ts`](https://github.com/google-labs-code/stitch-skills/blob/main/post_process.ts) script accompanies this method to finalize asset inlining after the initial JSX-based extraction, ensuring all relative paths are resolved against the correct base directory.