https://github.com/stainless-code/layers
Headless manager for modal/dialog/drawer/popover/toast UI — open any layer from anywhere, manage it as an ordered named stack, and optionally await a typed result. UI-agnostic core + React/Preact/Vue/Solid/Angular/Svelte adapters.
https://github.com/stainless-code/layers
angular dialog drawer headless modal monorepo overlay popover preact react solid stack state-management svelte toast typescript vue
Last synced: 1 day ago
JSON representation
Headless manager for modal/dialog/drawer/popover/toast UI — open any layer from anywhere, manage it as an ordered named stack, and optionally await a typed result. UI-agnostic core + React/Preact/Vue/Solid/Angular/Svelte adapters.
- Host: GitHub
- URL: https://github.com/stainless-code/layers
- Owner: stainless-code
- License: mit
- Created: 2026-07-09T16:15:03.000Z (13 days ago)
- Default Branch: main
- Last Pushed: 2026-07-12T18:25:23.000Z (10 days ago)
- Last Synced: 2026-07-12T20:09:29.328Z (10 days ago)
- Topics: angular, dialog, drawer, headless, modal, monorepo, overlay, popover, preact, react, solid, stack, state-management, svelte, toast, typescript, vue
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/@stainless-code/layers
- Size: 416 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Contributing: .github/CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Roadmap: docs/roadmap.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# Layers
Modals are just async functions you forgot to `await`.
Headless layer/stack manager — modals, dialogs, drawers, popovers, toasts are **layers in named stacks**. Open any layer from anywhere and `await` a typed result. A zero-dependency core plus React, Preact, Solid, Angular, Vue, Lit, Alpine, and Svelte adapters. State coordination, not UI ownership: Layers owns ordering, keys, transitions, blockers, and the `await client.open(...)` contract; you own rendering, focus, portals, and a11y.
> Experimental — the API may change between minor releases. Pin your version. ([Stability & versioning](https://stainless-code.com/layers/concepts/stability))
Full docs: [stainless-code.com/layers](https://stainless-code.com/layers).
## Packages
| Package | Peer | Install |
| ------------------------------------------------------------------ | ------------------------------------------------------------------- | -------------------------------------------------- |
| [`@stainless-code/layers`](packages/core) | — | `bun add @stainless-code/layers` |
| [`@stainless-code/react-layers`](packages/react) | `react` | `bun add @stainless-code/react-layers` |
| [`@stainless-code/preact-layers`](packages/preact) | `preact` | `bun add @stainless-code/preact-layers` |
| [`@stainless-code/solid-layers`](packages/solid) | `solid-js` | `bun add @stainless-code/solid-layers` |
| [`@stainless-code/angular-layers`](packages/angular) | `@angular/core` | `bun add @stainless-code/angular-layers` |
| [`@stainless-code/vue-layers`](packages/vue) | `vue` | `bun add @stainless-code/vue-layers` |
| [`@stainless-code/lit-layers`](packages/lit) | `lit`, `@lit/context` | `bun add @stainless-code/lit-layers` |
| [`@stainless-code/alpine-layers`](packages/alpine) | `alpinejs` | `bun add @stainless-code/alpine-layers` |
| [`@stainless-code/svelte-layers`](packages/svelte) | `svelte` | `bun add @stainless-code/svelte-layers` |
| [`@stainless-code/layers-devtools`](packages/devtools) | — (Solid panel) | `bun add -d @stainless-code/layers-devtools` |
| [`@stainless-code/react-layers-devtools`](packages/react-devtools) | `react`, `@stainless-code/react-layers`, `@tanstack/react-devtools` | `bun add -d @stainless-code/react-layers-devtools` |
Install only the adapter for your framework — it pulls the core in transitively and re-exports it, so adapter APIs (`StackProvider`, `useStack`, …) and core APIs (`LayerClient`, `layerOptions`, …) both come from the one package. Each adapter lists its framework as a required peer. Svelte ships two entries: `@stainless-code/svelte-layers` (runes, 5.7+) and `@stainless-code/svelte-layers/store` (stores, 3+). Devtools packages are optional; see [Devtools](https://stainless-code.com/layers/guides/devtools).
## Taste (React)
```tsx
import {
layerOptions,
StackProvider,
StackOutlet,
useLayer,
type LayerComponentProps,
} from "@stainless-code/react-layers";
type ConfirmPayload = { title: string };
type ConfirmResponse = boolean;
function ConfirmDialog({
call,
payload,
}: LayerComponentProps) {
return (
{payload.title}
void call.end(true)}>Yes
void call.end(false)}>No
);
}
const confirm = layerOptions({
stack: "confirm",
key: ["confirm", "remove"],
component: ConfirmDialog,
});
function App() {
return (
);
}
function useRemove() {
const c = useLayer(confirm);
return async () => {
const ok = await c.open({ title: "Remove?" });
// ^? boolean
};
}
```
## Docs
- [Getting started](https://stainless-code.com/layers/guides/getting-started)
- [When to use Layers](https://stainless-code.com/layers/concepts/when-to-use)
- [Concepts](https://stainless-code.com/layers/concepts)
- [Adapters](https://stainless-code.com/layers/adapters)
- [Reference](https://stainless-code.com/layers/reference)
- [Changelog](https://stainless-code.com/layers/changelog)
## Concepts
- **`useLayer(options)`** — wired handle (drive + observe). **`useLayerState`** / **`useLayerQueuedState`** / **`useQueuedStack`** — observe-only. Core **`createLayer(options, client)`** for headless callers. See [Concepts](https://stainless-code.com/layers/concepts).
## Develop
This is a [bun workspaces](https://bun.sh/docs/install/workspaces) monorepo; root scripts fan out across packages with `bun run --filter '*' `.
```bash
bun install
bun run check # build (core-first) + format + lint + test + test:dom + typecheck
```
See [`.github/CONTRIBUTING.md`](.github/CONTRIBUTING.md) to contribute and [`AGENTS.md`](AGENTS.md) for agent instructions.
## License
MIT.