Skip to content
Layers
Esc
navigateopen⌘Jpreview
On this page

Preact

Preact adapter — same API surface as React, built with preact/compat.

@stainless-code/preact-layers mirrors the React adapter API. The core is re-exported; every export name matches React except the package and peer.

Install

npm install @stainless-code/preact-layers
pnpm add @stainless-code/preact-layers
yarn add @stainless-code/preact-layers
bun add @stainless-code/preact-layers

Peer: preact >=10.19.0 (useSyncExternalStore via preact/compat)

Delta from React

Topic Preact
Package @stainless-code/preact-layers
Peer preact (not react)
JSX runtime Built with h — no react/jsx-runtime dependency
API names Identical — StackProvider, useLayerClient, useStack, useLayer, useLayerState, StackOutlet, StackSubscribe, useStackHandles, useMutationFlow, useLayerGroup, createStackHook

Client

import { StackProvider, StackOutlet } from "@stainless-code/preact-layers";

function App() {
  return (
    <StackProvider>
      <StackOutlet stack="confirm" />
    </StackProvider>
  );
}

Confirm dialog

import {
  layerOptions,
  useLayer,
  type LayerComponentProps,
} from "@stainless-code/preact-layers";

type ConfirmPayload = { title: string };
type ConfirmResponse = boolean;

function ConfirmDialog({
  call,
  payload,
}: LayerComponentProps<ConfirmPayload, ConfirmResponse>) {
  return (
    <div role="dialog">
      <h2>{payload.title}</h2>
      <button onClick={() => void call.end(true)}>Yes</button>
      <button onClick={() => void call.end(false)}>No</button>
    </div>
  );
}

const confirm = layerOptions<ConfirmPayload, ConfirmResponse>({
  stack: "confirm",
  key: ["confirm", "remove"],
  component: ConfirmDialog,
});

function RemoveButton() {
  const c = useLayer(confirm);
  return (
    <button
      onClick={() => void c.open({ title: "Remove item?" })}
    >
      Remove
    </button>
  );
}
import {
  layerOptions,
  useLayer,
} from "@stainless-code/preact-layers";

function ConfirmDialog({
  call,
  payload,
}) {
  return (
    <div role="dialog">
      <h2>{payload.title}</h2>
      <button onClick={() => void call.end(true)}>Yes</button>
      <button onClick={() => void call.end(false)}>No</button>
    </div>
  );
}

const confirm = layerOptions({
  stack: "confirm",
  key: ["confirm", "remove"],
  component: ConfirmDialog,
});

function RemoveButton() {
  const c = useLayer(confirm);
  return (
    <button
      onClick={() => void c.open({ title: "Remove item?" })}
    >
      Remove
    </button>
  );
}

Primitives

Same options-bag contract as ReactuseStack({ stack, select, compare }), useLayer(options) to drive, useLayerState({ key, stack, … }) to observe.

Ergonomic APIs

Same as React — see React for useStackHandles, StackSubscribe, useMutationFlow, useLayerGroup, and createStackHook.

Learn more

Last updated on September 4, 2026

Was this page helpful?