Adapter hooks
Hook and provider signatures across React, Preact, Solid, Angular, Vue, Lit, Alpine, and Svelte adapters.
Wire a definition to imperative ops, or observe stack snapshots for rendering — same options bags on every adapter, framework-native wrappers only. React/Preact/Solid/Angular/Vue/Lit/Alpine re-export the full core. Svelte re-exports selectively — core’s headless createLayer is createLayerHandle.
Wired vs observe
| Role | Hooks | Default return |
|---|---|---|
Wire — createLayer + reactive fields |
useLayer (React / Preact / Solid / Vue / Lit); injectLayer (Angular); createLayer (Alpine / Svelte) |
WiredLayerHandle — ops + state / queued / top |
| Observe mounted — whole stack or per-key filter | useStack, useLayerState |
LayerState[] (or selected T) |
| Observe queued — serial-waiting layers | useQueuedStack, useLayerQueuedState |
LayerState[] (or selected T) |
Wired handles take a full layerOptions bag (including key) and expose payload-only open / upsert:
const confirm = useLayer(layerOptions({ key: "confirm", component: Confirm }));
const ok = await confirm.open({ title: "Remove?" });
// confirm.state — mounted same-key layers
// confirm.top — topmost mounted same-key layer (or null)
Observe hooks take an options bag and never add imperative ops. Per-key hooks default to LayerState[] — empty when nothing matches, not null:
const layers = useLayerState({ key: "confirm", stack: "default" });
// layers.length === 0 when closed
Lit: pass the element host first (useLayer(this, options), useLayerState(this, { key })).
Options bags
Shared across React, Preact, Solid, Angular, Vue, Lit, and Alpine:
interface UseStackOptions<T = LayerState[]> {
stack?: string; // @default "default"
select?: (states: LayerState[]) => T;
compare?: (a: T, b: T) => boolean; // @default Object.is
}
interface UseLayerStateOptions<Key extends LayerKey, …> {
key: Key;
stack?: string;
select?: (states: LayerState<…>[]) => U;
compare?: (a: U, b: U) => boolean; // key-filtered hooks default shallowArrayEqual
}
Optional trailing client?: LayerClient on every hook; omit to use context (useLayerClient / setLayerClient / provideLayerClient). Lit hooks are (host, optionsBag, client?); context via provideLayerClient(host) / <stack-provider>.
Svelte runes/store use the same option shapes; runes entries return SvelteStack<T> (.current + callFor), store entries return Readable<T> plus standalone callFor(client, stackId, state).
Provider / client acquisition
| Adapter | Provider / setup | Client hook |
|---|---|---|
| React | StackProvider({ client?, children }) |
useLayerClient(): LayerClient |
| Preact | StackProvider({ client?, children }) |
useLayerClient(): LayerClient |
| Solid | <LayerClientContext.Provider value={client}> |
useLayerClient(): LayerClient |
| Angular | provideLayerClient(client?): FactoryProvider + LAYER_CLIENT token |
useLayerClient(): LayerClient (injection context) |
| Vue | provideLayerClient(client?): LayerClient |
useLayerClient(): LayerClient |
| Lit | provideLayerClient(host, client?): LayerClient / <stack-provider .client> |
useLayerClient(host).current: LayerClient |
| Alpine | setLayerClient(client?) (optional pin before first use; getLayerClient() lazy-inits) |
getLayerClient(): LayerClient |
| Svelte (runes) | setLayerClient(client?): LayerClient |
useLayerClient(): LayerClient |
| Svelte (store) | setLayerClient(client?): LayerClient |
useLayerClient(): LayerClient |
Hook matrix
| Hook | React / Preact / Solid | Angular | Vue | Lit | Alpine | Svelte (runes) | Svelte (store) |
|---|---|---|---|---|---|---|---|
| Wired handle | useLayer(options, client?) |
injectLayer (useLayer alias) |
useLayer(options, client?) |
useLayer(host, options, client?) |
createLayer |
createLayer |
createLayer |
| Mounted stack | useStack(opts?, client?) |
useStack / injectStack |
useStack(opts?, client?) |
useStack(host, opts?, client?) |
useStack |
useStack |
useStack |
| Queued stack | useQueuedStack(opts?, client?) |
useQueuedStack / injectQueuedStack |
useQueuedStack(opts?, client?) |
useQueuedStack(host, opts?, client?) |
useQueuedStack |
createQueuedStack |
createQueuedStack |
| Mounted per-key | useLayerState(opts, client?) |
useLayerState / injectLayerState |
useLayerState(opts, client?) |
useLayerState(host, opts, client?) |
createLayerState |
createLayerState |
createLayerState |
| Queued per-key | useLayerQueuedState(opts, client?) |
useLayerQueuedState / injectLayerQueuedState |
useLayerQueuedState(opts, client?) |
useLayerQueuedState(host, opts, client?) |
createLayerQueuedState |
createLayerQueuedState |
createLayerQueuedState |
| Client | useLayerClient() |
useLayerClient() |
useLayerClient() |
useLayerClient(host) → .current |
getLayerClient() |
useLayerClient() |
useLayerClient() |
Return shapes
| Hook kind | React / Preact | Solid | Angular | Vue | Lit | Alpine | Svelte (runes) | Svelte (store) |
|---|---|---|---|---|---|---|---|---|
| Wired handle | WiredLayerHandle |
same | same + state/queued/top as Signal |
same + refs on fields | same + StackController fields |
same + Alpine reactive fields |
WiredLayerHandle + runes getters |
WiredLayerStoreHandle (Readable fields) |
useStack / useQueuedStack |
T |
Accessor<T> |
Signal<T> |
Readonly<Ref<T>> |
StackController<T> (.current) |
AlpineStack<T> (.current + callFor) |
SvelteStack<T> |
Readable<T> |
useLayerState / useLayerQueuedState |
U (default LayerState[]) |
Accessor<U> |
Signal<U> |
Readonly<Ref<U>> |
StackController<U> (.current) |
AlpineStack<U> |
SvelteStack<U> |
Readable<U> |
open(payload: PayloadArg<P>["payload"]) => Promise<R>
(payload: PayloadArg<P>["payload"]) => Promise<R>upsert(payload: PayloadArg<P>["payload"]) => Promise<R>
(payload: PayloadArg<P>["payload"]) => Promise<R>dismiss(...args: HandleDismissArgs<R>) => Promise<boolean>
Dismiss the bound instance (or `{ id }`). Response optional iff `undefined extends R` ({@link HandleDismissArgs} / {@link EndArgs } gate).
(...args: HandleDismissArgs<R>) => Promise<boolean>update(patch: Partial<P>, opts?: { id?: string }) => void
(patch: Partial<P>, opts?: { id?: string }) => voidcancelQueued(...args: CancelQueuedArgs<R>) => boolean
Resolves and removes a serially queued layer without mounting (skips blockers). No `id` → FIFO head for this key; `{ id }` → exact queued match. Response may be omitted when `undefined extends R` ({@link CancelQueuedArgs} / {@link EndArgs } gate).
(...args: CancelQueuedArgs<R>) => booleanclientLayerClient
LayerClientstackLayerStack<P, R, E, D>
LayerStack<P, R, E, D>optionsLayerOptions<P, R, E, D, RP> & {
key: DataTag<LayerKey, R, E>;
}
LayerOptions<P, R, E, D, RP> & {
key: DataTag<LayerKey, R, E>;
}currentLayer<P, R, E, D> | null
Live-checked bound instance (`null` when not in the stack).
Layer<P, R, E, D> | nullWired adapters extend LayerHandle with reactive state, queued, and top (top = last entry in state, the topmost same-key mounted layer).
Ergonomic wrappers
| Wrapper | React / Preact / Solid | Angular | Vue | Lit | Alpine | Svelte |
|---|---|---|---|---|---|---|
| Stack render | StackOutlet |
renderStack(vcr) |
StackOutlet |
<stack-outlet> (defineStackElements()) |
x-layer-outlet |
{#each …current} + callFor |
| Headless render | useStackHandles |
useStackHandles |
useStackHandles |
useStackHandles(host, …) |
Rank-2 layerStack + callFor |
— |
| Nested stack | useLayerGroup → Outlet |
useLayerGroup → renderInto(vcr) |
useLayerGroup → Outlet |
useLayerGroup(host, call) → outlet() |
useLayerGroup → nest x-layer-outlet |
useLayerGroup → stack |
| Async actions | useMutationFlow |
same | same | useMutationFlow(host, call) |
same | same |
| App factory | createStackHook |
createStackHook → renderInto |
createStackHook |
createStackHook → CEs |
createStackHook → { setClient, useAppStack } |
not shipped |
Stack rendering divergences
Angular — no shipped StackOutlet. Imperative rendering into a ViewContainerRef:
function renderStack(
vcr: ViewContainerRef,
stackId?: string,
rootProps?: unknown,
): void;
Svelte — no .svelte host components. Render in your markup:
{#each stack.current as state (state.id)}
{@const call = stack.callFor(state)}
{/each}
Store entry: {#each $stack as state} with callFor(client, stackId, state).
Alpine — x-layer-outlet="'stackId'" on <template> (quote the id — bare names hit window); Rank-2 Alpine.data('layerStack') + x-for + callFor. No component on layerOptions.