kitcn

Mutations

Execute mutations with TanStack Query.

import { useMutation } from '@tanstack/react-query';
import { useCRPC } from '@/lib/convex/crpc';

function CreateUser() {
  const crpc = useCRPC();
  const createUser = useMutation(crpc.user.create.mutationOptions());

  return (
    <button
      disabled={createUser.isPending}
      onClick={() => createUser.mutate({ name: 'John', email: 'john@example.com' })}
    >
      {createUser.isPending ? 'Creating...' : 'Create User'}
    </button>
  );
}

mutationOptions

The mutationOptions method creates options for TanStack Query's useMutation hook.

const crpc = useCRPC();

// Basic usage
const createUser = useMutation(crpc.user.create.mutationOptions());

// With callbacks
const updateUser = useMutation(
  crpc.user.update.mutationOptions({
    onSuccess: (data) => {
      toast.success('Updated successfully');
    },
    onError: (error) => {
      toast.error(error.data?.message ?? 'Update failed');
    },
  })
);

See API Reference for the full signature and options.

Mutation Keys

Get type-safe mutation keys for cache operations:

const crpc = useCRPC();

// Get mutation key
const mutationKey = crpc.user.create.mutationKey();
// => ['convexMutation', 'user:create']

Common Patterns

Toast Promise

Use toast.promise for loading/success/error states:

const createProject = useMutation(crpc.project.create.mutationOptions());

const onSubmit = (data: FormData) => {
  toast.promise(createProject.mutateAsync({ title: data.title }), {
    loading: 'Creating project...',
    success: 'Project created!',
    error: (e) => e.data?.message ?? 'Failed to create project',
  });
};

Form Submission

Handle form submission with cleanup:

const updateUser = useMutation(
  crpc.user.update.mutationOptions({
    onSuccess: () => {
      form.reset();
      closeModal();
      toast.success('Profile updated');
    },
    onError: () => {
      toast.error('Update failed');
    },
  })
);

const onSubmit = (data: FormData) => {
  updateUser.mutate(data);
};

Inline Callbacks

Pass callbacks directly to mutate:

const deleteSession = useMutation(crpc.session.delete.mutationOptions());

deleteSession.mutate(
  { id: sessionId },
  {
    onSuccess: () => router.push('/sessions'),
    onError: () => toast.error('Delete failed'),
  }
);

Actions as Mutations

Convex actions can be used as mutations for external API calls:

// Actions work with mutationOptions
const scrapeLink = useMutation(crpc.scraper.scrapeLink.mutationOptions());

useEffect(() => {
  if (url) {
    scrapeLink.mutate({ url });
  }
}, [url]);

// Access mutation state
if (scrapeLink.isPending) return <Spinner />;
if (scrapeLink.data) return <LinkPreview data={scrapeLink.data} />;

Note: Actions don't have real-time subscriptions. Use mutationOptions for one-shot calls to external APIs, or queryOptions if you need query caching.

Next Steps

API Reference

mutationOptions

crpc.path.to.mutation.mutationOptions(
  options?   // TanStack Query mutation options (except mutationFn)
)

All standard TanStack Query mutation options are supported except mutationFn (reserved):

OptionTypeDescription
onSuccess(data, variables, context) => voidCalled on successful mutation
onError(error, variables, context) => voidCalled on error
onMutate(variables) => contextCalled before mutation (for optimistic updates)
onSettled(data, error, variables, context) => voidCalled on completion
retrynumber | booleanRetry failed mutations

On this page