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>;
idle — no global status