Open Source · One Wasm Engine · TypeScript

Compress anything.
Everywhere.

One typed API over nine codecs — gzip, deflate, zlib, zstd, lz4, snappy, brotli, lzma, bzip2 — plus a ZIP container, all from a single WebAssembly engine. Native speed where it wins, libdeflate density where it matters. Node 18+, Bun, and the browser.

quick-start.ts
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.

Nine Codecs, One API
Named imports when you know what you want — gzip, zstd, brotli — or compress() / decompress() for generic dispatch with auto-detect.
One Clear Knob
mode: 'speed' | 'balanced' | 'ratio'. No tuning maze — each codec picks a sensible level. level is still there when you need exact control.
Adaptive Dispatch
zstd runs native libzstd on Bun (the speed ceiling) and the Wasm engine elsewhere — same library, same bytes. mode: 'ratio' uses libdeflate for denser gzip than native zlib.
A Single Wasm Engine
libdeflate, zstd, lz4, brotli, snappy, LZMA and bzip2 in one committed, published .wasm. Consumers never need Emscripten — it just loads.
ZIP Container
Read and write standard ZIPs — store, deflate, or denser zstd (method 93). ZIP64 past 4 GB / 65,535 entries, filtered extraction, per-entry metadata.
Web-Standard Streams
TransformStreams that drop into any pipeThrough(). gzip/zlib/deflate ride the platform's native CompressionStream — true incremental streaming, zero Wasm.
Multi-Core Parallel
Split big payloads into independent blocks and compress them across a worker pool — the ZKP1 container. On 34 MB of logs, parallel gzip is 3.4× faster than native and denser too.
Worker Pool
Push compression off the main thread with zipkit/workers — one worker per core, an AbortSignal you can honor, and an inline fallback where threads aren't available.
HTTP Middleware
Drop-in response compression for Hono, Express, and Elysia. Negotiates Accept-Encoding and serves the best the client supports — brotli → zstd → gzip → deflate.
Lossless Image & Video
Domains portable libraries skip: encodeImage (QOI) beats brotli on raw pixels, and encodeFrames (frame-delta + zstd) halves plain zstd on temporal frames.
pack() — Just the Smallest
Don't care which codec wins? pack() tries brotli, lzma, bzip2, and zstd-ultra, keeps the densest, and tags it so unpack() can reverse it.
Typed & Tree-Shakeable
TypeScript-first with JSDoc on every export, sideEffects: false, and byte-only codecs (Uint8Array). Same import on Node 18+, Bun, and the browser.

Nine 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 / decompress pair
  • Async helpers lazy-load the engine on first use
  • decompress() auto-detects gzip, zlib, and zstd
  • Bytes in, bytes out — strings convert via strToU8 / strFromU8
codecs.ts
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 level values are clamped, never rejected
modes.ts
// 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, optionally with zstd

Build and read standard ZIPs in memory. Mix methods per entry, attach metadata, and filter on the way out — or opt into zstd for much denser archives between ZipKit-aware peers.

  • store / deflate interoperate with every ZIP tool
  • filter runs on metadata, before any decompression
  • ZIP64 kicks in automatically past 4 GB / 65,535 entries
  • Per-entry mtime, unixPermissions, and comments
archive.ts
import { zip, unzip } from 'zipkit';

const archive = await zip([
  { name: 'index.html', data: html },
  { name: 'app.js', data: js, method: 'zstd' },
  { name: 'logo.png', data: png, method: 'store' },
]);

// Extract only what you need
const files = await unzip(archive, {
  filter: (e) => e.name.endsWith('.js'),
});

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 CompressionStream for 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
stream.ts
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 / decompress with --mode and --codec
  • zip / unzip and info to inspect any file
  • Auto-detects the format on decompress
  • Runs on both Node and Bun
terminal — zipkit bench
$ 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.

terminal
01
Add zipkit to your project
$ npm install zipkit
02
Compress your first bytes
const gz = await gzip(strToU8('hello'));

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.

libdeflate zstd lz4 brotli snappy LZMA bzip2 WebAssembly TypeScript Node.js 18+ Bun Browser ZIP64 QOI

Ready to compress
everything?

Nine codecs, a ZIP container, streams, workers, and middleware — in one tiny typed API that runs the same on Node, Bun, and the browser.