Skip to main content
atomWithRefresh creates an atom that can be manually refreshed to re-execute its read function.

Import

import { atomWithRefresh } from 'jotai/utils'

Signature

// Read-only overload
function atomWithRefresh<Value>(
  read: (get: Getter, options: Options) => Value,
): WritableAtom<Value, [], void>

// Writable overload
function atomWithRefresh<Value, Args extends unknown[], Result>(
  read: (get: Getter, options: Options) => Value,
  write: (get: Getter, set: Setter, ...args: Args) => Result,
): WritableAtom<Value, Args | [], Result | void>

Parameters

read
(get: Getter, options: Options) => Value
required
The read function that computes the atom’s value. This function is re-executed when the atom is refreshed
write
(get: Getter, set: Setter, ...args: Args) => Result
Optional write function for custom write behavior. If omitted, the atom only supports refresh (empty args)

Return Value

Returns a writable atom that:
  • Reads using the provided read function
  • Can be written to with no arguments to trigger a refresh
  • Can be written to with arguments if a custom write function was provided

Usage Example

import { useAtom } from 'jotai'
import { atomWithRefresh } from 'jotai/utils'

const timestampAtom = atomWithRefresh(() => Date.now())

function Timestamp() {
  const [timestamp, refresh] = useAtom(timestampAtom)

  return (
    <div>
      <p>Current time: {new Date(timestamp).toLocaleTimeString()}</p>
      <button onClick={() => refresh()}>Refresh</button>
    </div>
  )
}

Async Data Fetching

import { atomWithRefresh } from 'jotai/utils'

const userAtom = atomWithRefresh(async () => {
  const response = await fetch('/api/user')
  return response.json()
})

function UserProfile() {
  const [user, refresh] = useAtom(userAtom)

  return (
    <div>
      <h1>{user.name}</h1>
      <button onClick={() => refresh()}>Refresh User Data</button>
    </div>
  )
}

With Dependencies

import { atom } from 'jotai'
import { atomWithRefresh } from 'jotai/utils'

const userIdAtom = atom(1)

const userDataAtom = atomWithRefresh(async (get) => {
  const userId = get(userIdAtom)
  const response = await fetch(`/api/users/${userId}`)
  return response.json()
})

function UserComponent() {
  const [userId, setUserId] = useAtom(userIdAtom)
  const [userData, refreshUserData] = useAtom(userDataAtom)

  return (
    <div>
      <select value={userId} onChange={(e) => setUserId(Number(e.target.value))}>
        <option value={1}>User 1</option>
        <option value={2}>User 2</option>
      </select>
      <p>{userData.name}</p>
      <button onClick={() => refreshUserData()}>Force Refresh</button>
    </div>
  )
}

With Custom Write Function

import { atomWithRefresh } from 'jotai/utils'

const dataAtom = atomWithRefresh(
  async () => {
    const response = await fetch('/api/data')
    return response.json()
  },
  async (get, set, newData: any) => {
    // Custom write: update server then refresh
    await fetch('/api/data', {
      method: 'POST',
      body: JSON.stringify(newData),
    })
    // Refresh to get updated data from server
    set(dataAtom) // Empty call triggers refresh
  }
)

function DataEditor() {
  const [data, updateData] = useAtom(dataAtom)

  const handleSave = async () => {
    await updateData({ ...data, modified: true })
  }

  const handleRefresh = () => {
    updateData() // No args = refresh
  }

  return (
    <div>
      <pre>{JSON.stringify(data, null, 2)}</pre>
      <button onClick={handleSave}>Save</button>
      <button onClick={handleRefresh}>Refresh</button>
    </div>
  )
}

Periodic Refresh

import { useAtom } from 'jotai'
import { atomWithRefresh } from 'jotai/utils'
import { useEffect } from 'react'

const liveDataAtom = atomWithRefresh(async () => {
  const response = await fetch('/api/live-data')
  return response.json()
})

function LiveDashboard() {
  const [data, refresh] = useAtom(liveDataAtom)

  useEffect(() => {
    // Auto-refresh every 5 seconds
    const interval = setInterval(() => {
      refresh()
    }, 5000)

    return () => clearInterval(interval)
  }, [refresh])

  return <div>Live Data: {JSON.stringify(data)}</div>
}

Notes

  • Calling the atom’s setter with no arguments triggers a refresh
  • The read function is re-executed completely, not just re-evaluated
  • Useful for polling, manual refresh, or cache invalidation patterns
  • Works with both sync and async read functions
  • If a custom write function is provided and you call it with no arguments, it triggers a refresh (not the write function)

Build docs developers (and LLMs) love