Dockview: Build VS Code-Style Layouts Without Breaking a Sweat
If you have ever used VS Code, you know the feeling: dragging a file tab to the side to create a split view, popping a terminal into its own floating window, or rearranging your entire workspace on the fly. That experience is powered by a sophisticated docking layout engine, and recreating it from scratch is a monumental undertaking. Dockview packages that entire paradigm into a single library with zero external dependencies. Whether you are building a code editor, a data analysis dashboard, or an admin panel that needs flexible window management, dockview gives you the building blocks to make it happen.
What Ships in the Box
Dockview is not a one-trick pony. The feature list reads like a wish list for complex UI applications:
- Tabbed Panel Groups with drag-and-drop reordering between groups
- Floating Panels that detach from the main grid and hover over content
- Popout Windows that open groups in entirely separate browser windows
- Resizable Split Views in horizontal and vertical orientations
- Full Serialization so layouts can be saved to JSON and restored later
- Eight Built-in Themes including dark, light, VS Code, Abyss, and Dracula variants
- Multi-Framework Support via separate packages for React, Vue, Angular, and vanilla TypeScript
- Zero Dependencies at the core engine level
The architecture is a monorepo where dockview-core handles all the heavy lifting in plain TypeScript, and framework-specific packages like dockview (React), dockview-vue, and dockview-angular provide the bindings.
Getting Docked In
Installation depends on your framework of choice. For React projects:
npm install dockview
or
yarn add dockview
For other frameworks, swap the package name accordingly: dockview-vue for Vue 3, dockview-angular for Angular, or dockview-core if you want the raw engine without any framework wrapper.
One important note: you need to import the CSS stylesheet separately. This is easy to forget and will leave you staring at an invisible layout wondering what went wrong.
Your First Docking Layout
Registering Panel Components
The core concept in Dockview is simple: you define panel components, register them by name, and then use the API to add them to the layout. Here is a minimal React setup:
import { DockviewReact, DockviewReadyEvent, IDockviewPanelProps } from 'dockview';
import 'dockview/dist/styles/dockview.css';
interface PanelParams {
content: string;
}
const TextPanel = (props: IDockviewPanelProps<PanelParams>) => {
return (
<div style={{ padding: '1rem' }}>
<p>{props.params.content}</p>
</div>
);
};
const components = {
textPanel: TextPanel,
};
function App() {
const onReady = (event: DockviewReadyEvent) => {
event.api.addPanel({
id: 'editor',
component: 'textPanel',
params: { content: 'Welcome to the editor panel.' },
});
};
return (
<DockviewReact
components={components}
onReady={onReady}
className="dockview-theme-abyss"
style={{ height: '100vh' }}
/>
);
}
The onReady callback fires once the layout engine is initialized, handing you an API object that controls everything. The components map links string keys to your React components, and addPanel places them into the layout.
Splitting the View
A single panel is not much of a docking layout. Let us add a second panel positioned to the right of the first:
const onReady = (event: DockviewReadyEvent) => {
event.api.addPanel({
id: 'editor',
component: 'textPanel',
params: { content: 'Main editor content goes here.' },
});
event.api.addPanel({
id: 'sidebar',
component: 'textPanel',
params: { content: 'Sidebar with project files.' },
position: { referencePanel: 'editor', direction: 'right' },
});
event.api.addPanel({
id: 'terminal',
component: 'textPanel',
params: { content: '$ npm run build' },
position: { referencePanel: 'editor', direction: 'below' },
});
};
The position object uses a reference panel and a direction (left, right, above, below, or within for tabbing). This creates a classic IDE layout: editor on the left, sidebar on the right, and a terminal docked below the editor.
Tabs Within Groups
When you use direction: 'within', panels stack as tabs inside the same group. This is how you create tabbed interfaces without any extra configuration:
event.api.addPanel({
id: 'file_a',
component: 'textPanel',
params: { content: 'Contents of file A' },
});
event.api.addPanel({
id: 'file_b',
component: 'textPanel',
params: { content: 'Contents of file B' },
position: { referencePanel: 'file_a', direction: 'within' },
});
Users can then drag tabs between groups, reorder them, or tear them off into floating panels, all built in.
Power Moves
Floating Panels and Popout Windows
Sometimes a panel needs to break free from the grid. Dockview supports floating panels that hover over the main layout, as well as popout windows that open in entirely separate browser windows:
const onReady = (event: DockviewReadyEvent) => {
// A floating panel with explicit position and size
event.api.addPanel({
id: 'color_picker',
component: 'textPanel',
params: { content: 'Pick a color' },
floating: { x: 200, y: 150, width: 320, height: 240 },
});
// Pop a group out into its own browser window
const panel = event.api.addPanel({
id: 'detached_console',
component: 'textPanel',
params: { content: 'Running in its own window' },
});
const group = panel.group;
if (group) {
event.api.addPopoutGroup(group);
}
};
Popout windows are a rare feature among layout libraries. Most competitors either do not support them or require significant workarounds.
Saving and Restoring Layouts
For any serious application, layout persistence is non-negotiable. Users expect their workspace to look the same when they come back. Dockview makes this straightforward with toJSON and fromJSON:
import { DockviewApi } from 'dockview';
function usePersistentLayout(api: DockviewApi | undefined) {
const STORAGE_KEY = 'app_layout';
// Save layout on every change
React.useEffect(() => {
if (!api) return;
const disposable = api.onDidLayoutChange(() => {
const layout = api.toJSON();
localStorage.setItem(STORAGE_KEY, JSON.stringify(layout));
});
return () => disposable.dispose();
}, [api]);
// Restore layout on init
const restoreLayout = React.useCallback(() => {
if (!api) return;
const saved = localStorage.getItem(STORAGE_KEY);
if (saved) {
try {
api.fromJSON(JSON.parse(saved));
} catch {
console.warn('Failed to restore layout, starting fresh.');
api.clear();
}
}
}, [api]);
return { restoreLayout };
}
One word of caution: if fromJSON encounters a component name that is not in your registered components map, it can leave the API in a broken state. Always wrap deserialization in error handling and have a fallback to a default layout.
Theming Your Dock
Dockview ships with eight themes out of the box, but you can go further with CSS custom properties or entirely custom theme objects:
import { DockviewTheme } from 'dockview';
const customTheme: DockviewTheme = {
className: 'my-app-theme',
name: 'My App',
gap: 4,
dndOverlayMounting: 'relative',
dndPanelOverlay: 'group',
};
For fine-grained control, the library exposes over 20 CSS variables covering background colors, tab styling, separator borders, and floating panel shadows. Variables like --dv-background-color and --dv-activegroup-visiblepanel-tab-background-color let you match any design system without fighting the library.
Rendering Modes: A Subtle but Important Choice
Dockview offers two rendering strategies for hidden panels. The default onlyWhenVisible mode removes DOM elements when panels are not active, saving memory but losing scroll positions and other DOM state. The always mode keeps the full DOM tree intact and simply hides inactive panels with CSS.
event.api.addPanel({
id: 'stateful_panel',
component: 'textPanel',
params: { content: 'I keep my scroll position' },
renderer: 'always',
});
Choose always for panels with rich internal state like text editors or canvas elements. Stick with the default for simpler content where memory efficiency matters more.
Things to Keep in Mind
Dockview is a powerful tool, but power comes with complexity. The API surface is large, with dozens of methods, properties, and events. Budget some time for learning the layout model, especially the relationship between panels, groups, and the grid.
The library is actively maintained with v5.0.0 dropping just days ago, bringing improved Angular change detection, better Vue header action props, slimmed bundles, and various bug fixes. The sole maintainer is remarkably responsive, but with 116 open issues, some edge cases around drag operations between popout windows and the main window may still bite you.
React components remain mounted even when their panels are hidden in the default rendering mode, which means hooks continue to run. For performance-sensitive panels, consider wrapping them with visibility-aware logic to pause expensive operations when they are not visible.
The Layout Manager Your App Deserves
Dockview fills a gap that surprisingly few libraries address well. While alternatives like FlexLayout and Golden Layout exist, dockview stands out with its zero-dependency core, multi-framework support, and active maintenance cadence. Golden Layout has not seen a release in three years, and FlexLayout remains React-only at v0.x. Dockview hit v5.0.0 with a clean TypeScript API, eight themes, and the full spectrum of docking features.
If your application needs the kind of flexible, user-driven layout that people expect from professional desktop tools, dockview is the library that gets you there without reinventing the wheel. Your users get drag-and-drop tabs, floating panels, and popout windows. You get a clean API and zero dependency headaches. Everyone wins.