Skip to main content
Perform mutations and side effects with the createMutation primitive. It provides methods to trigger mutations and tracks their state reactively in SolidJS.

Signature

function createMutation<TData, TError, TVariables, TContext>(
  options: Accessor<CreateMutationOptions<TData, TError, TVariables, TContext>>,
  queryClient?: Accessor<QueryClient>,
): CreateMutationResult<TData, TError, TVariables, TContext>

Parameters

options
Accessor<CreateMutationOptions<TData, TError, TVariables, TContext>>
required
A Solid accessor (function) returning mutation configuration options.
queryClient
Accessor<QueryClient>
Accessor returning a custom QueryClient instance. If not provided, uses the client from context.

Returns

CreateMutationResult<TData, TError, TVariables, TContext>
object
Reactive mutation state and methods.

Type Parameters

  • TData - Type of data returned by the mutation
  • TError - Type of error (defaults to DefaultError)
  • TVariables - Type of variables passed to the mutation (defaults to void)
  • TContext - Type of context returned by onMutate (defaults to unknown)

Examples

Basic Usage

import { createMutation, useQueryClient } from '@tanstack/solid-query'
import { Show } from 'solid-js'

function CreateTodo() {
  const queryClient = useQueryClient()

  const mutation = createMutation(() => ({
    mutationFn: async (newTodo) => {
      const res = await fetch('/api/todos', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(newTodo),
      })
      return res.json()
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['todos'] })
    },
  }))

  const handleCreate = () => {
    mutation.mutate({ title: 'New Todo', completed: false })
  }

  return (
    <div>
      <button onClick={handleCreate} disabled={mutation.isPending}>
        {mutation.isPending ? 'Adding...' : 'Add Todo'}
      </button>
      <Show when={mutation.isError}>
        <div>Error: {mutation.error?.message}</div>
      </Show>
    </div>
  )
}

With TypeScript

import { createMutation } from '@tanstack/solid-query'

interface Todo {
  id: number
  title: string
  completed: boolean
}

interface CreateTodoVariables {
  title: string
  completed: boolean
}

function CreateTodo() {
  const mutation = createMutation(() => ({
    mutationFn: async (variables: CreateTodoVariables): Promise<Todo> => {
      const res = await fetch('/api/todos', {
        method: 'POST',
        body: JSON.stringify(variables),
      })
      return res.json()
    },
  }))

  // Type-safe mutation call
  const handleCreate = () => {
    mutation.mutate({ title: 'Learn Solid Query', completed: false })
  }

  return <button onClick={handleCreate}>Create</button>
}

Optimistic Updates

import { createMutation, useQueryClient } from '@tanstack/solid-query'

function UpdateTodo() {
  const queryClient = useQueryClient()

  const mutation = createMutation(() => ({
    mutationFn: updateTodo,
    onMutate: async (updatedTodo) => {
      // Cancel outgoing refetches
      await queryClient.cancelQueries({ queryKey: ['todos'] })
      
      // Snapshot previous value
      const previousTodos = queryClient.getQueryData(['todos'])
      
      // Optimistically update cache
      queryClient.setQueryData(['todos'], (old) => {
        return old.map(todo => 
          todo.id === updatedTodo.id ? updatedTodo : todo
        )
      })
      
      // Return context with snapshot
      return { previousTodos }
    },
    onError: (err, updatedTodo, context) => {
      // Rollback on error
      if (context?.previousTodos) {
        queryClient.setQueryData(['todos'], context.previousTodos)
      }
    },
    onSettled: () => {
      // Always refetch after error or success
      queryClient.invalidateQueries({ queryKey: ['todos'] })
    },
  }))

  return <div>{/* ... */}</div>
}

Using mutateAsync

import { createMutation } from '@tanstack/solid-query'

function CreateTodo() {
  const mutation = createMutation(() => ({
    mutationFn: createTodo,
  }))

  const handleSubmit = async () => {
    try {
      const newTodo = await mutation.mutateAsync({ title: 'New Todo' })
      console.log('Created todo:', newTodo)
      // Navigate or show success message
    } catch (error) {
      console.error('Failed to create todo:', error)
    }
  }

  return <button onClick={handleSubmit}>Submit</button>
}

Per-Mutation Callbacks

import { createMutation } from '@tanstack/solid-query'

function CreateTodo() {
  const mutation = createMutation(() => ({
    mutationFn: createTodo,
  }))

  const handleCreate = () => {
    mutation.mutate(
      { title: 'New Todo' },
      {
        onSuccess: (data) => {
          console.log('Created:', data)
        },
        onError: (error) => {
          console.error('Failed:', error)
        },
      }
    )
  }

  return <button onClick={handleCreate}>Create</button>
}

Reset Mutation State

import { createMutation } from '@tanstack/solid-query'
import { Show } from 'solid-js'

function CreateTodo() {
  const mutation = createMutation(() => ({
    mutationFn: createTodo,
  }))

  const handleCreate = () => {
    mutation.reset() // Reset previous state
    mutation.mutate({ title: 'New Todo' })
  }

  return (
    <div>
      <button onClick={handleCreate}>Create</button>
      <Show when={mutation.isSuccess}>
        <div>Successfully created!</div>
      </Show>
    </div>
  )
}

Multiple Mutations

import { createMutation, useQueryClient } from '@tanstack/solid-query'

function TodoActions() {
  const queryClient = useQueryClient()

  const createMutation = createMutation(() => ({
    mutationFn: createTodo,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['todos'] })
    },
  }))

  const deleteMutation = createMutation(() => ({
    mutationFn: deleteTodo,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['todos'] })
    },
  }))

  return (
    <div>
      <button 
        onClick={() => createMutation.mutate({ title: 'New' })}
        disabled={createMutation.isPending}
      >
        Create
      </button>
      <button 
        onClick={() => deleteMutation.mutate(1)}
        disabled={deleteMutation.isPending}
      >
        Delete
      </button>
    </div>
  )
}

Error Handling with Retry

import { createMutation } from '@tanstack/solid-query'

function CreateTodo() {
  const mutation = createMutation(() => ({
    mutationFn: createTodo,
    retry: 3,
    retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
    onError: (error) => {
      console.error('Mutation failed after retries:', error)
    },
  }))

  return <div>{/* ... */}</div>
}

Reactive Mutation Options

import { createSignal, createMemo } from 'solid-js'
import { createMutation } from '@tanstack/solid-query'

function CreateTodo() {
  const [shouldRetry, setShouldRetry] = createSignal(true)

  const mutationOptions = createMemo(() => ({
    mutationFn: createTodo,
    retry: shouldRetry() ? 3 : 0,
  }))

  const mutation = createMutation(mutationOptions)

  return <div>{/* ... */}</div>
}

Mutation with Loading State

import { createMutation } from '@tanstack/solid-query'
import { Show, Switch, Match } from 'solid-js'

function CreateTodo() {
  const mutation = createMutation(() => ({
    mutationFn: createTodo,
  }))

  return (
    <div>
      <button onClick={() => mutation.mutate({ title: 'New' })}>
        Create Todo
      </button>
      
      <Switch>
        <Match when={mutation.isPending}>
          <div>Creating...</div>
        </Match>
        <Match when={mutation.isError}>
          <div>Error: {mutation.error?.message}</div>
        </Match>
        <Match when={mutation.isSuccess}>
          <div>Todo created successfully!</div>
        </Match>
      </Switch>
    </div>
  )
}

Notes

The options parameter must be an accessor (function) to integrate with SolidJS reactivity system.
Use mutate for fire-and-forget operations and mutateAsync when you need to await the result or handle errors with try/catch.
Always invalidate or update relevant queries after a successful mutation to keep your UI in sync with the server state.

Build docs developers (and LLMs) love