Skip to Content
HooksuseMutation

useMutation

useMutation runs an async action on demand and tracks mutation state, result data, errors, variables, and lifecycle callbacks.

Live Example

On-demand mutation

This demo shows a form-style mutation with submit, reset, and abort controls.

Form submission

Save profile name

idle

This demo uses a local async mutation to model a save button, status badge, validation error, and abort control.

No successful mutation yet.

Import

import { useMutation } from "react-rsc-kit/client";

Signature

const mutation = useMutation<TData, TError, TVariables>(mutationFn, options?);

Parameters

NameTypeDefaultDescription
mutationFn(variables, context) => Promise<TData>requiredAsync mutation function. context.signal can be used for cancellation.
options.initialDataTData | nullnullInitial mutation data before any successful call.
options.onMutate(variables) => TContext | Promise<TContext>undefinedRuns before the mutation and can return lifecycle context.
options.onSuccess(data, variables, context) => void | Promise<void>undefinedRuns after a successful mutation.
options.onError(error, variables, context) => void | Promise<void>undefinedRuns after a failed mutation.
options.onSettled(data, error, variables, context) => void | Promise<void>undefinedRuns after success or error.

Returns

KeyDescription
dataLast successful mutation data or initialData.
errorLast mutation error.
status"idle" | "loading" | "success" | "error".
variablesVariables from the latest mutation attempt.
submittedAtTimestamp for the latest mutation attempt.
loadingAlias for isPending.
isPendingWhether a mutation is currently running.
isIdleWhether no active or completed mutation state is pending.
isSuccessWhether the last mutation succeeded.
isErrorWhether the last mutation failed.
mutateFire-and-forget mutation trigger.
mutateAsyncPromise-returning mutation trigger.
resetRestores the initial mutation snapshot.
abortAborts the active mutation.

Usage

"use client"; import { useMutation } from "react-rsc-kit/client"; type SaveTodoResult = { id: number; title: string; }; export function SaveTodoButton() { const mutation = useMutation<SaveTodoResult, Error, string>(async (title, { signal }) => { const response = await fetch("/api/todos", { method: "POST", body: JSON.stringify({ title }), headers: { "content-type": "application/json", }, signal, }); if (!response.ok) { throw new Error("Unable to save todo."); } return (await response.json()) as SaveTodoResult; }); return ( <button type="button" onClick={() => mutation.mutate("Ship docs")}> {mutation.isPending ? "Saving..." : "Save Todo"} </button> ); }

API Example

"use client"; import { useMutation } from "react-rsc-kit/client"; type CreatePostInput = { title: string; body: string; }; type CreatePostResponse = { id: number; title: string; body: string; }; export function CreatePostForm() { const createPost = useMutation<CreatePostResponse, Error, CreatePostInput>( async (payload, { signal }) => { const response = await fetch("/api/posts", { method: "POST", headers: { "content-type": "application/json", }, body: JSON.stringify(payload), signal, }); if (!response.ok) { throw new Error("Unable to create post."); } return (await response.json()) as CreatePostResponse; }, ); return ( <form onSubmit={(event) => { event.preventDefault(); createPost.mutate({ title: "Ship useMutation docs", body: "Document API mutation flows clearly.", }); }} > <button type="submit" disabled={createPost.isPending}> {createPost.isPending ? "Creating..." : "Create Post"} </button> {createPost.isError ? <p>{createPost.error?.message}</p> : null} {createPost.isSuccess ? <p>Created post #{createPost.data?.id}</p> : null} </form> ); }

Notes

  • mutate swallows promise rejections by design. Use mutateAsync when you need the promise.
  • Pass context.signal into fetch so abort() can cancel the request cleanly.
  • Starting a new mutation aborts the previous one and ignores stale results.
  • reset restores the original mount-time initialData, not later prop changes.
  • For advanced retries, optimistic cache updates across queries, invalidation, and server-state coordination, use TanStack Query.
Last updated on