Data

Form Actions and Mutations

Process mutating requests, validate form data, redirect after success, and keep Task Tracker writes server-owned.

actionsformsredirectsmutations

Actions handle non-GET mutations. In the Task Tracker, creating or updating a task belongs here.

Minimal working example

import { redirect } from "react-bun-ssr";
import type { Action } from "react-bun-ssr/route";

export const action: Action = async ({ formData }) => {
  const title = String(formData?.get("title") ?? "").trim();
  const assignee = String(formData?.get("assignee") ?? "").trim();

  if (!title) {
    return { error: "Title is required" };
  }

  await Promise.resolve({ title, assignee });
  return redirect("/tasks");
};

export default function NewTaskPage() {
  return (
    <form method="post">
      <input name="title" placeholder="Ship docs redesign" />
      <input name="assignee" placeholder="Owner" />
      <button type="submit">Create task</button>
    </form>
  );
}

Why redirect-after-success is preferred

A redirect keeps server truth authoritative and simplifies the client transition model:

  • submit
  • mutate on the server
  • redirect to the canonical read route
  • let the destination loader produce the fresh state

Rules

  • Use actions for writes, not loaders.
  • Return plain validation payloads when you want to stay on the same page.
  • Return redirect() when the mutation should land on another route.
  • Throw typed route errors when validation must bubble to a catch boundary.

Next step

Read Error Handling to model validation and exceptional states precisely.