Open Source · One Wasm Engine · TypeScript

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.

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.

Ten 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, tar & 7z
Read and write ZIP (store/deflate/zstd) with ZIP64, streaming, and WinZip AES-256 — plus tar/.tar.gz/.tar.zst and .7z, all interoperable with the standard tools.
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.
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.
Dictionary & Delta
Beat generic codecs where they're weak: a trained zstd dictionary shrinks many small, similar payloads, and compressDelta encodes only what changed between revisions — logs, chat, snapshots.
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.

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 / decompress pair
  • 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
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 — 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; verify checks CRC-32
  • tar / .tar.gz / .tar.zst and .7z (7-Zip-compatible)
archive.ts
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 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?

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.