@diegoaltoworks/zipper
v1.0.6
Published
An isomorphic TypeScript package for downloading and zipping files in browser and Node.js
Downloads
44
Maintainers
Readme
Zipper
An isomorphic TypeScript package for downloading multiple files and combining them into a ZIP archive. Works in both browser and Node.js environments!
By Diego Alto | GitHub | NPM | Documentation | Live Demo
Features
- ⚡ Isomorphic: Works in both browser and Node.js environments
- 📦 TypeScript Support: Full type definitions included
- 🚀 Concurrent Downloads: Download multiple files in parallel
- 📊 Progress Tracking: Built-in progress callbacks
- 🛡️ Error Handling: Robust error handling with optional error callbacks
- 🌐 Modern Dependencies: Uses standard npm packages (JSZip, File-Saver)
- 🔧 Flexible: Continue on errors or fail fast
- ⚙️ Customizable: Custom fetch options, timeouts, and ZIP filename
Installation
npm install @diegoaltoworks/zipperThat's it! File-saver is included automatically for browser support.
Environment Support
- Browser: Auto-triggers ZIP download
- Node.js: Returns Buffer for server-side use
- Next.js: Works in both Client Components and API Routes
- Express: Perfect for server-side ZIP generation
Usage
Browser Example (Auto-Download)
import { downloadZipFile, type FileInput } from '@diegoaltoworks/zipper';
const files: FileInput[] = [
{ url: '/api/documents/report1.pdf', name: 'Report 1.pdf' },
{ url: '/api/documents/report2.pdf', name: 'Report 2.pdf' },
];
await downloadZipFile(files, { zipFilename: 'reports.zip' });
// ✓ Triggers browser download automaticallyNode.js Example (Get Buffer)
import { createZipFile } from '@diegoaltoworks/zipper';
import { writeFile } from 'fs/promises';
const files = [
{ url: 'https://example.com/file1.pdf', name: 'Document A.pdf' },
{ url: 'https://example.com/file2.pdf', name: 'Document B.pdf' }
];
const buffer = await createZipFile(files);
// ✓ Returns Buffer in Node.js
await writeFile('output.zip', buffer);Alternative: Use Default Export
import zipper from '@diegoaltoworks/zipper';
// Same as downloadZipFile
await zipper(files, { zipFilename: 'download.zip' });Next.js API Route Example
// app/api/download/route.ts
import { NextResponse } from 'next/server';
import { createZipFile } from '@diegoaltoworks/zipper';
export async function GET() {
const buffer = await createZipFile([
{ url: 'https://example.com/file1.pdf', name: 'file1.pdf' },
{ url: 'https://example.com/file2.pdf', name: 'file2.pdf' }
]);
return new NextResponse(buffer, {
headers: {
'Content-Type': 'application/zip',
'Content-Disposition': 'attachment; filename="download.zip"'
}
});
}With Options
import { downloadZipFile, type FileInput, type DownloadOptions } from '@diegoaltoworks/zipper';
const files: FileInput[] = [
{ url: 'https://example.com/file1.pdf', name: 'Document A.pdf' },
{ url: 'https://example.com/file2.pdf', name: 'Document B.pdf' }
];
const options: DownloadOptions = {
zipFilename: 'my-documents.zip',
onProgress: (current, total) => {
console.log(`Downloaded ${current} of ${total} files`);
// Update your UI progress indicator here
},
onError: (error, file) => {
console.error(`Failed to download ${file.name}:`, error.message);
// Handle individual file errors
},
continueOnError: true, // Continue downloading even if some files fail
timeout: 30000, // 30 second timeout per file
fetchOptions: {
headers: {
'Authorization': 'Bearer your-token-here'
}
}
};
await downloadZipFile(files, options);React Example
import { useState } from 'react';
import { downloadZipFile, type FileInput } from '@diegoaltoworks/zipper';
function DownloadButton() {
const [progress, setProgress] = useState('');
const [isDownloading, setIsDownloading] = useState(false);
const handleDownload = async () => {
setIsDownloading(true);
const files: FileInput[] = [
{ url: '/api/file1.pdf', name: 'file1.pdf' },
{ url: '/api/file2.pdf', name: 'file2.pdf' }
];
try {
await downloadZipFile(files, {
zipFilename: 'files.zip',
onProgress: (current, total) => {
setProgress(`${current}/${total}`);
}
});
setProgress('Complete!');
} catch (error) {
console.error('Download failed:', error);
} finally {
setIsDownloading(false);
}
};
return (
<div>
<button onClick={handleDownload} disabled={isDownloading}>
{isDownloading ? `Downloading... ${progress}` : 'Download ZIP'}
</button>
</div>
);
}API Reference
createZipFile(files, options?)
Downloads multiple files and creates a ZIP archive (works in both browser and Node.js).
Parameters
filesFileInput[]- Array of files to downloadurlstring- The URL to fetch the file fromnamestring- The filename to use in the ZIP archive
options?DownloadOptions- Optional configurationonProgress?(current: number, total: number) => void- Progress callbackonError?(error: Error, file: FileInput) => void- Error callbackcontinueOnError?boolean- Continue on errors (default:true)timeout?number- Request timeout in milliseconds (default:30000)fetchOptions?RequestInit- Additional fetch options (headers, etc.)
Returns
Promise<Buffer>in Node.jsPromise<Blob>in browser
Throws
- Throws an error if no files are provided
- Throws an error if all downloads fail
- Throws an error if
continueOnErrorisfalseand any download fails
downloadZipFile(files, options?)
Downloads multiple files, creates a ZIP, and triggers browser download.
Parameters
filesFileInput[]- Array of files to downloadoptions?DownloadOptions- Optional configurationzipFilename?string- Name of the ZIP file (default:'download.zip')- All options from
createZipFileare also supported
Returns
Promise<void> - Resolves when download is triggered
Throws
- Same errors as
createZipFile - Throws an error if browser is not supported
Type Definitions
interface FileInput {
url: string;
name: string;
}
interface DownloadOptions {
zipFilename?: string;
onProgress?: (current: number, total: number) => void;
onError?: (error: Error, file: FileInput) => void;
continueOnError?: boolean;
timeout?: number;
fetchOptions?: RequestInit;
}Demo & Documentation
Live Demo Application
Try the full-stack demo application at zipper-demo.vercel.app featuring:
- Interactive file selection and downloads
- Server-side ZIP generation with Next.js API routes
- Real-time progress tracking
- Error handling demonstrations
- Mobile-responsive UI
Source code: github.com/diegoaltoworks/zipper-demo
Interactive Documentation
Browse examples and code samples at diegoaltoworks.github.io/zipper
The documentation site (docs/ directory) includes:
- Browser usage examples with live demos
- Server-side usage patterns
- Code playground for experimentation
- Multiple file type demonstrations
- 100+ test files for testing (PDFs, PNGs, text files)
To run the docs locally:
cd docs
npm install
npm run devDevelopment
Setup
npm installBuild
npm run buildOutputs ESM and CJS modules to dist/:
dist/index.esm.js- ES moduledist/index.cjs.js- CommonJS moduledist/index.d.ts- TypeScript definitions
Test
npm test # Run tests
npm run test:watch # Watch mode
npm run test:coverage # Coverage reportLint
npm run lint # Check for issues
npm run lint:fix # Auto-fix issues
npm run format # Format with PrettierType Check
npm run type-checkBrowser Compatibility
This package works in all modern browsers that support:
- Fetch API
- Blob
- Promise
- AbortController
For older browser support, you may need polyfills.
License
MIT
Credits
Built with:
- JSZip - Create ZIP files
- FileSaver.js - Save files in the browser
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
