Skip to content

How to Download ZIP Files in Next.js

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.