adm-zip vs archiver vs node-zip vs yauzl vs yazl vs zip-lib
Choosing the Right ZIP Library for Node.js Applications
adm-ziparchivernode-zipyauzlyazlzip-libSimilar Packages:

Choosing the Right ZIP Library for Node.js Applications

These libraries handle ZIP file creation and extraction in Node.js, but they serve different architectural needs. archiver is the industry standard for streaming large files without crashing memory. adm-zip offers a simple, synchronous API for small, local files but struggles with streams. yauzl and yazl are low-level, high-performance tools built specifically for streaming data, often used as building blocks for other tools. node-zip is deprecated and should be avoided. zip-lib provides a modern, promise-based wrapper around native bindings, useful when you need specific compression features not found in pure JS solutions.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
adm-zip02,178140 kB54a month agoMIT
archiver02,97339.6 kB1723 months agoMIT
node-zip0217-1911 years ago-
yauzl0822110 kB122 months agoMIT
yazl038358.7 kB202 years agoMIT
zip-lib04568.8 kB12 months agoMIT

Node.js ZIP Libraries: Streaming vs. Memory Trade-offs

Handling ZIP files in Node.js is a common requirement, from bundling assets for download to processing user uploads. However, not all ZIP libraries are built the same. The critical difference lies in how they handle data: do they load the whole file into memory, or do they stream it piece by piece? Let's break down the technical realities of adm-zip, archiver, node-zip, yauzl, yazl, and zip-lib.

🚨 Deprecation Warning: node-zip

Before diving into the viable options, we must address node-zip. This package is deprecated and no longer maintained. It relies on older native bindings that often fail to compile on modern Node.js versions.

// āŒ DO NOT USE
// const zip = require('node-zip'); 
// This package is abandoned and poses security risks.

Recommendation: If you see node-zip in a legacy codebase, plan a migration to archiver or yazl immediately. Do not start new projects with it.

šŸ’¾ Memory Model: Loading All vs. Streaming

The most important architectural decision is between loading the entire file into RAM (Buffer) versus processing it as a Stream.

adm-zip loads everything into memory. It is synchronous and blocking by default.

  • Great for small files (< 50MB) or local CLI tools.
  • Dangerous for web servers handling large files; it can crash your process with an "Out of Memory" error.
// adm-zip: Loads entire file into RAM
const AdmZip = require('adm-zip');
const zip = new AdmZip();

// Adds file to memory buffer immediately
zip.addLocalFile("./large-video.mp4"); 

// Blocks the event loop until writing is done
zip.writeZip("./output.zip"); 

archiver uses streams. It processes data in chunks.

  • Ideal for HTTP responses, large file processing, and pipes.
  • Keeps memory usage low regardless of file size.
// archiver: Streams data directly to disk or network
const archiver = require('archiver');
const output = require('fs').createWriteStream('./output.zip');
const archive = archiver('zip');

output.on('close', () => console.log('Done'));
archive.pipe(output);

// Streams file content without loading it all into RAM
archive.file('./large-video.mp4', { name: 'video.mp4' }); 
archive.finalize();

yazl and yauzl are also stream-based but offer a lower-level API.

  • yazl (writer) and yauzl (reader) give you raw control over the ZIP structure.
  • They are lighter than archiver but require you to manage more details manually.
// yazl: Low-level streaming write
const yazl = require('yazl');
const zipFile = new yazl.ZipFile();
const output = require('fs').createWriteStream('./output.zip');

zipFile.outputStream.pipe(output);

// You must explicitly end the stream
zipFile.addFile('./large-video.mp4', 'video.mp4');
zipFile.end();

zip-lib attempts to bridge the gap with a Promise-based API, often wrapping native libraries.

  • It offers async/await syntax which is cleaner than callbacks.
  • Still generally loads more into memory than pure streams but less than adm-zip.
// zip-lib: Promise-based API
const { zip, unzip } = require('zip-lib');

// Returns a promise, non-blocking
await zip(['./large-video.mp4'], './output.zip');

šŸ“¦ Adding Files: Simplicity vs. Control

How you add files to the archive varies significantly between high-level and low-level libraries.

adm-zip makes it trivial to add local folders recursively.

  • One line of code handles directory traversal.
// adm-zip: Recursive folder add
const zip = new AdmZip();
// Automatically adds all files in 'src' folder
zip.addLocalFolder("./src", "dist/src"); 

archiver also supports recursive directory appending easily via plugins or built-in methods.

  • It maintains the stream benefit while offering high-level convenience.
// archiver: Recursive directory append
const archive = archiver('zip');
// Streams all files in 'src' folder
archive.directory('./src', 'dist/src'); 
archive.finalize();

yazl requires you to manually handle file discovery if you want to add a folder.

  • You typically pair it with readdir or glob to find files first.
  • This adds code complexity but gives you exact control over what gets included.
// yazl: Manual file addition (no built-in recursive folder add)
const fs = require('fs');
const path = require('path');
const yazl = require('yazl');
const zipFile = new yazl.ZipFile();

// You must manually iterate files
const files = ['file1.txt', 'file2.txt']; 
files.forEach(f => zipFile.addFile(f, path.basename(f)));

zipFile.end();

zip-lib provides a clean array-based input for multiple files.

  • Simpler than yazl for multiple files but less flexible than archiver for complex glob patterns.
// zip-lib: Array of files
const { zip } = require('zip-lib');
// Zips specific files listed in array
await zip(['file1.txt', 'file2.txt'], './output.zip');

šŸ“„ Extraction: Reading ZIP Files

Reading archives has similar trade-offs between ease of use and memory safety.

adm-zip extracts synchronously to the disk.

  • Simple for build scripts, but blocks the server thread.
// adm-zip: Sync extraction
const zip = new AdmZip("./archive.zip");
// Blocks execution until all files are written
zip.extractAllTo("./target-folder", true); 

yauzl is the gold standard for streaming extraction.

  • It emits events for each entry, allowing you to pipe data directly to a destination stream.
  • Essential for processing large uploads without disk I/O bottlenecks.
// yauzl: Streaming extraction
const yauzl = require('yauzl');

yauzl.open("./archive.zip", { lazyEntries: true }, (err, zipfile) => {
  zipfile.on('entry', (entry) => {
    zipfile.openReadStream(entry, (err, readStream) => {
      // Pipe directly to file or processing logic
      readStream.pipe(require('fs').createWriteStream(entry.fileName));
      zipfile.readEntry(); // Read next entry
    });
  });
});

archiver is primarily for creation. For extraction, you would typically use yauzl or unzip-stream alongside it.

  • zip-lib offers a simple async extract function similar to adm-zip but non-blocking.
// zip-lib: Async extraction
const { unzip } = require('zip-lib');
// Returns a promise, doesn't block the event loop
await unzip('./archive.zip', './target-folder');

🌐 Real-World Scenarios

Scenario 1: "Download Report" API Endpoint

You need to generate a ZIP of 500MB of logs and send it to the browser.

  • āœ… Best Choice: archiver
  • Why? You can pipe the archive directly to the HTTP response (res). adm-zip would try to load 500MB into RAM, likely crashing your server instance.
// Express example with archiver
app.get('/download', (req, res) => {
  const archive = archiver('zip');
  res.setHeader('Content-Type', 'application/zip');
  res.setHeader('Content-Disposition', 'attachment; filename=logs.zip');
  
  archive.pipe(res); // Stream directly to client
  archive.directory('/var/logs', false);
  archive.finalize();
});

Scenario 2: Local Build Script / CLI Tool

You are writing a command-line tool to bundle a small project folder (under 10MB) for deployment.

  • āœ… Best Choice: adm-zip
  • Why? Simplicity wins here. You don't need streams for small local files. The synchronous API makes the script logic linear and easy to debug.
// CLI script with adm-zip
const zip = new AdmZip();
zip.addLocalFolder('./dist');
zip.writeZip('./release.zip');
console.log('Build complete!');

Scenario 3: Serverless Function (AWS Lambda)

You need to unzip a user upload in a Lambda function where cold start time and bundle size matter.

  • āœ… Best Choice: yauzl / yazl
  • Why? These libraries have zero native dependencies and are very small. zip-lib might require compiling native binaries which can be tricky in serverless environments depending on the runtime.
// Lambda handler with yauzl
exports.handler = async (event) => {
  // Process stream efficiently with minimal bundle size
  // ... implementation using yauzl events ...
};

šŸ“Š Summary Table

Featureadm-ziparchiveryauzl / yazlzip-libnode-zip
Primary ModeSynchronous / BufferStreamingStreamingAsync / PromiseNative / Deprecated
Memory UsageHigh (Loads all)Low (Chunks)Low (Chunks)MediumHigh
Ease of Use⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Recursive DirBuilt-inBuilt-inManualLimitedBuilt-in
Best ForCLI, Small FilesWeb Servers, Large FilesHigh Perf, Custom LogicModern Async AppsNone (Avoid)

šŸ’” Final Recommendation

The choice comes down to data size and execution context.

If you are building a web server or handling large files, archiver is the safest and most robust choice. Its streaming architecture protects your application from memory spikes, and its API is mature enough to handle complex directory structures without much fuss.

If you are writing small scripts, CLI tools, or dealing with tiny files locally, adm-zip remains the king of simplicity. Its synchronous API removes the need for complex async/await chains for trivial tasks.

For specialized high-performance needs or serverless environments where every kilobyte of bundle size counts, the yauzl/yazl duo provides the leanest, most efficient foundation, though it demands more code from the developer.

Avoid node-zip entirely. It belongs to the past. For a modern, promise-based experience with native speed, zip-lib is a solid alternative if archiver feels too heavy for your specific use case.

How to Choose: adm-zip vs archiver vs node-zip vs yauzl vs yazl vs zip-lib

  • adm-zip:

    Choose adm-zip for simple scripts, CLI tools, or build steps where you need to zip small folders synchronously and don't care about streaming. It is perfect for local file manipulation where the entire file fits comfortably in RAM. Avoid it for web servers handling large uploads or downloads, as it loads everything into memory at once.

  • archiver:

    Choose archiver when building web servers, APIs, or data pipelines that need to stream ZIP files directly to the user or disk. It is the best choice for handling large datasets because it processes data in chunks, preventing memory crashes. Its plugin ecosystem also makes it ideal if you need to support formats beyond standard ZIP.

  • node-zip:

    Do NOT choose node-zip for any new project. It is officially deprecated and no longer maintained. Using it introduces security risks and compatibility issues with modern Node.js versions. Migrate existing projects to archiver or yazl immediately.

  • yauzl:

    Choose yauzl when you need maximum performance and control for reading (extracting) ZIP files via streams. It is ideal for backend services that process uploaded archives where memory efficiency is critical. Be prepared to write more boilerplate code compared to higher-level libraries like adm-zip.

  • yazl:

    Choose yazl when you need a lightweight, pure-JavaScript solution for creating ZIP files via streams without the extra features of archiver. It is perfect for microservices or serverless functions where bundle size matters and you only need basic ZIP creation capabilities with high efficiency.

  • zip-lib:

    Choose zip-lib if you require a modern, Promise-based API and need access to native compression algorithms for better speed or ratio. It is a good middle ground if you find archiver too complex but need more robustness than adm-zip offers for medium-sized files.

README for adm-zip

ADM-ZIP for NodeJS

ADM-ZIP is a pure JavaScript implementation for zip data compression for NodeJS.

Build Status

Installation

With npm do:

$ npm install adm-zip

Electron file system support described below.

What is it good for?

The library allows you to:

  • decompress zip files directly to disk or in memory buffers
  • compress files and store them to disk in .zip format or in compressed buffers
  • update content of/add new/delete files from an existing .zip

Dependencies

There are no other nodeJS libraries that ADM-ZIP is dependent of

Examples

Basic usage

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.

Electron original-fs

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 });
.
.
.

Security

Please report security vulnerabilities privately. See SECURITY.md.