Skip to content

Core

isAction

Type guard to check if a value is a Reatom action.

This function determines whether the given value is an action by checking if it’s an atom with non-reactive behavior (actions are non-reactive atoms).

withActionMiddleware

Creates an extension that adds middleware to an action, wrapping only the call function, not the state.

Unlike withMiddleware, which wraps the entire middleware chain (including the state update), withActionMiddleware wraps only the call function that gets passed to actionMiddleware. This allows you to decorate the action’s execution logic without interfering with the action’s state management.

Example

// Creating a retry middleware for actions
const withRetry = (maxAttempts: number) =>
withActionMiddleware((target) => {
return (next, ...params) => {
let attempts = 0
while (attempts < maxAttempts) {
try {
return next(...params)
} catch (error) {
attempts++
if (attempts >= maxAttempts) throw error
}
}
}
})
// Using the middleware
const fetchData = action(async () => {
const response = await fetch('/api/data')
return response.json()
}).extend(withRetry(3))

action

Creates a logic and side effect container.

Actions are used to encapsulate complex logic, perform side effects (like API calls), and orchestrate multiple state updates. Unlike atoms, actions are meant to be called with parameters and can return values.

Actions also have atom-like features (subscribe, extend) and track their call history.

Example

// Create an action that fetches data and updates state
const fetchUserData = action(async (userId: string) => {
const response = await wrap(fetch(`/api/users/${userId}`))
const data = await wrap(response.json())
// Update state atoms with the fetched data
userName(data.name)
userEmail(data.email)
return data // Actions can return values
}, 'fetchUserData')
// Call the action
fetchUserData('user123')

withActions

Extension that binds actions to an atom or action as methods.

This extension adds methods to an atom or action by converting them to Reatom actions. Each method is converted to an action with the same name and bound to the target. The name of each action will be prefixed with the target’s name for better debugging.

Example 1

const counter = atom(0, 'counter').extend(
withActions({
increment: (amount = 1) => counter((prev) => prev + amount),
decrement: (amount = 1) => counter((prev) => prev - amount),
reset: () => counter(0),
}),
)
counter.increment(5) // Can now call these methods directly
counter.reset()

Example 2

const counter = atom(0, 'counter').extend(
withActions((target) => ({
increment: (amount = 1) => target.set((prev) => prev + amount),
decrement: (amount = 1) => target.set((prev) => prev - amount),
reset: () => target.set(0),
})),
)

isConnected

Checks if an atom has active subscriptions.

This function determines if an atom is currently connected to any subscribers, which indicates that the atom is being actively used somewhere in the application. This is useful for optimizations or conditional logic based on whether an atom’s changes are being observed.

addGlobalExtension

Registers a global extension that will be automatically applied to all atoms and actions created after registration.

This function allows you to add behavior to all Reatom entities in your application, such as tracking, logging, analytics, or debugging capabilities. Extensions registered with this function will be applied before any local extensions defined on individual atoms.

Example

import { addGlobalExtension, isAction, withCallHook } from '@reatom/core'
// Track all action calls for analytics
addGlobalExtension((target) => {
if (isAction(target)) {
target.extend(withCallHook(console.log))
}
return target
})

computedMiddleware

The hurt of atom internal logic

cacheMiddleware

Cache checking middleware, placed in every atom’s middlewares

anonymizeNames

Useful for security reasons, if you need to increase your runtime complexity. It’s important to call this function before creating any atoms.

atom

Creates a mutable state container.

The atom is the core primitive for storing and updating mutable state in Reatom. Atoms can be called as functions to read their current value or to update the value.

Example

// Create with initial value
const counter = atom(0, 'counter')
// Read current value
const value = counter() // -> 0
// Update with new value
counter.set(5) // Sets value to 5
// Update with a function
counter.set((prev) => prev + 1) // Sets value to 6

computed

Creates a derived state container that lazily recalculates only when read.

Computed atoms automatically track their dependencies (other atoms or computed values that are called during computation) and only recalculate when those dependencies change. The computation is lazy - it only runs when the computed value is read AND subscribed to.

Example

const counter = atom(5, 'counter')
const doubled = computed(() => counter() * 2, 'doubledCounter')
// Reading triggers computation only if subscribed
const value = doubled() // -> 10

isComputed

Checks if the provided target is a READONLY computed atom

context

Core context object that manages the reactive state context in Reatom.

The context is responsible for tracking dependencies between atoms, managing computation stacks, and ensuring proper reactivity. It serves as the foundation for Reatom’s reactivity system and provides access to the current context frame.

top

Gets the current top frame in the Reatom context stack.

Returns the currently active frame in the execution stack, which contains the current atom being processed and its state.

clearStack

Clears the current Reatom context stack.

This is primarily used to force explicit context preservation via wrap(). By clearing the stack, any atom operations outside of a properly wrapped function will throw “missing async stack” errors, ensuring proper context handling.

bind

Light version of wrap that binds a function to the current reactive context.

Unlike the full wrap function, bind does not follow abort context, making it more lightweight but less safe for certain async operations. Use this when you need to preserve context but don’t need the abort handling capabilities of wrap.

mock

Mocks an atom or action for testing purposes.

This function replaces the original behavior of an atom or action with a custom callback function for the duration of the mock. This is useful for isolating units of code during testing and controlling their behavior.

extend

Applies extensions to atoms or actions.

This is the core extension mechanism in Reatom that allows adding functionality to atoms and actions. Extensions can add properties, methods, or modify behavior. Extended atoms maintain their original reference identity.

Example

// Extending an atom with reset capability
const counter = atom(0, 'counter').extend(
withReset(0), // Adds counter.reset() method
withLogger('COUNTER'), // Adds logging middleware
)

withTap

Creates an extension that allows observing state changes without modifying them.

This extension adds a middleware that calls the provided callback function whenever the atom’s state changes, passing the target atom, new state, and previous state. This is useful for side effects like logging, analytics, or debugging.

Example

const counter = atom(0, 'counter').extend(
withTap((target, state, prevState) => {
console.log(`${target.name} changed from ${prevState} to ${state}`)
}),
)

withParams

Extension that transforms parameters before they reach the atom or action. Useful as the .set atom method can’t be reassigned and changed.

This utility lets you change how parameters are processed when an atom or action is called, enabling custom parameter handling, validation, or transformation.

Example

// Convert from any unit to meters
const length = atom(0, 'length').extend(
withParams((value: number, unit: 'cm' | 'm' | 'km') => {
switch (unit) {
case 'cm':
return value / 100
case 'm':
return value
case 'km':
return value * 1000
default:
return value
}
}),
)
length(5, 'km') // Sets value to 5000 meters

batch

Runs a callback as a nested batch and optionally flushes the queue after the outermost batch completes.

Use shouldNotify: true for user-facing write batches that must notify synchronously after all nested writes finish. Leave it false when wrapping reads such as computed values or effects.

Example

import { atom, batch } from '@reatom/core'
const count = atom(0, 'count')
batch(() => {
count.set(1)
count.set(2)
}, true)

notify

Processes all scheduled tasks in the current context’s queues.

This function is called automatically after tasks have been scheduled via enqueue. It processes tasks in the following priority order:

  1. Hook tasks

  2. Compute tasks

  3. Cleanup tasks

  4. Effect tasks

The function resets priority after each task execution to ensure higher priority tasks (which may have been added during execution) are processed first.