Export - Theme-matched
One xlsx, light or dark - styles read from the same --sg-* tokens the grid renders with. (requires @svgrid/enterprise)
A live, editable Svelte 5 data grid example from the SvGrid gallery (Data Export & Import). See the SvGrid documentation for the full API.
About this example
A theme-matched Excel export from the Svelte 5 data grid. Click export and the xlsx header row, body rows and zebra stripes match whichever theme the grid is showing, light or dark, because the styles are read from the live --sg-* CSS tokens the grid renders with. Re-theme the page and the next export follows without any export configuration.
Click any export button - the resulting file's header row, body rows, and zebra stripes match whichever theme the grid is showing (light or dark). The styles come from the live --sg-* CSS tokens the grid already renders with, so a re-theme of the page re-themes the export.
Imports, features and API used
Imports: @svgrid/grid, @svgrid/enterprise, ../shared/seed
Table features registered: rowSortingFeature, columnFilteringFeature
Columns: company (Company), orderId (Order ID), product (Product), sellDate (Sell date), quantity (Qty), price (Price), country (Country)
SvGridApi methods called: api.exportData()
Frequently asked questions
Where do the export colours come from?
From the computed --sg-* custom properties on the grid element at export time: header background, row background, alternate row background, border and text colours. They become cell fills and fonts in the workbook.
Can I override the export styling?
Yes. exportData accepts a styles object with header, rows and per-cell styles that take precedence over the theme tokens when you want the file to look different from the screen.
Does this apply to PDF too?
Yes. The same token snapshot styles the PDF header and rows, so the two formats look alike.
Related documentation
Related articles
- What's New in SvGrid Enterprise - Export, Pivot, Import, Print, and AI - A practical look at @svgrid/enterprise - pivot tables, Excel and PDF export, data import, printing, and an AI assistant. Covers when each feature earns its place and what to watch out for in production.
- Importing CSV into a Svelte Data Grid - Parse a user-uploaded CSV file into objects, map its headers to grid columns, validate rows before committing, and handle the edge cases that actually bite you in production.
- CSV vs XLSX vs TSV - Which Export Format? - A practical breakdown of CSV, XLSX, and TSV for data grid exports - when each format earns its keep, what each one silently loses, and how to wire all three in SvGrid.
Source code (56-export-theme-matched.svelte)
<script lang="ts">
/**
* 56. Export - theme-matched styles (Pro)
* --------------------------------------
* Click any export button - the resulting file's header row, body rows,
* and zebra stripes match whichever theme the grid is showing (light or
* dark). The styles come from the live `--sg-*` CSS tokens the grid
* already renders with, so a re-theme of the page re-themes the export.
*/
import {
SvGrid,
tableFeatures,
rowSortingFeature,
columnFilteringFeature,
type GridColumns,
} from '@svgrid/grid'
import {
installEnterprise,
setLicenseKey,
type EnterpriseGridApi,
type ExportStyles,
type ExportFormat,
} from '@svgrid/enterprise'
import { makeOrders, type Order } from '../shared/seed'
setLicenseKey('SVENTERPRISE-DEV-DEMO')
const features = tableFeatures({ rowSortingFeature, columnFilteringFeature })
const rows = makeOrders(80)
let api = $state<EnterpriseGridApi<typeof features, Order> | null>(null)
let busy = $state<ExportFormat | null>(null)
let lastExport = $state<string | null>(null)
function readToken(name: string, fallback: string): string {
if (typeof window === 'undefined') return fallback
const v = getComputedStyle(document.documentElement).getPropertyValue(name).trim()
return v || fallback
}
function currentTheme(): 'light' | 'dark' {
if (typeof document === 'undefined') return 'light'
return document.documentElement.dataset.theme === 'dark' ? 'dark' : 'light'
}
function themeStyles(): ExportStyles {
const fg = readToken('--sg-fg', '#0f172a')
const bg = readToken('--sg-bg', '#ffffff')
const headerBg = readToken('--sg-header-bg', '#f1f5f9')
const headerFg = readToken('--sg-header-fg', '#0f172a')
const rowAltBg = readToken('--sg-row-alt-bg', '#f8fafc')
const border = readToken('--sg-border', '#e2e8f0')
return {
headerRow: {
color: headerFg, backgroundColor: headerBg,
fontWeight: 'bold', border: `1px solid ${border}`, textAlign: 'left',
},
rows: {
color: fg, backgroundColor: bg, border: `1px solid ${border}`,
},
rowAlternate: { backgroundColor: rowAltBg },
}
}
async function doExport(format: ExportFormat) {
if (!api) return
busy = format
lastExport = null
try {
await api.exportData({
format,
filename: `orders-${currentTheme()}`,
styles: themeStyles(),
})
lastExport = `${format.toUpperCase()} - ${currentTheme()} theme`
} catch (err) {
console.error('[export]', err)
} finally {
busy = null
}
}
const columns: GridColumns<Order> = [
{ field: 'company', header: 'Company', editorType: 'text', width: 160 },
{ field: 'orderId', header: 'Order ID', editorType: 'text', width: 140 },
{ field: 'product', header: 'Product', editorType: 'text', width: 180 },
{
field: 'sellDate', header: 'Sell date', editorType: 'date', width: 120,
format: { type: 'date', pattern: 'y-m-d' },
},
{ field: 'quantity', header: 'Qty', editorType: 'number', width: 80 },
{
field: 'price', header: 'Price', editorType: 'number', width: 110,
format: { type: 'currency', currency: 'USD' },
},
{ field: 'country', header: 'Country', editorType: 'text', width: 110 },
]
</script>
<section class="flex flex-col flex-1 min-h-0 gap-3">
<div class="flex flex-wrap items-center gap-2 text-sm shrink-0">
<span class="font-medium">Export with current theme:</span>
{#each ['xlsx', 'pdf', 'html'] as fmt (fmt)}
<button
type="button"
onclick={() => doExport(fmt as ExportFormat)}
disabled={busy !== null}
class="rounded-md border px-3 py-1.5 disabled:opacity-50 ex-btn"
>
{busy === fmt ? 'Exporting…' : fmt.toUpperCase()}
</button>
{/each}
{#if lastExport}
<span class="text-xs text-emerald-600 dark:text-emerald-400">✓ {lastExport}</span>
{/if}
<span class="ml-auto ex-hint">
Flip the gallery theme (sidebar sun/moon) then re-export to compare.
</span>
</div>
<div class="flex-1 min-h-0">
<SvGrid responsive={true}
columnResize
data={rows}
columns={columns}
features={features}
filterMode="menu"
selectionMode="cell"
showRowNumbers={true}
showPagination={true}
pageSize={25}
enableInlineEditing={false}
enableCellSelection={true}
rowHeight={36}
containerHeight="100%"
fitColumns={true}
onApiReady={(next) => (api = installEnterprise(next))}
/>
</div>
</section>
<style>
/* Toolbar chrome follows the same --sg-* tokens the export reads. */
.ex-btn {
border-color: var(--sg-border, #cbd5e1);
background: var(--sg-bg, #ffffff);
color: var(--sg-fg, #0f172a);
}
.ex-btn:hover:not(:disabled) { background: var(--sg-row-hover-bg, #f8fafc); }
.ex-hint { color: var(--sg-muted, #64748b); }
</style>More Data Export & Import examples
- Excel / CSV import - File picker + column mapping + per-row validation preview before commit. Reads xlsx / csv / tsv / json with format auto-detect.
- Export + Print - Enterprise feature pack: download to Excel, PDF, CSV, TSV, HTML, or open a printable view in a new window.
- Export - Header + Footer + Logo - Branded xlsx: PNG logo + title + subtitle in the page header, generated date + page numbers in the footer.
- Export - Cell images - Product grid with thumbnail column. On xlsx export each thumbnail is embedded as a real picture cell.
- Export - Multiple sheets - One xlsx with 5 tabs - All orders + per-region splits - independent of the current grid filter.