Skip to content

Docking layouts for React that you own

Build your editor, terminal, or dashboard with tabs, splits, and floating panels. Compose the chrome, style it your way, and save the whole layout as JSON.

Get started View on GitHub

Open source · MIT · early adopter release

A working layout with sample data. Your changes stay in this browser.

Tap tabs to switch panels. On wider screens, drag to rearrange.

What dashfoo gives you

Start with DashfooLayout, or compose Layout and Tabset parts into your own chrome. In-app floating panels use the same model, history, and drag-docking.

Headless by design
The engine renders markup tagged with data-dashfoo attributes and leaves the visual theme to you. Style the chrome to match your product.
One serializable model
The whole layout is one plain, zod-validated object: your single source of truth. Save it, diff it, restore it, or send it over the wire.
Real drag-docking
Drag a tab to restack, split, reorder, or dock to an edge. Resizable splitters and maximize come built in, pointer-driven, with no config.
Undo/redo & self-healing
Undo and redo come for free, and tree invariants self-heal after every action, to keep the layout tree consistent.
Controlled or not
Run it uncontrolled and let dashfoo own the state, or drive the model yourself and persist it to localStorage or your backend.
Familiar foundations
react-resizable-panels, @dnd-kit/dom, XState v5, and zod sit behind adapters. They are installed as dependencies; application code works with dashfoo’s components and model.

Up and running in one component

Install the engine and the React layer, give the mount a height, and resolve tab content with a registry or a factory. Tab content never lives in the model. That’s the whole setup.

Read the full guide
pnpm add @dashfoo/core @dashfoo/react @dashfoo/theme
import { model, row, tabset, tab } from "@dashfoo/core";
import { DashfooLayout } from "@dashfoo/react";
import "@dashfoo/theme/dashfoo.css";

const layout = model(row([
  tabset([tab("chart", "Chart"), tab("notes", "Notes")]),
]));

export const Dashboard = () => (
  <div style={{ height: "70vh" }}>
    <DashfooLayout
      defaultModel={layout}
      factory={(node) => <p>{node.name} content</p>}
    />
  </div>
);

Is dashfoo the right fit?

dashfoo combines a split/tab tree with in-app floating panels. Native browser-window popouts and a freeform widget grid are outside its scope.

Reach for dashfoo when…

  • You need real docking: drag tabs to stack, split, or dock to an edge.
  • You want to own every pixel of the chrome and style raw markup yourself.
  • You want the layout as a serializable model you can save, diff, and restore.
  • You want undo/redo and self-healing tree invariants for free.

Reach for something else when…

  • You need a free 2D grid with absolute x/y/w/h. Reach for react-grid-layout.
  • You need a complete dashboard product, including its widgets and data.
  • A simple two-pane resizable split is all you need. Use react-resizable-panels.
  • You need panels that detach into separate browser windows.

Build the dashboard your product needs

Start with the default theme, then make the chrome your own.