Eazip.js
EazipGitHub
Guides

Create a ZIP File in the Browser with JavaScript

Turn browser files, blobs, and URLs into a ZIP entirely client-side — one createZip call, no server, no upload, with the options that actually matter.

Browsers can build real ZIP archives — no server round trip, no uploading the user's files anywhere. This page is the plain-JavaScript how-to: what can go into a browser-made ZIP, the one call that builds it, and the handful of options worth knowing.

The one call

import { createZip } from '@eazip/core';

const result = await createZip({
  files: fileInput.files, // a FileList from <input type="file" multiple>
  zipName: 'export.zip',
});

result.download();

createZip() resolves when the archive is ready; result.download() hands it to the browser's normal download flow. That's a working feature — Eazip.js is MIT-licensed and needs no account. (Package setup and a step-by-step first run live in Getting Started / JavaScript.)

What can go in

One files array accepts every source, mixed freely:

SourceExample
Picked or dropped filesFile, FileList, Blob
Generated contentnew Blob([csvText]) with a filename
Remote URLs'https://assets.example.com/report.pdf'
Named entries{ url, filename } or { file, filename }

Two things to know about entries:

  • filename controls the unzipped result, and forward slashes create folders: { file: photo, filename: 'photos/cover.jpg' }.
  • Remote URLs must be fetchable from the page — same-origin, public with CORS, or signed URLs your server minted. The rules are in Inputs and sources, and the per-platform signing recipes in the storage guides (S3, R2, Supabase).

The options that matter

const result = await createZip({
  files,
  zipName: 'photos.zip',
  compressionLevel: 0, // store-only: JPEGs and MP4s don't compress twice
  maxZipSizeBytes: 1_000_000_000, // split output into ~1 GB parts
  onProgress: (progress) => render(progress),
});
  • compressionLevel: 0 skips Deflate entirely — the right choice when the sources are already compressed (photos, video, existing archives). It saves real CPU time and the output is barely larger.
  • maxZipSizeBytes splits large output into numbered parts (export_part01.zip, …); call result.downloadAll() for every part. See Multi-ZIP splitting.
  • onProgress reports file and byte progress for your own UI. For cancel buttons and richer state, use startZip() instead — the shape is in Show progress, cancel, and retry.
  • Failed URL fetches don't destroy the archive by default: usable output ships and failures are reported — see Partial results and errors.

Where the browser stops

Honest boundaries, because they're architectural rather than fixable:

  • Memory holds the archive while it builds — multi-gigabyte ZIPs in a tab are unreliable (the details).
  • The tab is the runtime — navigation or reload ends the job.
  • Legacy ZIP limits don't apply (output is ZIP64), but your users' ancient extraction tools might care — ZIP files over 4 GB.

When exports outgrow the tab, the same createZip call takes strategy: 'cloud' and a public key, and the job moves to managed Eazip without changing your UI — see Scale with Eazip.

FAQ

Does this upload my users' files anywhere?

Not on the Local path — files are read and packaged inside the page. If you later opt into the managed path, sources are fetched by the service; that switch is explicit (strategy: 'cloud').

Can I create a ZIP from a folder the user picks?

Yes — <input type="file" webkitdirectory> yields a FileList whose entries carry relative paths; map them to filename values to preserve the folder structure inside the archive.

How big a ZIP can the browser build?

Hundreds of megabytes is routine; multiple gigabytes is where tab memory makes it a gamble. ZIP large files in the browser covers the mitigation options in order.

Do I need a Web Worker?

No — bring your own if your app's main thread is busy, but the library runs fine without one for typical export sizes.