When working with Remix, handling data submissions and mutations can be done using either <Form> or the useFetcher() hook, both of which are powerful tools. While both mechanisms serve to handle form submissions and data mutations, they are designed for different use cases.
In this article, we will see the differences between them, their use cases, and how to use them effectively in your Remix applications.
Remix Form
In Remix, Form is used for dealing with traditional form submissions. It simplifies the system of sending facts to the server, dealing with redirections, and handling validations. The Form component is commonly used when you want to send shape records as a POST request, however it may also be used for GET requests.
Key Features of <Form>
- Automatic Postback: When a user submits a form, Remix handles the request using a POST method to trigger a specific action in the loader or action function.
- Declarative Approach: Remix automatically manages form submission and response data without requiring manual data fetching.
- Integration with Actions and Loaders: Forms automatically map to corresponding action functions for data mutations, making CRUD operations simple.
- Progressive Enhancement: Even if JavaScript is disabled, the <Form> component will gracefully degrade to standard HTML form behaviour.
Standard Form Submission (POST request):
This is the most common use case for Form, where facts are submitted to the server via a POST request. Remix handles the shape submission, server-facet action, and redirection automatically.
Syntax:
import { Form } from "@remix-run/react";
function MyForm() {
return (
<Form method= "post" >
<input type="text" name = "username" />
<button type="submit" > Submit </button>
</Form>
);
}
Form Submission with GET request:
You can also use Form for GET requests to send information as query parameters. This is useful for search paperwork or filtering outcomes.
Syntax:
import { Form } from "@remix-run/react";
function SearchForm() {
return (
<Form method= "get" action = "/search" >
<input type="text" name = "query" />
<button type="submit" > Search </button>
</Form>
);
}
Form Submission with different HTTP strategies:
Remix permits you to specify the approach inside the Form thing to use PUT, DELETE, or other HTTP techniques. This is beneficial for operations like updating or deleting assets.
Syntax:
import { Form } from "@remix-run/react";
function DeleteItemForm({ itemId }) {
return (
<Form method= "delete" action = {`/items/${itemId}`}>
<button type="submit" > Delete </button>
</Form>
);
}
Example: Implementing a basic Form
// app/routes/index.tsx
import { Link } from "@remix-run/react";
export default function Index() {
return (
<div>
<h1>Welcome to Remix </h1>
< Link to = "/contact" > Contact Us </>
</div>
);
}
// app/routes/contact.jsx
import { Form, useActionData } from '@remix-run/react';
import { json } from '@remix-run/node';
export let action = async ({ request }) => {
let formData = await request.formData();
let name = formData.get('name');
let email = formData.get('email');
let message = formData.get('message');
return json({ name, email, message });
};
export default function ContactForm() {
let actionData = useActionData();
return (
<div>
<h1>Contact Us </h1>
< Form method="post" >
<label>
Name: <input type="text" name="name" />
</label>
< br />
<label>
Email: <input type="email" name="email" />
</label>
< br />
<label>
Message: <textarea name="message" > </textarea>
</label>
< br />
<button type="submit" > Submit </button>
</>
{
actionData && (
<div>
<h2>Form Data </h2>
< p > Name: {actionData.name} </p>
< p > Email: {actionData.email} </p>
< p > Message: {actionData.message} </p>
</div>
)
}
</div>
);
}
Output:

Fetcher in Remix
The useFetcher() hook is another powerful tool in Remix, designed to manage non-standard form submissions or any situation where you need to handle data fetching or mutation manually. Unlike <Form>, useFetcher() offers more control over data interactions without the limitations of full-page form submissions.
Key Features of useFetcher
- Non-Form Interactions: Ideal for AJAX-like interactions where you need to submit or fetch data dynamically, without affecting the current page or route.
- Data Mutation Flexibility: useFetcher() allows you to fetch or mutate data for any route, regardless of where the component lives in the component tree.
- No Default Behavior: Unlike <Form>, the useFetcher() hook does not trigger full-page navigation. This makes it perfect for background data fetching, live updates, or partial page updates.
- Trigger Loaders and Actions Dynamically: Fetcher allows you to trigger loaders or actions for any route without navigating to it, making it highly versatile for advanced use cases.
Fetcher Form Submission:
Similar to a regular form submission but with out triggering a full web page reload. It allows for a more dynamic person experience.
Syntax:
import { useFetcher } from "@remix-run/react";
function MyForm() {
const fetcher = useFetcher();
return (
<fetcher.Form method= "post" action = "/submit" >
<input type="text" name = "username" />
<button type="submit" > Submit </button>
</fetcher.Form>
);
}
Fetcher for Data Fetching:
Use fetcher.Load to fetch data from the server with out triggering navigation. This is useful for loading additional information at the page dynamically.
Syntax:
import { useFetcher } from "@remix-run/react";
import { useEffect } from "react";
function LoadData() {
const fetcher = useFetcher();
useEffect(() => {
fetcher.load("/api/data");
}, []);
return (
<div>
{
fetcher.data ? (
<div>{ fetcher.data.message } </div>
) : (
<div>Loading...</div>
)
}
</div>
);
}
Fetcher for Submitting Non-Form Data:
Use fetcher.Publish to ship facts to the server programmatically, with out the usage of a form detail. This may be used for sending JSON or different non-shape data.
Syntax:
import { useFetcher } from "@remix-run/react";
function SubmitJson() {
const fetcher = useFetcher();
const handleSubmit = () => {
const data = { username: "john_doe" };
fetcher.submit(data, { method: "post", action: "/submit-json" });
};
return (
<div>
<button onClick= { handleSubmit } > Submit JSON </button>
{ fetcher.data && <div>{ fetcher.data.responseMessage } </div> }
</div>
);
}
Difference Between Form and Fetcher
| Feature | Form | Fetcher |
|---|---|---|
| Behavior | Standard form submission | AJAX-like behavior for non-form interactions |
| Navigation | Triggers full-page navigation | No page navigation by default |
| Use Case | General form submissions | Dynamic updates, background fetching, partial UI updates |
| Progressive Enhancement | Yes | No, requires JavaScript |
| Manual Control | Minimal control (handled by Remix) | Full control over data submission and response handling |
When to Use Form vs. Fetcher
Use <Form> when:
- You need standard form submissions.
- You want progressive enhancement (i.e., the form should still work without JavaScript).
- You don't need to control form submission manually or handle background updates.
Use useFetcher() when:
- You need more control over form submission and response handling.
- You want to handle partial page updates without full-page navigation.
- You need to interact with other routes or loaders dynamically, like updating data or fetching content dynamically.
Conclusion
Both <Form> and useFetcher() are essential tools in the Remix framework, each serving different purposes based on the application's needs. <Form> is ideal for traditional form submissions, offering built-in server-side handling and progressive enhancement. On the other hand, useFetcher() provides greater flexibility for managing background data mutations and dynamic interactions without page reloads.