React
EnergyProvider, the energy hooks, and the EnergyGate and EnergyIndicator components.
The React layer wraps the core engine in a provider and exposes hooks that re-render exactly when energy state changes. Import from @kumbatio/energy-system/react (requires React 19.2+).
Provider
Wrap your app once. The provider creates its own engine, or accepts one you created:
import { EnergyProvider } from '@kumbatio/energy-system/react'
import { localStoragePersistence } from '@kumbatio/energy-system/persistence'
export function App() {
return (
<EnergyProvider defaultLevel={100} persistence={localStoragePersistence()}>
<Screen />
</EnergyProvider>
)
}Prop
Type
With applyToDOM left on, the provider keeps document.body stamped for the CSS path automatically.
Hooks
import {
useEnergyState,
useEnergyLevel,
useEnergyLevelCycler,
useStrategy,
useEnergyGate,
useEnergyPresence,
} from '@kumbatio/energy-system/react'
import { uiVisibilityStrategy, presenceAtOrAbove } from '@kumbatio/energy-system'
function Screen() {
const state = useEnergyState() // full EnergyState (level, timestamp, source, ...)
const [level, setLevel] = useEnergyLevel() // tuple: level + setter
const cycle = useEnergyLevelCycler() // () => void, 100 → 75 → ... → 0 → 100
const ui = useStrategy(uiVisibilityStrategy) // resolved config for current level
const canDoComplexWork = useEnergyGate(75) // boolean: level >= 75
const aiChat = useEnergyPresence(presenceAtOrAbove(75)) // 'visible' | 'muted' | 'hidden'
return (
<div>
<button onClick={cycle}>Energy: {level}</button>
{ui.sidebar && <aside>Sidebar</aside>}
{canDoComplexWork && <PlanningBoard />}
</div>
)
}| Hook | Returns |
|---|---|
useEnergyState() | The full EnergyState |
useEnergyLevel() | [level, setLevel] - setter takes (level, source?) |
useEnergyLevelCycler() | A stable function that cycles to the next level |
useStrategy(strategy) | The strategy's config, memoized per level |
useEnergyGate(minLevel) | true when the current level meets or exceeds minLevel |
useEnergyPresence(presenceMap) | The resolved EnergyPresence for the current level |
All hooks must be used inside an EnergyProvider - they throw otherwise.
EnergyGate
Declarative gating for a subtree. Headless - it renders no wrapper element:
import { EnergyGate } from '@kumbatio/energy-system/react'
// Shorthand: needs at least 75 energy, hidden below
<EnergyGate min={75}>
<AiChatPanel />
</EnergyGate>
// Low-energy-only affordance
<EnergyGate max={25}>
<RecoveryHint />
</EnergyGate>
// Full presence map + fallback; function children receive the resolved
// presence so 'muted' can style itself
<EnergyGate presence={aiChatPresence} fallback={<QuietPlaceholder />}>
{(presence) => <AiChatPanel muted={presence === 'muted'} />}
</EnergyGate>min and max together create a band (visible only inside the range). See Presence for how presence maps work.
EnergyIndicator
A headless render-prop component for building your own energy control - battery, gauge, emoji, whatever:
import { EnergyIndicator } from '@kumbatio/energy-system/react'
<EnergyIndicator>
{({ level, label, description, cycle, setLevel, levels }) => (
<button onClick={cycle} title={description}>
{label} ({level})
</button>
)}
</EnergyIndicator>The render props also include state, definition, and cognitiveProfile for richer indicators.
Full React reference: /docs/api/react.
