Hyperlinks: Insert > Link and HYPERLINK
Excel's two ways of putting a link in a cell. Insert > Link (Ctrl+K) puts one ON the cell, so editing the text keeps it and clearing the cell takes it away; the HYPERLINK function puts one in a formula with a friendly name. A target that reads like an address moves the selection instead of leaving the page, so a cell links to another sheet; anything with a scheme opens in a new tab. A single click follows, a drag selects. Links ride in getState(), move with an insert, and go into the .xlsx both ways. (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's two ways of putting a link in a Svelte 5 spreadsheet cell. Insert > Link, or Ctrl+K, puts a link ON the cell: the cell keeps whatever it says and the link is kept beside it, so editing the text keeps the link and clearing the cell takes it away. The HYPERLINK function puts one in a formula, showing a friendly name. A target that reads like an address moves the selection instead of leaving the page, so a cell can link to another sheet; anything with a scheme opens in a new tab. A single click follows, a drag selects. Links ride in getState(), move with an insert or a delete, and go into the .xlsx both ways. Enterprise, in @svgrid/enterprise.
Excel's two ways of putting a link in a cell, both here.
Insert > Link (Ctrl+K) a link ON the cell: the cell keeps whatever it says, and the link is kept beside it. Edit the text and the link stays; clear the cell and it goes. This is what column C uses. =HYPERLINK(url, name) a link IN a formula, which shows the friendly name. Column E uses it, built from the ticket id beside it.
A target that reads like an address moves the selection instead of leaving the page: the Owner column links to the People sheet, so clicking one goes there. Anything with a scheme, or a bare www., opens in a new tab. A single click follows the link, a drag from the same cell selects, which is Excel's rule.
Links ride in getState() under links, move with an insert or a delete, and go into the .xlsx as real hyperlinks, an external one as a relationship and an internal one as a location, both read back.
Try: click a ticket in C, then an owner in D. Press Ctrl+K on a cell to add or edit a link, and Insert > Remove to take one off. Insert a row above 2 and watch the links move with their rows. File > Save As, and open the file in Excel.
Imports, features and API used
Imports: @svgrid/enterprise
Frequently asked questions
Is the link on the text or on the cell?
On the cell, the way Excel keeps one. The cell holds whatever was typed and the link sits beside it, so editing the text keeps the link, a format change keeps it, saving keeps it, and clearing the cell takes it away. The HYPERLINK function is the other way, a link that lives in the formula.
Can a link go to another sheet?
Yes. A target that reads like an address, Sheet2!B4 or B4 or a defined name, moves the selection there instead of leaving the page, switching sheets if it has to. Anything with a scheme, or a bare www., opens in a new tab.
Do links survive a save?
Yes, in both directions. An external link is written as a relationship with TargetMode External, which is what Excel writes, and an internal one as a location on the hyperlink element; a file opened back carries both, tooltips included.
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.
- Pivot Tables in Svelte - Summarize Data Without a Spreadsheet - Run a cross-tab pivot directly inside your Svelte app using @svgrid/enterprise createPivotModel - no Excel, no server-side aggregation, no stale exports.
- Spreadsheet-Style Cell Range Selection in SvGrid - How to enable drag-to-select cell ranges, read live selection state, and build a status-bar footer that sums and averages the selected values.
Source code (489-sheet-hyperlinks.svelte)
<script lang="ts">
/**
* 479. Hyperlinks: Insert > Link and the HYPERLINK function
* ---------------------------------------------------------
* Excel's two ways of putting a link in a cell, both here.
*
* Insert > Link (Ctrl+K) a link ON the cell: the cell keeps whatever
* it says, and the link is kept beside it. Edit
* the text and the link stays; clear the cell
* and it goes. This is what column C uses.
* =HYPERLINK(url, name) a link IN a formula, which shows the friendly
* name. Column E uses it, built from the ticket
* id beside it.
*
* A target that reads like an address moves the selection instead of
* leaving the page: the Owner column links to the People sheet, so
* clicking one goes there. Anything with a scheme, or a bare www., opens
* in a new tab. A single click follows the link, a drag from the same
* cell selects, which is Excel's rule.
*
* Links ride in `getState()` under `links`, move with an insert or a
* delete, and go into the .xlsx as real hyperlinks, an external one as a
* relationship and an internal one as a location, both read back.
*
* Try: click a ticket in C, then an owner in D. Press Ctrl+K on a cell to
* add or edit a link, and Insert > Remove to take one off. Insert a row
* above 2 and watch the links move with their rows. File > Save As, and
* open the file in Excel.
*/
import { SvSheet, createWorkbook, createSheetDocument } from '@svgrid/enterprise'
const tickets = [
{ id: 'SG-1041', title: 'Frozen panes drift on zoom', owner: 'Ada', status: 'Open' },
{ id: 'SG-1042', title: 'Paste from Excel loses italics', owner: 'Grace', status: 'Open' },
{ id: 'SG-1043', title: 'CSV export quotes twice', owner: 'Linus', status: 'Closed' },
{ id: 'SG-1044', title: 'Sparkline colours ignore the theme', owner: 'Ada', status: 'Open' },
{ id: 'SG-1045', title: 'Name Box refuses a sheet-qualified ref', owner: 'Grace', status: 'Open' },
]
const rows: string[][] = [
['Id', 'Owner', 'Title', 'Status', 'On the tracker'],
...tickets.map((t, i) => [
t.id,
t.owner,
t.title,
t.status,
`=HYPERLINK("https://example.com/issues/" & A${i + 2}, "Open " & A${i + 2})`,
]),
]
const people: string[][] = [
['Person', 'Team', 'Open tickets'],
['Ada', 'Grid', '=COUNTIFS(Tickets!B2:B6, A2, Tickets!D2:D6, "Open")'],
['Grace', 'Sheet', '=COUNTIFS(Tickets!B2:B6, A3, Tickets!D2:D6, "Open")'],
['Linus', 'Platform', '=COUNTIFS(Tickets!B2:B6, A4, Tickets!D2:D6, "Open")'],
]
const wb = createWorkbook([
{ name: 'Tickets', cells: rows },
{ name: 'People', cells: people },
])
const doc = createSheetDocument({ workbook: wb })
const at = { rowIdAt: (i: number) => `r${i}`, columnIdAt: (i: number) => String.fromCharCode(65 + i) }
const sheet = doc.get('Tickets')
sheet.formats.set([[0, 0, 0, 4]], { bold: true, fill: '#e2e8f0', color: '#0f172a' }, at)
sheet.widths.A = 90
sheet.widths.B = 90
sheet.widths.C = 260
sheet.widths.E = 150
sheet.freeze = { rows: 1, cols: 0 }
// Column C: a link on each title, out to the tracker. Column B: a link to
// the person on the other sheet, which moves the selection rather than
// leaving the page.
sheet.links = Object.fromEntries(tickets.map((t, i) => [
`r${i + 1}`,
{
C: { target: `https://example.com/issues/${t.id}`, tip: `${t.id} on the tracker` },
B: { target: `People!A${['Ada', 'Grace', 'Linus'].indexOf(t.owner) + 2}`, tip: `${t.owner} on the People sheet` },
},
]))
const peopleSheet = doc.get('People')
peopleSheet.formats.set([[0, 0, 0, 2]], { bold: true, fill: '#e2e8f0', color: '#0f172a' }, at)
peopleSheet.widths.A = 110
peopleSheet.widths.B = 110
peopleSheet.widths.C = 110
// And back the other way: each person links to the ticket list.
peopleSheet.links = {
r1: { A: { target: 'Tickets!A1', tip: 'Back to the tickets' } },
r2: { A: { target: 'Tickets!A1', tip: 'Back to the tickets' } },
r3: { A: { target: 'Tickets!A1', tip: 'Back to the tickets' } },
}
</script>
<SvSheet document={doc} height="100%" rows={16} columns={8} />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).