Skip to main content
The useSetAtom hook returns a setter function for a writable atom without subscribing to value changes. Use this hook when you only need to update the atom value and don’t need to read it.

Signature

function useSetAtom<Value, Args extends unknown[], Result>(
  atom: WritableAtom<Value, Args, Result>,
  options?: Options,
): SetAtom<Args, Result>

Type Definitions

type SetAtom<Args extends unknown[], Result> = (...args: Args) => Result

type Options = {
  store?: Store
}

Parameters

atom
WritableAtom<Value, Args, Result>
required
The writable atom to update. Must be a primitive atom or a writable derived atom.
options
Options
Optional configuration object.
options.store
Store
Custom store to use instead of the default Provider store.

Returns

setAtom
SetAtom<Args, Result>
A setter function to update the atom’s value. The function signature depends on the atom’s write function:
  • For primitive atoms: (value: Value | ((prev: Value) => Value)) => void
  • For writable derived atoms: (...args: Args) => Result
The setter function has a stable identity and won’t change across re-renders.

Examples

Basic Usage with Primitive Atom

import { atom, useSetAtom, useAtomValue } from 'jotai'

const countAtom = atom(0)

function Controls() {
  const setCount = useSetAtom(countAtom)

  return (
    <div>
      <button onClick={() => setCount(0)}>Reset</button>
      <button onClick={() => setCount((prev) => prev + 1)}>Increment</button>
    </div>
  )
}

function Display() {
  const count = useAtomValue(countAtom)
  return <div>Count: {count}</div>
}

With Writable Derived Atom

import { atom, useSetAtom } from 'jotai'

const countAtom = atom(0)

const incrementAtom = atom(
  null,
  (get, set, amount: number) => {
    set(countAtom, get(countAtom) + amount)
  }
)

function Controls() {
  const increment = useSetAtom(incrementAtom)

  return (
    <div>
      <button onClick={() => increment(1)}>+1</button>
      <button onClick={() => increment(5)}>+5</button>
      <button onClick={() => increment(10)}>+10</button>
    </div>
  )
}

Multiple Actions

import { atom, useSetAtom } from 'jotai'

type Todo = { id: number; text: string; done: boolean }

const todosAtom = atom<Todo[]>([])

const addTodoAtom = atom(
  null,
  (get, set, text: string) => {
    const newTodo = {
      id: Date.now(),
      text,
      done: false,
    }
    set(todosAtom, [...get(todosAtom), newTodo])
  }
)

const removeTodoAtom = atom(
  null,
  (get, set, id: number) => {
    set(todosAtom, get(todosAtom).filter(todo => todo.id !== id))
  }
)

const toggleTodoAtom = atom(
  null,
  (get, set, id: number) => {
    set(todosAtom, get(todosAtom).map(todo =>
      todo.id === id ? { ...todo, done: !todo.done } : todo
    ))
  }
)

function TodoControls() {
  const addTodo = useSetAtom(addTodoAtom)
  const removeTodo = useSetAtom(removeTodoAtom)
  const toggleTodo = useSetAtom(toggleTodoAtom)

  return (
    <div>
      <button onClick={() => addTodo('New task')}>Add Todo</button>
      {/* Use removeTodo and toggleTodo in todo items */}
    </div>
  )
}

With Custom Store

import { atom, createStore, useSetAtom } from 'jotai'

const countAtom = atom(0)
const myStore = createStore()

function Controls() {
  const setCount = useSetAtom(countAtom, { store: myStore })

  return (
    <button onClick={() => setCount((prev) => prev + 1)}>
      Increment
    </button>
  )
}

Async Write Action

import { atom, useSetAtom } from 'jotai'

const todosAtom = atom<Todo[]>([])

const fetchTodosAtom = atom(
  null,
  async (get, set) => {
    const response = await fetch('/api/todos')
    const todos = await response.json()
    set(todosAtom, todos)
  }
)

function TodoList() {
  const fetchTodos = useSetAtom(fetchTodosAtom)

  return (
    <button onClick={() => fetchTodos()}>
      Fetch Todos
    </button>
  )
}

Form Handler

import { atom, useSetAtom } from 'jotai'

const nameAtom = atom('')
const emailAtom = atom('')

const submitFormAtom = atom(
  null,
  async (get, set) => {
    const name = get(nameAtom)
    const email = get(emailAtom)

    await fetch('/api/submit', {
      method: 'POST',
      body: JSON.stringify({ name, email }),
    })

    // Reset form
    set(nameAtom, '')
    set(emailAtom, '')
  }
)

function Form() {
  const submitForm = useSetAtom(submitFormAtom)

  return (
    <form onSubmit={(e) => {
      e.preventDefault()
      submitForm()
    }}>
      {/* form fields */}
      <button type="submit">Submit</button>
    </form>
  )
}

Notes

  • Unlike useAtom, this hook does not subscribe to atom value changes, preventing unnecessary re-renders
  • The setter function has a stable identity across re-renders
  • Throws an error if used with a read-only atom
  • Perfect for event handlers and callbacks where you only need to update state
  • The setter can be safely passed to child components without causing re-renders

Build docs developers (and LLMs) love