Zebra rows
The `zebraRows` prop stripes every other DATA row with the theme's --sg-row-alt-bg token. Pinned, group, detail and summary rows keep their single background, so a pinned total row still reads as one solid band. The stripe follows whatever preset or dark mode the page is on.
A live, editable Svelte 5 data grid example from the SvGrid gallery (Rows & Cells). See the SvGrid documentation for the full API.
About this example
Striped rows in the Svelte 5 data grid with the zebraRows prop. Every other data row gets the theme's --sg-row-alt-bg background, while pinned, group, detail and summary rows keep their single background so a pinned totals row still reads as one solid band. The stripe colour is a theme token, so it follows whichever preset or dark mode the page uses.
Set the zebraRows prop on <SvGrid responsive={true}> and every other DATA row gets the --sg-row-alt-bg background. Only data rows stripe - pinned, group, detail, and summary rows keep their single background, so a pinned total row reads as one solid band even when zebra is on.
The stripe color is the theme's --sg-row-alt-bg token, so it follows whatever preset/dark-mode the page is using.
Imports, features and API used
Imports: @svgrid/grid
Table features registered: rowSortingFeature
Columns: region (Region), rep (Sales rep), deals (Deals), revenue (Revenue)
Frequently asked questions
How do I stripe the rows?
Add zebraRows to <SvGrid>. No CSS is needed; the alternate rows use the --sg-row-alt-bg token of the active theme.
How do I change the stripe colour?
Override --sg-row-alt-bg on the grid or its container. A subtle tint of the row background is what the built-in presets use.
Why are group and summary rows not striped?
Striping only applies to data rows. Group headers, detail panels, pinned rows and summary rows keep their own background so they stay visually distinct.
Related documentation
Source code (174-zebra-rows.svelte)
<script lang="ts">
/**
* 174. Zebra rows
* ----------------
* Set the `zebraRows` prop on <SvGrid responsive={true}> and every other DATA row gets
* the `--sg-row-alt-bg` background. Only data rows stripe - pinned,
* group, detail, and summary rows keep their single background, so a
* pinned total row reads as one solid band even when zebra is on.
*
* The stripe color is the theme's `--sg-row-alt-bg` token, so it
* follows whatever preset/dark-mode the page is using.
*/
import {
SvGrid,
tableFeatures,
rowSortingFeature,
type GridColumns,
} from '@svgrid/grid'
let zebraRows = $state(true)
type Row = { region: string; rep: string; deals: number; revenue: number }
const REGIONS = ['North', 'South', 'East', 'West', 'Central']
const REPS = ['A. Lovelace', 'G. Hopper', 'A. Turing', 'M. Hamilton', 'L. Torvalds', 'D. Knuth']
const rows: Row[] = Array.from({ length: 24 }, (_, i) => ({
region: REGIONS[i % REGIONS.length]!,
rep: REPS[i % REPS.length]!,
deals: 3 + ((i * 7) % 19),
revenue: 12_000 + ((i * 4310) % 90_000),
}))
// A pinned total row to show it stays a single background under zebra.
const totals: Row[] = [{
region: 'Total',
rep: '',
deals: rows.reduce((s, r) => s + r.deals, 0),
revenue: rows.reduce((s, r) => s + r.revenue, 0),
}]
const features = tableFeatures({ rowSortingFeature })
const columns: GridColumns<Row> = [
{ field: 'region', header: 'Region' },
{ field: 'rep', header: 'Sales rep' },
{ field: 'deals', header: 'Deals', align: 'right', format: { type: 'number' } },
{ field: 'revenue', header: 'Revenue', align: 'right',
format: { type: 'currency', currency: 'USD', options: { maximumFractionDigits: 0 } } },
]
</script>
<section class="flex flex-col flex-1 min-h-0 gap-3">
<header>
<h2 class="text-base font-semibold">Zebra rows</h2>
<p class="text-xs mt-1" style="color: var(--sg-muted);">
Toggle the <code>zebraRows</code> prop. Alternating <em>data</em> rows take the theme's
<code>--sg-row-alt-bg</code> color. The pinned <strong>Total</strong> row stays a single
band - pinned, group, detail, and summary rows are never striped.
</p>
<label class="zr-toggle mt-2">
<input type="checkbox" bind:checked={zebraRows} />
<code>zebraRows={'{'}{zebraRows}{'}'}</code>
</label>
</header>
<div class="zr-wrap">
<SvGrid responsive={true}
columnResize
data={rows}
columns={columns}
features={features}
pinnedTopRows={totals}
zebraRows={zebraRows}
sortable
showRowSelection={false}
showColumnFilters={false}
rowHeight={32}
containerHeight="100%"
/>
</div>
</section>
<style>
.zr-wrap {
flex: 1; min-height: 0;
border: 1px solid var(--sg-border, #e2e8f0);
border-radius: 8px;
overflow: hidden;
}
.zr-toggle {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 12px;
cursor: pointer;
user-select: none;
}
.zr-toggle code {
background: var(--sg-header-bg, #f1f5f9);
border: 1px solid var(--sg-border, #e2e8f0);
border-radius: 4px;
padding: 1px 6px;
}
</style>More Rows & Cells examples
- Managed row dragging (grid-to-grid) - Reorder rows by dragging their grip, or move a row from one grid into another - both grids share a rowDragGroup, so the row leaves the source and lands in the target. The grid mutates its own data on drop and fires onRowDragEnd on the receiver.
- External drop zones (row drag) - Drag a row out of the grid onto any element - an Archive or Delete bucket - via the rowDropZone action. The row leaves the grid and the zone's onDrop handles it. In-grid reorder still works.
- Custom cells + themes - Avatars, sparklines, progress bars, density toggle, dark mode, full a11y.
- Sparkline cells - In-cell mini charts as a first-class column type: set `sparkline` on a number-array column and the grid paints an inline SVG. Line, area, bar (with +/- coloring), and win/loss - no chart library, no custom snippet.
- Conditional formatting (engine) - Excel-style value-driven cell coloring as a declarative `conditionalFormats` engine prop: gradient heat maps (alpha ramp, zero-centred, banded, column-comparison), in-cell data bars, icon sets, and predicate rules - scoped per column, no per-cell snippet.