React 19 Actions

Handle asynchronous form submissions and mutations cleanly with React 19 Actions, useActionState, and useOptimistic updates.

TL;DR

  1. Execute asynchronous form submissions seamlessly using action functions.
  2. Track pending state and returned values with useActionState.
  3. Render instant visual feedback to users with useOptimistic.

Form Action Fundamentals

    Async Form Action Prop

    Pass async handler directly to form action.

    async function updateName(formData: FormData) {
      'use server';
      await db.update(formData.get('name'));
    }
    return <Form action={updateName} />;
    Action with Transition

    Trigger action imperatively with startTransition.

    const [isPending, startTransition] = useTransition();
    const handleSave = () => {
      startTransition(async () => {
        await saveCart();
      });
    };
    Button Form Action Override

    Direct specific submit button to alternative action.

    <ButtonBase formAction={deleteItem}>
      Delete Item
    </ButtonBase>

State with useActionState

    Hook Initialization

    Receive state, action trigger, and pending flag.

    const [state, formAction, isPending] = useActionState(
      async (prev, formData: FormData) => {
        return await registerUser(formData);
      },
      null
    );
    Pending Submit Button

    Disable button automatically while action runs.

    <ButtonBase type="submit" disabled={isPending}>
      {isPending ? 'Saving...' : 'Submit'}
    </ButtonBase>
    Action Error Return

    Return structured validation errors from action.

    if (!name) return { error: 'Name is required' };

Optimistic Updates with useOptimistic

    Declare Optimistic Hook

    Define temporary state applied before server response.

    const [optList, setOptList] = useOptimistic(
      messages,
      (cur, next: string) => [...cur, { text: next }]
    );
    Dispatch Optimistic Value

    Trigger optimistic update before awaiting network.

    setOptList(newText);
    await sendMessage(newText);
    Instant Visual Feedback

    Display message immediately with sending indicator.

    <CardView opacity={msg.sending ? 0.6 : 1}>
      <Text>{msg.text}</Text>
    </CardView>

Action Best Practices

    Form Reset Integration

    Reset form inputs automatically after success.

    const formRef = useRef<HTMLFormElement>(null);
    const onSubmit = async (data: FormData) => {
      await action(data);
      formRef.current?.reset();
    };
    Avoid preventDefault

    Let React 19 manage submission lifecycle natively.

    // NO NEED: e.preventDefault() is obsolete
    <Form action={myAction} />
    Progressive Enhancement

    Actions work before client JavaScript hydrates.

    // Server actions execute natively via standard POST
    <Form action={serverAction}>

Tips

  1. Pass asynchronous functions directly to the form action prop to handle submissions without manual event prevention.
  2. Use useOptimistic to update UI immediately while background server actions complete, reverting automatically if actions fail.

Warnings

  1. Do not invoke preventDefault() inside action handlers because React 19 manages form lifecycle submissions automatically.
  2. Always handle potential submission errors inside useActionState actions to prevent unhandled rejection crashes during network loss.

In Practice

FAQ