Bundle size — measured, budgeted, reproducible
Measured 2026-07-05 · esbuild bundle+minify, target es2022, charset utf8 ·
identical method for every library. Reproduce: pnpm size and the snippet
at the bottom.
Inkroom
| Artifact | minified | gzip | brotli |
|---|---|---|---|
@inkroom/editor JS (core + markdown engine + full UI + 10 locales) | 457.9KB | 138.4KB | 118.8KB |
style.css (both themes, RTL) | 14.2KB | 3.4KB | — |
DOMPurify (~9KB gzip), KaTeX, Mermaid and Shiki load as on-demand async chunks — they never sit in the initial payload.
Same method, same day: the alternatives
| JS gzip | CSS gzip | Total | |
|---|---|---|---|
| Inkroom | 138.4KB | 3.4KB | 141.8KB |
| tui.editor 3.2.2 (what Inkroom replaces) | 171.9KB | 106.3KB | 278.2KB |
Headless frameworks (TipTap, Lexical, Slate) are omitted from the table not out of charity but honesty: their number excludes the markdown parser, the toolbar, the dialogs, the table UI and the localization you would still have to ship. A fair comparison is your finished editor against Inkroom — and that number is the one above plus everything you build.
Where the bytes go
| Share | Component |
|---|---|
| ~40% | ProseMirror (view, model, transform, tables — the editing engine) |
| ~28% | micromark + mdast (a full CommonMark+GFM parser/serializer — the lossless guarantee lives here) |
| ~22% | Inkroom core + UI chrome |
| ~5% | 10 bundled locales |
| ~5% | everything else |
Budgets (CI-enforced)
- brotli ≤ 120KB — the plan’s target, met on modern serving (every major CDN)
- gzip ≤ 145KB — regression guard at today’s measurement +5%
- CSS gzip ≤ 8KB
Honest note: the original internal target was 120KB gzip. A real CommonMark parser plus a production editing engine plus a complete UI has a floor, and we chose features over the number — then wrote the number down here instead of quietly editing history. Roadmap items that shrink gzip further: per-locale lazy loading (−4KB), viewport-scoped source highlighting.
Reproduce it yourself
import { build } from "esbuild";import { gzipSync, brotliCompressSync } from "node:zlib";
const r = await build({ stdin: { contents: 'import Editor from "<package>"; console.log(Editor);', resolveDir: "." }, bundle: true, minify: true, format: "esm", target: "es2022", charset: "utf8", write: false,});const js = r.outputFiles[0].contents;console.log((gzipSync(js, { level: 9 }).length / 1024).toFixed(1) + "KB gzip");