Domotion

Optimize output size

Get from a 60 KB hero to a 25 KB one without sacrificing fidelity.

SVG output from Domotion is moderately sized by default — a hero card runs 20–60 KB, a multi-frame animation 80–200 KB. If that's good enough for your context, ship it. If you need it smaller, the techniques on this page typically halve the file size.

Always run optimizeSvg()

The single highest-leverage step is one line:

import { optimizeSvg } from "domotion";
const small = optimizeSvg(svg);

This runs SVGO with a structure-preserving plugin set: shorter path commands, lower decimal precision, collapsed transforms. Typical reduction is 30–50%, with bigger wins on path-heavy text. See optimizeSvg() for plugin details.

Capture only what you'll show

The biggest cost is glyph outlines, and you only pay for glyphs that appear in the captured tree. Two patterns:

  • Crop to the visible region. Don't capture a 1200×800 body when the demo is a 600×300 card. Use the bounding box of the card as the viewport.
  • Strip irrelevant subtrees. Hide the cookie banner / nav / footer with page.evaluate(() => el.remove()) before capturing.

Lean into glyph deduplication

Path mode emits each unique glyph once into <defs> and references it with <use>. So 100 instances of the letter "e" in body text cost ~one path's worth of bytes, not 100. Two implications:

  • Repeating UI patterns (a list of items with similar text) compress well — repeated glyphs share defs.
  • Mixing many fonts and weights on one capture costs more than sticking with one or two — different weights are different glyphs.

Prefer crossfade transitions in animated SVGs

An all-crossfade animation hits the merge fast path: generateAnimatedSvg de-duplicates elements across frames, emitting stable elements (logo, header, things that don't change) once with a permanent opacity: 1. Changing elements get a small visibility timeline. The output is dramatically smaller than the "emit each frame as a clipped group" fallback used for push-left and scroll.

If you can express your story as a series of state changes within the same screen, all-crossfade will give you the smallest result.

Skip overlays when state can be captured

Any overlay disables the merge fast path. If the typed text or click state could just be part of the captured HTML for the next frame, it's both smaller and (slightly) cleaner.

Keep frame count modest

Each frame is one capture's worth of glyph outlines. Even with deduplication, going from 4 frames to 12 typically more than doubles the output. Aim for the smallest number of frames that tells the story — 3–5 is usually enough.

Round and reduce

If you're hand-authoring HTML for a demo (not capturing your real product), round coordinates and avoid sub-pixel positioning where you can. Per-character x offsets are stored to one decimal place after optimizeSvg — fewer non-zero decimals compress better.

Compress for transport

SVGs are plain text. Two transport-level wins worth knowing:

  • gzip — most CDNs and servers gzip text/svg+xml responses by default. Path data compresses very well; expect another 50–70% reduction on the wire.
  • Brotli — even better than gzip when supported, typically another 10–15% on top.

Concretely: a 60 KB SVG often arrives at the browser as ~12 KB over Brotli. Don't over-optimize the source if your transport handles compression.

Measure first

If you're spending time on optimization, instrument it:

const raw = elementTreeToSvg(tree, w, h);
const small = optimizeSvg(raw);
console.log(`raw=${raw.length} optimized=${small.length} ratio=${(small.length/raw.length).toFixed(2)}`);

Then look at the structure of the SVG itself: which glyph defs are biggest, which gradients are unused, where the byte count concentrates. SVG is text — grep and an editor get you a long way.

See also