API reference
Five exports: a provider, a producer, a viewport, an imperative hook, and the underlying store factory. Everything is fully typed.
Exports
ts
import {
StatusBarProvider, // holds the store
StatusBar, // producer — registers content while mounted
StatusBarViewport, // host — reads a scope and renders it
useStatusBar, // imperative show()/hide()
createStatusStore, // the plain-JS store (tests / DI)
} from "@mrmartineau/react-status-bar";
import type { StatusEntry, StatusBarMode, StatusStore } from "@mrmartineau/react-status-bar"; <StatusBarProvider>
| Prop | Type | Default | Notes |
|---|---|---|---|
children | ReactNode | — | Your app subtree. |
store | StatusStore | auto | Inject your own store from createStatusStore() — useful for tests, or to share a store across islands/roots. |
<StatusBar>
A side-effect component. Renders null; while mounted it upserts an entry into the store, and removes it on unmount.
| Prop | Type | Default | Notes |
|---|---|---|---|
children | ReactNode | — | Content to contribute. |
priority | number | lowest | Lower is more important (P0 > P1 > P5); lowest-numbered entry wins in replace mode. Unset → lowest, rendered last. |
scope | string | "global" | Target bar. Changing it migrates the entry correctly. |
id | string | auto | Stable identity across remounts. Defaults to a useId(). |
<StatusBarViewport>
Subscribes to a single scope and renders it. Owns presentation (replace vs stack) and optionally portals its output.
| Prop | Type | Default | Notes |
|---|---|---|---|
scope | string | "global" | Which scope to read. |
mode | "replace" | "stack" | "replace" | replace → top entry only; stack → all entries, sorted. |
empty | ReactNode | null | Rendered inside the always-mounted live region when idle. |
separator | ReactNode | — | Between items in stack mode. Marked aria-hidden. |
portalTarget | HTMLElement | string | null | — | Renders inline if omitted; warns in dev if a selector matches nothing. |
className | string | — | Added to the live region. |
ariaLive | "off" | "polite" | "assertive" | "polite" | Live-region politeness. Reserve assertive for urgent states. |
renderItem | (entry: StatusEntry) => ReactNode | — | Custom per-item wrapper (badges, icons, transitions). |
useStatusBar({ scope? })
Returns an imperative handle. The entry it manages is removed automatically when the calling component unmounts.
ts
const sb = useStatusBar({ scope?: string });
sb.show(node: ReactNode, opts?: { priority?: number }): void; // idempotent upsert
sb.hide(): void; // remove the entry createStatusStore()
The plain-JS store behind the provider. Inject it via <StatusBarProvider store={...}>, or unit-test it directly with no React involved.
ts
const store = createStatusStore();
store.upsert({ id, scope, priority, node }); // add or update (idempotent by id)
store.remove(scope, id); // remove an entry
store.subscribe(scope, listener); // → unsubscribe fn
store.getSnapshot(scope); // StatusEntry[] (stable until changed) Types
ts
type StatusEntry = {
id: string; // unique per producer instance
scope: string; // logical bar id
priority: number; // lower = more important (P0 wins); sorts first
order: number; // monotonic registration order (recency tiebreak)
node: React.ReactNode;
};
type StatusBarMode = "replace" | "stack";
type StatusStore = ReturnType<typeof createStatusStore>;