Shortcut config
Capabilities are off by default - opt into sort / filter / edit / group / paging with one boolean shortcut each. No `features` array, no fine-grained props. Toggle the switches to build the config live.
A live, editable Svelte 5 data grid example from the SvGrid gallery (Getting Started). See the SvGrid documentation for the full API.
About this example
Every SvGrid capability is off by default, so a bare <SvGrid> is a plain read-only table. This Svelte 5 example switches sorting, filtering, editing, selection, grouping, paging, column resize and row resize on one boolean prop at a time, with no features array and no fine-grained props, and shows the resulting config live. Capabilities that stay off are never downloaded, because their code loads on demand.
Every capability is OFF by default - a bare <SvGrid responsive={true}> is a plain, read-only table. You opt into each power feature with a single boolean shortcut prop, with no tableFeatures({ ... }) import and no fine-grained prop juggling:
sortable -> click headers to sort filterable -> per-column filter menu editable -> double-click a cell to edit selectable -> click a cell, drag for a range groupable -> "Group by this column" in the column menu pageable -> pagination footer columnResize -> drag a header edge; double-click it to autosize rowResize -> drag a row's bottom edge
The last two also show what "off by default" buys: their code is loaded on demand, so a grid that never switches them on never downloads it.
Toggle the switches below and watch the same grid gain each capability. The features set is EMPTY - there is no rowSortingFeature / columnFilteringFeature wiring here. Every capability is switched on by a single boolean shortcut, which injects whatever feature it needs.
Imports, features and API used
Imports: @svgrid/grid, ../shared/seed
Table features registered: ...
Columns: firstName (First name), lastName (Last name), department (Department), country (Country), age (Age), salary (Salary), joinedAt (Joined)
Frequently asked questions
Do I have to import tableFeatures to enable sorting or filtering?
No. The boolean shortcut props sortable, filterable, editable, selectable, groupable and pageable each inject the feature they need. tableFeatures is the fine-grained route for when you want to configure a feature beyond its defaults.
What does a grid with no capabilities enabled cost?
Only the core renderer. Column resize and row resize, for example, are loaded on demand the first time a grid turns them on, so a grid that never enables them never fetches that code.
How do I autosize a column?
With columnResize on, drag a header edge to set a width or double-click the edge to fit the column to its content.
Related documentation
Related articles
- Render Your First Svelte Data Grid in Under 5 Minutes - How to add a fast, accessible, sortable data grid to a Svelte 5 app with SvGrid - covering data, typed columns, feature composition, and the imperative API.
- How We Started Building SvGrid - The boundary we drew on day one - between the logic engine and the render layer - is what everything else rests on. Here is how that line was drawn and what it cost to get right.
Source code (135-shortcut-config.svelte)
<!-- Documented in: docs/help/getting-started.md -->
<script lang="ts">
/**
* 135. Shortcut config (no feature wiring)
* ----------------------------------------
* Every capability is OFF by default - a bare <SvGrid responsive={true}> is a plain,
* read-only table. You opt into each power feature with a single boolean
* shortcut prop, with no `tableFeatures({ ... })` import and no
* fine-grained prop juggling:
*
* sortable -> click headers to sort
* filterable -> per-column filter menu
* editable -> double-click a cell to edit
* selectable -> click a cell, drag for a range
* groupable -> "Group by this column" in the column menu
* pageable -> pagination footer
* columnResize -> drag a header edge; double-click it to autosize
* rowResize -> drag a row's bottom edge
*
* The last two also show what "off by default" buys: their code is loaded
* on demand, so a grid that never switches them on never downloads it.
*
* Toggle the switches below and watch the same grid gain each capability.
* The `features` set is EMPTY - there is no `rowSortingFeature` /
* `columnFilteringFeature` wiring here. Every capability is switched on by
* a single boolean shortcut, which injects whatever feature it needs.
*/
import { SvGrid, tableFeatures, type GridColumns } from '@svgrid/grid'
import { makePeople, type Person } from '../shared/seed'
// Deliberately empty - the shortcuts below do all the wiring. (A typed
// feature set, even an empty one, also anchors the grid's column types.)
const features = tableFeatures({})
const rows = makePeople(200)
const columns: GridColumns<Person> = [
{ field: 'firstName', header: 'First name', editorType: 'text' },
{ field: 'lastName', header: 'Last name', editorType: 'text' },
{ field: 'department', header: 'Department', editorType: 'text' },
{ field: 'country', header: 'Country', editorType: 'text' },
{ field: 'age', header: 'Age', editorType: 'number' },
{
field: 'salary',
header: 'Salary',
editorType: 'number',
format: { type: 'currency', currency: 'USD', options: { maximumFractionDigits: 0 } },
},
{
field: 'joinedAt',
header: 'Joined',
editorType: 'date',
format: { type: 'date', pattern: 'y-m-d' },
},
]
// Each switch maps 1:1 to a shortcut prop on the grid below.
let sortable = $state(false)
let filterable = $state(false)
let editable = $state(false)
let selectable = $state(false)
let groupable = $state(false)
let pageable = $state(false)
let columnResize = $state(false)
let rowResize = $state(false)
const toggles = [
{ key: 'sortable', get: () => sortable, set: (v: boolean) => (sortable = v), hint: 'Click a header to sort' },
{ key: 'filterable', get: () => filterable, set: (v: boolean) => (filterable = v), hint: 'Open the column menu to filter' },
{ key: 'editable', get: () => editable, set: (v: boolean) => (editable = v), hint: 'Double-click a cell to edit' },
{ key: 'selectable', get: () => selectable, set: (v: boolean) => (selectable = v), hint: 'Click a cell, drag for a range' },
{ key: 'groupable', get: () => groupable, set: (v: boolean) => (groupable = v), hint: 'Column menu -> Group by this column' },
{ key: 'pageable', get: () => pageable, set: (v: boolean) => (pageable = v), hint: 'Pagination footer appears' },
{ key: 'columnResize', get: () => columnResize, set: (v: boolean) => (columnResize = v), hint: 'Drag a header edge; double-click it to autosize' },
{ key: 'rowResize', get: () => rowResize, set: (v: boolean) => (rowResize = v), hint: "Drag a row's bottom edge" },
]
const ALL = [
(v: boolean) => (sortable = v),
(v: boolean) => (filterable = v),
(v: boolean) => (editable = v),
(v: boolean) => (selectable = v),
(v: boolean) => (groupable = v),
(v: boolean) => (pageable = v),
(v: boolean) => (columnResize = v),
(v: boolean) => (rowResize = v),
]
function allOn() { for (const set of ALL) set(true) }
function allOff() { for (const set of ALL) set(false) }
const enabledCount = $derived(
[sortable, filterable, editable, selectable, groupable, pageable, columnResize, rowResize]
.filter(Boolean).length,
)
</script>
<section class="flex flex-col flex-1 min-h-0 gap-3">
<div
class="shrink-0 rounded-lg border px-4 py-3"
style="border-color: var(--sg-border); background: var(--sg-header-bg);"
>
<p class="text-sm font-semibold" style="color: var(--sg-fg);">
Empty <code>features</code> set - capabilities come from boolean shortcuts
</p>
<p class="mt-1 text-xs" style="color: var(--sg-muted);">
Capabilities are off by default. Flip a switch to opt in; the grid
below receives exactly the props you toggle.
</p>
<div class="mt-3 flex flex-wrap items-center gap-2">
{#each toggles as t (t.key)}
<label
class="sc-chip"
class:is-on={t.get()}
title={t.hint}
>
<input
type="checkbox"
checked={t.get()}
onchange={(e) => t.set((e.currentTarget as HTMLInputElement).checked)}
/>
<code>{t.key}</code>
</label>
{/each}
<span class="mx-1 h-5 w-px" style="background: var(--sg-border);"></span>
<button type="button" class="sc-btn" onclick={allOn}>All on</button>
<button type="button" class="sc-btn" onclick={allOff}>All off</button>
</div>
<pre class="sc-code mt-3"><code><SvGrid
data={rows} columns={columns}{sortable ? '\n sortable' : ''}{filterable ? '\n filterable' : ''}{editable ? '\n editable' : ''}{selectable ? '\n selectable' : ''}{groupable ? '\n groupable' : ''}{pageable ? '\n pageable' : ''}{columnResize ? '\n columnResize' : ''}{rowResize ? '\n rowResize' : ''}
/></code></pre>
</div>
<div class="flex-1 min-h-0">
<SvGrid responsive={true}
data={rows}
columns={columns}
features={features}
{sortable}
{filterable}
{editable}
{selectable}
{groupable}
{pageable}
{columnResize}
{rowResize}
pageSize={25}
selectionMode="none"
rowHeight={36}
containerHeight="100%"
fitColumns={true}
/>
</div>
<footer class="shrink-0 text-xs" style="color: var(--sg-muted);">
{rows.length} rows · {enabledCount}/6 capabilities enabled · the grid is a
plain read-only table until you opt in.
</footer>
</section>
<style>
.sc-chip {
display: inline-flex; align-items: center; gap: 6px;
padding: 4px 10px;
border: 1px solid var(--sg-border);
border-radius: 999px;
font-size: 12px;
cursor: pointer;
user-select: none;
background: var(--sg-bg);
color: var(--sg-muted);
transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
}
.sc-chip.is-on {
border-color: var(--sg-accent);
color: var(--sg-fg);
background: color-mix(in oklab, var(--sg-accent) 12%, transparent);
}
.sc-chip input { accent-color: var(--sg-accent); cursor: pointer; }
.sc-chip code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.sc-btn {
padding: 4px 10px;
border: 1px solid var(--sg-border);
border-radius: 6px;
background: var(--sg-bg);
color: var(--sg-fg);
font-size: 12px;
cursor: pointer;
}
.sc-btn:hover { border-color: var(--sg-accent); }
.sc-code {
margin: 0;
padding: 10px 12px;
border-radius: 8px;
background: var(--sg-bg);
border: 1px solid var(--sg-border);
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 11.5px;
line-height: 1.5;
color: var(--sg-fg);
overflow-x: auto;
}
</style>More Getting Started examples
- Trading desk - live - 10,000 securities ticking on a 500 ms feed. Pinned Symbol + P&L, per-company logo marks, direction-coloured sparklines, sector chips, a KPI strip, and a notifications bell that flags standout movers. The hero.
- Quick start - A realistic 25-row × 9-column grid with sort, filter, selection, inline editing, and column resize all enabled.
- Admin template - Self-contained admin app: sidebar + three pages (Dashboard, Orders w/ Enterprise export bar, Customers w/ inline edit). Read end-to-end in one file.
- 100k rows × 100 columns - Row + column virtualization. Chunked load with progress + cancellation.
- 1 million rows - A literal 1,000,000-row dataset with sort, filter, group, scroll, and inline edit all on. Chunked generation with progress.