Compress anything.
Everywhere.
One typed API over ten codecs — gzip, deflate, zlib, zstd, lz4, snappy, brotli, lzma, bzip2, xz — plus ZIP (streaming & AES), tar and 7z containers, all from a single WebAssembly engine. Native speed where it wins, libdeflate density where it matters. Node 18+, Bun, and the browser.
import { gzip, zstd, compress, decompress } from 'zipkit'; const gz = await gzip(bytes); // balanced default const small = await zstd(bytes, { mode: 'ratio' }); // auto-detects the format on the way back const original = await decompress(small);
Every codec you'd reach for,
one tiny API
Best-in-class C libraries compiled into a single Wasm engine, wrapped in a small, typed, tree-shakeable TypeScript surface.
gzip, zstd, brotli — or compress() / decompress() for generic dispatch with auto-detect.mode: 'speed' | 'balanced' | 'ratio'. No tuning maze — each codec picks a sensible level. level is still there when you need exact control.mode: 'ratio' uses libdeflate for denser gzip than native zlib..wasm. Consumers never need Emscripten — it just loads.store/deflate/zstd) with ZIP64, streaming, and WinZip AES-256 — plus tar/.tar.gz/.tar.zst and .7z, all interoperable with the standard tools.TransformStreams that drop into any pipeThrough(). gzip/zlib/deflate ride the platform's native CompressionStream — true incremental streaming, zero Wasm.ZKP1 container. On 34 MB of logs, parallel gzip is 3.4× faster than native and denser too.Accept-Encoding and serves the best the client supports — brotli → zstd → gzip → deflate.encodeImage (QOI) beats brotli on raw pixels, and encodeFrames (frame-delta + zstd) halves plain zstd on temporal frames.zstd dictionary shrinks many small, similar payloads, and compressDelta encodes only what changed between revisions — logs, chat, snapshots.pack() tries brotli, lzma, bzip2, and zstd-ultra, keeps the densest, and tags it so unpack() can reverse it.sideEffects: false, and byte-only codecs (Uint8Array). Same import on Node 18+, Bun, and the browser.Ten codecs,
one set of patterns
Named when you know, generic when you don't
Import the codec by name for tree-shaking, or dispatch generically with compress(). On the way back, decompress() sniffs the header and routes itself.
- ✓ Every codec is a
compress/decompresspair - ✓ Async helpers lazy-load the engine on first use
- ✓
decompress()auto-detects gzip, zlib, zstd, and xz - ✓ Bytes in, bytes out — strings convert via
strToU8/strFromU8
import { brotli, unbrotli, compress, decompress } from 'zipkit'; // Named — tree-shakeable, async, lazy engine const c = await brotli(bytes, { mode: 'ratio' }); const back = await unbrotli(c); // Generic dispatch + auto-detect const z = await compress(bytes, 'zstd', { level: 19 }); const orig = await decompress(z); // sniffs zstd
One knob, not a tuning maze
Pick an intent — speed, balanced, or ratio — and ZipKit chooses a sensible level and path for each codec. Reach for level only when you want exact control.
- ✓
speed— lower levels and native runtime paths - ✓
balanced— the default; practical levels, adaptive dispatch - ✓
ratio— denser paths, libdeflate for gzip/deflate/zlib - ✓ Out-of-range
levelvalues are clamped, never rejected
// Fast: lowest latency, native paths where they win await gzip(bytes, { mode: 'speed' }); // Balanced: the default — good speed, good ratio await zstd(bytes); // Ratio: smallest output, libdeflate density await zstd(bytes, { mode: 'ratio' }); // Or pin an exact level await brotli(bytes, { level: 11 });
ZIP archives — zstd, AES & streaming
Build and read standard ZIPs, opt into zstd for denser archives, encrypt with one option, or stream multi-GB archives without buffering. Plus tar and 7z containers.
- ✓
password— WinZip AES-256, opens in 7-Zip / WinZip - ✓
zipStream()writes incrementally, memory-bounded - ✓ ZIP64 past 4 GB / 65,535 entries;
verifychecks CRC-32 - ✓
tar/.tar.gz/.tar.zstand.7z(7-Zip-compatible)
import { zip, unzip } from 'zipkit'; // Encrypted (AES-256) — opens in 7-Zip / WinZip const archive = await zip([ { name: 'index.html', data: html }, { name: 'app.js', data: js, method: 'zstd' }, ], { password: 'secret' }); const files = await unzip(archive, { password: 'secret' }); // Or stream a huge archive straight to disk import { zipStream } from 'zipkit/zip'; await zipStream(entries).pipeTo(dest);
Drops into any stream
Web-standard TransformStreams compose with fetch bodies, files, and sockets — anything that speaks Web Streams. gzip/zlib/deflate stream incrementally on the platform's native engine.
- ✓ Native
CompressionStreamfor gzip/zlib/deflate - ✓ Constant memory — no buffering for unbounded input
- ✓ Other codecs buffer and compress on flush, still composable
- ✓ Standard output any conformant decompressor can read
import { compressionStream } from 'zipkit/streams'; const res = await fetch('/big.json'); await res.body .pipeThrough(compressionStream('gzip')) .pipeTo(dest); // true incremental streaming, zero Wasm
See every codec on your data
The CLI ships with the library. zipkit bench runs every codec on a real file and prints ratio and timings, so you can pick the right trade-off for your payload — not a synthetic one.
- ✓
compress/decompresswith--modeand--codec - ✓
zip/unzipandinfoto inspect any file - ✓ Auto-detects the format on decompress
- ✓ Runs on both Node and Bun
$ zipkit bench big.log bench big.log (97.0 KB) codec ratio size comp decomp gzip 5.1% 5.0 KB 0.3ms 0.1ms zstd 5.6% 5.5 KB 0.1ms 0.1ms brotli 3.4% 3.3 KB 160ms 0.1ms lz4 12.9% 12.6 KB 0.1ms 0.0ms lzma 3.8% 3.7 KB 5.7ms 0.3ms ✓ all roundtrips byte-identical
Install & start compressing
Requires Node.js v18+ or Bun. The Wasm engine ships with the package — no Emscripten, no native build step.
Prefer the terminal? npm install -g zipkit · then zipkit compress data.json --mode ratio
Best-in-class C libraries,
one Wasm engine
Each codec is the reference implementation, statically linked into a single ~1.4 MB module — loaded once, cached for the page or process lifetime.
Ready to compress
everything?
Ten codecs; ZIP (streaming & AES), tar and 7z containers; dictionary & delta; streams, workers, and middleware — in one tiny typed API that runs the same on Node, Bun, and the browser.