useServerFn

Wraps a server function for mutation-style usage in client components. Tracks loading, error, and result state automatically, and provides a function to execute the server call.

Import

import { useServerFn } from "catmint/hooks";

Signature

function useServerFn<TInput, TOutput>(
  fn: (input: TInput) => Promise<TOutput>,
): ServerFnState<TInput, TOutput>;
interface ServerFnState<TInput, TOutput> {
  execute: (input: TInput) => Promise<TOutput>;
  data: TOutput | undefined;
  error: Error | undefined;
  loading: boolean;
  reset: () => void;
}

Parameters

ParameterTypeRequiredDescription
fn(input: TInput) => Promise<TOutput>YesA server function created with createServerFn.

Return Value

PropertyTypeDescription
execute(input: TInput) => Promise<TOutput>Call this to invoke the server function. Returns the result and updates data. Throws on error.
dataTOutput | undefinedThe most recent successful result, or undefined if not yet called.
errorError | undefinedThe most recent error, or undefined if no error occurred. Cleared on the next execute call.
loadingbooleantrue while the server function is in flight.
reset() => voidResets data, error, and loading to their initial values.

Examples

Basic mutation

// ProfileForm.client.tsx
import { useServerFn } from "catmint/hooks";
import { updateProfile } from "./actions";

function ProfileForm({ profile }) {
  const { execute, loading, error } = useServerFn(updateProfile);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget as HTMLFormElement);
    await execute({ name: formData.get("name") as string });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" defaultValue={profile.name} />
      <button disabled={loading}>{loading ? "Saving..." : "Save"}</button>
      {error && <p className="error">{error.message}</p>}
    </form>
  );
}

Using the result

const { execute, data } = useServerFn(createItem);

const handleCreate = async () => {
  const item = await execute({ title: "New Item" });
  console.log("Created:", item.id);
};

Resetting state

const { execute, data, reset } = useServerFn(deleteItem);

// After a successful deletion, reset to allow another
const handleDelete = async (id: string) => {
  await execute({ id });
  setTimeout(() => reset(), 3000); // Clear success state after 3s
};

See Also