Skip to main content
Reatom v1000 is an epoch release that solves the most significant pain point of v3: explicit context management. The new version operates with implicit global context like other signal libraries, while still supporting custom contexts for advanced use cases.

What’s New in v1000

Implicit Context Management

The biggest change is that you no longer need to pass ctx everywhere:
// v3 - Explicit context
const getValue = (ctx: Ctx) => {
  const data = ctx.spy(dataAtom)
  return data
}

// v1000 - Implicit context
const getValue = () => {
  const data = dataAtom()
  return data
}

Key Benefits

  • Simpler API - No more ctx parameter threading
  • Better DX - Code reads more naturally
  • Still powerful - Custom contexts available when needed
  • Familiar patterns - Similar to other signal libraries
Reatom v1000 uses implicit context by default, but you can still create custom contexts for SSR, testing, or isolation. This gives you the best of both worlds.

Migration Strategy

1

Update dependencies

Update all Reatom packages to v1000:
npm install @reatom/core@latest @reatom/react@latest
# or
pnpm rm @reatom/core @reatom/react && pnpm i @reatom/core@latest @reatom/react@latest
2

Update TypeScript types

Replace old type names with new ones throughout your codebase.
3

Remove ctx parameters

Remove ctx from all atom callbacks and action bodies.
4

Update API calls

Replace v3 methods with v1000 equivalents.
5

Test thoroughly

Run your test suite and verify behavior matches expectations.

API Changes Reference

Core Context APIs

v3v1000Notes
ctx.spy(atom)atom()Call atom as function
ctx.get(atom)peek(atom)Non-reactive read
atom(ctx, value)atom.set(value)Update atom
atom(ctx, (s) => s)atom.set((s) => s)Update with callback
ctx.schedule(promise)wrap(promise)Preserve context
ctx.spy(atom, cb)ifChanged(atom, cb)React to changes
ctx.spy(action, cb)getCalls(action).forEach(cb)React to action

Type Changes

v3v1000Notes
Atom<T>AtomLike<T>Generic atom interface
AtomMut<T>Atom<T>Mutable atom
Ctx(removed)Use implicit context

Primitive Changes

v3v1000Notes
atom(callback)computed(callback)Derived state
reaction(callback)effect(callback)Side effects
reatomAsync(cb)action(cb).extend(withAsync())Async action
reatomResource(cb)computed(cb).extend(withAsyncData())Data fetching

Extension Changes

v3v1000Notes
anAtom.onChange(cb)anAtom.extend(withChangeHook(cb))Change listener
onConnect(atom, cb)atom.extend(withConnectHook(cb))Mount/unmount
take(atom, (ctx, v, SKIP) => ...)take(atom, (v) => v || throwAbort())Filter values
withConcurrencywithAbortCancellation

Step-by-Step Examples

1. Simple Atom Migration

Before (v3):
import { atom } from '@reatom/core'
import type { Ctx } from '@reatom/core'

const counterAtom = atom(0, 'counter')

const getValue = (ctx: Ctx) => {
  return ctx.spy(counterAtom)
}

const setValue = (ctx: Ctx, value: number) => {
  counterAtom(ctx, value)
}
After (v1000):
import { atom } from '@reatom/core'

const counter = atom(0, 'counter')

const getValue = () => {
  return counter()
}

const setValue = (value: number) => {
  counter.set(value)
}

2. Computed Atom Migration

Before (v3):
import { atom } from '@reatom/core'

const firstNameAtom = atom('', 'firstName')
const lastNameAtom = atom('', 'lastName')

const fullNameAtom = atom((ctx) => {
  return `${ctx.spy(firstNameAtom)} ${ctx.spy(lastNameAtom)}`
}, 'fullName')
After (v1000):
import { atom, computed } from '@reatom/core'

const firstName = atom('', 'firstName')
const lastName = atom('', 'lastName')

const fullName = computed(() => {
  return `${firstName()} ${lastName()}`
}, 'fullName')

3. Action Migration

Before (v3):
import { action, atom } from '@reatom/core'
import type { Ctx } from '@reatom/core'

const listAtom = atom([], 'list')
const loadingAtom = atom(false, 'loading')

const fetchList = action((ctx) => {
  loadingAtom(ctx, true)
  
  return fetch('/api/list')
    .then(res => res.json())
    .then(data => {
      listAtom(ctx, data)
      loadingAtom(ctx, false)
    })
}, 'fetchList')
After (v1000):
import { action, atom, wrap } from '@reatom/core'

const list = atom([], 'list')
const isLoading = atom(false, 'isLoading')

const fetchList = action(async () => {
  isLoading.set(true)
  try {
    const response = await wrap(fetch('/api/list'))
    const data = await response.json()
    list.set(data)
  } finally {
    isLoading.set(false)
  }
}, 'fetchList')
Use wrap() around promises to preserve Reatom context across async boundaries. This is crucial for proper context management.

4. Resource Migration

Before (v3):
import { reatomResource } from '@reatom/core'

const userResource = reatomResource(async (ctx, id: string) => {
  const response = await fetch(`/api/users/${id}`)
  return response.json()
}, 'userResource')
After (v1000):
import { computed, atom, withAsyncData, wrap } from '@reatom/core'

const userId = atom('', 'userId')

const userResource = computed(async () => {
  const id = userId()
  if (!id) return null
  
  const response = await wrap(fetch(`/api/users/${id}`))
  return response.json()
}, 'userResource').extend(
  withAsyncData({ initState: null })
)

// Access via:
// userResource.data() - the user object
// userResource.ready() - loading state
// userResource.error() - error if any
// userResource.retry() - refetch

5. Effect Migration

Before (v3):
import { atom } from '@reatom/core'

const searchAtom = atom('', 'search')

const reaction = reatomResource(async (ctx) => {
  const query = ctx.spy(searchAtom)
  if (!query) return
  
  const results = await fetch(`/api/search?q=${query}`)
  console.log('Search results:', results)
}, 'searchReaction')
After (v1000):
import { atom, effect, wrap } from '@reatom/core'

const search = atom('', 'search')

effect(async () => {
  const query = search()
  if (!query) return
  
  const response = await wrap(fetch(`/api/search?q=${query}`))
  const results = await response.json()
  console.log('Search results:', results)
}, 'searchEffect')

6. React Integration Migration

Before (v3):
import { useAtom } from '@reatom/react'
import { counterAtom } from './model'

const Counter = () => {
  const [count, setCount] = useAtom(counterAtom)
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  )
}
After (v1000):
import { reatomComponent } from '@reatom/react'
import { counter } from './model'

const Counter = reatomComponent(() => {
  return (
    <button onClick={() => counter.set((v) => v + 1)}>
      Count: {counter()}
    </button>
  )
})
reatomComponent is a computed-enhanced React component that automatically tracks dependencies and re-renders efficiently.

Advanced: Custom Contexts

While v1000 uses implicit context by default, you can still create custom contexts for:
  • SSR - Isolate state per request
  • Testing - Clean state between tests
  • Multi-tenancy - Separate state per tenant

Clear Default Context

import { clearStack } from '@reatom/core'

// Remove default context, require explicit context
clearStack()

Create Custom Context

import { createContext } from '@reatom/core'

// For SSR
const handleRequest = async (req, res) => {
  const ctx = createContext()
  
  ctx.start(() => {
    // Your app logic here with isolated context
    const html = renderApp()
    res.send(html)
  })
}

Testing with Custom Context

import { createTestCtx } from '@reatom/testing'
import { expect, test } from 'vitest'

test('counter increments', () => {
  const ctx = createTestCtx()
  
  ctx.start(() => {
    counter.set(0)
    counter.set((v) => v + 1)
    expect(counter()).toBe(1)
  })
})

Common Migration Issues

Issue: “ctx is not defined”

Problem:
const value = computed((ctx) => {
  return ctx.spy(dataAtom) // Error: ctx.spy is not a function
})
Solution:
const value = computed(() => {
  return dataAtom() // No ctx needed
})

Issue: “Missing async stack”

Problem: Async operations lose context because promises aren’t wrapped. Solution: Wrap all promises with wrap():
const fetchData = action(async () => {
  const response = await wrap(fetch('/api/data')) // ✅ Wrapped
  return response.json()
}, 'fetchData')
Ensure your build target is ES2017 or higher. Lower targets transform async/await into .then() chains, which breaks context preservation.

Issue: TypeScript errors after migration

Problem:
const myAtom: Atom<number> = atom(0) // Type error
Solution:
import type { AtomLike, Atom } from '@reatom/core'

// Atom is now for mutable atoms specifically
const myAtom: Atom<number> = atom(0)

// AtomLike is the generic interface
function acceptsAnyAtom(a: AtomLike<number>) {
  // ...
}

Package Deduplication

After updating, deduplicate Reatom packages to avoid version conflicts: NPM:
npm i --prefer-dedupe @reatom/react@latest
Yarn:
yarn add @reatom/react@latest && yarn dedupe "@reatom/*"
PNPM:
pnpm rm @reatom/core @reatom/react && pnpm i @reatom/core@latest @reatom/react@latest
Reatom core is a singleton package with internal state. Multiple versions can cause type incompatibilities and runtime errors.

Versioning Strategy

Reatom v1000 uses epoch-based versioning:
  • Epoch (1000, 2000, etc.) - Major architectural changes
  • Major (1001, 1002, etc.) - Breaking changes within epoch
  • Minor/Patch - Standard SemVer
The next epoch (v2000) is planned for 2026-2027.

Migration Checklist

  • Update all @reatom/* packages to v1000+
  • Deduplicate dependencies
  • Replace Ctx types with implicit context
  • Replace Atom with AtomLike where appropriate
  • Replace AtomMut with Atom
  • Change atom(callback) to computed(callback)
  • Change ctx.spy(atom) to atom()
  • Change ctx.get(atom) to peek(atom)
  • Change atom(ctx, value) to atom.set(value)
  • Change ctx.schedule(promise) to wrap(promise)
  • Replace reatomAsync with action().extend(withAsync())
  • Replace reatomResource with computed().extend(withAsyncData())
  • Replace reaction with effect
  • Replace extension methods with extend() calls
  • Update test helpers to use createTestCtx
  • Run full test suite
  • Check build target is ES2017+

Getting Help

If you encounter issues during migration:

Further Reading

  • [History and Evolutionhttps://github.com/reatom/reatom - Why v1000 was created
  • Performance Guide - Optimize your migrated app
  • DevTools Guide - Debug during migration

Build docs developers (and LLMs) love