Skip to content
Theme

State Managers for Any Kind of Apps

6 min read Original article ↗

Code Examples Explore different Reatom features • Hover over highlighted elements to learn more

import { atom, computed, effect } from '@reatom/core'

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

const isEven = computed(() => counter() % 2 === 0)

effect(() => console.log(isEven()))
// log: false

counter.increment()
// log: true

counter.decrement(2)
// log nothing (memoized)
import { atom, computed, effect } from '@reatom/core'

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

const isEven = computed(() => counter() % 2 === 0)

effect(() => console.log(isEven()))
// log: false

counter.increment()
// log: true

counter.decrement(2)
// log nothing (memoized)
import { atom } from "@reatom/core"
import { reatomComponent } from "@reatom/react"

const data = atom([])
  .extend(() => ({
    isLoading: atom(false),
  }))
  .extend((target) => ({
    async load() {
      target.isLoading.set(true)
      const payload = await api.getData()
      target.set(payload)
      target.isLoading.set(false)
    },
  }))

const UserPage = reatomComponent(() => {
  if (data.isLoading()) return <div>Loading...</div>

  return (
    <ul>
      {data().map((item) => (
        <li key={item.id}>{item.value}</li>
      ))}
    </ul>
  )
})
import { atom } from "@reatom/core"
import { reatomComponent } from "@reatom/react"

const data = atom([])
  .extend(() => ({
    isLoading: atom(false),
  }))
  .extend((target) => ({
    async load() {
      target.isLoading.set(true)
      const payload = await api.getData()
      target.set(payload)
      target.isLoading.set(false)
    },
  }))

const UserPage = reatomComponent(() => {
  if (data.isLoading()) return <div>Loading...</div>

  return (
    <ul>
      {data().map((item) => (
        <li key={item.id}>{item.value}</li>
      ))}
    </ul>
  )
})
import { reatomForm } from '@reatom/core'
import { reatomComponent, bindField } from '@reatom/react'
import { Button, TextInput, PasswordInput, Stack } from '@mantine/core'

export const loginForm = reatomForm({
  username: '',
  password: '',
}, {
  validateOnBlur: true,
  onSubmit: async (values) => { 
    return await api.login(values)
  },
})

export const LoginForm = reatomComponent(() => {
  const { submit, fields } = loginForm
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault()
        submit()
      }}
    >
      <Stack>
        <TextInput
          label="Username"
          placeholder="Enter your username"
          {...bindField(fields.username)}
        />

        <PasswordInput
          label="Password"
          placeholder="Enter your password"
          {...bindField(fields.password)}
        />

        <Button type="submit" loading={!submit.ready()}>
          Login
        </Button>
      </Stack>
    </form>
  )
})
import { reatomForm } from '@reatom/core'
import { reatomComponent, bindField } from '@reatom/react'
import { Button, TextInput, PasswordInput, Stack } from '@mantine/core'

export const loginForm = reatomForm({
  username: '',
  password: '',
}, {
  validateOnBlur: true,
  onSubmit: async (values) => { 
    return await api.login(values)
  },
})

export const LoginForm = reatomComponent(() => {
  const { submit, fields } = loginForm
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault()
        submit()
      }}
    >
      <Stack>
        <TextInput
          label="Username"
          placeholder="Enter your username"
          {...bindField(fields.username)}
        />

        <PasswordInput
          label="Password"
          placeholder="Enter your password"
          {...bindField(fields.password)}
        />

        <Button type="submit" loading={!submit.ready()}>
          Login
        </Button>
      </Stack>
    </form>
  )
})
import { reatomRoute } from "@reatom/core"
import { reatomComponent } from "@reatom/react"
import { z } from "zod"

export const goodsRoute = reatomRoute("goods/:category")

export const goodsBrandRoute = goodsRoute.reatomRoute({
  path: ":brand",
  search: z.object({
    sort: z.enum(["asc", "desc"]).optional(),
  }),
  async loader(params) {
    const url = `/api/goods/${params.category}/${params.brand}?sort=${params.sort}`
    const resp = await fetch(url)
    return resp.json()
  },
})

export const BrandGoodsTable = reatomComponent(() => {
  const { data, error, ready } = goodsBrandRoute.loader
  if (!ready()) {
    return <div>Loading...</div>
  }

  if (error()) {
    return <div>Error: {error().message}</div>
  }

  return (
    <Table>
      {data()?.map((item) => (
        <Table.Row key={item.id}>
          <Table.Cell>{item.id}</Table.Cell>
          <Table.Cell>{item.name}</Table.Cell>
          <Table.Cell>{item.price}</Table.Cell>
        </Table.Row>
      ))}
    </Table>
  )
})
import { reatomRoute } from "@reatom/core"
import { reatomComponent } from "@reatom/react"
import { z } from "zod"

export const goodsRoute = reatomRoute("goods/:category")

export const goodsBrandRoute = goodsRoute.reatomRoute({
  path: ":brand",
  search: z.object({
    sort: z.enum(["asc", "desc"]).optional(),
  }),
  async loader(params) {
    const url = `/api/goods/${params.category}/${params.brand}?sort=${params.sort}`
    const resp = await fetch(url)
    return resp.json()
  },
})

export const BrandGoodsTable = reatomComponent(() => {
  const { data, error, ready } = goodsBrandRoute.loader
  if (!ready()) {
    return <div>Loading...</div>
  }

  if (error()) {
    return <div>Error: {error().message}</div>
  }

  return (
    <Table>
      {data()?.map((item) => (
        <Table.Row key={item.id}>
          <Table.Cell>{item.id}</Table.Cell>
          <Table.Cell>{item.name}</Table.Cell>
          <Table.Cell>{item.price}</Table.Cell>
        </Table.Row>
      ))}
    </Table>
  )
})
Base API Base building blocks example Framework bindings Data loading and binding to UI (React) Forms An example of a reactive form model Routing Routing and data fetching example

Powerful Features Everything you need for modern web applications

Signal Architecture

Modern reactive programming paradigm. The feature of reactivity in Signals: TC39

Small and Performant

Minimal bundle size with maximum efficiency. Treeshaking, benchmarking, microoptimizations and thoughtful internal architecture

Forms

Comprehensive form state management, most powerful validation system and best in class dynamic fields

Router

Dead simple mighty router. Cutting edge combine: async concurrency, typesafe params, suspense

Framework Agnostic

Abstract and solid for simple testing and microfrontends

Debug

A simple logger and advanced devtools to keep all the flow and bugs in the palm of your hand.

Ecosystem

Enormous ecosystem. Incredible extension system and huge built-in set of tiny helpers

Our Journey

Foundation

combine a state and a selector into ACID atom

Architecture

touching OOP principles

Process management

event - is a temporal state

Async Context

discover async context

Atomization

contract-first reactivity, computed factory pattern

Stepping up the game

async context ponyfill, errors reactivity, suspense, transactions

Sync Engine

sync-engine with CvRDTs and CmRDTs

UI Library

largest headless UI library

"Reatom has been my primary tool for over a year now. Its flexible reactive primitives allow for concise, readable, and maintainable code, while offering maximum control with minimal effort. Perfect fit for interacting with external APIs."

"The integration with @reatom/jsx works exceptionally well for Telegram Mini Apps, browser extensions, and embedded widgets."

"One of the biggest strengths of Reatom is that it's not tied to any specific UI framework. This makes it easy to keep logic outside components, resulting in simpler components and more testable overall code."

"Some features that are quite tricky to implement with other libraries are surprisingly easy with Reatom. Incredibly powerful."

"Used it for a small internal project. The approach felt unusual after working with Redux and MobX, but I decided to give it a chance. Using actions instead of reducers cuts out a lot of boilerplate. Computed atoms replace selectors nicely. I really like the auto type inference—it's a huge help during refactoring. Feels like magic, honestly."

"My advice for new adopters: spend 2–3 days exploring the library and getting a feel for the philosophy of atomization."

Your Feedback

Share your experience with Reatom and help others discover its power.

Leave Feedback