Docking layout
SvDockLayout: an IDE-style docking workspace - drag a tab onto another pane and drop on an edge to split or the centre to stack as a tab, drag the splitters to resize, close panes. The whole layout is a serializable, bindable DockNode tree.
A live, editable Svelte 5 component example from the SvGrid gallery (Layout). Read the SvGrid UI component docs for the full API.
About this example
SvDockLayout in Svelte 5, an IDE-style docking workspace. Drag a tab onto another pane and drop it on an edge to split or on the centre to stack it as a tab, drag the splitters to resize and close panes with the x. The whole layout is a serializable DockNode tree bound through bind:layout, so a workspace can be saved and restored.
SvDockLayout - a basic docking workspace (IDE-style). Drag any tab onto another pane and drop it on an EDGE to split or the CENTRE to stack it as a tab; drag the splitters to resize; close panes with the x. The whole layout is a serializable DockNode tree bound to layout.
Imports, features and API used
Imports: @svgrid/grid
Frequently asked questions
What does the layout tree look like?
A DockNode is either a split with a direction, sizes and children, or a pane with tabs and an active id. Every drag rewrites the tree and bind:layout reflects it, so JSON.stringify gives a saveable workspace.
How do I decide between split and stack?
By where you drop. The edges of a pane show a highlight for a split on that side; the centre highlights for adding the tab to that pane.
What happens when the last tab of a pane closes?
The pane is removed and its sibling takes the space, so the tree never keeps empty panes.
Related documentation
Related articles
- Saved Views - Persist Grid Layout and Filters - Give users named, switchable snapshots of column order, sorting, filters, and grouping - persisted to localStorage or a server adapter - using SvGrid's createNamedViews API.
- Avoiding Layout Thrash in Custom Grid Cells - Layout thrash from interleaved DOM reads and writes is the most common cause of scroll jank in grids with custom cells - here is how to find it and design your way out of it.
- Column Resizing and Reordering - Let Users Shape the Grid - How to wire drag-to-resize and drag-to-reorder in SvGrid, then persist the layout to localStorage so it survives page reloads.
Source code (361-dock-layout.svelte)
<script lang="ts">
/**
* SvDockLayout - a basic docking workspace (IDE-style). Drag any tab onto
* another pane and drop it on an EDGE to split or the CENTRE to stack it as a
* tab; drag the splitters to resize; close panes with the x. The whole layout
* is a serializable `DockNode` tree bound to `layout`.
*/
import { SvDockLayout, dockGroup, dockTabs, dockPane, type DockNode } from '@svgrid/grid'
let layout = $state<DockNode>(
dockGroup('row', [
dockTabs([dockPane('explorer', 'Explorer')]),
dockGroup('column', [
dockTabs([dockPane('editor', 'index.ts'), dockPane('readme', 'README.md')]),
dockTabs([dockPane('terminal', 'Terminal'), dockPane('problems', 'Problems')]),
], [0.68, 0.32]),
], [0.24, 0.76]),
)
const files = ['src/index.ts', 'src/app.svelte', 'src/lib/dock.ts', 'README.md', 'package.json']
</script>
<div class="wrap">
<header>
<h2>Docking layout</h2>
<p>Drag a tab onto another pane - drop on an <b>edge</b> to split, the <b>centre</b> to stack as a tab. Drag the dividers to resize, and close panes with the x.</p>
</header>
<div class="stage">
<SvDockLayout bind:layout>
{#snippet pane(p)}
{#if p.id === 'explorer'}
<ul class="explorer">
{#each files as f (f)}<li>{f}</li>{/each}
</ul>
{:else if p.id === 'editor'}
<pre class="code">export function dock() {'{'}
return 'drag me around'
{'}'}</pre>
{:else if p.id === 'readme'}
<div class="doc"><h3>README</h3><p>A basic docking layout built on a serializable tree.</p></div>
{:else if p.id === 'terminal'}
<pre class="term">$ npm run dev
ready in 312 ms</pre>
{:else if p.id === 'problems'}
<div class="doc"><p>No problems detected.</p></div>
{/if}
{/snippet}
</SvDockLayout>
</div>
</div>
<style>
.wrap { padding: 20px; display: flex; flex-direction: column; gap: 14px; height: 100%; box-sizing: border-box; }
header h2 { margin: 0 0 4px; font-size: 20px; font-weight: 700; }
header p { margin: 0; color: var(--sg-muted, #64748b); font-size: 13.5px; line-height: 1.5; }
.stage { flex: 1; min-height: 420px; }
.explorer { list-style: none; margin: 0; padding: 8px; font-size: 13px; display: flex; flex-direction: column; gap: 2px; }
.explorer li { padding: 5px 8px; border-radius: 6px; cursor: default; }
.explorer li:hover { background: var(--sg-row-hover-bg, #f1f5f9); }
.code, .term { margin: 0; padding: 12px; font-size: 12.5px; line-height: 1.6; font-family: ui-monospace, monospace; }
.term { color: var(--sg-muted, #64748b); }
.doc { padding: 12px; font-size: 13px; }
.doc h3 { margin: 0 0 6px; font-size: 14px; }
</style>More Layout examples
- Account & security settings console - A real SaaS settings surface composed from the UI kit: SvMenubar app bar, promise + Undo-action toasts on save, SvPopconfirm on destructive rows, SvHoverCard teammate previews, and frame input adornments (leading icons, prefix affixes, masked API key). Profile / Team / Security tabs over SvCard + SvStat.
- Invoice builder - An adornment-heavy money form: currency prefixes, % suffixes, masked tax IDs (frame adornments), line items with SvPopconfirm delete + Undo action toasts, live SvStat totals, an SvHoverCard tax hint, and a promise toast on Send. Pure UI-kit composition.
- Operations KPI dashboard - A KPI console: SvStat + SvSparkline tiles, an SvGauge SLA dial, SvHoverCard drill-down previews per service, an SvMenubar toolbar (View / Range / Actions) and a promise toast on Refresh. Pure UI-kit composition, no grid dependency.
- Form: rich field types - SvForm reaching the whole input suite from one schema - phone, country, mask, combobox, radio, slider, tags, datetime and file - plus per-field help text, a readonly field, column span, and a promise toast on submit.
- Form: cascading fields - SvForm dependent selects - a child list derives from the parent value via a function `options`, and `dependsOn` clears the child when the parent changes so a stale selection never lingers. Country -> State -> City.