Sources
Your source owns state and behavior.
Pass it to igniteCore through the entrypoint for your source library.
Use states to derive values for the view and commands to call the source.
Choose a source adapter
Section titled “Choose a source adapter”An Ignite source adapter connects your source to Ignite’s observation and command APIs.
| Source | Import igniteCore from |
|---|---|
| XState | ignite-element/xstate |
| Redux | ignite-element/redux |
| MobX | ignite-element/mobx |
Network requests, storage, timers, and other I/O stay in your source or application.
Create independent state per element
Section titled “Create independent state per element”Choose the input that creates a new source for each element:
| Source library | Input to igniteCore | What each element gets |
|---|---|---|
| XState | source: toggleMachine | A new actor created from the machine definition |
| Redux | source: slice | A new store created from the slice |
| MobX | source: () => new Counter() | A new observable returned by the function |
The MobX function must create a new observable each time it is called.
For shared state, pass the existing observable directly instead of returning a singleton from a factory.
The complete modules below export a core with derived state and commands.
Definitions and fresh factories also provide a private runtime per useIgnite(core) call. Existing actor, store or observable instances share state instead.
Use that core to register a view, as shown in Getting started.
What states receives
Section titled “What states receives”The callback input depends on the source adapter.
| Source | Input to states |
|---|---|
| XState | The actor snapshot, including context, matches(...), and can(...) for state machines. |
| Redux | The store’s getState() result, or the selected slice state when you pass a slice. |
| MobX | The result of toJS(store), rather than the observable instance itself. |
The name snapshot in the examples is a callback parameter, not a shared shape across all sources.
Command target
Section titled “Command target”The configuration’s source tells Ignite what to create or observe.
Inside commands: ({ source }), source provides commands for the active actor or store.
For a machine, slice, or factory, the callback receives the created instance’s command interface.
The examples use { source: store } to name that callback value store.
XState
Section titled “XState”Install Ignite and XState:
pnpm add ignite-element@beta xstateThe Getting started light switch passes a machine definition to igniteCore.
Each mounted light switch gets its own actor.
For shared state, pass an existing started actor; see Shared XState source.
XState machine states describe behavior; Ignite’s states callback derives values for views.
Keep guards, invoked services, cancellation, retries, and persistence in XState or the application.
Install Ignite and Redux Toolkit:
pnpm add ignite-element@beta @reduxjs/toolkitCreate a store per element
Section titled “Create a store per element”Save this complete module as redux-slice.ts:
import { createSlice } from "@reduxjs/toolkit";import { igniteCore } from "ignite-element/redux";
const slice = createSlice({ name: "counter", initialState: { count: 0 }, reducers: { increment: (state) => { state.count += 1; }, },});
export const core = igniteCore({ source: slice, states: (snapshot) => ({ count: snapshot.count }), commands: ({ source: store }) => ({ increment: () => store.dispatch(slice.actions.increment()), }),});Pass the slice itself as source.
Ignite creates a separate store for each element using this core.
The states callback receives the slice state, so the count is snapshot.count.
The increment() command dispatches the slice’s action to the store.
Configure your own store
Use a store factory when you need custom middleware or combined reducers.
The factory must create a new store each time it is called.
Independent hook construction, including factory middleware/enhancers, must be safe to repeat and discard without cleanup; see ownership.
Save this alternative module as redux-factory.ts:
import { configureStore, createSlice } from "@reduxjs/toolkit";import { igniteCore } from "ignite-element/redux";
const slice = createSlice({ name: "counter", initialState: { count: 0 }, reducers: { increment: (state) => { state.count += 1; }, },});
export const core = igniteCore({ source: () => configureStore({ reducer: slice.reducer }), states: (snapshot) => ({ count: snapshot.count }), commands: ({ source: store }) => ({ increment: () => store.dispatch(slice.actions.increment()), }),});Here states receives the store’s full state.
For a combined reducer, read its root shape, such as snapshot.counter.count.
For shared state, pass an existing store; see Shared Redux store.
Install Ignite and MobX:
pnpm add ignite-element@beta mobxCreate an observable per element
Section titled “Create an observable per element”Save this complete module as mobx-factory.ts:
import { igniteCore } from "ignite-element/mobx";import { makeAutoObservable } from "mobx";
class Counter { count = 0; constructor() { makeAutoObservable(this); } increment() { this.count += 1; }}
export const core = igniteCore({ source: () => new Counter(), states: (snapshot) => ({ count: snapshot.count }), commands: ({ source: store }) => ({ increment: () => store.increment() }),});makeAutoObservable(this) makes each Counter instance observable.
source: () => new Counter() lets Ignite create a new instance for each element, or each hook.
Existing instances remain shared.
Independent views release their runtime automatically; call core.dispose() when the core’s owner ends.
Incrementing one element changes only that element’s counter.
The states callback reads the observable data copied by toJS.
Use that snapshot for data fields such as count.
Call actions on the observable received by commands, rather than on the snapshot.
The increment() command calls store.increment() on the observable instance.
For shared state, pass an existing observable; see Shared MobX observable.
Related guides
Section titled “Related guides”- Shared sources: share state between views and own its lifetime.
- Routing: connect navigation to source behavior.
Find these pages again in the sidebar’s Guides group.
Where next
Section titled “Where next”- Views: render the values and commands exposed by your core.
- Ownership & cleanup: choose shared or isolated state and place cleanup at its owner boundary.
When the owning feature or session permanently discards a core, call core.dispose().
An external source stays under its owner’s control until nothing else needs it.