method-override-middleware
The methodOverride middleware reads a hidden _method field from form data and overrides the request method accordingly. This enables HTML forms to submit PUT, PATCH, and DELETE requests, even though HTML forms only natively support GET and POST.
Installation
The method override middleware is included with Remix. No additional installation is needed.
Import
import { methodOverride } from 'remix/method-override-middleware'API
methodOverride(options?)
Returns a middleware function that reads the _method field from parsed form data and overrides the request method.
let router = createRouter({
middleware: [
formData(),
methodOverride(),
],
})formData must come first
The methodOverride middleware reads from the parsed form data in the context. The formData middleware must appear before methodOverride in the middleware chain, otherwise there is no parsed form data to read.
Options
| Option | Type | Default | Description |
|---|---|---|---|
fieldName | string | '_method' | The form field name that contains the intended HTTP method. |
methods | string[] | ['PUT', 'PATCH', 'DELETE'] | The methods that are allowed as override values. Any other value in the field is ignored. |
How It Works
- The middleware checks if the request method is
POST(the only method that can carry a form body in HTML). - It reads the value of the
_methodfield from the parsed form data. - If the value is one of the allowed methods (PUT, PATCH, or DELETE by default), the request method is changed to that value.
- The
_methodfield is removed from the form data so handlers do not see it.
This means your route handlers can use RESTful methods naturally:
// This handler responds to DELETE requests,
// including those from forms using _method="DELETE"
router.map(deletePostRoute, ({ params }) => {
await db.delete(posts, { where: eq(posts.columns.id, params.id) })
return redirect('/posts')
})Why Method Override Is Needed
HTML forms only support two methods: GET and POST. There is no way to set method="DELETE" or method="PUT" on a <form> element. Without method override, you must route all mutations through POST and distinguish operations by URL or hidden fields.
With method override, you can design routes using standard REST conventions:
| Action | Method | URL |
|---|---|---|
| List posts | GET | /posts |
| Create post | POST | /posts |
| Update post | PUT | /posts/:id |
| Delete post | DELETE | /posts/:id |
Examples
Basic Form with DELETE
<form method="post" action="/posts/42">
<input type="hidden" name="_method" value="DELETE" />
<button type="submit">Delete Post</button>
</form>The middleware changes the request method from POST to DELETE before the route handler runs.
RestfulForm Pattern
Create a reusable component pattern for RESTful forms:
function restfulForm(method: string, action: string, content: string) {
let hiddenField = method !== 'POST'
? `<input type="hidden" name="_method" value="${method}" />`
: ''
return `
<form method="post" action="${action}">
${hiddenField}
${content}
</form>
`
}
// Usage
return html`
${new SafeHtml(restfulForm('DELETE', `/posts/${post.id}`, `
<button type="submit">Delete</button>
`))}
${new SafeHtml(restfulForm('PUT', `/posts/${post.id}`, `
<input type="text" name="title" value="${post.title}" />
<button type="submit">Update</button>
`))}
`Custom Field Name
methodOverride({
fieldName: '_httpMethod',
})<form method="post" action="/posts/42">
<input type="hidden" name="_httpMethod" value="PUT" />
<!-- ... -->
</form>Middleware Order
The recommended order places formData before methodOverride:
let router = createRouter({
middleware: [
logger(),
compression(),
staticFiles('./public'),
csrf(),
formData(),
methodOverride(),
session(sessionCookie, sessionStorage),
],
})Related
- Middleware --- How middleware works in Remix.
- form-data-middleware --- Parsing form data that method override reads.
- Forms & Mutations --- Working with forms in Remix.