Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Actions and forms

An action is a function a route module exports under the name action. It runs on the server when a form is submitted to the route's address with POST (or PUT, PATCH, DELETE). Such a form is submitted with the Form component — an ordinary form tag to which the framework adds submission without a page reload.

The save function

Add a function for creating a post to app/data.server.ts:

// app/data.server.ts
export async function createPost(title: string, body: string) {
  const post: Post = { id: posts.length + 1, title, body };
  posts.push(post);
  return post;
}

A page with a form

Add the route route("posts/new", "routes/new-post.tsx") and create the file:

// app/routes/new-post.tsx
import { Form, redirect } from "react-router";
import type { Route } from "./+types/new-post";
import { createPost } from "../data.server";
 
export async function action({ request }: Route.ActionArgs) {
  const formData = await request.formData();
  const title = String(formData.get("title"));
  const body = String(formData.get("body"));
 
  const post = await createPost(title, body);
  return redirect(`/posts/${post.id}`);
}
 
export default function NewPost() {
  return (
    <Form method="post">
      <input name="title" placeholder="Title" />
      <textarea name="body" placeholder="Post text" />
      <button type="submit">Publish</button>
    </Form>
  );
}

Open http://localhost:5173/posts/new, fill in the fields and click "Publish". The form goes to the same address, action runs, and redirect opens the new post's page. Back on the home page you will see the post in the list.

How it works

  1. Form intercepts the submission and sends the data to the server with a POST request.
  2. On the server action runs, and request.formData() returns the form fields by their name attributes.
  3. After the action the framework calls the page loaders again — the data on screen is always fresh.
  4. redirect from react-router responds with a move to another address.

Without JavaScript the form still works: the browser submits it the ordinary way, and the server responds with a redirect.

Linking to the form

On the home page add a link to the new page:

// app/routes/home.tsx
<Link to="/posts/new">Write a post</Link>

Submitting to another address

The action attribute of Form says which route handles the submission. For example, a delete button can live on the post page:

// app/routes/post.tsx
<Form method="post" action={`/posts/${post.id}/delete`}>
  <button type="submit">Delete</button>
</Form>

You need a posts/:postId/delete route with an action function; it does not need a component.

Common mistake

A field without a name attribute does not end up in formData: formData.get("title") returns null, and String(null) turns into the string "null" — which then becomes the post title. Make sure every field has a name that matches the key the action reads.

Thanks for reading!

Donate to the site — we work hard for you.