# jaspr_hooks — instructions and API reference for AI coding agents Canonical URL: https://code-growers.github.io/jaspr_hooks/llms.txt Documentation: https://code-growers.github.io/jaspr_hooks/ Package: https://pub.dev/packages/jaspr_hooks Repository: https://github.com/Code-Growers/jaspr_hooks Issue tracker: https://github.com/Code-Growers/jaspr_hooks/issues This file is the machine-oriented reference for jaspr_hooks. Read it before editing a Jaspr application. For richer examples, follow the documentation URL on each hook entry. ## Purpose and compatibility jaspr_hooks provides ordered local-state and lifecycle hooks for native Jaspr HTML components. It does not embed Flutter, and flutter_hooks cannot be used in a native Jaspr component tree. Current package version: 0.1.2 Dart constraint: >=3.8.0 <4.0.0 Jaspr constraint: >=0.23.3 <0.24.0 (Jaspr 0.23.x only) Universal import: package:jaspr_hooks/jaspr_hooks.dart Browser DOM import: package:jaspr_hooks/web.dart Before installation, inspect the application's pubspec.yaml. Do not install this version into a project on an incompatible Jaspr release. Do not add flutter_hooks or replace Jaspr components with Flutter widgets. ## Installation From the Jaspr application's package root, run: dart pub add jaspr_hooks Or add the dependency explicitly: dependencies: jaspr_hooks: ^0.1.2 Use the universal entry point for components that do not expose native DOM types: import 'package:jaspr_hooks/jaspr_hooks.dart'; When using node keys, native events, or browser observers, import the web entry point instead. It re-exports the universal API: import 'package:jaspr_hooks/web.dart'; import 'package:universal_web/web.dart' as web; Keep the application's normal Jaspr imports for Component, BuildContext, DOM factories, ValueNotifier, AsyncSnapshot, and related types. ## Hook hosts - HookComponent extends StatelessComponent. Subclass it and call hooks from its build(BuildContext) method. - StatefulHookComponent extends StatefulComponent. Use it when hooks must coexist with a Jaspr State or state mixins; call hooks from the associated State.build method. - HookBuilder is a small hook-enabled region. Pass builder: (context) { ... } and call hooks synchronously inside that builder. - Calling a hook outside an active build of one of these hosts throws StateError. Minimal example: import 'package:jaspr/dom.dart'; import 'package:jaspr/jaspr.dart'; import 'package:jaspr_hooks/jaspr_hooks.dart'; class Counter extends HookComponent { const Counter({super.key}); @override Component build(BuildContext context) { final count = useState(0); return button( onClick: () => count.value++, [Component.text('Count: ${count.value}')], ); } } ## Rules of hooks 1. Call hooks synchronously at the top level of a HookComponent build, a StatefulHookComponent State.build, a HookBuilder builder, or a custom use... function invoked from one of those locations. 2. Call the same hook runtime types in the same order on every build. Never put a hook call behind a condition, variable-length loop, early return, event handler, timer, Future callback, or Stream callback. 3. Put conditions inside the hook callback instead. For repeated variable-length content, extract a hook component per item. 4. Ordered keys define hook resource identity. Include every captured value that changes the resource or callback identity. A default empty key list preserves a value for the mount. A nullable omitted key list on effects means rerun on every client build. 5. Do not mutate state while rendering except where a hook's documented initialization requires it. Trigger changes from events, effects, or subscriptions. 6. Hooks are local lifecycle state. Do not share a hook-owned disposable object beyond the lifetime of its owning component unless another owner takes responsibility explicitly. State preservation compares hook runtime type and ordered keys. Key equality follows flutter_hooks semantics: NaN matches NaN, and 0.0 differs from -0.0. ## SSR and hydration contract Jaspr constructs independent hook state on the server and browser. Hook state is not serialized automatically. Keep server initializers and first client output deterministic. Use PreloadStateMixin, AsyncStatelessComponent, serialized @client properties, or jaspr_riverpod for server-loaded data. - useEffect, usePostFrameEffect, useEffectOnce, useMount, useUnmount, useUpdateEffect, useTimeout, and useInterval never run during server or static rendering. - useEffect runs synchronously during browser builds. usePostFrameEffect runs after the next completed browser frame. - useFuture, useStream, and useOnStreamChange accept nullable sources. Passing a non-null source during server rendering throws StateError. Always call the hook in both environments; pass null on the server and supply deterministic initialData when relevant. - useDebounced returns null and creates no Timer on the server. useTimeout and useInterval also create no server Timer. - useListenable, useValueListenable, and useListenableSelector may read deterministic current values on the server but attach no live listener. useOnListenableChange also attaches no server listener. - useExternalStore requires getServerSnapshot during SSR. When also supplied on the client, it preserves that snapshot through initial hydration before subscribing and reading getSnapshot. - useState, useMemoized, useCallback, useRef, useLatest, useDisposable, useInherited, useImperativeHandle, useReducer, usePrevious, useValueChanged, useStreamController, and convenience state controllers can initialize on either platform; their initializers must be deterministic. - useAsyncAction and useOptimistic expose deterministic initial values during SSR. AsyncAction.dispatch and optimistic mutation methods throw there; keep all action mutations in browser event handlers. - Browser lifecycle value hooks return unknown during SSR and the first hydration build. They read browser state and subscribe after the first client frame. Their change-callback variants do not fire for that initial synchronization. - Browser DOM hooks from package:jaspr_hooks/web.dart attach after the first client frame. Value hooks return null before attachment; useNodeKey has no currentNode on the server; event and observer callbacks do not run there. - useId derives deterministic markup ids from tree and hook position. useEvent returns a stable callable whose latest handler works on server and client. - Focus, active-element, abort-controller, animation-frame, history, and clipboard hooks are browser-only. They expose null or inert controller state during SSR and activate after the first client frame. - Never conditionally skip a hook because context.binding.isClient is false. Select a nullable source or branch inside an effect while preserving hook call order. Safe async source pattern: final future = useMemoized?>( () => context.binding.isClient ? loadUser() : null, [context.binding.isClient, userId], ); final snapshot = useFuture(future, initialData: serverUser); ## Custom hooks and low-level lifecycle Prefer composing built-in hooks in a top-level function whose name begins with use. This preserves a small API and reuses tested lifecycle behavior. Use the low-level API only when composition cannot model the resource: - Hook is immutable configuration. Pass optional ordered keys to its constructor and implement createState(). - HookState> owns mutable lifecycle state. - initHook() runs once after attachment. Do not watch inherited components there; read them in build(). - build(BuildContext context) returns the current hook value. - didUpdateHook(H oldHook) handles a preserved state receiving new configuration. - deactivate() forwards Jaspr deactivation. - dispose() releases owned resources. - setState(callback) mutates hook state and requests an unconditional rebuild. - markMayNeedRebuild() requests an optional rebuild; shouldRebuild() decides whether it proceeds. - context, hook, and mounted expose the owning context, current configuration, and attachment state. - reportHookError(error, stackTrace) reports an isolated lifecycle failure through the Jaspr binding. - Register an instance with R use(Hook hook). - Hook states are disposed in reverse call order. Each cleanup failure is reported without preventing the remaining hook states from being cleaned up. Custom hooks obey exactly the same unconditional call-order and SSR rules as built-in hooks. class CounterHook extends Hook { const CounterHook({super.keys}); @override CounterHookState createState() => CounterHookState(); } class CounterHookState extends HookState { var count = 0; void increment() => setState(() => count++); @override int build(BuildContext context) => count; } int useHookCounter([List? keys]) => use(CounterHook(keys: keys)); Related exported types: - ObjectRef is the mutable cell returned by useRef. - ExternalStoreSubscribe and ExternalStoreSnapshotEquals support useExternalStore. - Store and Reducer support useReducer. - ToggleController, CounterController, ListController, MapController, SetController, and QueueController are stable convenience-state controllers. - AsyncAction, AsyncActionHandler, ActionConcurrency, ActionDroppedException, and ActionCancelledException support useAsyncAction. - OptimisticState and OptimisticUpdateHandle support useOptimistic. - Dispose is void Function(), returned by effect callbacks for cleanup. - IsMounted is bool Function(), returned by useIsMounted. - DocumentVisibility has unknown, visible, and hidden values. - PreferredColorScheme has unknown, light, and dark values. - MediaQueryMatch has unknown, matches, and doesNotMatch values. PreferredMotion has unknown, reduce, and noPreference values. - Browser lifecycle callback typedefs receive previous and current enum values. - ElementSize, WindowSize, IntersectionSnapshot, ResizeObserverBox, WindowSizeOptions, IntersectionOptions, WebEventListenerOptions, and MutationObserverOptions support package:jaspr_hooks/web.dart. - StableEventCallback supports useEvent. FocusController, HistoryEntry, HistoryController, ClipboardStatus, ClipboardController, and AnimationFrameCallback support the new browser integrations. ## API reference: framework ### use Signature: R use(Hook hook) Returns the value built by a custom HookState and registers that state at the current call position. Use only inside a hook host build or a custom use... function. Lifecycle and identity are controlled by the Hook runtime type and keys. Docs: https://code-growers.github.io/jaspr_hooks/hooks/custom-hook/ ### useContext Signature: BuildContext useContext() Returns the BuildContext of the currently building hook host. Works during server and client builds. Calling it outside an active hook build throws StateError. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-context/ ### useId Signature: String useId({String prefix = 'jh'}) Returns a deterministic HTML id based on component-tree and hook position. It stays stable across rebuilds and matches between SSR and hydration when tree and hook order match. Changing prefix replaces the id; empty prefixes throw ArgumentError. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-id/ ### useEvent Signature: StableEventCallback useEvent(R Function(T value) handler) Returns one stable callable that always invokes the latest handler. Use a record argument for multi-value events. It owns no browser resource and works during server and client builds. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-event/ ## API reference: foundation ### useLatest Signature: ObjectRef useLatest(T value) Returns one stable ObjectRef whose value is refreshed every build without requesting a rebuild. The hook owns the reference but not value. It works universally; use deterministic inputs when they affect SSR output. Prefer useState for render state. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-latest/ ### useDisposable Signature: T useDisposable(T Function() create, void Function(T value) dispose, [List keys = const []]) Creates and owns T immediately, preserves it while ordered keys match, and disposes the exact resource on replacement or unmount. Creation and cleanup can run on server and client, so create must be deterministic and platform-safe. Do not dispose the value elsewhere. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-disposable/ ### useExternalStore Signature: T useExternalStore(ExternalStoreSubscribe subscribe, T Function() getSnapshot, {T Function()? getServerSnapshot, ExternalStoreSnapshotEquals? equals}) Reads immutable snapshots and owns the browser unsubscribe callback. A server render without getServerSnapshot throws StateError. Supplying the server snapshot on the client preserves hydration output until post-frame subscription; equals optionally filters rebuilds. subscribe(notify) must return synchronous Dispose. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-external-store/ ### useInherited Signature: T? useInherited({Object? aspect}) Returns the nearest matching Jaspr inherited component and registers a dependency during build. aspect is forwarded to implementations supporting selective dependencies. It works universally and does not own the ancestor; handle the nullable result unless ancestry is guaranteed. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-inherited/ ### useImperativeHandle Signature: void useImperativeHandle(ObjectRef? target, T Function() createHandle, [List? keys]) Publishes a limited imperative API through target. Omitted keys recreate every build; matching keys preserve it; target changes replace it. Replacement/unmount clears the old target only if it still contains that handle. Handles are local and not serialized; use useDisposable for owned cleanup. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-imperative-handle/ ### useTimeout Signature: void useTimeout(VoidCallback callback, Duration? delay) Owns one client Timer. null disables/cancels; delay changes restart; callback-only changes do not restart and the latest callback runs. It cancels at unmount and creates no server Timer. Keep the hook call unconditional and pass null when disabled. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-timeout/ ### useInterval Signature: void useInterval(VoidCallback callback, Duration? interval, {bool immediate = false}) Owns a periodic client Timer and optionally queues an immediate first invocation. null pauses; interval/immediate changes replace timers; callback changes use the latest closure without reconnecting. It creates no server Timer and cancels all pending work on unmount. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-interval/ ## API reference: primitives ### useState Signature: ValueNotifier useState(T initialData) Creates, subscribes to, and disposes an owned ValueNotifier. Assigning a different .value rebuilds the owner. initialData is used only when the hook state is first created and must be deterministic for SSR. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-state/ ### useEffect Signature: void useEffect(Dispose? Function() effect, [List? keys]) Runs a synchronous browser-client effect that may return a synchronous cleanup. With omitted/null keys, cleanup and effect run on every client build. With stable keys, state is preserved. With changed keys, a replacement effect initializes and the replaced state is cleaned after the build. The final cleanup runs on disposal. It is skipped on the server. Never return a Future. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-effect/ ### usePostFrameEffect Signature: void usePostFrameEffect(Dispose? Function() effect, [List? keys]) Schedules the effect after the next completed browser frame. It is skipped on the server. A key change or disposal invalidates stale scheduled work; cleanup from a completed run occurs before its replacement and on disposal. Use for committed-DOM work. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-post-frame-effect/ ### useMemoized Signature: T useMemoized(T Function() valueBuilder, [List keys = const []]) Evaluates valueBuilder immediately and preserves its result until ordered keys change. It does not dispose the value. The builder runs during SSR, so keep it deterministic and free of browser-only work. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-memoized/ ### useCallback Signature: T useCallback(T callback, [List keys = const []]) Preserves the callback instance until ordered keys change. Include every captured value whose change requires a fresh closure. Equivalent to memoizing the callback. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-callback/ ### useRef Signature: ObjectRef useRef(T initialValue) Returns a stable mutable ObjectRef. Changing .value does not rebuild. Use for bookkeeping or imperative handles, not render state. The initial value must be deterministic when used during SSR. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-ref/ ### useValueChanged Signature: R? useValueChanged(T value, R? Function(T oldValue, R? oldResult) valueChange) Returns null on the first build. When value differs on a later build, calls valueChange with the previous input and previous result, stores the nullable result, and returns it. It does not schedule a rebuild itself. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-value-changed/ ## API reference: lifecycle and state ### useReducer Signature: Store useReducer(Reducer reducer, {required StateT initialState, required ActionT initialAction}) Creates a Store exposing state and dispatch(action). Initialization computes reducer(initialState, initialAction). Dispatch uses the latest reducer and rebuilds only when nextState != current state. Reducers and initial values must be deterministic and side-effect free during SSR. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-reducer/ ### usePrevious Signature: T? usePrevious(T value) Returns null initially and the input from the preceding build thereafter. It records history without scheduling a rebuild. Server build history is not transferred to the independent client hook tree. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-previous/ ### useIsMounted Signature: IsMounted useIsMounted() Returns a stable bool Function() that is true while attached and false after disposal. Prefer cancellation when available; use this callback as a final guard before updating hook-owned state after delayed work. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-is-mounted/ ### useDebounced Signature: T? useDebounced(T value, Duration timeout) Returns null until value remains unchanged for timeout, then stores it and rebuilds. A value or timeout change cancels and restarts the Timer. Disposal cancels it. On the server it always returns null and creates no Timer. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-debounced/ ## API reference: convenience state and lifecycle ### useToggle Signature: ToggleController useToggle([bool initialValue = false]) Returns a stable controller exposing value, setValue, toggle, and reset. Effective mutations rebuild; reset restores the initially captured value. It initializes universally and methods throw after disposal. Later initialValue changes do not replace existing state. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-toggle/ ### useCounter Signature: CounterController useCounter([int initialValue = 0]) Returns a stable integer controller exposing value, increment([amount]), decrement([amount]), setValue, and reset. No-op changes do not rebuild; reset uses the captured initial value. It initializes universally and methods throw after disposal. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-counter/ ### useList Signature: ListController useList([Iterable initialValue = const []]) Snapshots initialValue and returns a stable controller with an unmodifiable live value plus add/addAll, insert/insertAll, setAt, remove/removeAt, replaceAll, and clear. Effective operations rebuild. Copy value when a historical snapshot is required; methods throw after disposal. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-list/ ### useMap Signature: MapController useMap([Map initialValue = const {}]) Copies initialValue and returns a stable controller with an unmodifiable live value plus setValue, setAll, remove, replaceAll, and clear. Effective changes rebuild. A nullable remove result cannot distinguish an absent key from a present null value; inspect containsKey when needed. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-map/ ### useSet Signature: SetController useSet([Iterable initialValue = const []]) Snapshots unique values and returns a stable controller with an unmodifiable live value plus add, addAll, remove, replaceAll, and clear. Membership no-ops do not rebuild. Keep equality/hash codes stable and sort before rendering if deterministic order is required. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-set/ ### useQueue Signature: QueueController useQueue([Iterable initialValue = const []]) Snapshots a FIFO sequence and returns a stable controller exposing value, length, isEmpty/isNotEmpty, enqueue/enqueueAll, dequeue, replaceAll, and clear. dequeue on empty throws StateError. value is an unmodifiable live list; methods throw after disposal. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-queue/ ### useEffectOnce Signature: void useEffectOnce(Dispose? Function() effect) Runs a synchronous effect on the first browser-client build and owns its optional cleanup until unmount. It is useEffect with empty keys and is skipped entirely during server/static rendering. Use keyed useEffect when captured dependencies must change. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-effect-once/ ### useMount Signature: void useMount(VoidCallback effect) Runs a cleanup-free callback once on the first browser-client build. It is skipped on the server and later callback changes do not rerun it. Do not allocate a resource requiring cleanup; use useEffectOnce instead. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-mount/ ### useUnmount Signature: void useUnmount(VoidCallback effect) Runs the latest callback when the browser-client hook is disposed. It does not register or run during SSR. Prefer colocated setup/cleanup in useEffectOnce when both belong to one resource; avoid ordinary UI mutations during teardown. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-unmount/ ### useUpdateEffect Signature: void useUpdateEffect(Dispose? Function() effect, [List? keys]) Skips the first client build, then follows useEffect replacement and cleanup semantics. null/omitted keys run on every later build; ordered keys run on later key changes. Server effects are skipped. Empty keys mean it never runs because no update changes them. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-update-effect/ ## API reference: async ### useFuture Signature: AsyncSnapshot useFuture(Future? future, {T? initialData, bool preserveState = true}) Subscribes on the browser and exposes none/waiting/done data or error snapshots. Keep Future identity stable with useMemoized. Replacing it ignores stale completions; preserveState keeps prior snapshot data while transitioning. A non-null Future on the server throws StateError; pass null and use initialData/server-data facilities. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-future/ ### useStream Signature: AsyncSnapshot useStream(Stream? stream, {T? initialData, bool preserveState = true}) Subscribes on the browser and exposes none/waiting/active/done snapshots. Replacing the Stream cancels the old subscription; preserveState controls retention of the old snapshot. Disposal cancels. A non-null Stream on the server throws StateError. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-stream/ ### useStreamController Signature: StreamController useStreamController({bool sync = false, VoidCallback? onListen, VoidCallback? onCancel, List? keys}) Creates an owned broadcast StreamController and closes it on disposal or keyed replacement. onListen and onCancel update without replacement when state is preserved. The controller can initialize on either platform, but its stream must not be passed non-null to client-only subscription hooks during SSR. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-stream-controller/ ### useOnStreamChange Signature: StreamSubscription? useOnStreamChange(Stream? stream, {void Function(T event)? onData, void Function(Object error, StackTrace stackTrace)? onError, void Function()? onDone, bool? cancelOnError}) Subscribes on the browser and invokes the latest callbacks without rebuilding automatically. Returns the active subscription or null. Replacing stream or cancelOnError replaces and cancels the subscription; disposal cancels it. A non-null Stream on the server throws StateError. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-on-stream-change/ ## API reference: actions ### useAsyncAction Signature: AsyncAction useAsyncAction({required StateT initialState, required AsyncActionHandler action, ActionConcurrency concurrency = ActionConcurrency.sequential}) Returns a stable controller with state, isPending, error, stackTrace, dispatch(input), and reset(). sequential queues; latest applies only the newest result; drop rejects overlaps; concurrent applies completion order. reset/disposal invalidate queued and UI results but cannot physically cancel running Futures. Initial state renders on the server; dispatch there throws StateError. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-async-action/ ### useOptimistic Signature: OptimisticState useOptimistic(StateT authoritativeState, StateT Function(StateT current, UpdateT update) reducer) Returns a stable controller whose value reapplies pending updates over the latest authoritative state. add returns a handle with commit/rollback; run automates rollback or authoritative commit around a Future; reset clears all updates. Reducers must be pure. SSR returns authoritative state, while add/run/reset throw there. Handles become invalid after resolution, reset, or disposal. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-optimistic/ ## API reference: listenables ### useListenable Signature: T useListenable(T listenable) Returns the same nullable Listenable and rebuilds on every client notification. Replacing the source moves the listener. No listener is attached on the server; deterministic model values may still be read for markup. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-listenable/ ### useListenableSelector Signature: R useListenableSelector(Listenable? listenable, R Function() selector) Evaluates selector and rebuilds on the client only when a notification produces a selection != the previous selection. Replacing the source moves the listener; updating the selector refreshes the stored selection. SSR evaluates the selector but attaches no listener. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-listenable-selector/ ### useValueNotifier Signature: ValueNotifier useValueNotifier(T initialData, [List? keys]) Creates and disposes an owned ValueNotifier but does not subscribe the owning component. Use useValueListenable or useListenableSelector when the owner should observe it. Ordered key changes replace and dispose it. Initialization is available during SSR. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-value-notifier/ ### useValueListenable Signature: T useValueListenable(ValueListenable valueListenable) Returns .value and rebuilds on client notifications. Replacing the source moves the listener. SSR reads the deterministic current value but attaches no listener. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-value-listenable/ ### useOnListenableChange Signature: void useOnListenableChange(Listenable? listenable, VoidCallback listener) Invokes the latest callback on client notifications without rebuilding automatically. Replacing the source moves the listener. No listener is attached during SSR. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-on-listenable-change/ ## API reference: browser lifecycle ### useDocumentVisibility Signature: DocumentVisibility useDocumentVisibility() Returns unknown during SSR and the first hydration build, then visible or hidden after the first client frame. It can remain unknown when the browser lacks a supported visibility API. Rebuilds on later visibility changes and cancels its browser listener on disposal. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-document-visibility/ ### useOnDocumentVisibilityChange Signature: void useOnDocumentVisibilityChange(DocumentVisibilityCallback callback) After first-frame browser synchronization establishes a baseline, invokes the latest callback with (previous, current) for distinct visibility changes. It does not fire for initial synchronization and has no server listener. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-on-document-visibility-change/ ### usePreferredColorScheme Signature: PreferredColorScheme usePreferredColorScheme() Returns unknown during SSR and the first hydration build, then light or dark after the first client frame. It can remain unknown when the browser lacks a supported media-query API. Rebuilds when prefers-color-scheme changes and cleans up the media-query listener. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-preferred-color-scheme/ ### useOnPreferredColorSchemeChange Signature: void useOnPreferredColorSchemeChange(PreferredColorSchemeCallback callback) After first-frame browser synchronization establishes a baseline, invokes the latest callback with (previous, current) for distinct light/dark preference changes. It does not fire for initial synchronization and has no server listener. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-on-preferred-color-scheme-change/ ### useMediaQuery Signature: MediaQueryMatch useMediaQuery(String query) Returns unknown during SSR and first hydration, then matches or doesNotMatch after post-frame browser synchronization. Query changes reset to unknown and replace the owned listener. Prefer CSS for styling-only responsiveness; use this hook when application behavior needs the value. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-media-query/ ### useOnMediaQueryChange Signature: void useOnMediaQueryChange(String query, MediaQueryCallback callback) Establishes a post-frame baseline, then invokes the latest callback with previous/current values for distinct changes. Initial synchronization and query replacement do not invoke it. It attaches nothing on the server and owns listener cleanup. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-on-media-query-change/ ### usePreferredMotion Signature: PreferredMotion usePreferredMotion() Typed prefers-reduced-motion value: unknown during SSR/first hydration, then reduce or noPreference. It owns the underlying media-query listener. Treat unknown as a deterministic safe default and preserve essential feedback when reducing motion. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-preferred-motion/ ### useOnPreferredMotionChange Signature: void useOnPreferredMotionChange(PreferredMotionCallback callback) After initial browser synchronization, invokes the latest callback for distinct reduced-motion preference transitions. It does not fire for the initial baseline and attaches nothing on the server. Use usePreferredMotion when the value must rebuild rendered output. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-on-preferred-motion-change/ ## API reference: browser DOM These APIs require import 'package:jaspr_hooks/web.dart'. Import package:universal_web/web.dart as web when naming native browser types. The web entry point also exports the universal hook API. ### useNodeKey Signature: GlobalNodeKey useNodeKey({String? debugLabel}) Creates one stable key for a rendered browser node. Attach it to exactly one simultaneous component. currentNode is absent during SSR and before attachment; DOM hooks reconcile attached-node changes after frames. The initial debugLabel is preserved with key identity. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-node-key/ ### useFocus Signature: FocusController useFocus(GlobalNodeKey target, {bool enabled = true}) Returns a stable controller with nullable hasFocus plus focus and blur operations. It attaches after rendering, follows target/enabled changes, and cleans its document focus listener on replacement or disposal. SSR state is null; calling imperative methods before attachment throws StateError. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-focus/ ### useFocusWithin Signature: bool? useFocusWithin(GlobalNodeKey target, {bool enabled = true}) Returns whether focus is on the keyed element or a descendant. It is null during SSR, while disabled, and before attachment. The owned focus listener reconnects for target changes and is removed on disposal. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-focus-within/ ### useActiveElement Signature: web.Element? useActiveElement() Tracks document.activeElement after hydration and rebuilds for focus transitions. It returns null during SSR and first hydration, owns one document listener, and releases it on disposal. Do not retain old returned nodes across rebuilds. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-active-element/ ### useAbortController Signature: web.AbortController? useAbortController([List keys = const []]) Creates a controller after browser attachment and aborts it when ordered keys change or the hook unmounts. It returns null during SSR and first hydration. Do not reuse an aborted signal. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-abort-controller/ ### useAnimationFrame Signature: void useAnimationFrame(AnimationFrameCallback callback, {bool enabled = true}) Owns a repeating requestAnimationFrame loop that invokes the latest callback with a Duration timestamp. Disabling or disposing cancels the pending frame. It schedules nothing during SSR. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-animation-frame/ ### useHistoryState Signature: HistoryController? useHistoryState() Returns a post-frame controller containing the current URI/state with push, replace, back, forward, and go operations. Own push/replace changes update synchronously; popstate handles traversal. SSR and first hydration return null. State must be structured-clone compatible. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-history-state/ ### useClipboard Signature: ClipboardController useClipboard() Returns a stable plain-text Clipboard API controller with isSupported, status, text, error, readText, and writeText. It starts inert during SSR and activates after hydration. Operations require browser support, a secure context, permission, and usually a user gesture; always handle rejected Futures. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-clipboard/ ### useElementSize Signature: ElementSize? useElementSize(GlobalNodeKey target, {ResizeObserverBox box = ResizeObserverBox.contentBox}) Owns a ResizeObserver and rebuilds with width/height for contentBox, borderBox, or devicePixelContentBox. Returns null during SSR, before attachment, and when unsupported. Target/box/node changes reset and reconnect; disposal disconnects. Prefer CSS container queries for styling-only behavior. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-element-size/ ### useWindowSize Signature: WindowSize? useWindowSize([WindowSizeOptions options = const WindowSizeOptions()]) Returns post-frame viewport width/height and owns the resize listener plus optional debounce Timer. A negative debounce throws ArgumentError. SSR/first hydration returns null; option changes reset/reconnect. Prefer responsive CSS or useMediaQuery when numeric dimensions are unnecessary. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-window-size/ ### useIntersection Signature: IntersectionSnapshot? useIntersection(GlobalNodeKey target, [IntersectionOptions options = const IntersectionOptions()]) Owns an IntersectionObserver and returns isIntersecting/ratio after a result. Options configure keyed root, rootMargin, thresholds, and freezeOnceVisible. Thresholds must be non-empty values from zero to one. SSR/unsupported browsers return null; target/root/options/node changes reconnect. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-intersection/ ### useEventListener Signature: void useEventListener(String type, void Function(E event) listener, {WebEventTargetResolver? target, bool enabled = true, WebEventListenerOptions options = const WebEventListenerOptions()}) Owns a post-frame native event listener; target defaults to window and may resolve a keyed node. Type, resolved target, enabled, or capture/passive/once options control replacement; callback-only updates use the latest closure. SSR attaches nothing. E must match the actual event and passive callbacks must not preventDefault. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-event-listener/ ### useOnClickOutside Signature: void useOnClickOutside(GlobalNodeKey target, void Function(web.PointerEvent event) listener, {List> additionalTargets = const [], bool enabled = true}) Owns a captured window pointerdown listener and calls the latest callback when the composed event path contains none of the attached target nodes. No attached node means events are ignored. Include portals/triggers as additionalTargets and preserve keyboard dismissal/accessibility separately. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-on-click-outside/ ### useHover Signature: bool? useHover(GlobalNodeKey target, {bool enabled = true}) Reads initial :hover after attachment, then owns pointerenter/pointerleave listeners. Returns null during SSR, while disabled/unattached, or when unsupported. Target/enabled/node changes reset and reconnect. Do not make essential content or controls hover-only; prefer CSS for styling. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-hover/ ### useMutationObserver Signature: void useMutationObserver(GlobalNodeKey target, WebMutationCallback callback, {MutationObserverOptions options = const MutationObserverOptions()}) Owns a MutationObserver and sends immutable record batches to the latest callback. Options select childList, attributes, characterData, subtree, old values, and attributeFilter; an ineffective configuration throws ArgumentError. SSR attaches nothing. Prefer Jaspr state for DOM Jaspr already controls and avoid mutation feedback loops. Docs: https://code-growers.github.io/jaspr_hooks/hooks/use-mutation-observer/ ## Agent completion checklist - Confirm the target uses native Jaspr 0.23.x and a compatible Dart SDK. - Add jaspr_hooks without adding flutter_hooks or Flutter SDK dependencies. - Import package:jaspr_hooks/jaspr_hooks.dart, or package:jaspr_hooks/web.dart for DOM integrations, and choose the smallest suitable hook host. - Keep all hook calls unconditional, synchronous, and in a stable order. - Include correct keys and respect ownership/cleanup behavior. - Preserve deterministic server HTML and first hydration output. - Pass null Future/Stream sources on the server; do not skip their hook calls. - Supply getServerSnapshot for SSR external stores and preserve null/unknown DOM fallbacks through hydration. - Run dart format, dart analyze, and the project's relevant VM/browser/server tests. - Summarize changed dependencies, converted components, SSR decisions, and verification results. Full rendered documentation: https://code-growers.github.io/jaspr_hooks/ AI-agent guide: https://code-growers.github.io/jaspr_hooks/guides/ai-agents/