smart-components
Smart State ✦

API reference

useSmartState signature, options, return shape, and gotchas.

Signature

function useSmartState<T>(
  initial: T | (() => T),
  defaultContext?: string,
  options?: UseSmartStateOptions<T>,
): readonly [T, Dispatch<SetStateAction<T>>, SmartStateAI<T>];

T is inferred from initial. The runtime shape used for prompting & validation is read from the value of initial, so JS callers get the same behavior without annotations.

Options

OptionTypeDefaultDescription
shapeShapeDescriptorinferredEscape hatch when initial is null, undefined, or empty.
cachebooleantrueLRU(16) keyed by (shape, context).
maxTokensnumber512Per-call token cap.
temperaturenumber0.7Sampling temperature.
onGenerate(value: T) => voidFires after a successful generate, with the parsed value.

Return shape

The hook returns a tuple [value, setValue, ai]:

FieldTypeDescription
valueTCurrent value. Full useState semantics.
setValueDispatch<SetStateAction<T>>Setter. Cancels any in-flight generate.
ai.generate(contextOverride?: string) => voidTrigger an AI fill. Optional per-call context.
ai.status'idle' | 'loading' | 'ready' | 'error'State machine.
ai.errorError | nullSet when status is error.
ai.reset() => voidReset status/error to idle (does not change value).

ShapeDescriptor

type ShapeLeaf = 'string' | 'number' | 'boolean';

type ShapeDescriptor =
  | ShapeLeaf
  | { type: 'object'; fields: Record<string, ShapeDescriptor> }
  | { type: 'array'; item: ShapeDescriptor };

Examples:

'string'
{ type: 'array', item: 'string' }
{ type: 'object', fields: { name: 'string', age: 'number' } }
{ type: 'array', item: { type: 'object', fields: { title: 'string', done: 'boolean' } } }

Drop-in useState behavior

const [count, setCount] = useSmartState(0);
setCount(5);
setCount((c) => c + 1);

If you don't pass defaultContext, ai.generate() is still callable — you'll need to pass a context override every time.

Errors

const [v, , ai] = useSmartState(0, 'random');

{ai.status === 'error' && <p style={{ color: 'red' }}>{ai.error?.message}</p>}

Common error reasons:

  • cannot infer shapeinitial is null/undefined/empty and options.shape wasn't passed.
  • JSON.parse failed — the model returned non-JSON. Try lowering temperature.
  • expected <type>, got <type> — shape validation failed. The hook returns a specific field path in the error.

Cache

LRU(16) keyed by (JSON.stringify(shape), context). Hits are synchronous — no re-render flash. Opt out:

useSmartState(0, 'random', { cache: false });

Notes & gotchas

  • setValue mid-generate cancels the in-flight call — user intent beats AI.
  • useSmartState({ tags: [] }) throws at mount — the inner empty array can't be introspected. Seed it (tags: ['example']) or pass options.shape.
  • Date, Map, Set, RegExp, functions, bigint, symbol in initial throw at mount — JSON-serializable types only.
  • Requires the SmartClient to support 'complete'. No streaming — generate is atomic; setValue fires once on success.
  • JS works identically. No generics needed — the shape comes from initial at runtime.