Excel keyboard shortcuts
The muscle memory a spreadsheet user arrives with: Ctrl+Arrow jumps to the edge of the data region (a run-boundary search, so it hops gaps rather than running to the end), Ctrl+Shift+Arrow extends there, Ctrl+A takes the current region then the sheet, Ctrl+Space / Shift+Space take the column or row, Ctrl+D / Ctrl+R fill, Ctrl+; stamps the date. One enableSheet() call. Ctrl+Z undoes a whole fill in one press, not one per cell. (requires @svgrid/enterprise)
A live, editable Svelte 5 data grid example from the SvGrid gallery (Spreadsheet). See the SvGrid documentation for the full API.
About this example
Excel keyboard shortcuts on a Svelte 5 SvGrid with one enableSheet() call: Ctrl+Arrow jumps to the edge of the data region with a run-boundary search that hops gaps instead of running to the last row, Ctrl+Shift+Arrow extends the selection there, Ctrl+A takes the current region and then the sheet, Ctrl+Space and Shift+Space take the column or row, Ctrl+D and Ctrl+R fill down and right, Ctrl+; stamps the date. A whole fill is one undo step. Enterprise, in @svgrid/enterprise.
The muscle memory a spreadsheet user arrives with, on a plain <SvGrid>. One call turns it on:
import { enableSheet } from '@svgrid/enterprise' enableSheet()
Navigation Ctrl+Arrow jump to the edge of the data region. Not "move a long way" - it is a run-boundary search, so from a filled cell it runs to the last filled cell before the next blank, and from the edge of a block it hops the gap to the next block. Leave a gap in a column and try it both ways. Ctrl+Shift+Arrow the same jump, extending the selection. Ctrl+A select the current region; press it again to take the whole sheet. Ctrl+Space select the column. Shift+Space select the row.
Fill and entry Ctrl+D fill down. With a range selected it copies the top row into the rest; with one cell it pulls from the cell above, which is what makes it useful while typing. Ctrl+R fill right, same two behaviours. Ctrl+; stamp today's date. Ctrl+Shift+; stamp the time. Ctrl+' copy the cell above VERBATIM. When the formula engine lands this is how you get an unshifted copy to edit. Ctrl+Z one press per action, not per cell - a fill down 30 rows walks back in one.
The grid still owns Ctrl+C / X / V, Ctrl+F and the arrow keys. A command only takes a key when it has something to do: Ctrl+D on the top row with nothing above it declines, and the key falls through.
Imports, features and API used
Imports: @svgrid/grid, @svgrid/enterprise
Table features registered: rowSortingFeature
Columns: task (Task), owner (Owner), q1 (Q1), q2 (Q2), q3 (Q3), q4 (Q4), due (Due)
Frequently asked questions
How do the shortcuts get onto the grid?
enableSheet() registers them once on the grid's shortcut registry; every SvGrid on the page then answers Ctrl+Arrow, Ctrl+Shift+Arrow, Ctrl+A, Ctrl+Space, Shift+Space, Ctrl+D, Ctrl+R and Ctrl+; the way Excel does.
What does Ctrl+Arrow do at a gap?
It stops at the edge of the current run of filled cells, or at the next filled cell when it starts on a blank one, so it hops across gaps rather than running to the end of the sheet.
Is a fill one undo step?
Yes. Ctrl+D and Ctrl+R write through the grid's command context in one batch, so Ctrl+Z takes the whole fill back at once, not one cell per press.
Related documentation
Related articles
- Paste from Excel into a Svelte Data Grid - How to wire up clipboard paste so users can drop a copied Excel or Google Sheets block directly into SvGrid - TSV parsing, type coercion, validation, and row growth all covered.
- How We Built Excel-Style Filters - How SvGrid implements per-column filter menus, type-aware operators, and a shared filter model that works identically for in-memory and server-side data.
- 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.
Source code (452-excel-shortcuts.svelte)
<script lang="ts">
/**
* 452. Excel keyboard shortcuts
* ------------------------------
* The muscle memory a spreadsheet user arrives with, on a plain <SvGrid>.
* One call turns it on:
*
* import { enableSheet } from '@svgrid/enterprise'
* enableSheet()
*
* Navigation
* Ctrl+Arrow jump to the edge of the data region. Not "move a long
* way" - it is a run-boundary search, so from a filled
* cell it runs to the last filled cell before the next
* blank, and from the edge of a block it hops the gap to
* the next block. Leave a gap in a column and try it
* both ways.
* Ctrl+Shift+Arrow the same jump, extending the selection.
* Ctrl+A select the current region; press it again to take the
* whole sheet.
* Ctrl+Space select the column. Shift+Space select the row.
*
* Fill and entry
* Ctrl+D fill down. With a range selected it copies the top row
* into the rest; with one cell it pulls from the cell
* above, which is what makes it useful while typing.
* Ctrl+R fill right, same two behaviours.
* Ctrl+; stamp today's date. Ctrl+Shift+; stamp the time.
* Ctrl+' copy the cell above VERBATIM. When the formula engine
* lands this is how you get an unshifted copy to edit.
* Ctrl+Z one press per action, not per cell - a fill down 30
* rows walks back in one.
*
* The grid still owns Ctrl+C / X / V, Ctrl+F and the arrow keys. A command
* only takes a key when it has something to do: Ctrl+D on the top row with
* nothing above it declines, and the key falls through.
*/
import {
SvGrid,
tableFeatures,
rowSortingFeature,
type GridColumns,
} from '@svgrid/grid'
import { enableSheet } from '@svgrid/enterprise'
enableSheet()
type Row = {
id: number
task: string
owner: string
q1: number | null
q2: number | null
q3: number | null
q4: number | null
due: string
}
// Deliberately gappy. A dense block would make Ctrl+Arrow look like
// Ctrl+Home, and the run-boundary behaviour is the whole point.
const SEED: Array<[string, string, Array<number | null>]> = [
['Design review', 'Ada', [12, 14, null, null]],
['Prototype', 'Ada', [8, 11, 9, null]],
['User testing', 'Lin', [null, 6, 7, 5]],
['', '', [null, null, null, null]],
['Rollout plan', 'Kai', [4, 4, null, null]],
['Migration', 'Kai', [9, 12, 15, null]],
['', '', [null, null, null, null]],
['Docs', 'Lin', [3, null, null, null]],
['Training', 'Ada', [null, null, 6, 8]],
]
const start = (): Row[] =>
SEED.map(([task, owner, q], i) => ({
id: i + 1,
task,
owner,
q1: q[0] ?? null,
q2: q[1] ?? null,
q3: q[2] ?? null,
q4: q[3] ?? null,
due: '',
}))
let rows = $state<Row[]>(start())
const features = tableFeatures({ rowSortingFeature })
const columns: GridColumns<Row> = [
{ id: 'task', field: 'task', header: 'Task', width: 180 },
{ id: 'owner', field: 'owner', header: 'Owner', width: 100 },
{ id: 'q1', field: 'q1', header: 'Q1', width: 80, editorType: 'number' },
{ id: 'q2', field: 'q2', header: 'Q2', width: 80, editorType: 'number' },
{ id: 'q3', field: 'q3', header: 'Q3', width: 80, editorType: 'number' },
{ id: 'q4', field: 'q4', header: 'Q4', width: 80, editorType: 'number' },
{ id: 'due', field: 'due', header: 'Due', width: 130 },
]
const CHEATSHEET: Array<[string, string]> = [
['Ctrl + Arrow', 'Jump to the edge of the data region'],
['Ctrl + Shift + Arrow', 'Extend the selection to that edge'],
['Ctrl + A', 'Select the current region, then the sheet'],
['Ctrl + Space', 'Select the column'],
['Shift + Space', 'Select the row'],
['Ctrl + D', 'Fill down'],
['Ctrl + R', 'Fill right'],
['Ctrl + ;', "Stamp today's date"],
['Ctrl + Shift + ;', 'Stamp the current time'],
["Ctrl + '", 'Copy the cell above, unchanged'],
['Ctrl + Z', 'Undo the whole action, not one cell'],
]
</script>
<section class="wrap">
<SvGrid
data={rows}
{columns}
{features}
selectionMode="cell"
enableCellSelection={true}
enableInlineEditing={true}
statusBar={true}
filterMode="none"
containerHeight={360}
/>
<aside class="keys">
<h3>Try these</h3>
<dl>
{#each CHEATSHEET as [combo, what] (combo)}
<div class="row">
<dt><kbd>{combo}</kbd></dt>
<dd>{what}</dd>
</div>
{/each}
</dl>
<div class="foot">
<p class="note">
Click a cell in Q1 first. The blank rows are there on purpose:
Ctrl+Down runs to the bottom of a block, then hops the gap on the
next press.
</p>
<button type="button" onclick={() => (rows = start())}>
Reset the data
</button>
</div>
</aside>
</section>
<style>
.wrap {
display: flex;
flex-direction: column;
gap: 14px;
}
/* The grid sizes itself from its column widths, so it is left to take the
full row rather than sharing one with the cheat sheet: a side-by-side
flex row makes the table overflow its track and paint over the aside. */
.keys {
font-size: 13px;
}
h3 {
margin: 0 0 8px;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--sg-muted, #64748b);
}
dl {
margin: 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
gap: 0 20px;
}
.row {
display: flex;
gap: 10px;
align-items: baseline;
padding: 4px 0;
border-bottom: 1px solid var(--sg-border, #e2e8f0);
}
dt {
flex: 0 0 130px;
}
dd {
margin: 0;
color: var(--sg-muted, #64748b);
}
kbd {
font-family: ui-monospace, Menlo, monospace;
font-size: 11px;
white-space: nowrap;
border: 1px solid var(--sg-border, #cbd5e1);
border-radius: 3px;
padding: 1px 5px;
}
.foot {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
margin-top: 10px;
}
.note {
margin: 0;
flex: 1 1 320px;
color: var(--sg-muted, #64748b);
line-height: 1.5;
}
button {
flex: 0 0 auto;
font: inherit;
font-size: 12px;
font-weight: 600;
padding: 6px 12px;
border-radius: 7px;
border: 1px solid var(--sg-border, #cbd5e1);
background: var(--sg-bg, #fff);
color: var(--sg-fg, #0f172a);
cursor: pointer;
}
button:hover {
background: var(--sg-row-hover-bg, #f1f5f9);
}
</style>More Spreadsheet examples
- Spreadsheet + Ribbon bar - The whole Excel surface as one component, <SvSheet workbook={wb} />: a six-tab ribbon (Home, Insert, Formulas, Data, Review, View), the Name Box and fx bar, sheet tabs and the Sum / Average / Count status bar. Format cells, merge them, comment on them, validate what goes in, colour them by rule, filter the region, protect the sheet; every button is the same call as its shortcut, so Ctrl+B and the Bold button cannot drift. A two-sheet P&L with the formats travelling in the document.
- Spreadsheet + formulas - Real formula engine inside the grid: cell refs (A1), ranges (A1:A10), SUM / AVG / IF / COUNTIF / ROUND, arithmetic, string concat, cycle detection.
- Per-cell custom borders (KPI) - Editable KPI scorecard. spreadsheetLayout paints spreadsheet-style per-edge custom borders via an absolute-positioned overlay (no border-collapse conflicts). Edit any quarter or target - the borders re-derive: green double = beat target, blue solid = hit, amber dotted = near miss, red dashed = bad miss; row champion gets a colored full frame.
- Cell merging (spreadsheet shell) - A real invoice rendered on an Excel-style shell: A / B / C / D / E column letters across the top, row numbers down the left. Brand band, bill-from / bill-to address blocks, meta block, line items, totals, notes, signatures - all assembled from MergeSpec + CellBorderSpec. Editable Qty / Rate / addresses / notes; totals recompute live.
- HyperFormula integration - Full HyperFormula engine wired into the grid as a peer-optional dep. Editable spreadsheet with A1-style cell refs, dozens of formulas across math (SUM / SUMIF), lookup (VLOOKUP / INDEX-MATCH), text (CONCAT / UPPER), date (TODAY / DATEDIF), logical (IF nests), financial (PMT / IRR / NPV), statistical (AVERAGE / MAX / RANK).