Exporting Self-Contained HTML From Chrome for Import

Chrome's Save As menu has three export modes, but only one gets close to a true self-contained HTML file. Here is which to pick and how to finish the job.

TUTORIALDEVELOPERPublished

Chrome's three Save As options, and why only one comes close

Press Ctrl+S (Cmd+S on a Mac) on any page in Chrome and you get three choices: Webpage, Complete, Webpage, HTML Only, and MHTML, Single File. If your goal is to export HTML from Chrome as a single portable file for an import tool, only one of those three names is honest about what it actually produces, and it's probably not the one you'd guess.

Webpage, Complete writes the markup to an .html file and drops every image, stylesheet, and script into a sibling _files folder next to it. The two pieces have to travel together. Move the .html file on its own and every relative reference breaks. That rules it out as a single self-contained file. It's also the mode most people reach for by default, since it sits first in the dropdown.

Webpage, HTML Only writes just the markup, no _files folder at all. It's the smallest of the three, and the least useful here: every external resource reference stays in the file, pointing at images and stylesheets that are no longer attached. Open it later and you get bare text where the page used to be.

MHTML looks self-contained. It is not plain HTML

MHTML, Single File produces one file with everything folded in, which is why it's tempting to treat as the answer. It isn't plain HTML underneath, though. MHTML wraps the page in MIME multipart/related encoding, the same technique browsers use for HTML email attachments: an email-style header carrying the title, source URL, and timestamp, followed by the HTML and then each resource, base64-encoded and separated by boundary strings. Chrome 86 and later writes this format through Save As. The same output is also available programmatically through the pageCapture extension API's saveAsMHTML method, which is what most automatic capture extensions call under the hood instead of reimplementing the format themselves. One restriction worth knowing: the resulting file can only be reopened from the local filesystem, and only in a browser's main frame, never inside an iframe.

Why MHTML is not the self-contained HTML an importer wants

A tool built to parse plain HTML, including UnHTML's drop zone, reads an .html file as HTML: tags, attributes, a DOM. An .mhtml file isn't that. It's a MIME container with an HTML part buried inside base64-wrapped boundaries, and a plain HTML parser won't unwrap it correctly. Drop an .mhtml file into an importer expecting self-contained HTML and, at best, you get nothing readable.

There's a second gap worth knowing about, even if you never hit the file-format issue. Embedded JavaScript does not execute when an MHTML file is reopened; only the HTML already rendered at capture time survives. For a mostly-static page, that rarely matters. For anything that builds part of its layout after load, the capture is missing whatever ran after the snapshot.

If you already have an .mhtml file and need plain HTML from it, reopen it in Chrome and re-save using a real single-file extension. Don't try to hand-edit the MIME container. Firefox doesn't read MHTML natively at all, so an .mhtml file saved on one machine isn't something you can safely hand to a colleague on a different browser either.

Building a true single-file HTML with everything inlined

A genuinely self-contained HTML file inlines its CSS and images directly into the markup, usually as base64 data URIs, so the single file has no outside references left to break. Chrome doesn't do this natively. There's no DevTools panel or Save As option that walks a page and rewrites every <img src> and <link rel="stylesheet"> into an inline data URI; that conversion step belongs to a third-party capture extension.

The steps, in order:

  1. Load the page fully in Chrome and scroll through it once, so any lazy-loaded images and sections render into the DOM before capture. Skip this and your capture will be missing whatever hadn't loaded yet.
  2. Run a single-file capture extension that inlines CSS and images as data URIs, rather than Chrome's native Save As. Most of these add a toolbar button that triggers the capture on the active tab, no separate app required.
  3. Save the result as one .html file, with a name that will still make sense once it's sitting in a downloads folder next to a dozen others.
  4. Open that file directly from disk, not through a local server, and confirm every image and style renders with no broken references, no network tab activity, and nothing pointing back at the original domain. A capture that still reaches out to the live site isn't actually self-contained.

One honest trade-off: base64 encoding adds roughly 33 percent to the size of whatever it wraps, so an inlined single-file export ends up reliably larger than the sum of the page's original separate assets. A 200 KB hero image becomes close to 266 KB once it's base64 text sitting inside the HTML. A handful of large photos can turn a page into a multi-megabyte file this way. That's the cost of true portability, not a bug in the process, and it's worth knowing before a file size surprises you on the way into an importer.

What a static capture keeps, and what it drops

A single-file export, MHTML or inlined HTML, freezes the DOM at the moment of capture. Anything already rendered is kept: text, layout, images that have loaded, styles that have applied. Anything that happens after that moment isn't, because none of the page's JavaScript runs again once the file is reopened offline.

This distinction matters most on pages built around client-side behavior: content that loads on scroll, data that arrives from a delayed fetch, or views that change with client-side routing. Take an infinite-scroll feed. A static capture only has the handful of items that had already loaded when you hit save, not the rest of the feed a visitor would reach by scrolling further. A mostly-static marketing page or documentation article is fine with a plain Save As; it captures everything that matters. A JavaScript-heavy single-page app is a different story, and a capture tool that drives a real browser session, waiting for network activity to settle before taking the snapshot, is the better fit.

When Save As is not enough

Two paths exist outside Chrome's own export menu, and both are worth knowing before you assume a browser Save As is the only route in.

Safari users have a fourth option Chrome doesn't: the .webarchive format. It bundles the HTML with its linked CSS, images, and JavaScript into one of Apple's binary property list files, readable natively only in Safari and built with a different encoding than MHTML's MIME container. UnHTML accepts .webarchive files directly as an alternative to self-contained HTML, the more practical route if you're already on a Mac and would rather skip installing a capture extension. See saving a page as a Safari web archive for the full walkthrough, including where macOS puts the file by default.

For a live site, or anything that renders its layout with JavaScript after load, skip the browser's Save As dialog altogether. Use the open-source capture CLI or bookmarklet instead. It drives the page the way a real visit would, waits for the layout to settle, then writes the result as a scene.json rather than an HTML file, sidestepping the plain-HTML-parsing problem entirely. The process for a React or Vue site, including how the CLI handles client-side routing, is covered in importing a React or Vue site into Figma.

The bottom line

Of Chrome's three Save As options, none hands you a ready-to-use single file the way a self-contained HTML importer expects, not without an extra step. Webpage, Complete splits into two pieces that have to stay together. Webpage, HTML Only drops the resources entirely. MHTML bundles everything, but into a MIME container, not plain HTML, and any JavaScript that ran after load is gone either way, no matter which of the three modes produced the file. For a mostly-static page, the reliable route is a capture extension that inlines CSS and images as data URIs into one real .html file, accepting the roughly 33 percent base64 overhead as the cost of portability. The Safari webarchive and the live-capture CLI are the right fallbacks when Save As can't do the job: webarchive if you're already on a Mac, the CLI if the page renders its content with JavaScript after the initial load. Once an import lands as layers, see turning an import into reusable components for the next step. And if a capture turns out larger than the free tier allows, the Pro tier removes the 500-layer cap on the pricing page.

Questions

Does Chrome's MHTML export work with UnHTML?
No. UnHTML's drop zone parses plain HTML, and an .mhtml file is not plain HTML, it is a MIME multipart/related bundle with a header and base64-encoded parts. Re-save the page as a genuine single HTML file with resources inlined, or use the .webarchive or scene.json paths instead.
What is the difference between Chrome's Webpage, Complete and Webpage, HTML Only save options?
Webpage, Complete saves the markup plus a sibling _files folder holding every image, stylesheet, and script the page referenced, so the HTML file alone is not self-contained. Webpage, HTML Only saves just the markup with none of those resources, and most references break. Neither produces one self-contained file on its own.
Why does a self-contained HTML export sometimes come out bigger than the live page?
Inlining images and other binary assets as base64 data URIs adds roughly 33 percent to their size versus the original binary. A page with several large images can end up noticeably heavier as one inlined HTML file than it was as separate requests.
Will a self-contained HTML export capture content that JavaScript renders after page load?
Only what has already rendered into the DOM at the moment of capture. A static HTML snapshot does not re-run embedded scripts, so content that loads on scroll, on a delayed fetch, or behind client-side routing needs the live-capture CLI path instead of a browser Save As.