Intent-first business logic runtime. Headless and backend-friendly. Deterministic state, computed graph, and orchestrated async effects. React is just a view layer.
npm install logic-runtime-react-z !Downloads
Intent-first business logic runtime: React is a view โ logic lives elsewhere.
A headless, deterministic, intent-driven runtime for frontend & backend logic.
React components stay pure. Business logic is fully testable, replayable, and framework-agnostic.
> Intent is the only entry point.
> React is optional. createLogic is the product. Everything else is an adapter.
---
- No business logic in React components
- Intent is the only entry point
- Predictable async flows
- Reactive computed graph with caching
- Headless & backend-friendly
- Deterministic testing & devtools replay
---
```
UI / HTTP / Queue / Cron
โ
emit(intent)
โ
effects / middleware
โ
intent handlers
โ
mutate state
โ
computed (derived state) / subscribers
Think events โ behavior โ state โ derived state.
---
`bash`
npm install logic-runtime-react-z
---
`ts
import { createLogic } from "logic-runtime-react-z"
const counterLogic = createLogic({
state: { count: 0 },
intents: bus => {
bus.on("inc", ({ setState }) => {
setState(s => {
s.count++
})
})
bus.on
setState(s => {
s.count += payload
})
})
},
})
async function main() {
const runtime = counterLogic.create()
await runtime.emit("inc")
await runtime.emit("add", 5)
console.log(runtime.state.count)
}
main()
`
โ No UI
โ Fully testable
โ Deterministic
> ๐ก This is the core usage.
> createLogic() + runtime.emit() already gives you state, computed and effects.
> React integration is just a convenience layer on top of this runtime.
---
`ts`
computed: {
double: ({ state }) => state.count * 2,
triple: ({ state }) => state.count * 3,
}
- state inside computed is reactive.state.count
- Reading automatically tracks dependencies.
- Computed values are cached and only re-evaluated when tracked dependencies change.
---
`ts
// counter.logic.ts
import { createLogic, effect } from "logic-runtime-react-z"
export const counterLogic = createLogic({
name: "counter",
state: {
count: 1,
loading: false,
},
computed: {
double: ({ state }) => state.count * 2,
triple: ({ state }) => state.count * 3,
},
intents: bus => {
bus.on("inc", ({ setState }) => {
setState(s => {
s.count++
})
})
bus.on
setState(s => {
s.count += payload
})
})
bus.on
setState(s => {
s.loading = true
})
await new Promise(r => setTimeout(r, 1000))
setState(s => {
s.count += payload
s.loading = false
})
})
// effects = side-effects only (no state mutation)
bus.effect(
"inc-async",
effect(async ({ payload }) => {
console.log("effect run:", payload)
}).takeLatest()
)
},
actions: {
inc({ emit }) {
return () => emit("inc")
},
add({ emit }) {
return (n: number) => emit("add", n)
},
incAsync({ emit }) {
return (n: number) => emit("inc-async", n)
},
},
})
`
---
`tsx
import React from "react"
import { withLogic } from "logic-runtime-react-z"
import { counterLogic } from "./counter.logic"
function CounterView(props) {
const { state, actions, emit } = props
return (
disabled={state.loading}
onClick={() => actions.incAsync(5)}
>
Async +5
export const CounterPage = withLogic(counterLogic, CounterView)
`
โ Props are inferred when using withLogic, no manual generics required.
---
`ts
import { createLogic } from "logic-runtime-react-z"
const authLogic = createLogic({
state: {
user: null,
loading: false,
},
intents: bus => {
bus.on("login", async ({ setState }) => {
setState(s => {
s.loading = true
})
await new Promise(r => setTimeout(r, 500))
setState(s => {
s.user = { name: "Alice" }
s.loading = false
})
})
bus.on("logout", ({ setState }) => {
setState(s => {
s.user = null
})
})
},
})
async function run() {
const runtime = authLogic.create()
await runtime.emit("login")
await runtime.emit("logout")
console.log(runtime.getSnapshot())
}
run()
`
โ Same runtime, same behavior, no React involved.
โ No React
โ Replayable
---
Hooks are optional convenience layers on top of the same logic runtime.
They do not own state, they only subscribe to it.
#### useRuntime โ full snapshot
`ts
import { useRuntime } from "logic-runtime-react-z"
function Debug() {
const snapshot = useRuntime(counterLogic)
return
{JSON.stringify(snapshot, null, 2)}โ Subscribes to full snapshot
โ Includes state + computed
โ Read-only
#### useActions โ actions only (no re-render)
`ts
import { useActions } from "logic-runtime-react-z"function Buttons() {
const actions = useActions(counterLogic)
return (
<>
>
)
}
`
โ No re-render on state change
โ Fully inferred action types
โ Ideal for buttons / handlers
#### useComputed โ Subscribe to computed values
`ts
import { useComputed } from "logic-runtime-react-z"function Stats() {
const { double, triple } = useComputed(counterLogic)
return (
<>
Double: {double}
Triple: {triple}
>
)
}function DoubleOnly() {
const double = useComputed(counterLogic, c => c.double)
return
{double}
}
`โ Only derived data
โ Cached & reactive
โ No state mutation possible
#### useComputed with selector (recommended)
`ts
function DoubleOnly() {
const double = useComputed(
counterLogic,
c => c.double
) return
Double: {double}
}
`โ Component re-renders only when double changes
โ No extra dependencies
โ Type-safe selector
#### useLogicSelector โ State selector (Redux-like)
`ts
import { useLogicSelector } from "logic-runtime-react-z"function CountLabel() {
const count = useLogicSelector(
counterLogic.create(),
state => state.count
)
return {count}
}
`โ Memoized selector
โ Fine-grained subscriptions
โ Familiar mental model
---
๐งฑ Composing Multiple Logic Modules
`ts
import { composeLogic } from "logic-runtime-react-z"
import { userLogic } from "./user.logic"
import { cartLogic } from "./cart.logic"const app = composeLogic({
user: userLogic,
cart: cartLogic,
})
await app.emit("login")
const state = app.getState()
state.user
state.cart
`---
๐งช Unit Test Example
`ts
const logic = createLogic({
state: { value: 0 }, computed: {
squared: ({ state }) => state.value * state.value,
},
intents: bus => {
bus.on("set", ({ payload, setState }) => {
setState(s => {
s.value = payload
})
})
},
})
const runtime = counterLogic.create()
await runtime.emit("set", 4)
expect(runtime.computed.squared).toBe(16)
``โ Computed values are tested like plain data
---
| Capability / Library | logic-runtime-react-z | Redux | Zustand |
|--------------------------|:---------------------:|:-----:|:-------:|
| Intent-first model | โ
| โ | โ |
| State-first model | โ | โ
| โ
|
| First-class effects | โ
| โ | โ |
| Computed graph | โ
| โ | โ ๏ธ |
| Deterministic execution | โ
| โ | โ |
| Logic outside React | โ
| โ | โ |
| Backend-safe | โ
| โ | โ |
##### โ ๏ธ via selectors, not a true dependency graph
---
- Redux & Zustand manage state
- logic-runtime-react-z orchestrates logic
---
- Intents are processed sequentially
- State mutations are isolated
- Async flows are predictable
- Same inputs โ same outputs
---
MIT