Kumbatio
Quickstarts

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>
  )
}
HookReturns
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.