Skip to content

Uploading Files in Next.js

Next.js/

To upload files in Next.js, you need to set up an API route that can handle incoming requests and read files from them. You also need a client side component that has an input that allows the user to upload a file. Now let’s get those things set up.

Uploading a File to a Server

Start by setting up a basic API route that can handle incoming requests.

Create a file app/api/file/route.ts if you are using App Router in your project. For Page Router, you will have to do some adjustments to the code and create a file in pages/api/file.ts instead.

If you are sending a single file and your server can handle it, you can send it as raw data. If you want to include other data in your request or have multiple files, skip ahead and see how to use multipart/form-data instead.

components/FileUpload.tsx
'use client'

import React from 'react';

export default function FileUpload() {
  async function handleFileUpload(event: React.ChangeEvent<HTMLInputElement>) {
    if (!event.target.files || event.target.files.length === 0) {
      return; // User canceled file selection
    }

    const file = event.target.files[0];

    await fetch('/api/file', {
      method: 'POST',
      body: file,
      headers: {
        'Content-Type': file.type,
      },
    });
  }

  return (
    <input
      type="file"
      onChange={handleFileUpload}
    />
  );
}

You can set the Content-Type header to communicate to the server what kind of file the data represents.

From there you can interpret the received file data from the request and do what you need to do with it. In Next.js you can convert the received data from request into a Blob by calling blob method on request.

api/file/route.ts
export async function POST(request: Request): Promise<Response> {
  const blob = await request.blob();

  console.log(blob);

  return new Response();
}

To upload multiple files you can use multipart/form-data instead.

First, set the input to allow multiple file selection.

components/FileUpload.tsx
<input
  type="file"
  onChange={handleFileUpload}
  multiple={true}
/>

Next, create a FormData object and append each file individually to it. To easily loop over the files, you can convert files from a FileList to an array using Array.from. This approach also allows you to add other data to the request that you send to the server — otherData in this case.

components/FileUpload.tsx
async function handleFileUpload(event: React.ChangeEvent<HTMLInputElement>) {
  if (!event.target.files || event.target.files.length === 0) {
    return; // User canceled file selection
  }

  const files = event.target.files;
  const formData = new FormData();

  for (const file of Array.from(files)) {
    formData.append('files', file);
  }

  formData.append('otherData', 'some data');

  await fetch('/api/file', {
    method: 'POST',
    body: formData,
  });
}

On the server, you can access all of the files by reading the form data from request with the formData method and then calling getAll method. Pass it the same key name you used on the client when appending files to form data.

api/file/route.ts
export async function POST(request: Request): Promise<Response> {
  const formData = await request.formData();

  console.log(formData.getAll('files'));

  return new Response();
}