useMutation
Bridges TanStack Query mutations with Legend-State. Returns mutation state as an Observable, so every status field (isPending, isSuccess, isError, etc.) is reactive. Options follow TanStack Query’s standard UseMutationOptions — refer to TanStack Query docs for full option details.
Both variants require a QueryClientProvider ancestor — the client is injected from context.
useMutation
Idle
Submit a todo — observe mutation state transitions.
Basic mutation
Section titled “Basic mutation”import { function Show<T>(props: Props<T>): ReactElement (+2 overloads)
Show } from "@usels/core";import { const useMutation: <TData = unknown, TVariables = void, TContext = unknown>(options?: DeepMaybeObservable<CreateMutationOptions<TData, TVariables, TContext>>) => Observable<MutationState<TData, TVariables, TContext>>
useMutation } from "@usels/tanstack-query";
function function CreateUser(): JSX.Element
CreateUser() { const const mutation: any
mutation = useMutation<unknown, void, unknown>(options?: DeepMaybeObservable<CreateMutationOptions<unknown, void, unknown>>): Observable<MutationState<unknown, void, unknown>>
Core observable function for bridging TanStack Query mutations with Legend-State.
Must be called inside a scope wrapped by a QueryClientProvider —
getQueryClient() injects the client from context. Teardown is registered
via onUnmount; option changes propagate via observe.
useMutation({ mutationFn: (user: { name: string; email: string;}) => any
mutationFn: (user: { name: string; email: string;}
user: { name: string
name: string; email: string
email: string }) => any
fetch("/api/users", { method: string
method: "POST", body: any
body: any
JSON.any
stringify(user: { name: string; email: string;}
user), }).any
then((r: any
r) => r: any
r.any
json()), });
return ( <JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div> <JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button DOMAttributes<HTMLButtonElement>.onClick?: MouseEventHandler<HTMLButtonElement> | undefined
onClick={() => const mutation: any
mutation.any
mutate.any
get()({ name: string
name: "John", email: string
email: "john@test.com" })} ButtonHTMLAttributes<HTMLButtonElement>.disabled?: boolean | undefined
disabled={const mutation: any
mutation.any
isPending.any
get()} > <function Show<T>(props: Props<T>): ReactElement (+2 overloads)
Show if: any
if={const mutation: any
mutation.any
isPending} else?: any
else="Create User"> Creating... </function Show<T>(props: Props<T>): ReactElement (+2 overloads)
Show> </JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button> <function Show<T>(props: Props<T>): ReactElement (+2 overloads)
Show if: any
if={const mutation: any
mutation.any
isError}> <JSX.IntrinsicElements.p: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p>Error: {const mutation: any
mutation.any
error.any
get()?.any
message}</JSX.IntrinsicElements.p: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p> </function Show<T>(props: Props<T>): ReactElement (+2 overloads)
Show> <function Show<T>(props: Props<T>): ReactElement (+2 overloads)
Show if: any
if={const mutation: any
mutation.any
isSuccess}> <JSX.IntrinsicElements.p: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p>User created!</JSX.IntrinsicElements.p: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p> </function Show<T>(props: Props<T>): ReactElement (+2 overloads)
Show> </JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div> );}import { Show } from "@usels/core";import { createMutation } from "@usels/tanstack-query";
function CreateUser() { "use scope"; const state$ = createMutation({ mutationFn: (user: { name: string; email: string }) => fetch("/api/users", { method: "POST", body: JSON.stringify(user), }).then((r) => r.json()), });
return ( <div> <button onClick={() => state$.mutate.get()({ name: "John", email: "john@test.com" })} disabled={state$.isPending.get()} > <Show if={state$.isPending} else="Create User"> Creating... </Show> </button> <Show if={state$.isError}> <p>Error: {state$.error.get()?.message}</p> </Show> <Show if={state$.isSuccess}> <p>User created!</p> </Show> </div> );}With callbacks
Section titled “With callbacks”import { useMutation, useQueryClient } from "@usels/tanstack-query";
function AddTodo() { const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: (text: string) => fetch("/api/todos", { method: "POST", body: JSON.stringify({ text }), }).then((r) => r.json()), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["todos"] }); }, }); return <button onClick={() => mutation.mutate.get()("New todo")}>Add Todo</button>;}import { createMutation, getQueryClient } from "@usels/tanstack-query";
function AddTodo() { "use scope"; const queryClient = getQueryClient(); const state$ = createMutation({ mutationFn: (text: string) => fetch("/api/todos", { method: "POST", body: JSON.stringify({ text }), }).then((r) => r.json()), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["todos"] }); }, }); return <button onClick={() => state$.mutate.get()("New todo")}>Add Todo</button>;}Async mutation
Section titled “Async mutation”import { useMutation } from "@usels/tanstack-query";
function SubmitForm() { const mutation = useMutation({ mutationFn: (data: FormData) => fetch("/api/submit", { method: "POST", body: data }).then((r) => r.json()), });
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); const result = await mutation.mutateAsync.get()(new FormData(e.currentTarget)); console.log("Submitted:", result); };
return <form onSubmit={handleSubmit}>...</form>;}import { createMutation } from "@usels/tanstack-query";
function SubmitForm() { "use scope"; const state$ = createMutation({ mutationFn: (data: FormData) => fetch("/api/submit", { method: "POST", body: data }).then((r) => r.json()), });
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); const result = await state$.mutateAsync.get()(new FormData(e.currentTarget)); console.log("Submitted:", result); };
return <form onSubmit={handleSubmit}>...</form>;}export type { CreateMutationOptions, MutationState } from "./core";export { createMutation } from "./core";/** * UseMutationOptions is an alias for CreateMutationOptions for backward compatibility. */export type UseMutationOptions<TData = unknown, TVariables = void, TContext = unknown> = CreateMutationOptions<TData, TVariables, TContext>;/** * Connects TanStack Query Mutation with Legend-State observables. * Uses MutationObserver to manage mutation state as observables. */export type UseMutation = typeof createMutation;export declare const useMutation: UseMutation;