LayoutProvider
The root context provider that owns the layout store and tab registry.
LayoutProvider is the top-level wrapper for every layout. It creates an isolated Zustand store and makes the tab registry available to all descendants. Multiple providers can coexist on the same page without interfering with each other.
Props
initialLayout (required)
The initial shape of the layout tree. Build it with createPanel and createSplit:
initialLayout is read once on mount — the same way React's defaultValue works on an uncontrolled input. After mount the store owns the layout. Changing this prop on a subsequent render does nothing.
If your sizes don't sum to 100, the library normalises them automatically so they do. You can also call normalize(layout) yourself before passing it in — useful when constructing layouts programmatically:
To swap the layout at runtime, use REPLACE_LAYOUT:
dispatch comes from useLayout. See all available actions there.
To fully reset the store (different user, different mode), remount with a new key:
registry (required)
Maps tabType strings to render functions and metadata. See TabRegistry for the full shape.
onChange
Called after every successful state mutation with the new layout tree and the action that caused it.
layout— the full serializable layout tree after the mutation. Save this anywhere and pass it back asinitialLayouton the next mount.action— theLayoutActionthat triggered the change (e.g.{ type: 'RESIZE_SPLIT', ... }). Use this to skip persistence for high-frequency actions like dragging:
See the Persistence guide for database patterns, debouncing, and schema versioning.
generateId
Custom id generator for new nodes created at runtime (splits, tabs). Defaults to a short random string. Override this for SSR determinism or cross-instance uniqueness:
Multiple providers
Each LayoutProvider instance is fully isolated — its own store, its own registry, its own id namespace. DOM ids are auto-prefixed per provider using React's useId() so aria-controls / aria-labelledby associations never collide across instances.
Replacing the layout externally
If you need to swap the entire layout tree at runtime (e.g., switching between saved workspaces), dispatch REPLACE_LAYOUT:
Reading the registry at runtime
Two hooks let you read the registry from any descendant component:
Use these when a component needs to render registry metadata — for example, reading entry.icon or entry.title to display in a custom tab label, or checking entry.closable before showing a close button.
These hooks are the read-only complement to the registry prop. The registry itself is stable across renders — it doesn't cause re-renders when the layout changes.