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.