Migration
Breaking-change policy and migration notes for semver releases.
Layers is pre-1.0. Breaking API changes are expected and ship in minor releases (e.g. 0.1 → 0.2), not majors. Treat semver as a signal, not a guarantee of zero breaks until 1.0 — see Stability & versioning.
How breaks are announced
- Each intentional break gets a Changelog entry tied to a GitHub Release.
- Step-by-step upgrade guidance lives on this page when a release requires consumer action.
0.x — wired handles and observe reshape
Three coordinated 0.x changes: wired handles (createLayer / useLayer), observe hooks return arrays, and stack lookup semantics tightened.
Observe: useLayer(key, …) → useLayerState({ key, … })
The old useLayer was observe-only and returned a single LayerState | null. It is now useLayerState and returns LayerState[] — empty when nothing matches.
12const layer = useLayer("confirm");if (layer) { /* render */ }No newline at end of file12const layers = useLayerState({ key: "confirm" });// layers.length === 0 when closedNo newline at end of file
Per-key queued observation is useLayerQueuedState({ key, … }). Whole-stack queued observation is useQueuedStack({ stack?, select?, compare? }).
Stack hooks: positional args → options bag
1const states = useStack("confirm", (s) => s.filter(…));No newline at end of file1234const states = useStack({stack: "confirm",select: (s) => s.filter(…),});No newline at end of file
Optional trailing client?: LayerClient is unchanged.
Wire: useLayer(options).open(payload)
client.open({ …layerOptions, payload }) remains valid. Wired handles bind identity and expose payload-only ops:
1await client.open({ ...confirmOptions, payload: { title: "Remove?" } });No newline at end of file12const confirm = useLayer(confirmOptions);await confirm.open({ title: "Remove?" });No newline at end of file
Headless (no adapter): createLayer(options, client) from @stainless-code/layers. On Svelte adapters, import that factory as createLayerHandle — createLayer is the wired handle.
Core: LayerStack.find is topmost same-key
find(key) now returns the topmost mounted layer with that key signature (findLast), not the first. Matters in parallel stacks with duplicate keys.
Core: cancelQueued optional { id }
1stack.cancelQueued(key, response); // FIFO head onlyNo newline at end of file12handle.cancelQueued(response); // FIFO head for this keyhandle.cancelQueued(response, { id: "…" }); // exact queued instanceNo newline at end of file
On LayerStack: cancelQueued(key, response, opts?: { id? }). Omit id → first-queued (FIFO) for the key; pass id → exact queued match.
Adapter entry names
| Role | React / Preact / Solid / Vue / Lit | Angular | Alpine | Svelte |
|---|---|---|---|---|
| Wired | useLayer |
injectLayer (useLayer alias) |
createLayer |
createLayer |
| Mounted per-key | useLayerState |
injectLayerState |
createLayerState |
createLayerState |
| Queued stack | useQueuedStack |
injectQueuedStack |
useQueuedStack |
createQueuedStack |
| Queued per-key | useLayerQueuedState |
injectLayerQueuedState |
createLayerQueuedState |
createLayerQueuedState |
0.x — omit dismiss response when void
Toasts and other void-result layers no longer need call.end(undefined). Omit the response whenever undefined extends R — same rule as omitting .open()’s payload (PayloadArg / EndArgs). Applies to call.end/dismiss, stack dismiss/dismissAll/cancelQueued, and handle dismiss/cancelQueued.
1await call.end(undefined); // void toastNo newline at end of file1await call.end();No newline at end of file
Type tighten: LayerHandle.dismiss / cancelQueued used to accept a missing response for every R. Bare handle.dismiss() is now an error when R does not admit undefined (pass true/false for confirms).
LayerClient.dismissAll / LayerGroup.dismissAll stay loosely typed (response?: unknown) — stacks on a client are heterogeneous.