adm-zip, client-zip, jszip, and zip-local are JavaScript libraries designed to handle ZIP archive creation and extraction, but they target fundamentally different runtimes and use cases. adm-zip is a pure Node.js implementation optimized for synchronous file system operations on the server. client-zip is a lightweight, streaming library built specifically for modern browsers, leveraging the Compression Streams API to avoid memory bloating. jszip is a versatile, cross-platform library that works in both Node.js and browsers, offering a rich feature set for manipulating archives entirely in memory. zip-local is an older, synchronous Node.js wrapper that has largely fallen out of favor due to maintenance concerns and lack of modern async support. Understanding the runtime constraints (server vs. browser) and data flow (streaming vs. in-memory) is critical for selecting the right tool.
Handling ZIP archives in JavaScript requires a clear understanding of your runtime environment. The ecosystem splits sharply between server-side tools that interact with the file system and client-side tools that manage memory streams. adm-zip, client-zip, jszip, and zip-local each occupy specific niches. Making the wrong choice can lead to memory crashes in the browser or blocked event loops on the server. Let's examine how they solve common engineering problems.
The first decision point is where the code runs. Some libraries rely on Node.js core modules like fs (file system), while others depend on browser APIs like Blob and ReadableStream.
adm-zip is strictly for Node.js. It uses synchronous file system calls to read and write archives directly to disk. This makes it fast for small to medium tasks on the server but unusable in a browser.
// adm-zip: Node.js only
const AdmZip = require('adm-zip');
const zip = new AdmZip();
// Adds a file from the local disk
zip.addLocalFile("/path/to/file.txt");
// Writes directly to the server's file system
zip.writeZip("/path/to/archive.zip");
client-zip is strictly for the browser. It leverages the modern Compression Streams API to process data on the fly. It cannot access the server's file system and does not run in Node.js without complex shims.
// client-zip: Browser only
import { createZip } from "client-zip";
const files = [
{ name: "report.txt", input: new Blob(["Data"]) },
{ name: "image.png", input: await fetch("/img.png") }
];
// Returns a stream you can download directly
const stream = createZip(files);
const blob = await new Response(stream).blob();
jszip is universal. It runs in Node.js and browsers. However, it operates entirely in memory. In Node.js, you must manually read files into buffers and write buffers back to disk.
// jszip: Universal (Node & Browser)
const JSZip = require("jszip");
const zip = new JSZip();
// Must manually read file content into memory first
zip.file("file.txt", "File content string");
// Generates a blob (browser) or buffer (Node)
const content = await zip.generateAsync({ type: "nodebuffer" });
// You must then write 'content' to disk manually in Node
zip-local is an older Node.js library. Like adm-zip, it touches the file system synchronously. It is rarely chosen for new work due to stagnation.
// zip-local: Node.js only (Legacy)
const zip = require('zip-local');
// Sync operation on local file
zip.sync.zip("/path/to/file.txt").compress().save("/path/to/archive.zip");
How the library handles data determines if your application scales. Loading a 2GB archive into RAM will crash a browser tab or spike server costs. Streaming processes data in chunks, keeping memory usage low.
client-zip uses true streaming. It processes files one by one and outputs a stream immediately. You can zip gigabytes of data without running out of memory.
// client-zip: Low memory footprint via streaming
const largeFiles = getHugeFileList(); // Array of 1000s of files
// Starts downloading immediately without waiting for all files to load
const stream = createZip(largeFiles.map(f => ({
name: f.name,
input: fetch(f.url) // Streams directly from network to zip
})));
jszip loads everything into memory. You must wait for all files to be added and processed before you get the final output. This creates a high memory peak.
// jszip: High memory usage
const zip = new JSZip();
// All files must be loaded into RAM before generation starts
files.forEach(file => {
zip.file(file.name, file.content);
});
// Blocks until the entire archive is built in memory
const result = await zip.generateAsync({ type: "blob" });
adm-zip buffers files in memory during the add phase but writes directly to disk during the save phase. It is more efficient than jszip for server tasks involving large files, but it still lacks true back-pressure streaming for input.
// adm-zip: Disk-based but buffers metadata
const zip = new AdmZip();
// Reads file into buffer then writes to disk immediately on save
zip.addLocalFile("large-video.mp4");
zip.writeZip("output.zip"); // Writes chunk by chunk to disk
zip-local behaves similarly to adm-zip but with less optimization. It reads files into memory before writing them out synchronously.
// zip-local: Synchronous buffering
zip.sync.zip("large-file.dat").compress().save("output.zip");
JavaScript thrives on non-blocking code. Libraries that force synchronous execution can freeze your user interface or block your server from handling other requests.
client-zip is fully asynchronous and promise-based. It integrates naturally with modern async/await patterns in the browser.
// client-zip: Native async/await
async function downloadZip() {
const stream = createZip(files);
// Non-blocking generation
const blob = await new Response(stream).blob();
triggerDownload(blob);
}
jszip is also fully asynchronous. Its generateAsync method ensures the UI thread remains responsive in browsers while computing compression.
// jszip: Async generation
async function createArchive() {
const zip = new JSZip();
zip.file("doc.txt", "content");
// Does not block the main thread
const blob = await zip.generateAsync({ type: "blob" });
}
adm-zip is primarily synchronous. Methods like writeZip block the Node.js event loop until the file is written. For small files, this is fine. For large files on a busy API server, this is a risk.
// adm-zip: Synchronous blocking
const zip = new AdmZip();
zip.addLocalFile("data.txt");
// Blocks the event loop here until disk write completes
zip.writeZip("archive.zip");
console.log("Done"); // Runs only after write finishes
zip-local is strictly synchronous. It offers no async alternatives, making it a poor fit for modern high-performance Node.js services.
// zip-local: Strictly synchronous
zip.sync.zip("file.txt").compress().save("archive.zip");
// No callback, no promise, just blocking
Sometimes you need to read an existing ZIP, remove a file, and save it again. Not all libraries support modification equally well.
jszip excels at in-memory manipulation. You can load an archive, iterate over files, delete specific entries, and update content easily.
// jszip: Advanced manipulation
const zip = await JSZip.loadAsync(existingBlob);
// Remove a file
zip.remove("deprecated-config.json");
// Update a file
zip.file("readme.md", "Updated content");
const newBlob = await zip.generateAsync({ type: "blob" });
adm-zip allows modification but requires reloading the archive. It is powerful for server-side extraction where you need to pull specific files to disk.
// adm-zip: Extraction and modification
const zip = new AdmZip("archive.zip");
const entries = zip.getEntries();
// Extract specific file to disk
zip.extractEntryTo("config.json", "/output/dir", false, true);
// Add new file and rewrite
zip.addFile("new.txt", Buffer.from("data"));
zip.writeZip("archive.zip");
client-zip is designed for creation, not modification. It does not support opening an existing ZIP to edit it. You must reconstruct the archive from source files.
// client-zip: Creation only
// Cannot open existing.zip to remove a file
// Must re-create from original sources
const stream = createZip(sourceFiles.filter(f => f.name !== "remove-me.txt"));
zip-local supports basic extraction and creation but lacks the robust API for complex in-memory manipulation found in jszip.
// zip-local: Basic extraction
zip.sync.unzip("archive.zip").extract("./output");
Users select multiple large log files in the browser to download as a single archive.
client-zipjszip would likely run out of memory if logs are large.// Using client-zip for streaming large browser downloads
const stream = createZip(logFiles.map(file => ({
name: file.name,
input: file.blob
}));
A CI/CD pipeline needs to zip a build folder and upload it to S3.
adm-zip// Using adm-zip for simple server-side archiving
const zip = new AdmZip();
zip.addLocalFolder("./dist");
zip.writeZip("./build-archive.zip");
A web app allows users to upload a DOCX (which is a ZIP), modify an XML file inside, and save it back.
jszip// Using jszip for in-memory document editing
const zip = await JSZip.loadAsync(uploadBlob);
const xml = await zip.file("document.xml").async("string");
// ... modify xml ...
zip.file("document.xml", updatedXml);
const newBlob = await zip.generateAsync({ type: "blob" });
You are maintaining an old internal tool that zips small config files synchronously.
zip-local.adm-zip for better long-term support, though zip-local may still function for trivial tasks.| Feature | adm-zip | client-zip | jszip | zip-local |
|---|---|---|---|---|
| Primary Runtime | Node.js | Browser | Universal | Node.js |
| Execution Model | Synchronous | Asynchronous (Stream) | Asynchronous | Synchronous |
| Memory Strategy | Disk-buffered | True Streaming | In-Memory | In-Memory/Disk |
| Modify Existing | Yes | No | Yes (Excellent) | Limited |
| Large File Safety | Moderate | High | Low (OOM risk) | Low |
| Maintenance Status | Active | Active | Active | Stale/Deprecated |
Select your tool based on where the code runs and how big your data is.
client-zip for generating new archives, especially if files are large. Use jszip only if you need to edit existing archives or if file sizes are small and predictable.adm-zip for straightforward file system operations and scripting. It is the standard for server-side ZIP tasks.jszip is the only viable option if the same codebase must run in both Node and the browser, provided you can manage the memory overhead.zip-local in any new architecture. It offers no distinct advantage over adm-zip and carries the risk of unmaintained dependencies.By matching the library to your runtime and data constraints, you ensure your application remains responsive and stable under load.
Choose adm-zip when building server-side Node.js applications that need to synchronously read or write ZIP files directly from the local file system. It is ideal for CLI tools, build scripts, or backend services where blocking the event loop briefly is acceptable and simplicity is preferred over streaming performance. Avoid it for large files or high-concurrency web servers where non-blocking I/O is required.
Choose client-zip for modern frontend applications running in the browser that need to generate ZIP files from user data, blobs, or fetch responses. It is the best choice when performance and memory efficiency are critical, as it uses streaming to handle large datasets without crashing the tab. Do not use this package in Node.js environments, as it relies on browser-specific APIs.
Choose jszip when you need a single library that works seamlessly in both Node.js and browser environments, or when you need to modify existing ZIP archives in memory without touching the file system. It is suitable for complex manipulation tasks like adding, removing, or updating specific files within an archive before saving. Be cautious with very large files, as it loads the entire archive into memory, which can lead to performance issues.
Avoid choosing zip-local for new projects. It is an older library that lacks active maintenance, modern async/await support, and the robust feature set of adm-zip or jszip. If you encounter this dependency in a legacy codebase, plan to migrate to adm-zip for synchronous Node.js tasks or jszip for cross-platform needs to ensure long-term stability and security.
ADM-ZIP is a pure JavaScript implementation for zip data compression for NodeJS.
With npm do:
$ npm install adm-zip
Electron file system support described below.
The library allows you to:
There are no other nodeJS libraries that ADM-ZIP is dependent of
var AdmZip = require("adm-zip");
// reading archives
var zip = new AdmZip("./my_file.zip");
var password = "1234567890";
var zipEntries = zip.getEntries(); // an array of ZipEntry records - add password parameter if entries are password protected
zipEntries.forEach(function (zipEntry) {
console.log(zipEntry.toString()); // outputs zip entries information
if (zipEntry.entryName == "my_file.txt") {
console.log(zipEntry.getData().toString("utf8"));
}
});
// outputs the content of some_folder/my_file.txt
console.log(zip.readAsText("some_folder/my_file.txt"));
// extracts the specified file to the specified location
zip.extractEntryTo(/*entry name*/ "some_folder/my_file.txt", /*target path*/ "/home/me/tempfolder", /*maintainEntryPath*/ false, /*overwrite*/ true);
// extracts everything
zip.extractAllTo(/*target path*/ "/home/me/zipcontent/", /*overwrite*/ true);
// creating archives
var zip = new AdmZip();
// add file directly
var content = "inner content of the file";
zip.addFile("test.txt", Buffer.from(content, "utf8"), "entry comment goes here");
// add local file
zip.addLocalFile("/home/me/some_picture.png");
// get everything as a buffer
var willSendthis = zip.toBuffer();
// or write everything to disk
zip.writeZip(/*target file name*/ "/home/me/files.zip");
// ... more examples in the wiki
For more detailed information please check out the wiki.
ADM-ZIP has supported electron original-fs for years without any user interractions but it causes problem with bundlers like rollup etc. For continuing support original-fs or any other custom file system module. There is possible specify your module by fs option in ADM-ZIP constructor.
Example:
const AdmZip = require("adm-zip");
const OriginalFs = require("original-fs");
// reading archives
const zip = new AdmZip("./my_file.zip", { fs: OriginalFs });
.
.
.
Please report security vulnerabilities privately. See SECURITY.md.