Skip to content

form-data-middleware

The formData middleware parses multipart/form-data and application/x-www-form-urlencoded request bodies. It makes the parsed FormData available to handlers and downstream middleware through the context.

Installation

The form data middleware is included with Remix. No additional installation is needed.

Import

ts
import { formData, FormData } from 'remix/form-data-middleware'

API

formData(options?)

Returns a middleware function that parses form data from the request body.

ts
let router = createRouter({
  middleware: [
    formData(),
  ],
})

FormData (Context Key)

A context key used to access the parsed form data in handlers:

ts
import { FormData } from 'remix/form-data-middleware'

router.map(route, ({ context }) => {
  let data = context.get(FormData)
  let title = data.get('title')
})

Options

OptionTypeDefaultDescription
uploadHandlerUploadHandlerMemory handlerA function that controls how uploaded files are stored. See Upload Handlers below.
maxFileSizenumber10 * 1024 * 1024 (10 MB)Maximum size in bytes for a single uploaded file.
maxFilesnumber10Maximum number of file uploads per request.
maxPartsnumber100Maximum total number of form fields (including files) per request.

Accessing Parsed Form Data

After the middleware runs, use the FormData context key to access the parsed data:

ts
import { FormData } from 'remix/form-data-middleware'

router.map(createPostRoute, async ({ context }) => {
  let data = context.get(FormData)

  let title = data.get('title')       // string | null
  let tags = data.getAll('tags')       // string[]
  let avatar = data.get('avatar')      // File | string | null

  // ...
})

The returned object is a standard FormData instance. Text fields are strings, and file fields are File objects.

Upload Handlers

An upload handler is a function that receives file upload information and decides how to store it. The default handler stores files in memory.

Memory Handler (Default)

Files are stored in memory as File objects. Suitable for small files and development:

ts
formData()

Custom Upload Handler

Write a handler to store files on disk, S3, or any other storage:

ts
import type { UploadHandler } from 'remix/form-data-middleware'

let diskUploadHandler: UploadHandler = async ({ filename, contentType, stream }) => {
  let path = `./uploads/${crypto.randomUUID()}-${filename}`
  let file = await Bun.write(path, stream)
  return new File([await Bun.file(path).bytes()], filename, { type: contentType })
}

formData({
  uploadHandler: diskUploadHandler,
})

The upload handler receives:

PropertyTypeDescription
namestringThe form field name
filenamestringThe original filename
contentTypestringThe MIME type of the file
streamReadableStreamA readable stream of the file contents

Examples

Basic Form Handling

ts
import { createRouter } from 'remix/fetch-router'
import { formData, FormData } from 'remix/form-data-middleware'

let router = createRouter({
  middleware: [
    formData(),
  ],
})

router.map(contactRoute, async ({ context }) => {
  let data = context.get(FormData)
  let name = data.get('name')
  let email = data.get('email')
  let message = data.get('message')

  await sendContactEmail({ name, email, message })
  return redirect('/contact/thanks')
})

File Upload with Size Limits

ts
formData({
  maxFileSize: 5 * 1024 * 1024,  // 5 MB per file
  maxFiles: 3,                     // Up to 3 files per request
})

If a file exceeds the limit, the middleware returns a 413 Payload Too Large response.

File Upload to Storage

ts
import { formData, FormData } from 'remix/form-data-middleware'

let router = createRouter({
  middleware: [
    formData({
      uploadHandler: s3UploadHandler,
      maxFileSize: 50 * 1024 * 1024,  // 50 MB
    }),
  ],
})

router.map(uploadRoute, async ({ context }) => {
  let data = context.get(FormData)
  let file = data.get('document')  // File object from upload handler

  return html`<p>Uploaded: ${file.name} (${file.size} bytes)</p>`
})

With Method Override

When using methodOverride, the formData middleware must come first:

ts
let router = createRouter({
  middleware: [
    formData(),
    methodOverride(),
  ],
})

Released under the MIT License.