State Management

NukeJS ships a built-in store for sharing reactive state across client components that live in separate React roots.

Because each "use client" component is hydrated into its own independent React root, React Context cannot carry state across component boundaries. The NukeJS store solves this — all state lives in window.__nukeStores, a page-global Map shared by every bundle regardless of how many times the store module is evaluated.

createStore

Call createStore once at module scope with a unique name and an initial state object. Define stores in their own files — no "use client" directive needed.

app/stores/counter.tstypescript
import { createStore } from 'nukejs'

export const counterStore = createStore('counter', { count: 0 })

If two bundles call createStore with the same name, the first one wins — subsequent calls reuse the existing entry and ignore initialState.

useStore

Call useStore inside any "use client" component to subscribe to a store. The component re-renders whenever state changes, regardless of which React root it lives in.

app/components/Counter.tsxtypescript
"use client"
import { useStore } from 'nukejs'
import { counterStore } from '../stores/counter'

export default function Counter() {
    const { count } = useStore(counterStore)

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={() => counterStore.setState(s => ({ count: s.count + 1 }))}>
                Increment
            </button>
            <button onClick={() => counterStore.setState({ count: 0 })}>
                Reset
            </button>
        </div>
    )
}

Selector — avoid unnecessary re-renders

Pass an optional selector as the second argument. The component only re-renders when the selected value changes (by reference equality).

app/components/Counter.tsxtypescript
// Re-renders only when `count` changes, not on any other state update
const count = useStore(counterStore, s => s.count)

Sharing state across components

Stores are most powerful when two separate client components need to stay in sync. Define the store in a shared file and import it from both:

app/stores/cart.tstypescript
import { createStore } from 'nukejs'

export type CartItem = { id: string; name: string; price: number }

export const cartStore = createStore('cart', {
    items: [] as CartItem[],
    total: 0,
})
app/components/AddToCartButton.tsxtypescript
"use client"
import { cartStore, type CartItem } from '../stores/cart'

export default function AddToCartButton({ item }: { item: CartItem }) {
    return (
        <button
            onClick={() =>
                cartStore.setState(s => ({
                    items: [...s.items, item],
                    total: s.total + item.price,
                }))
            }
        >
            Add to cart
        </button>
    )
}
app/components/CartIcon.tsxtypescript
"use client"
import { useStore } from 'nukejs'
import { cartStore } from '../stores/cart'

export default function CartIcon() {
    const { items, total } = useStore(cartStore)
    return (
        <span>
            🛒 {items.length} items — ${total.toFixed(2)}
        </span>
    )
}
app/pages/shop.tsxtypescript
// Server component — zero JS cost
import AddToCartButton from '../components/AddToCartButton'
import CartIcon from '../components/CartIcon'

export default function ShopPage() {
    const item = { id: '1', name: 'Widget', price: 9.99 }
    return (
        <div>
            <CartIcon />
            <AddToCartButton item={item} />
        </div>
    )
}

CartIcon updates instantly when the button is clicked — they are separate React roots, but both read and write through the same 'cart' entry in window.__nukeStores.

setState

Accepts a full replacement value or an updater function that receives the current state and returns the next state.

app/stores/cart.tstypescript
// Full replacement
cartStore.setState({ items: [], total: 0 })

// Updater function — receives current state, returns next state
cartStore.setState(s => ({ ...s, total: s.total + 5 }))

API reference

APIDescription
createStore(name, initialState)Creates or retrieves a named store backed by the page-global registry.
useStore(store)Subscribes a component to the full store state.
useStore(store, selector)Subscribes to a derived slice — re-renders only when the slice changes.
store.getState()Returns the current state snapshot outside of React.
store.setState(updater)Updates state and notifies all subscribers.
store.subscribe(listener)Registers a raw change listener. Returns an unsubscribe function.