Intent-first, logic-centric reactive state runtime with atoms, computed, async state, effects, and deterministic scheduling. React-agnostic core.
npm install chrono-state-z !Downloads
---
chrono-state-z is a reactive, intent-first state runtime designed to keep business logic outside React.
It provides atoms, computed values, async state, effects, scheduling, with a headless core and thin React bindings.
> React renders state. Logic lives elsewhere.
---
Use chrono-state-z when you need:
- Predictable state & side-effects
- Complex async flows (fetch → invalidate → retry)
- Logic reusable outside React (tests, workers, backend)
- Fine-grained reactivity (no global rerenders)
- Clear separation between logic and view
---
- Atom — small reactive state unit
- Computed — derived value, cached and reactive
- AsyncAtom — async state with suspense-style read
- Effect — reactive side-effect runner
- Scheduler — priority-based execution control
- Store / Intent — intent-driven state orchestration
- React hooks — thin bindings over the headless core
---
``bash`npm install react ## for react
npm install intentx-core-z chrono-state-z
---
`ts
import { atom, computed } from 'chrono-state-z'
const count = atom(0)
const double = computed(() => count() * 2)
count.set(5)
console.log(count()) // 5
console.log(double()) // 10
`
---
`ts
import { atom, effect } from 'chrono-state-z'
const count = atom(0)
const dispose = effect(() => {
console.log('Count changed:', count())
})
count.set(1) // logs: Count changed: 1
dispose()
`
---
`ts
import { asyncAtom } from 'chrono-state-z'
const user = asyncAtom(async () => {
const res = await fetch('/api/user')
return res.json()
})
await user.load()
const u = user()
`
Invalidate & refetch:
`ts`
user.invalidate()
user.invalidate('low')
---
`ts
import { asyncComputed, atom } from 'chrono-state-z'
const count = atom(2)
const doubleAsync = asyncComputed(async () => {
await new Promise(r => setTimeout(r, 50))
return count() * 2
})
await doubleAsync()
`
---
`ts
import { atom, transaction } from 'chrono-state-z'
const a = atom(1)
const b = atom(2)
transaction(() => {
a.set(10)
b.set(20)
})
console.log(a(), b())
`
---
`ts
import { createStore } from 'chrono-state-z'
type State = {
saving: boolean
value: string
}
const logic = createStore
saving: false,
value: ''
})
logic.on('SAVE', async ({ state, setState }) => {
setState(s => { s.saving = true })
await fakeApiSave(state().value)
setState(s => { s.saving = false })
})
`
Emit intent:
`ts`
await logic.emit('SAVE')
---
`tsx
import { atom, useAtom } from 'chrono-state-z'
const count = atom(0)
function Counter() {
const value = useAtom(count)
return (
)
}
`
---
`tsx
import { computed, useComputed } from 'chrono-state-z'
const total = computed(() => price() * qty())
function TotalView() {
const value = useComputed(total)
return
---
$3
`tsx
// atom() returns a callable reactive value
const user = atom({ id: 1, name: 'Alice', age: 20 })function Username() {
const name = useAtomSelector(user, u => u.name)
return {name}
}
`---
$3
`tsx
import { useStore } from 'chrono-state-z'
import type { Store } from 'chrono-state-z'function StoreView({ store }: { store: Store }) {
const state = useStore(store)
return {JSON.stringify(state, null, 2)}
}
`---
$3
`tsx
// Only re-renders when saving changes, not the whole store.
import { useStoreSelector } from 'chrono-state-z'function SavingBadge({ store }) {
const saving = useStoreSelector(store, s => s.saving)
// const saving = useStoreSelector(
// store,
// s => s.meta,
// shallowEqual
// )
return saving ? 'Saving...' : 'Idle'
}
`
- selector should be pure and stable.
- If you need dynamic selection, memoize the selector.---
$3
`ts
import { useWatch } from 'chrono-state-z'function AuthGuard() {
useWatch(
() => user(),
(u) => {
if (u?.role === 'admin') {
redirect('/admin')
}
}
)
return null
}
`$3
`ts
const user = asyncAtom(fetchUser)// load explicitly
await user.load()
// read value (throws / suspends if not ready)
const u = user()
`---
🧩 Architecture Pattern
`ts
import { asyncAtom, computed } from 'chrono-state-z'export function createUserLogic() {
const user = asyncAtom(fetchUser)
const name = computed(() => user()?.name ?? 'Guest')
return {
user,
name,
reload: () => user.invalidate()
}
}
``tsx
function UserView({ logic }) {
const user = useAtom(logic.user)
const name = useComputed(() => logic.name()) return (
<>
Hello {name}
>
)
}``---
| Feature | chrono-state-z | Redux | Zustand | Jotai |
|---------------------------|---------------- |-------|--------- |------- |
| Fine-grained reactivity | ✅ | ❌ | ⚠️ | ✅ |
| Async primitives | ✅ | ⚠️ | ❌ | ⚠️ |
| Intent / effect layer | ✅ | ⚠️ | ❌ | ❌ |
| Scheduler / priority | ✅ | ❌ | ❌ | ❌ |
| Headless (non-React) core | ✅ | ❌ | ⚠️ | ❌ |
| Testability | ✅ | ⚠️ | ⚠️ | ❌ |
---
- Putting business logic inside React components
- Mutating atom values directly
- Coupling domain logic to UI events
- Skipping computed / effects for orchestration
---
- Logic lives outside React
- Deterministic, testable state
- Effects are explicit and traceable
- UI is a pure projection of state
---
MIT