Skip to content
Layers
Esc
navigateopen⌘Jpreview
On this page

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
WirecreateLayer + 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>
PropType
open(payload: PayloadArg<P>["payload"]) => Promise<R>
Type(payload: PayloadArg<P>["payload"]) => Promise<R>
upsert(payload: PayloadArg<P>["payload"]) => Promise<R>
Type(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).

Type(...args: HandleDismissArgs<R>) => Promise<boolean>
update(patch: Partial<P>, opts?: { id?: string }) => void
Type(patch: Partial<P>, opts?: { id?: string }) => void
cancelQueued(...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).

Type(...args: CancelQueuedArgs<R>) => boolean
clientLayerClient
TypeLayerClient
stackLayerStack<P, R, E, D>
TypeLayerStack<P, R, E, D>
optionsLayerOptions<P, R, E, D, RP> & { key: DataTag<LayerKey, R, E>; }
TypeLayerOptions<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).

TypeLayer<P, R, E, D> | null

Wired 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 useLayerGroupOutlet useLayerGrouprenderInto(vcr) useLayerGroupOutlet useLayerGroup(host, call)outlet() useLayerGroup → nest x-layer-outlet useLayerGroupstack
Async actions useMutationFlow same same useMutationFlow(host, call) same same
App factory createStackHook createStackHookrenderInto 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).

Alpinex-layer-outlet="'stackId'" on <template> (quote the id — bare names hit window); Rank-2 Alpine.data('layerStack') + x-for + callFor. No component on layerOptions.

Last updated on July 17, 2026

Was this page helpful?