Tree editing (drag / rename / sort)
SvTree with drag-drop reorder (drop before / after / inside), inline rename (double-click or F2) and sortable siblings. Uses the moveTreeNode helper.
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
SvTree editing in Svelte 5: drag-and-drop reordering that drops before, after or inside a node, inline rename on double-click or F2, and sortable siblings. onMove receives the dragged id, the target id and the drop position, and the exported moveTreeNode helper applies the move to the nodes array; onRename delivers the new label.
SvTree editing: drag-drop reorder (before / after / inside), inline rename (double-click or F2), and sortable siblings. Uses the exported moveTreeNode helper to apply a drop.
Imports, features and API used
Imports: @svgrid/grid
Frequently asked questions
How do I apply a drop?
In onMove call moveTreeNode(nodes, dragId, targetId, position) and assign the result. It removes the node from its old parent and inserts it before, after or inside the target.
How does inline rename work?
With editable on, double-click or F2 turns the label into an input; Enter commits through onRename and Escape cancels.
Why is reorder disabled while sorting?
A sorted tree orders siblings by label, so a manual position would be overridden immediately; the demo enables reorderable only when sort is off.
Related documentation
Related articles
- A Fill Handle (Drag to Fill) in SvGrid - Build a working spreadsheet-style fill handle on top of SvGrid's cell selection and editing - pointer tracking, range highlighting, series fill, and undo/redo integration all covered.
- 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.
- Row Drag-and-Drop Reordering in a Svelte Data Grid - Wire up drag-to-reorder rows in SvGrid using a handle cell, native HTML5 drag events, and a reactive state array - no library required.
Source code (328-tree-editable.svelte)
<script lang="ts">
/**
* SvTree editing: drag-drop reorder (before / after / inside), inline rename
* (double-click or F2), and sortable siblings. Uses the exported
* `moveTreeNode` helper to apply a drop.
*/
import { SvTree, moveTreeNode } from '@svgrid/grid'
import type { SvTreeNode, TreeDropPosition } from '@svgrid/grid'
let nodes = $state<SvTreeNode[]>([
{ id: 'work', label: 'Work', children: [
{ id: 'q1', label: 'Q1 planning' },
{ id: 'q2', label: 'Q2 roadmap' },
{ id: 'reviews', label: 'Reviews' },
] },
{ id: 'personal', label: 'Personal', children: [
{ id: 'trip', label: 'Trip ideas' },
{ id: 'books', label: 'Reading list' },
] },
{ id: 'inbox', label: 'Inbox' },
])
let expanded = $state<string[]>(['work', 'personal'])
let selected = $state<string | null>('q1')
let sort = $state<'none' | 'asc' | 'desc'>('none')
function renameNode(list: SvTreeNode[], id: string, label: string): SvTreeNode[] {
return list.map((n) => (n.id === id ? { ...n, label } : n.children ? { ...n, children: renameNode(n.children, id, label) } : n))
}
</script>
<div class="wrap">
<header>
<h2>Tree editing</h2>
<p>Drag a node to reorder (drop <strong>before</strong>, <strong>after</strong>, or <strong>inside</strong> a folder), double-click or press <strong>F2</strong> to rename, and sort siblings.</p>
<label class="sortsel">Sort
<select bind:value={sort}>
<option value="none">Manual (drag)</option>
<option value="asc">A -> Z</option>
<option value="desc">Z -> A</option>
</select>
</label>
</header>
<div class="card">
<SvTree
{nodes}
{selected}
expandedIds={expanded}
reorderable={sort === 'none'}
editable
sort={sort === 'none' ? undefined : sort}
onSelect={(id) => (selected = id)}
onToggle={(id, open) => (expanded = open ? [...expanded, id] : expanded.filter((e) => e !== id))}
onMove={(dragId, targetId, pos: TreeDropPosition) => (nodes = moveTreeNode(nodes, dragId, targetId, pos))}
onRename={(id, label) => (nodes = renameNode(nodes, id, label))}
/>
</div>
<p class="note">{sort === 'none' ? 'Manual order - drag to rearrange.' : 'Sorted - drag is disabled while a sort is active.'}</p>
</div>
<style>
.wrap { padding: 20px; max-width: 520px; display: flex; flex-direction: column; gap: 14px; }
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; }
.sortsel { display: inline-flex; align-items: center; gap: 8px; margin-top: 10px; font-size: 12px; font-weight: 600; color: var(--sg-muted, #64748b); }
.sortsel select { padding: 5px 8px; border: 1px solid var(--sg-border, #cbd5e1); border-radius: 7px; background: var(--sg-input-bg, #fff); color: inherit; font: inherit; font-size: 13px; }
.card { display: inline-block; }
.note { margin: 0; font-size: 12px; color: var(--sg-muted, #94a3b8); }
</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.