Skip to content

Bundle sizes

sigmx has no runtime dependencies, so the numbers below are the whole cost. They come from building the real source with esbuild and are regenerated with npm run size -- --json website/src/data/sizes.json; the headline figure everywhere in these docs is brotli, which is what a CDN serves.

what you registerbrotligzipminified
core only4.5 KB4.9 KB10.6 KB
core, precompiled expressions (no runtime compiler)3.8 KB4.2 KB9.0 KB
core + text4.7 KB5.0 KB10.9 KB
minimal preset (signals, text, show, on)5.1 KB5.6 KB12.2 KB
essentials preset (state, rendering, forms, requests, morph)9.0 KB9.8 KB22.7 KB
minimal + CSP compiler5.3 KB5.8 KB12.6 KB
minimal + fetch + server handlers (morph)8.2 KB8.9 KB20.3 KB
everything (standalone build)12.2 KB13.4 KB32.1 KB
everything, shipped dist/sigmx.standalone.js (esbuild + terser)11.5 KB12.6 KB31.5 KB

Measured 2026-09-06 with esbuild (minify, mangle internals), brotli quality 11 and gzip level 9. Regenerate with npm run size -- --json website/src/data/sizes.json.

For comparison, Datastar 1.0.3’s bundle (datastar.js, 23 plugins) is 11.8 KB brotli, 13.1 KB gzip and 32.8 KB minified, measured 2026-09-06 by npm run compare. sigmx’s “everything” build carries 50 plugins.

Each row is the increase over the core-only build when that plugin alone is registered. Plugins that share helpers cost less together than the sum of these rows.

plugin (added on top of the core)brotligzipminified
@delete1630 B1780 B3874 B
@get1626 B1780 B3874 B
@patch1611 B1781 B3874 B
@post1592 B1781 B3874 B
@put1592 B1781 B3874 B
patch-elements (morph)1345 B1484 B3942 B
bind714 B793 B1806 B
boost569 B633 B1412 B
on472 B537 B1178 B
animate466 B551 B1046 B
on-signal-patch462 B498 B1160 B
on-intersect445 B515 B1139 B
query-string429 B465 B955 B
collapse411 B456 B964 B
transition396 B428 B837 B
@ws394 B463 B999 B
on-raf387 B425 B968 B
on-resize372 B414 B941 B
init347 B384 B858 B
mask281 B344 B701 B
remove-me239 B269 B533 B
class214 B238 B563 B
on-interval212 B230 B459 B
style206 B258 B573 B
match-media204 B235 B471 B
indicator200 B234 B483 B
persist190 B209 B432 B
teleport189 B198 B401 B
scroll-into-view171 B234 B433 B
custom-validity166 B191 B397 B
ref155 B171 B361 B
attr148 B193 B407 B
json-signals146 B162 B327 B
signals142 B153 B309 B
computed141 B148 B302 B
replace-url137 B158 B305 B
show135 B156 B343 B
html120 B137 B259 B
text117 B134 B261 B
@intl114 B135 B254 B
cloak110 B118 B232 B
@fit105 B119 B204 B
effect101 B115 B225 B
@clipboard96 B119 B200 B
@toggleAll87 B98 B221 B
patch-signals67 B117 B245 B
@setAll47 B94 B212 B
@dispatch34 B73 B166 B
@peek19 B69 B163 B
@confirm16 B57 B106 B

Measured 2026-09-06 with esbuild (minify, mangle internals), brotli quality 11 and gzip level 9. Regenerate with npm run size -- --json website/src/data/sizes.json.

  • Core is createSigmx with no plugins: the signal graph, store, expression compiler and mutation observer. With precompiled expressions the compiler goes and the core is 3.8 KB.
  • The request functions share one client, so registering all five costs about the same as one.
  • patch-elements carries the DOM morph. Register it only if the server sends HTML; auto mode adds it whenever a request function is used.
  • Import from sigmx/plugins freely: the barrel is side-effect free and tree-shakes to what you name. Auto mode picks the list from your source for you.
  • The shipped dist/sigmx.standalone.js is passed through terser after esbuild, which is worth 6% brotli against the plain esbuild build of the same file. When you bundle sigmx yourself, your minifier decides; repeated plain statements compress better than clever compaction, which is why the source is written that way.

The four example apps under examples/ use auto mode for real, measured from their own npm run build on 2026-09-06:

example plugins brotli gzip minified
express and hono (sigmxAuto, runtime compiler) 14 9.1 KB 9.9 KB 23.2 KB
vite (sigmxAuto with a custom plugin, runtime compiler) 12 9.2 KB 10.1 KB 23.6 KB
astro (auto mode and precompiled expressions, no fallback) 13 8.2 KB 8.9 KB 21.8 KB

The overview walks through how those lists were chosen.

The kernel is close to its floor for what it does; build targets from es2021 to esnext make no measurable difference. The remaining levers were each measured by removing the feature from a scratch copy of the 0.1.x source and rebuilding; the table is in gzipped bytes saved on that build and has not been re-measured since, so read it for the order of magnitude. Rows for features 0.2.0 has since removed are gone.

lever core everything what you give up
drop the last-statement value in expressions 104 100 data-text="$a = 1; $a * 2" would yield undefined; single expressions unaffected
drop namespace enumeration on $ 95 102 Object.keys($.user), 'x' in $.user, delete $.user.x; JSON.stringify keeps working
drop data-ignore 60 65 no way to fence off a subtree
drop per-attribute error isolation 28 37 one broken expression aborts the scan; onError goes away
plain arrays (no mutation notify) 22 16 $items.push(x) would need $items = [...$items, x]
request client without form encoding – 194 contentType: 'form'
request client without retry, backoff, reconnect – 153 streams end on the first failure
morph without moving elements by id – 150 elements with ids cannot move across ancestors; they are recreated
patch-elements without full-document responses – 95 responses containing <!doctype>, <html>, <head> or <body>

Two levers change nothing for people who do not use the feature and are the natural next step: making form encoding a plugin the request client looks up (about 190 bytes only when registered), and splitting the retry policy the same way. The rest trade documented behaviour for a few dozen bytes, which is not worth it by default; the numbers are here so the trade is explicit if you ever need it. The larger lever already taken is precompiling expressions, which removes 749 bytes brotli from any build.

Transport matters more than code at this size. Three rows below were re-measured on the shipped 31.5 KB standalone file on 2026-09-06; the rest were measured on the 0.1.x standalone file (30.1 KB minified, 11.6 KB gzip, 10.5 KB brotli) and are marked as such.

approach result verdict
brotli instead of gzip 12.6 KB → 11.5 KB (−9%), current build do it: precompress and serve Content-Encoding: br
zstd (Content-Encoding: zstd, Chrome and Firefox) 12.3 KB at level 19, current build between gzip and brotli; no reason to prefer it here
Precompiled expressions (a build plugin turns every attribute expression into a function table) core 4.5 KB → 3.8 KB (−16%), current build, and no new Function at all implemented: see precompiled expressions
Shared-dictionary brotli (Compression Dictionary Transport, Use-As-Dictionary) a new release compressed against the previous one: 10.5 KB → 4.9 KB (−54%), 0.1.x build real for returning visitors on Chromium; a server header, not a code change
Lazy directive chunks (core first, each directive loaded on first sight) initial payload 4.4 KB gzip; a typical directive chunk 200 B, the request client 1.9 KB, 0.1.x build practical with HTTP/2 and modulepreload; trades bytes on first paint for round trips
Roadroller (demoscene self-extracting packer) raw 30.1 KB → 13.8 KB, but 10.5 KB → 10.3 KB under brotli, 0.1.x build only for raw-byte limits such as 13 KB game jams; pointless behind HTTP compression
Closure Compiler SIMPLE / ADVANCED SIMPLE grew the file to 14.8 KB gzip (it re-lowered syntax); ADVANCED refused the code, 0.1.x build whole-program renaming also cannot survive a Proxy-based store, where property names are data
Bytecode (V8 code cache, Hermes, QuickJS) not measurable: browsers do not accept bytecode from the network; shipping an engine such as QuickJS in WebAssembly costs 200 KB and more before any of our code dead end on the web
WebAssembly (Rust, Zig, AssemblyScript) not measured: WebAssembly cannot touch the DOM, so every attribute, event and mutation becomes a JavaScript import call; the smallest DOM-binding toolchains start above 20 KB, and Wasm bytecode compresses worse than JavaScript because brotli’s built-in dictionary is tuned for text wrong tool for a DOM library

The short version: serve brotli, add a dictionary header if your users come back, and precompile expressions if your templates are static. Bytecode and WebAssembly cannot help a library whose entire job is talking to the DOM.