These five packages provide solutions for handling ZIP archives within the Node.js runtime. adm-zip is the most widely adopted pure JavaScript library for synchronous zip and unzip operations. yazl focuses on asynchronous zip creation with stream support. node-zip is a legacy package that is no longer maintained. zip-a-folder and zip-lib serve as higher-level utilities that wrap core libraries to simplify common tasks like compressing directories or handling promise-based workflows.
Handling compressed archives is a common requirement in backend development, from deploying artifacts to generating user downloads. The Node.js ecosystem offers several options, but they differ significantly in maintenance status, API design, and performance characteristics. This analysis compares adm-zip, node-zip, yazl, zip-a-folder, and zip-lib to help you select the right tool for your architecture.
Before comparing features, we must address the longevity of these packages. Using an unmaintained library in a production build pipeline introduces security risks and compatibility issues.
adm-zip is actively maintained and widely trusted. It has no native dependencies, making it easy to deploy across different environments.
node-zip is deprecated. The repository is archived, and it has not received updates in years. It should not be used in new projects.
// node-zip: Deprecated API pattern
const Zip = require('node-zip');
const zip = new Zip();
// WARNING: No longer receiving security patches
yazl is stable and maintained, focusing specifically on writing zips. It is often paired with yauzl for reading.
zip-a-folder and zip-lib are smaller utilities. zip-a-folder wraps adm-zip, while zip-lib offers a promise-based interface. Check their recent commit history before adopting for critical paths.
// zip-lib: Check maintenance status
const zipLib = require('zip-lib');
// Verify last update before relying on this in production
The most significant technical difference lies in how these libraries handle I/O. Blocking the event loop can degrade server performance under load.
adm-zip uses a synchronous API. This simplifies code flow but blocks the Node.js event loop during compression or extraction.
// adm-zip: Synchronous execution
const AdmZip = require('adm-zip');
const zip = new AdmZip();
zip.addLocalFolder("./dist");
zip.writeZip("./archive.zip"); // Blocks thread until complete
yazl uses streams and callbacks. This allows non-blocking operations, which is critical for high-concurrency servers.
// yazl: Asynchronous stream
const yazl = require("yazl");
const zipFile = new yazl.ZipFile();
zipFile.addFile("./index.html", "index.html");
zipFile.end();
zipFile.outputStream.pipe(fs.createWriteStream("archive.zip"));
zip-lib wraps operations in Promises. This enables clean async/await syntax without manual stream management.
// zip-lib: Promise-based
const zipLib = require('zip-lib');
await zipLib.zip('./dist', './archive.zip'); // Returns a promise
zip-a-folder abstracts the process into a single function call, usually returning a promise or accepting a callback.
// zip-a-folder: High-level abstraction
const zipFolder = require('zip-a-folder');
await zipFolder('./src', './output.zip');
node-zip used a callback-based async model but is now obsolete.
// node-zip: Legacy callback style
zip.generate(function (err, data) {
if (err) throw err;
// Handle buffer
});
Developers often need to compress entire folders. The effort required varies between packages.
adm-zip provides a dedicated method for local folders. It handles recursion automatically.
// adm-zip: Direct folder support
const zip = new AdmZip();
zip.addLocalFolder("./project/dist", "dist");
zip.writeZip("./build.zip");
yazl does not have a built-in "add folder" method. You must walk the directory tree yourself and add files individually.
// yazl: Manual file iteration
const files = getAllFilesRecursive("./dist");
files.forEach(f => zipFile.addFile(f, path.relative("./dist", f)));
zipFile.end();
zip-a-folder is designed specifically for this task. It requires the least amount of code.
// zip-a-folder: One-line folder zip
await zipFolder("./project/dist", "./build.zip");
zip-lib also supports directory zipping via its main function.
// zip-lib: Directory support
await zipLib.zip("./project/dist", "./build.zip");
node-zip required manual buffer management for multiple files, adding complexity.
// node-zip: Manual buffer handling
zip.file("index.html", fs.readFileSync("index.html"));
Not all libraries support unzipping. Some are write-only, which impacts architectural choices.
adm-zip supports both reading and writing. You can extract all contents or specific entries.
// adm-zip: Full extraction
const zip = new AdmZip("./archive.zip");
zip.extractAllTo("./output", true); // true = overwrite
yazl is write-only. To unzip, you must install a companion library like yauzl. This separation of concerns can be beneficial for bundle size.
// yazl: Cannot unzip (Write-only)
// Must use yauzl for extraction instead
zip-lib supports both operations via distinct methods.
// zip-lib: Extract method
await zipLib.unzip("./archive.zip", "./output");
zip-a-folder is primarily focused on creating zips from folders. Extraction is not its main focus and may require other tools.
// zip-a-folder: Limited extraction support
// Primarily designed for zipping folders, not extracting
node-zip supported reading but is no longer safe to use for this purpose.
// node-zip: Legacy reading
zip.load(data);
const file = zip.file("index.html");
Memory management is critical when handling large archives. Loading entire files into memory can crash a process.
adm-zip loads the archive into memory. For very large files (hundreds of MBs), this can cause high memory pressure.
// adm-zip: In-memory operation
// Caution with large files:
const zip = new AdmZip("./large-file.zip"); // Loads fully into RAM
yazl streams data. It does not need to hold the entire archive in memory, making it safer for large outputs.
// yazl: Stream-friendly
// Data flows through without loading everything into RAM
zipFile.addReadStream(fs.createReadStream("large-file.bin"), "large-file.bin");
zip-lib typically uses streams under the hood but exposes a promise interface. Check documentation for large file limits.
// zip-lib: Promise wrapper over streams
await zipLib.zip("./large-folder", "./large.zip");
zip-a-folder relies on adm-zip internally, so it inherits the memory characteristics of adm-zip.
// zip-a-folder: Inherits adm-zip memory behavior
// Watch memory usage on large directories
node-zip had similar memory constraints but lacks modern optimizations.
// node-zip: Older memory handling
// No modern stream optimizations available
| Feature | adm-zip | node-zip | yazl | zip-a-folder | zip-lib |
|---|---|---|---|---|---|
| Status | ā Active | ā Deprecated | ā Active | ā ļø Wrapper | ā ļø Niche |
| API Style | Sync | Async (Legacy) | Async Stream | Async/Sync | Promise |
| Read/Write | Both | Both | Write Only | Write Focus | Both |
| Memory | High (In-memory) | High | Low (Stream) | High | Medium |
| Complexity | Low | Low | Medium | Very Low | Low |
For most backend services and build tools, adm-zip remains the default choice. Its synchronous API is easier to reason about in scripts, and its read/write support covers all bases. The memory trade-off is acceptable for typical build artifacts.
For web servers generating dynamic downloads, yazl is superior. Streaming prevents memory spikes when multiple users request large files simultaneously. Remember to pair it with yauzl if you also need to read archives.
Avoid node-zip entirely. The risk of security vulnerabilities outweighs any familiarity benefits. Migrate existing code to adm-zip as soon as possible.
Use zip-a-folder for simple CLI utilities where development speed matters more than fine-grained control. Use zip-lib if your team strictly enforces promise-based patterns and finds adm-zip's synchronous style inconsistent with your codebase.
When extracting archives, always validate paths to prevent Zip Slip vulnerabilities. adm-zip has had patches for this, but you must still ensure target paths are safe.
// Security check example for any library
const path = require('path');
const target = path.resolve("./output", entryName);
if (!target.startsWith(path.resolve("./output"))) {
throw new Error("Invalid path");
}
Regardless of the library, never extract user-uploaded zips without validating the contents. This protects your server from writing files to unauthorized locations.
Choose adm-zip for most server-side tasks where synchronous operations are acceptable. It is the industry standard for reading and writing zip files in Node.js without native dependencies. It is ideal for build scripts, artifact generation, and simple file manipulation where blocking the event loop briefly is not a concern.
Do NOT choose node-zip for new projects. It is deprecated and unmaintained. Existing projects using it should plan a migration to adm-zip or yazl to ensure security updates and compatibility with modern Node.js versions.
Choose yazl when you need to create zip files asynchronously or stream data directly into an archive. It is perfect for web servers that generate downloads on the fly without writing intermediate files to disk. It does not support unzipping, so pair it with yauzl if extraction is needed.
Choose zip-a-folder for quick CLI tools or scripts where you only need to compress a directory into a single file. It abstracts away the setup required by adm-zip. Avoid it for complex archive manipulation where you need fine-grained control over individual entries.
Choose zip-lib if you prefer a promise-based API for both zipping and unzipping without managing streams manually. It is suitable for modern async/await codebases that need a cleaner interface than adm-zip offers, though it may have a smaller community ecosystem.
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 });
.
.
.