How to Download ZIP Files in Next.js
In this post you will learn how to create a basic Next.js project for creating and downloading ZIP archives. From creating an API endpoint to triggering file download on the client, you will be guided through the full process.
Let’s start by setting up the project and installing the necessary dependencies.
Step 1 — Setting up Project
There are many NPM packages dealing with creating archives. I have chosen to use adm-zip
because it works with both disk memory and in-memory buffers. If you use serverless solutions for hosting your application, such as Vercel, sometimes you might not be able to access the file system directly and create new files.
Knowing that, install the dependencies for creating ZIP archives.
npm install adm-zip
If you are using TypeScript, you need to install type definitions for adm-zip
package as well.
npm install @types/adm-zip
Next, create an API endpoint file src/app/api/zip/route.ts
that you will use to prepare the ZIP file and return it to the user.
export async function GET() {
const headers = new Headers();
headers.append('Content-Disposition', 'attachment; filename=archive.zip');
headers.append('Content-Type', 'application/zip');
return new Response(null, {
headers,
});
}
For now, this code listens to GET
requests to api/zip
URL and sets some headers for file download. You can adjust filename=archive.zip
to a different filename if you want.
Finally, create a client component src/app/components/FileDownload.tsx
that will trigger the file download.
'use client';
export const FileDownload = () => {
async function handleDownload() {
const response = await fetch('/api/zip');
console.log(response.status);
}
return <button onClick={handleDownload}>Download</button>;
};
Currently, it calls the API endpoint and logs its HTTP response code.
Add it to a page component of your choice and test it out.
import { FileDownload } from './components/FileDownload';
export default function Home() {
return (
<main>
<FileDownload />
</main>
);
}
Step 2 — Sending ZIP File From API
First, you need to have some files you want to add to the ZIP file.
I have added an image.png
and a document.pdf
files to src/assets
folder. Now it’s time to add these files to the archive.
import path from 'node:path';
import AdmZip from 'adm-zip';
export async function GET() {
const headers = new Headers();
headers.append('Content-Disposition', 'attachment; filename=archive.zip');
headers.append('Content-Type', 'application/zip');
const zip = new AdmZip();
zip.addLocalFile(path.join(process.cwd(), 'assets', 'image.png'));
zip.addLocalFile(path.join(process.cwd(), 'assets', 'document.pdf'));
const zipBuffer = zip.toBuffer();
return new Response(zipBuffer, {
headers,
});
}
If you are working with buffers, you can add them to the ZIP file as well.
zip.addFile('your-filename', Buffer.from('text file content', 'utf8'));
In this example you are adding a file, called your-filename
, to the archive, which you can change to another name if you want. It’s content comes from a Buffer that is created from a text string encoded with utf8
.
Step 3 — Downloading ZIP Archive
With the ZIP file being sent from the server, the last thing remaining is to actually download it to the end user computer.
Adjust the component created in step 2 to include the following code that triggers a file download.
'use client';
export const FileDownload = () => {
async function handleDownload() {
const response = await fetch('/api/zip');
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'archive.zip';
link.click();
window.URL.revokeObjectURL(url);
}
return <button onClick={handleDownload}>Download</button>;
};
- First you fetch the archive from the
api/zip
URL - Then you convert the response body to a Blob by calling
blob
method - You create an URL object for the blob
- You then create an anchor element and attach the blob URL to it
- You can change the name of the file that will be downloaded by adjusting
link.download
property - Finally, you trigger a click on the anchor element and later delete the URL object to clean up the browser memory
Summary
In this tutorial you learned how to set up your Next.js project for downloading ZIP files. First, you set up your project and installed adm-zip
package. You learned how to create a ZIP file and add files to it programmatically. You saw how to send it as a response from of an API endpoint. Finally, you created a client-side React component that downloads the ZIP archive when clicking on a button.