logo
post image

Handling File Uploading in NextJS

NextJS Edge Runtime has made handling file uploading quite simple using the FormData interface. We can use FormData in the frontend to send upload request and also use this in server side to process uploads. No external modules are required to handle uploads.

Note that this will work only for App router and not for the Pages router.

Route Structure

We will handle file uploading using the API route handler. For this example, assume a POST request holding the uploaded data is sent at the route /api.

app/
|-- api/
|   |-- route.js
uploads/

Server side actions can also be used to handle file uploads on the server side.

Frontend

The frontend will be the usual — a form containing a file input and a submit button. The form when submitted will send a fetch request.

'use client'

export default function FileUpload() {
  async function uploadFile(e) {
    e.preventDefault()

    const file = document.querySelector("[name='file']").files?.[0]

    // if no file
    if (!file) {
      return
    }

    const body = new FormData()
    body.append('file', file)

    const response = await fetch('/api', {
      method: 'POST',
      credentials: 'include',
      body
    })

    let error = null
    if (response.status === 200) {
      error = ''
    } else if (response.status === 400) {
      const jsonResponse = await response.json()
      error = jsonResponse.message
    } else {
      error = 'Request Failed'
    }

    if (error) {
      alert(error)
    } else {
      alert('Success')
    }
  }

  return (
    <form onSubmit={uploadFile}>
      <input type="file" name="file"/>
      <button type="submit">Upload</button>
    </form>
  )
}

Server Side

On the server side, uploaded file can be fetched and validated using the FormData object.

import { NextResponse } from 'next/server'
import path from 'path'
import { writeFile } from 'fs/promises'

export async function POST(request) {
  try {
    const formData = await request.formData()
    const file = formData.get('file')

    // validate for type
    if (!['image/jpeg', 'image/png'].includes(file.type)) {
      throw new Error('Error: Only JPEG & PNG allowed')
    }

    // validate for size
    if (file.size > 1 * 1024 * 1024) {
      throw new Error('Error: Max 1 MB size allowed')
    }

    // save file
    const buffer = Buffer.from(await file.arrayBuffer())
    await writeFile(path.join(process.cwd(), `uploads/${file.name}`), buffer)

    return NextResponse.json({ error: 0 }, { status: 200 })
  } catch (error) {
    return NextResponse.json({ error: 1, message: error.message }, { status: 400 })
  }
}