demos
JavaScript Data Grid Demos
Every feature, running. 312 demos, one feature each, and every one is the real product generating its data in your browser from a fixed seed, so sorting, filtering and editing all do what they would do in your application. Nothing here is a video or a screenshot.
Want a whole application rather than one feature? See the showcase - real public datasets, live, in their own open-source repos.
Start here
Eight demos worth your first five minutes. The rest are below, by the job you came to do.
-
Dashboards
One filter moves the whole dashboard
Top reps, best-selling lines and headline figures, all built from one grid - filter it and every panel follows.
-
Live data
A trading book that moves
Currency formatting, a running P&L, live rankings and a streaming price feed - one dense grid doing a desk’s work.
-
Statistics
Process capability, built in
Cp, Cpk, Pp and Ppk from a single column, ready for a quality report without dropping into a spreadsheet.
-
Formatting
Rules that read the data
Highlight the outliers or the top tenth without naming a number - you describe it, the data sets the line.
-
Performance
A million rows, still smooth
Load a million rows and scroll them, timed live on your own machine, with no pages and no server behind it.
-
Editing
Edit right in the grid
Twenty-two editors on one screen - date, duration, colour, rating, slider - so input lives in the table, not a form.
-
Rendering
Renderer gallery
Sparklines, data bars, bullets and gauges - every built-in renderer, each drawing a column of its own.
-
Extensibility
Extend it to your own data
Give the grid a type of your own - how it stores, sorts, formats and edits - so it speaks your domain, not just text and numbers.
Then these
- A production line A capability figure, an out-of-tolerance list and a live measurement profile, all from one batch.
- Chaining, three levels deep Sales, then revenue per rep, then the top three - three grids, and one filter moves all of them.
- Sankey, chord and network Relationships between categories, drawn straight from the grid’s rows.
- A unit family of your own Register a unit family from its symbols and factors, then format cells in it.
- The statistics panel The full column profile as a docked panel that follows the filters.
- Locales, and it mirrors The same grid in English, Japanese and Arabic - which lays it out right to left.
Find a demo
Search by name, or jump to the part of the grid you are working on.
No demos match that. to see all 312.
Start here
The shortest path from an empty page to a working grid, and the screens it ends up inside.
Getting started
The first ninety seconds. Each of these is readable in full without scrolling the code.
-
Getting started
Hello grid
A working grid in ten lines, no build step and no dependencies
createGrid(el, { columns, rows }) -
Getting started
One million rows
Load a million rows and scroll them, timed on your machine
rows: 1_000_000 -
Getting started
Thirty columns, thirty types
The realistic wide grid: text, numbers, dates, booleans, all at once
columns: [{ type: … }] -
Getting started
Sizing and layout
Filling a container, fixed height, auto height, and resizing
autoHeight -
Getting started
Column defaults and presets
Declaring shared column behaviour once, used by six columns
columnDefaults · columnPresets -
Getting started
Loading data after the grid exists
An empty grid that fills from a fetch, with the loading state between
rows.load(data) -
Getting started
Type inference
Undeclared columns that still sort the way you would expect
default comparator
Fit it into your product UI
The same grid dressed as the product screens it ends up inside, styled through the theme's own tokens rather than against them.
-
Fit it into your product UI
Open positions board
A recruitment dashboard screen: a progress bar, a status pill and a templated kebab, on a card
render: 'progress' · 'pill' · cell.template -
Fit it into your product UI
Recent candidates
A candidate list: circular avatar, name over email, a stage pill and two action buttons
render: 'image' · 'twoline' · 'pill' · cell.template
Columns, types and rows
The shape of the grid: what a column holds, how a value is stored and read, and what a row can be.
Columns
Structure and geometry. Several of these are the reason to choose a grid over a table.
-
Columns
Column groups
Nested headers, collapsible, deciding which children survive a collapse
columnGroups · showWhen -
Columns
Grouping columns as you work
Form, rename, move and dissolve banded headers at runtime, by drag, by keyboard or through the model
grid.columns.groupColumns · Ctrl+Shift+Arrow -
Columns
Pinned columns
Left and right pinned regions scrolling independently
layout: { pin: 'start' | 'end' } -
Columns
Resize, including the neighbour
Drag a grip; shift-drag to take the width from the column beside it
resizePreview: 'live' | 'deferred' -
Columns
Reorder by drag
Moving columns, constrained to their group and pinned region
columns.move(id, index) -
Columns
Show, hide and the tool panel
The visibility list, and what generated columns keep out of it
columns.show / hide -
Columns
Two hundred columns
Body cells exist only for the columns in the horizontal window
columnVirtualisationAbove -
Columns
Auto-size and fit
Size a column to its content, or fit every column to the viewport
columns.autoSize() · columns.fit() · fit: 'content' -
Columns
Column spanning
A cell covering several columns, in its own layer so recycling cannot clip it
cell.spanColumns -
Columns
Row spanning
The same in the other axis, with a bounded scroll frame
cell.spanRows -
Columns
Merged cells
A region cell merged over its own rows, and a note row merged across every column to its right, real Excel merges on export
Column.rowSpan · Column.colSpan -
Columns
Custom headers
A header rendering a sparkline, a filter chip or a unit toggle
column.header.render -
Columns
Alignment
Left, right and centre, and why numbers default the way they do
align: 'left' | 'right' | 'center' -
Columns
Lock position
Columns that cannot be moved, or that always lead the row
layout.lockPosition -
Columns
Column tags
Tag columns, then show only the ones carrying a chosen tag
columnTagFilter · column.tags -
Columns
Aligned grids
A summary band whose columns stay locked to the detail grid
alignedGrids: [grid]
Types and formatting
A type is not a label. It is storage, comparison, parsing, formatting and an editor.
-
Types and formatting
The built-in types
text, number, dateString, boolean, lookup, object, json, secret and image, one column each
type: 'number' | 'date' | … -
Types and formatting
Numbers and currency
Precision, grouping, negatives in parentheses, currency by locale
format: 'currency:GBP:2' -
Types and formatting
Dates, times and zones
The same instant in four time zones
timeZone -
Types and formatting
Timestamps in a display zone
One instant, stored once and shown in the zone the grid resolves for the reader, with the zone named on the page
type: 'timestamp' · showOrigin -
Types and formatting
Durations
Elapsed time that sorts numerically and reads as 3h 12m
type: 'duration' -
Types and formatting
Units
Bytes, distances and rates, parsed in the unit the user typed
createUnitType() -
Types and formatting
Hex, binary and octal
A register dump: one value in four radixes, editable in any
createRadixType({ radix: 16 }) -
Types and formatting
IP addresses and CIDR
IPv4, IPv6 and subnets that sort correctly
type: 'ipv4' | 'ipv6' | 'cidr' -
Types and formatting
JSON and structured values
A nested object in a cell, summarised inline and expandable
type: 'json' -
Types and formatting
Lookups
Store an id, show a label, sort on the label and edit by picking one
lookup: { options } -
Types and formatting
Multi-value cells
Tags in one cell, with containsAny and containsAll filtering
multiple: true -
Types and formatting
The list column type
Chips with an overflow count, a token editor, and filters that ask about the whole list: hasAny, hasAll, hasNone and a count
type: 'list', typeOptions: { of } -
Types and formatting
Computed columns
A column derived from others, recomputed only when its inputs move
value: { deps, compute } -
Types and formatting
Pure versus impure computations
Why one is cached and offloadable and the other is not
value.pure -
Types and formatting
Every type
A column per type, from every group, each sorting on its value not its text
type: 'voltage' · 'bytes' · 'ipv4' · … -
Types and formatting
Configuring a type
One field, five presentations; the stored value never changes
createUnitType({ display, decimals, … }) -
Types and formatting
A unit family of your own
Register a unit system from symbols and their factors
registerUnitSystem · createUnitType -
Types and formatting
A type of your own
A part number, AB-1234-X, in five functions: matches, parse, format, compare
dataTypes: { partNumber: { base, matches, parse, format, compare } } -
Types and formatting
Money as a real type
Amounts that keep their own currency, shown in one display currency through your rate table, with a mixed total that refuses rather than adds unlike currencies
createCurrencyType({ display, rates })
Rows and layout
What a row is, how tall it is, and what sits outside the scrolling body.
-
Rows and layout
Auto row height
Wrapped text measured per row, and the ceiling where it degrades
autoHeight: true | 'visible' -
Rows and layout
Pinned columns and scroll extent
A centre column stays reachable behind a pinned region
pinned regions · horizontal extent -
Rows and layout
The row types
Leaf, group, footer, grand total, detail, synthesised and pinned, in one grid
Row.group · detail · grandTotal -
Rows and layout
Pinned rows
A grand total pinned below the scrolling body
grandTotalRow: 'bottom' -
Rows and layout
Row height per row
Height as a function of the row, and what that costs
rowHeight: (row) => number -
Rows and layout
Full-width rows
A row drawn as one band across every column, for section banners
fullWidth: { when, render }
Sort, filter, group and edit
Everything a person does to the data once it is on screen, from finding a row to changing it.
Sorting
Where it runs and what it costs, with real numbers.
-
Sorting
Single and multi-column
Click, shift-click, and the order index that appears when it means something
sort.set([{ col, dir }]) -
Sorting
Locale collation
Sorting names in Swedish, German and Turkish
locale · type.compare -
Sorting
Custom comparators
Sorting by priority words, versions, or a domain rule
value.compare -
Sorting
Sort at a million rows
Timed on the page, and honest about where it runs
measured, main thread
Filtering
One filter model behind every control, which is the point worth making.
-
Filtering
The filter menu
Per-column filtering with the operators each type supports, plus an inline filter row that writes the same condition
filterRow · filters.set(tree) -
Filtering
Set filters
Checkbox lists over distinct values, on a dictionary-encoded column
filter: 'set' -
Filtering
Ranges and relative dates
Between, before, after, and "last 30 days" that stays relative
op: 'between' -
Filtering
Quick filter, four ways
contains, words, fuzzy and regex over the same box
filters.quick(text, { mode }) -
Filtering
Find, without losing the neighbours
Ctrl+F highlights every match where it sits, and takes no row away
grid.find(text) -
Filtering
Header histograms
A distribution in every heading that is also the filter control
facets: { enabled: true } -
Filtering
Cross-filtering, done properly
Live column charts that cross-filter, where a filtered column keeps its own bars
facets -
Filtering
Facets for a server source
Supplying counts from an API when the grid holds one page
facets.provider -
Filtering
The advanced filter builder
Nested and/or/not groups against the same tree the menus produce
FilterSet -
Filtering
An application-level filter
A host predicate composed with the grid’s own, for permissions
hostFilter: { active, passes } -
Filtering
Filters in saved views
Round-tripping a filter model, including relative dates
state.get() / state.apply()
Grouping, totals and pivot
Reducing many rows to the few numbers someone asked for.
-
Grouping, totals and pivot
Grouping
One level, then several, surviving sort, filter and reload
columns.group(['region']) -
Grouping, totals and pivot
Group footers
A closing total per group
groupFooter: true -
Grouping, totals and pivot
The grand total row
Inline at the end, or pinned above the status bar
grandTotalRow: 'bottom' -
Grouping, totals and pivot
The aggregations
sum, avg, min, max, count, countValues, first, last, one column each
total: 'sum' -
Grouping, totals and pivot
Your own total function
A weighted mean or a median, and why it re-reduces
total: (values, ctx) => … -
Grouping, totals and pivot
Totals that keep up
A live feed against a million rows with a totals row, timed
incremental grand totals -
Grouping, totals and pivot
Totals of the view or the data
The same grid filtered, totals answering each question in turn
totalFilteredOnly: false -
Grouping, totals and pivot
Only what changed
Reducing only the columns an update touched
totalOnlyChangedColumns: true -
Grouping, totals and pivot
Pivot
Transposing values across distinct keys, with a column guard
columns.pivot(['status']) -
Grouping, totals and pivot
Pivot with several value columns
Generated column groups, and what the headings do
pivot.groupTotals -
Grouping, totals and pivot
The grid as a pivot table
One field down, one across, a measure summed at every crossing, with subtotals that match the grid and a cell drill
pivotView · pivot:drill
Tree data and master-detail
Hierarchies and detail rows, including the awkward cases rather than only the tidy one.
-
Tree data and master-detail
Tree data
A hierarchy from a parent reference
tree: { parentKey: 'parentId' } -
Tree data and master-detail
Hierarchy from a path
Rows carrying their own ancestry, missing levels synthesised
tree: { path } -
Tree data and master-detail
Orphans and cycles
Data that is not quite a tree. Nothing is dropped
orphans: 'Unassigned' -
Tree data and master-detail
A large tree
Fifty thousand nodes, ten deep, a collapsed branch costing nothing
virtualised tree -
Tree data and master-detail
Master-detail
A row expanding into a nested grid, several open at once
detail: { rows, config } -
Tree data and master-detail
A detail pane beside the list
The detail renders into your own element, one record at a time
detail: { target: '#pane' } -
Tree data and master-detail
An editable detail
Editing inside the nested grid, reported on the master
detail:cell:changed -
Tree data and master-detail
Lazy children
Fetching a branch on expand, with an abort when it closes first
tree.loadChildren
Selection and editing
An unusually broad editor set, and what happens when a write is refused.
-
Selection and editing
Row selection
Single and multiple row selection, visible in the grid and on the row
selection: 'multiple' -
Selection and editing
Checkboxes and select-all
A pinned checkbox column with a tri-state header
selection: { checkbox, headerCheckbox } -
Selection and editing
Cell ranges
Dragging a block, ctrl-clicking a second, and a summary of it
selection.setRange() -
Selection and editing
The fill handle
Dragging a corner to extend a series it recognises, with a pinned row-number column so a sort never loses count
fillHandle · rowNumbers -
Selection and editing
Clipboard
Copy and paste against Excel and Sheets
clipboard interop -
Selection and editing
Inline editing
Double-click, type, Tab, Enter, Escape, and clicking away
edit: { enabled: true } -
Selection and editing
The editor gallery
Twenty-two editors in one grid: date, duration, colour, rating, slider
editors/* -
Selection and editing
Validation
Refusing a value, showing why, keeping the editor open
edit.validate -
Selection and editing
Row editing
Editing a whole row as one transaction
edit: { mode: 'row' } -
Selection and editing
Optimistic writes
The server disagrees, and the cell reverts with an explanation
edit: { commit, confirm } -
Selection and editing
Undo and redo
A history stack over edits, fills and pastes
grid.history -
Selection and editing
Read-only and per-cell rules
Columns that never edit, and cells that depend on the record
edit.enabled: (row) => … -
Selection and editing
Bulk edits
Writing a value across a range, and what one undo then means
edit.setCells() -
Selection and editing
Confirm before delete, block a locked row
A delete waits on a confirm before it lands, and an edit on a locked row is refused with the reason shown
beforeDelete, beforeEdit, preventDefault() -
Selection and editing
Formula entry
A spreadsheet formula typed into a cell, and the evaluator reach limit
formulaFunctions -
Selection and editing
Editing a row on a form
Double-click a row to edit it in a drawer, one control per field
rowForm: { fields } -
Selection and editing
The form, four ways
The same row form as a drawer, a dialog, an inline region and a curated set
rowForm: { mode: 'drawer' · 'dialog', container } -
Selection and editing
Row reorder
Drag a handle to move rows, and persist the new order
rowReorder: true -
Selection and editing
Moving rows between grids
Drag a row out of one grid and into another
rowTransfer: { group } -
Selection and editing
Validation declared on the column
An invalid edit vetoed with an announced inline error, from rules stated as data
validation: { required, min, max, pattern, oneOf }
Rendering, formatting and theme
How each cell looks, the rules that colour it, and dressing the whole grid to match your product.
Cell rendering
Twenty-four built-in renderers. Visual first, explanatory second.
-
Cell rendering
The renderer gallery
Every built-in renderer in one grid, each in its own column
cell: { render: … } -
Cell rendering
Pills and variants
Status chips across the six semantic variants, in every theme
decoration: 'pill' · variant.map -
Cell rendering
Progress and bullet
Completion against a target, with a threshold marker
render: 'progress' | 'bullet' -
Cell rendering
Sparklines
Line, area and column charts inside a cell
render: 'line' | 'area' | 'column' -
Cell rendering
Win-loss, gauge, range
The compact chart set for KPI grids
render: 'winloss' | 'gauge' | 'range' -
Cell rendering
Pie and donut in a cell
Composition per row, at 24 pixels, still legible
render: 'pie' | 'donut' -
Cell rendering
Stacked bars
Several series in one cell, sharing a scale across the column
render: 'stacked' -
Cell rendering
Delta indicators
Change since last value, with direction and colour, on a live feed
render: 'delta' -
Cell rendering
Ratings, colours and icons
Interactive stars, colour swatches and value-mapped icon sets
render: 'rating' | 'colour' | 'icon' -
Cell rendering
Images, avatars and QR codes
Media in cells, including a QR generated with no dependency
type: 'image' · render: 'qrcode' -
Cell rendering
Two-line cells
A primary value with a caption beneath, at full row density
render: 'twoline' -
Cell rendering
Skeleton loading
What a remote source looks like while a block is in flight
render: 'skeleton' -
Cell rendering
Templates and your own renderer
The template compiler, then a custom renderer when it is not enough
cell.template · config.components -
Cell rendering
HTML in cells, safely
Templates escape by default; the opt-out and what it opens up
allowUnsafeTemplates: true -
Cell rendering
Cards, lists and feeds
Draw each row as a card instead of dividing it into columns
rowTemplate
Conditional formatting
Rules as live state rather than a compile step, editable on the page.
-
Conditional formatting
Rules from configuration
Colouring cells by value, seeded at build time
formatting: { col: [{ when, style }] } -
Conditional formatting
The formatting panel
A user building their own rules at runtime, undoable and saved
grid.formatting.add() -
Conditional formatting
Colour scales
A heat map across a numeric column, between bounds you declare
formatting scale rules -
Conditional formatting
Row class and row style
Styling whole rows from the record, and how it survives recycling
rowClass · rowStyle -
Conditional formatting
Change highlighting
Flashing a cell when its value moves
grid.highlight.cell() -
Conditional formatting
Rules that describe the data
The outliers, found in the data, beside a threshold you typed
when: { op: 'outlier' } -
Conditional formatting
Quantile colour scales
A scale that ignores the outlier flattening it
scale: { from: 'quantile' } -
Conditional formatting
Data bars and icon sets as rules
Proportional bars and per-band glyphs painted by the rule engine, undoable and saved with a view
formatting rules: dataBar, iconSet
Theming and accessibility
The pages where the theme itself is the subject.
-
Theming and accessibility
Four themes, one grid
Light, dark, high contrast and terminal, switched live
theme: 'high-contrast' -
Theming and accessibility
One switch, a whole dark dashboard
A layout window, a grid, a chart and a KPI strip all following one theme switch, plus square window corners
theme: 'auto' · corners: 'square' -
Theming and accessibility
High contrast, and why it is not just dark
Borders at 6.1:1 against 1.3:1, and drawn cell edges
WCAG 1.4.11 -
Theming and accessibility
Terminal
A phosphor console: one hue, monospaced, status by brightness
theme: 'terminal' -
Theming and accessibility
Your own theme
Overriding tokens rather than rules
--lattice-* tokens -
Theming and accessibility
Density
compact through spacious, with the row height following the token
density: 'comfortable' -
Theming and accessibility
Keyboard only
The whole grid driven without a mouse, keys shown as pressed
one tab stop · roving focus -
Theming and accessibility
Screen reader
What the grid announces, and the ARIA it writes
aria-rowindex · aria-selected -
Theming and accessibility
prefers-reduced-motion
Every animation the grid drops when the viewer asks for less
@media (prefers-reduced-motion: reduce) -
Theming and accessibility
Translating the grid
Every string the grid renders, from a message catalogue
locale: 'fr-FR' -
Theming and accessibility
Locales, and it mirrors
The same grid in English, Japanese and Arabic, which sets the grid right to left
locale · direction: 'rtl' -
Theming and accessibility
Right to left, in Arabic
Arabic orders laid out right to left from the locale alone, with start and end alignment beside left and right
locale: 'ar-EG' · align: 'start' · 'left'
Chrome and menus
The controls around the grid, and the rules behind what each one offers.
-
Chrome and menus
Header context menu
Right-click a heading. The menu, not the rendering
header:contextmenu -
Chrome and menus
The action rail
The grouped control rail down the edge of the grid
tool panel dock · rail actions -
Chrome and menus
Type a page to jump to it
The pager interaction, not the paged source
pagination -
Chrome and menus
Maximise
Full-screen a grid and come back to the same scroll and selection
grid.maximise -
Chrome and menus
The cell context menu
An item that writes is never offered on a cell that cannot be written
cell:contextmenu -
Chrome and menus
Host menu items and rail actions
Adding your own items to the grid menus from the application
menu extension -
Chrome and menus
A menu per column
A right-click menu that offers circuit actions on one column and billing actions on another
column.contextMenu -
Chrome and menus
How much furniture to show
Heading controls, scrollbars and cell alignment, switched on screen
headerControls -
Chrome and menus
N of M rows
Three counts that are not interchangeable, and which one is shown
rows.count() · matchCount() · totalCount()
Charts, statistics and dashboards
Turning rows into a picture and a number, with several grids and charts moving on one filter.
Charts
Every chart type drawn from the grid's own filtered rows, redrawing as it moves.
-
Charts
Your first chart
A chart from the grid's own data in six lines
createChart({ grid, container, type, x, y }) -
Charts
Line, area, bar and scatter
The cartesian types, one dataset
type: 'line' · 'step' · 'area' · 'bar' · 'combo' · 'pareto' -
Charts
Histogram and box plot
The distribution of a single column
type: 'histogram' · 'boxplot' · 'violin' -
Charts
Pie, donut, sunburst, treemap
Composition, flat and hierarchical
type: 'pie' · 'donut' · 'sunburst' · 'treemap' -
Charts
Sankey, chord and network
Relationships between categories
type, source, target, y -
Charts
Gauge, funnel and radar
The specialist types and what each is for
type: 'gauge' · 'funnel' · 'radar' -
Charts
Waterfall, stream, marimekko
Monthly rises and falls, a mix shifting over time, and regions against statuses
type: 'waterfall' · 'stream' · 'marimekko' -
Charts
Values on a map
Continents and countries from ISO codes
type: 'geomap', code, y -
Charts
A value per country, filled straight from its own boundary
Every country’s own shape, read from a geometry column and filled by a measure; no separate geometry pack, the choropleth draws each row’s own polygon
choropleth · geometry: '<col>' -
Charts
Pan the map, narrow the grid
Pan or zoom the map and it writes a single filter back onto the grid it reads from; provenance shows what was drawn against what matched
viewportFilter · chart.provenance().viewport -
Charts
A figure at every site, on a map
One marker per location carrying its own formatted figure, coloured by the same rule that colours the cell, so an availability wall is one rule set and one chart
type: 'markermap', lon, lat, label, value -
Charts
A network drawn as your network
A glyph per device where you put it, every circuit its own line coloured by the rule on the column, and a click that ties a line back to its row
nodes: [{ id, label, icon, x, y }] -
Charts
Candlestick
Open, high, low and close, one row a day
type: 'candlestick', measures: [open, high, low, close] -
Charts
Candlestick with a volume pane
Price and volume in one chart: a synchronised volume bar under every candle, one x axis, one tooltip, one zoom
volume: '<col>' · volumePane: { height } -
Charts
deck.gl over the grid
deck.gl draws exactly the rows the grid shows over a real map, and panning or zooming writes a filter back onto the grid
bindDeck(grid, { deck, layers, viewportFilter }) -
Charts
Gantt
A row label with a start and an end, coloured by phase
type: 'gantt', label, start, end -
Charts
Heatmap
A measure at the crossing of two categories
type: 'heatmap', x, series, y -
Charts
Charts follow the grid
Filter, sort or edit the grid and every chart bound to it redraws
createChart({ grid, type, x, y }) -
Charts
Click a chart to filter
Click a slice and the grid narrows to it
chart.on('click', …) -
Charts
Chart events
Click, hover and legend, wired to your app
chart.on('click' | 'hover' | 'legend') -
Charts
Brushing a time range
Drag a range on the chart and the grid narrows to it
brush: true -
Charts
Small multiples
One chart per category, on a shared scale
multiples: col -
Charts
Colour schemes
Built-in palettes, the default colour-blind safe
scheme: 'default' · 'bright' · 'earth' · 'mono' -
Charts
Axes and legends
A series per category, named by the legend
series, legend -
Charts
Data labels
Values beside the marks, or left to the axis
labels: true -
Charts
Charts with no grid on screen
The data engine driving charts on its own
createHeadlessGrid -
Charts
The correlation matrix
Every numeric column against every other, Pearson or Spearman
type: 'correlogram' -
Charts
Is this column normal?
A Q-Q plot against a line through the quartiles
type: 'qq', y -
Charts
Cumulative distribution
The ECDF, binless, beside a histogram of the same column
type: 'ecdf', y -
Charts
How concentrated is it?
A Lorenz curve, the picture a Gini number is read off
type: 'lorenz', y -
Charts
Statistical process control
Control limits against spec limits, which are not the same thing
type: 'control', y, baseline -
Charts
Density over a histogram
A kernel density estimate over the bars, so binning and data separate
curve: true, buckets -
Charts
A fitted line
Least squares per series, with R², agreeing with the API
fit: true -
Charts
Two metrics, two scales
A total and an average on independent left and right axes, each labelled so neither misleads
measures: [{ col, fn }, { col, fn, axis: 'right' }] -
Charts
Reference lines, targets and bands
Target and limit lines and a shaded range over a chart, with a mean line read from the data
annotations: [{ kind: 'line', compute: 'mean' }] -
Charts
A trend line and a forecast
Draw a least-squares trend straight through the points and carry it on as a dashed forecast, with a vertical event marker flagging the day a change went live
trend: { method: 'linear', forecast: 8 } -
Charts
A forecast with its confidence band
Shade the range a projection is likely to fall in, so a forecast shows its uncertainty rather than a single confident line: a wider band for where a future day may land, a tighter one for the fitted trend itself
trend: { forecast, band: 'confidence' } -
Charts
Alluvial flow
Follow a measure as it flows from one category into another, ribbon by ribbon
type: 'alluvial', source, target, value -
Charts
Arc diagram
Lay every node on one line and join related pairs with an arc, so the links read at a glance
type: 'arc', source, target, value -
Charts
Bubble map
Place a value at each point on the map and size the bubble by how large it is
type: 'bubblemap', lon, lat, size -
Charts
Bump chart
Watch a ranking change from one period to the next, where a crossing is a change of place
type: 'bump', x, series, y -
Charts
Calendar heatmap
A year of daily figures as one square per day, so busy runs and quiet spells stand out
type: 'calendar', x, y -
Charts
Series decomposition
Read a series as its trend, its seasonal rhythm and the residual, stacked to compare
type: 'decomposition', trend, seasonal, residual -
Charts
Diverging bars
Gains one way and losses the other off a shared centre, so direction reads before size
type: 'diverging', x, y -
Charts
Dumbbell chart
Two dots joined by a bar per row, so the distance from before to after is the picture
type: 'dumbbell', x, start, end -
Charts
Fan chart
An actual line that opens into a forecast band, widening the further ahead it reaches
type: 'fan', x, y, forecast, lower, upper -
Charts
Hexbin
Bin a dense scatter into hexagons, so a cloud too thick to read becomes a shape you can
type: 'hexbin', x, y -
Charts
Hex map
Snap places to a tidy hex grid so a small region reads as clearly as a large one
type: 'hexmap', lon, lat -
Charts
Icicle
A hierarchy as nested bars, each band as wide as the share of the total beneath it
type: 'icicle', y -
Charts
Parallel coordinates
One line per row across several axes at once, coloured by group to surface a pattern
type: 'parallel', columns, colourBy -
Charts
Ridgeline
A distribution per category, stacked on a shared scale so the shapes line up to compare
type: 'ridgeline', x, y -
Charts
ROC curve
See how well a score separates two outcomes, the curve bowing away from a coin toss
type: 'roc', score, label, positive -
Charts
Slope chart
Two points per line so the direction of change from one period to the next is the message
type: 'slope', x, series, y -
Charts
Scatterplot matrix
Every pair of measures as its own small scatter, to spot which pairs move together
type: 'splom', columns -
Charts
Waffle chart
A hundred squares filled in the proportion each part holds, an easier whole to read than a pie
type: 'waffle', x, y
Statistics
What the grid knows about its own numbers, and about how they have changed since the page loaded.
-
Statistics
Statistics in the totals row
Median, percentiles, deviation and spread, each over the filtered rows
total: 'median' · 'p95' · 'stddev' -
Statistics
Project a time series forward
Fit a column over the filtered rows and project the months ahead, drawn as its own line carrying on from the last reading, with the fit quality shown beside it
grid.statistics.forecast(col, { by, method, horizon }) -
Statistics
Flag the readings that do not fit
A column that marks the outliers in a series, scored by a robust measure a single spike cannot skew, with a chip that counts them and filters the grid to exactly those rows
shadow: { kind: 'anomalyFlag' } · anomalySummary -
Statistics
Profiling a column
Count, spread, quartiles and outliers from the API, into a panel of your own
grid.statistics.profile(col) -
Statistics
The statistics panel
The column profile as a docked tool panel, following the filters
toolPanel: { panels: ['statistics'] } -
Statistics
Correlation and weighted averages
Two-column figures from the API, over the filtered rows
grid.statistics.correlation(a, b) -
Statistics
Shadow columns: what changed
Delta, percent change and a move count against the value at page load
shadow: { of, kind: 'delta' } -
Statistics
Shadow columns: rank and movement
Rank, percentile and places moved; sort by places moved for live top movers
shadow: { of, kind: 'rankChange' } -
Statistics
Shadow columns: rate and streak
Speed of change, direction runs, and a sparkline of recent history
shadow: { of, kind: 'rate' } -
Statistics
Marking a baseline
"Mark all" resets every delta to now
grid.statistics.rebase(col) -
Statistics
Variation along an ordering
Volatility, growth and drawdown, from rows loaded out of order
grid.statistics.series(col, { by }) -
Statistics
Capability against a tolerance
Cp, Cpk, Pp and Ppk from one column declaration
grid.statistics.capability(col, { baseline }) -
Statistics
Confidence intervals, charted
The mean lead time of each supplier with its 95% interval as a whisker, and a tile that narrows as you filter
grid.statistics.interval(col) · chart error: true -
Statistics
Regression diagnostics
A fitted line with its confidence band, residual and QQ charts read straight from the fit shadow columns, and the coefficients in a panel that refits as you filter
grid.statistics.regressionModel · regressionPlots -
Statistics
Seasonality and smoothing
A daily series split into trend, weekly season and residual as shadow columns, a smoothed level, a correlogram and a stationarity verdict
shadow: tsSeasonal, tsTrend · statistics.adf, acf -
Statistics
An SPC study: control, range, capability
A drifting process with limits fixed on the first thirty readings, so the drift breaks a rule rather than being absorbed
chart type: control · movingRange · capability -
Statistics
Running totals
The one derived value that depends on the display order
running: { of, kind: 'total' } -
Statistics
Statistics of a selection
Drag a rectangle; it is summarised as one set of figures
grid.selection.statistics() -
Statistics
Statistical values in a column
A column's median, deviation and percentile, read into computed columns
value.compute -
Statistics
Statistics without a panel
Every figure available to your own interface, as an object
grid.statistics.* -
Statistics
A trading book
Money units, shadow columns, a running P&L and a live feed, in one grid
createUnitType · shadow · running · statistics -
Statistics
Analytical chemistry
Molarity, ppb and dose types, with a contaminated batch the rules find
type: molarity · ppb · doseRate -
Statistics
Manufacturing and process
A machined bore with a tolerance driving capability, drifting in one shift
spec · grid.statistics.capability -
Statistics
Fleet and network
Address, data-size and rate types, latency long-tailed so p99 bites
type: ipv4 · bytes · bitrate -
Statistics
What makes this segment different
Filter to a segment and rank every column by how far it has moved from the whole
grid.statistics.subsetVsPopulation() -
Statistics
This month against last
Two grids compared column by column, ranked by effect size rather than a p-value
grid.statistics.datasetVsDataset(otherGrid) -
Statistics
Pass, warn, fail against a spec
A hard spec turned into a verdict on every row, and a live pass rate on the total
shadow: { kind: 'specStatus' } · total: 'passRate' -
Statistics
Approximate figures, with the error bound shown
A constant-time estimate next to the figure it stands for, and the average over just the last few thousand readings
grid.statistics.maintenanceTier(fn) · windowed(col, fn, { kind, span }) -
Statistics
Manufacturing quality analysis
100,000 measurements from eight production lines: a process strip, a distribution, a control chart, two derived panels and the readings themselves, all recomputing from one line click
statistics.capability · mode: derived · createStat -
Statistics
A portfolio risk explorer
50,000 positions shaped like notebook output: three portfolio figures, exposure by sector, a derived sector summary and the positions, with an edit printed back as the Python cell output it would be
createStat · mode: derived · createChart
Sources and live data
Where the rows come from, from a resident array to a server pushing changes by the second.
Sources and live data
Four source modes with genuinely different contracts.
Read the worked example: security log triage over DuckDB and Parquet →
-
Sources and live data
The memory source
Everything resident, and the columnarisation threshold
source: { mode: 'memory' } -
Sources and live data
Bring rows in from a CSV
Paste a block or drop a file, each column typed and mapped onto your fields, shown as a preview so nothing lands until you confirm; a spreadsheet block copied from Excel pastes the same way
grid.import.preview() · apply() -
Sources and live data
Paged
A page at a time from a server that paginates
source: { mode: 'paged' } -
Sources and live data
Remote, with the server doing the work
Blocks fetched as you scroll, sort and filter pushed to the API
source: { mode: 'remote' } -
Sources and live data
Pushdown to a live OData service
A grid over the public Northwind service; filter and sort are pushed to it, and lastPlan shows the split
createPushdownSource · odataAdapter · lastPlan() -
Sources and live data
Reading Splunk live
A grid over Splunk’s own search REST API, filter and sort turned into SPL; run here against a mock of that API, since there is no public instance or credential this site can publish
createPushdownSource · splunkAdapter · lastPlan() -
Sources and live data
A hundred thousand rows in DuckDB, no server
DuckDB reads a Parquet file in the browser and the whole set is pulled in, so the plant subtotals and the grand total are computed over all 100,000 rows
createPushdownSource · duckdbAdapter · fullDataset -
Sources and live data
Editing a remote source, safely
Optimistic edit, reconcile to server truth, and a visible revert when the write is refused
createPushdownSource({ adapter, edit: true }) -
Sources and live data
Live pushdown to DuckDB, query by query
Every sort, filter and page becomes one SQL statement DuckDB answers, with only the window coming back and the generated SQL and timing on screen
createPushdownSource · duckdbAdapter · lastPlan() -
Sources and live data
Reading ClickHouse live, filter to SQL
A grid over ClickHouse’s own public playground: filter, sort, page and count are pushed as SQL, and a grouped average lands in a KPI tile
createPushdownSource · clickhouseAdapter · lastPlan() · source.aggregate() -
Sources and live data
Reading Elasticsearch live, filter to Query DSL
A grid over a mocked Elasticsearch _search API, since no public keyless cluster exists: filter, sort and paging turn into Query DSL, shown beside the grid
createPushdownSource · elasticsearchAdapter · adapter.bodyFor() -
Sources and live data
Spatial filtering pushed into DuckDB from a GeoParquet file
A small GeoParquet file of country locations, read by DuckDB’s spatial extension in the browser; drawing a box on the map pushes a within-the-box filter into the engine, and lastPlan shows the generated SQL
duckdbAdapter({ spatial: true }) · withinBbox · lastPlan() -
Sources and live data
Triage security logs at scale in the browser, no server
A day of firewall, DNS, auth and IDS events in a Parquet file, queried by DuckDB in the tab: each filter is a WHERE with the whole-set count, the top talkers come from a GROUP BY over everything that matches, and the SQL is on screen
duckdbAdapter · source.aggregate() · lastPlan() -
Sources and live data
Streaming
Rows arriving over time, first paint on the first chunk
source: { mode: 'stream' } -
Sources and live data
A bounded window
A never-ending feed held to a span of time, with a chart that keeps scrolling through the quiet
source.maxAge -
Sources and live data
Twenty thousand cell updates a second
A trading-desk feed with the frame budget on screen
grid.updates.stats() -
Sources and live data
Holding the feed
Pausing incoming data deliberately, with the backlog counted
grid.updates.pause() -
Sources and live data
Record and replay a live feed
Record a live feed, then drag a playhead back to replay the state the grid held a moment ago
grid.timeline -
Sources and live data
The transaction API
Batching adds, updates and removes into one applied change
rows.apply({ add, update, remove }) -
Sources and live data
Row identity
Why rowKey matters, and the features that switch off without it
rowKey -
Sources and live data
Load from a URL
Point a grid at a file and it fetches and reads it; a large NDJSON file streams in as it downloads
source: createUrlSource(url) -
Sources and live data
Starter kit: operations console
One feed drives orders, shipments and incidents grids and a throughput chart, with no backend
createDataRouter · MockWebSocket · opsFeed -
Sources and live data
Starter kit: trading terminal
One market feed fans to a quotes grid and a movers grid narrowed by its own route, plus a price chart
createDataRouter · attach filter · priceFeed -
Sources and live data
Starter kit: fleet telemetry
One telemetry feed, a live grid per device class and a units-online chart, with no backend
createDataRouter · MockWebSocket · rng -
Sources and live data
Starter kit: service desk
One ticket stream, a live lane per queue and an open-tickets chart, with no backend
createDataRouter · MockWebSocket -
Sources and live data
Ten million transactions in Parquet
Ten million card transactions queried by DuckDB in the tab: every filter, sort and page becomes one statement, and only the hundred rows on screen come back
duckdbAdapter · createPushdownSource
Data Router
One live feed split across many grids, charts and tiles at once, each pick narrowing the others.
One feed, many views
A single live feed split across grids, charts and tiles at once, each following its own slice.
-
One feed, many views
One feed, many views
Split a single live feed across two grids and a chart, with a pick in one narrowing another
createDataRouter -
One feed, many views
A feed that survives a reload
The routed rows written to the browser’s durable store and read straight back on the next visit, beside a fast feed held to a steady repaint rate so a firehose lands smoothly
router.persist() · router.restore() -
One feed, many views
A feed enriched from a lookup
One order feed joined to a customer lookup, held until known and released enriched
addSource(join: { from, localKey, fields, missing: 'hold' }) -
One feed, many views
A network operations centre
2,000 devices and 10,000 alarms on one feed: two grids, two charts and a KPI strip, with a live incident to watch the whole screen react
createDataRouter · router.link · router.attach(rollup) -
One feed, many views
A logistics operations platform
50,000 shipments, 2,000 vehicles and 5,000 exceptions on one feed: a KPI strip, two charts, a shipments grid, a derived carrier panel, and a work queue read as a grid, a board or a timeline
createDataRouter · router.subscribe · createKanban
Derived and chained
One grid built from another and following its filter, to any depth, with a single figure in a tile reading the same rows.
-
Derived and chained
Chaining, three levels deep
sales → revenue per rep → top three and a profile, with tiles reading the middle level; one filter moves all of it
source: { mode: 'derived', from, groupBy, profile } · createStat -
Derived and chained
A sales dashboard
Top reps by grouping, most-sold products by unnesting the order lines, and five tiles, one ignoring the filter
groupBy · unnest · scope: 'all' · createStat -
Derived and chained
A production line
A measurement profile transpose, an out-of-tolerance list, spread by line, and Cpk in a tile against the column spec
profile · where · statistics.capability -
Derived and chained
A support desk
Tickets by week, the busiest agent per queue, the commonest tags, and SLA breaches, from one filtered source
bucket · limitPer · unnest · where -
Derived and chained
Joining a second grid
Orders carry a customer id but no tier; a join brings it across from a customers grid, then groups revenue by tier
join: { with, on, select } -
Derived and chained
Cross-filtering, the path back up
Two summary panels over one book; click a row in either and it filters the book, so the other narrows
crossFilter: true · crossFilter.toggle -
Derived and chained
One queue, four tabs, three of them derived
All, Open and Breached SLA narrow each other two levels deep; Profile reads the same queue as a statistic instead of a filter
createTabs(el, { createGrid, tabs }) · from · profile -
Derived and chained
A grid that is the analysis of another grid
One grid holds the trading days and four more are computed from them: which figures move together, the same coefficients as a square, how one figure has behaved over the period, and how this set of stores compares with a separate one
source: { mode: 'derived', statistics: { fn } } -
Derived and chained
Two feeds that share no key, ranked as one list
Two incident feeds with different identifiers and different field names, stacked into a single ranking that says which feed each row came from
source: { mode: 'derived', from: [{ grid, label, map }] }
Kanban boards
The same rows as cards in status columns, moved by drag or keyboard and written straight back.
Kanban boards
The backlog as cards in status columns, with points, limits, lanes, sprints and the flow through them.
-
Kanban boards
A board (kanban) view
The same rows as cards in status columns: drag or move by keyboard, points per column, a work-in-progress limit, lanes, sprints and a pop-out child grid
createKanban -
Kanban boards
How work flows through the board
A cumulative-flow diagram of what sits in each column day by day, with cycle time, lead time and throughput read from the board’s own movements, so a slowdown shows before it becomes a surprise
board.flow · cfd() · cycleTime() -
Kanban boards
A board that flags work running late
Every open card timed from the moment it entered its column, with the ones running long marked: an amber edge past the warning limit, a red edge and tint past the breach, and its own clock for each column
sla: { warn, breach, enteredProperty }
Gantt and project plans
A task list read as a schedule: editable bars, dependencies and the critical path, recomputing as you drag.
Gantt and project plans
A task list as a schedule: dependencies, the critical path, people balanced across the plan, and Microsoft Project files read and written.
-
Gantt and project plans
A project plan (Gantt)
A task list as a schedule: editable bars, the four dependency links, the critical path, milestones and progress, recomputing as you drag
createGantt -
Gantt and project plans
A project plan, joined split view
The task tree and the timeline as one row-aligned surface: summary bars, progress rings, the people on each task, dependency arrows, real calendar dates, weekends shaded, and a baseline drawn behind each bar
gantt.mountSplit -
Gantt and project plans
A project plan, balanced across people
Put people on tasks and see who is booked past what they can do: the clashing bars are outlined and the avatar ringed, then one press shifts the lower-priority work later until the plan is clear
gantt.overAllocations · gantt.level() -
Gantt and project plans
Open a Microsoft Project plan
Read a Project .xml straight into a schedule, tasks, links, progress and people intact, drawn in the browser, and write it back out again, all with nothing sent to a server
importMSPDI · exportMSPDI
AI
A model that explains a figure, answers a question and proposes a change, always with a person in the loop.
Ask, explain and change with a model
A model that reads the figures, answers a question and proposes a change, with the grid checking every step and a person approving it.
-
Ask, explain and change with a model
Explain a figure in plain language
Ask this view a question in plain language from one console: type a question or tap a shortcut and read a few plain sentences built from the grid’s own computed figures, then filter the grid and ask again to watch the answer follow the rows on screen, with every figure reconciled against what the grid computed so an invented number never reaches the reading
createAI · ai.explain -
Ask, explain and change with a model
Shape your data in plain English
Describe the view you want in plain English and the grid reshapes itself, filtering, sorting and grouping to match: the model returns a query, the grid validates it against its own columns and runs it, and one request moves the grid, a chart and the KPI tiles together
createAI · ai.query · ai.applyQuery · ai.askBar -
Ask, explain and change with a model
Ask the grid to change data, safely
Ask in plain language to change data and approve it first: the model proposes a change, the grid finds the row and previews a before/after diff, nothing is written until you approve, and every change goes through the same gate a person’s edit does, so it can be blocked and it is reversible
createAI · ai.propose · ai.applyProposal · ai.actorBar -
Ask, explain and change with a model
Summarise delivery risk in one line
Turn a project plan and a board into the one line a delivery lead wants: how many tasks are at risk on the critical path, how the schedule and cost are tracking, and how many service limits have slipped, each figure computed by the plan and the board and reconciled so no number is invented, with task names and money held back unless you opt in
createAI · ai.riskSummary(sources)
Present, share and integrate
The last mile: showing a grid to a room, working with a team, and fitting it into an existing app.
Presenting
Showing a grid to a room rather than to one person at a desk.
-
Presenting
Presentation mode
Scale the whole grid up for a room, then come back
grid.presentation.start() · nudge() · stop() -
Presenting
Annotations
Draw over the grid while presenting, inert until a tool is chosen
grid.annotate -
Presenting
A mark that stays put
Seed a durable arrow and highlight at load, stored in content coordinates so they travel with the cells as you scroll
state.annotations · grid.annotate.add -
Presenting
PNG capture
Export the grid, or a range, as an image
grid.capture() -
Presenting
A panel of stat tiles
The same feed as a row of live figures beside a grid: a total, an average against a baseline, a count with good, warn and critical bands, and a sparkline, moving as data flows
createKPI -
Presenting
Raise and clear, held against a flapping feed
A KPI tile and a grid column that already grade themselves feed an alarm grid on the right: every raise and every clear arrives as its own row. Turn on a flapping reading and watch a hold silence it, a level has to persist before it counts
createAlarms · alarm:raised · alarm:cleared · holdMs -
Presenting
The health of the rows you are looking at
A grid of hosts with a health rail beside it reading the very same rows: readings grouped under headings that collapse, a heading carrying the worst state beneath it, and every figure moving the moment you narrow the grid
createKPI(el, { grid, tree }) -
Presenting
The time it is at every desk
Live clocks for each place a team works, ticking beside the figures those desks are measured on, all on one panel that follows the grid
tiles: [{ kind: 'clock', label, timeZone }] -
Presenting
A tile totalling rows the grid never loaded
A KPI panel beside a grid holding one page of a hundred thousand rows: the sum and average tiles ask the engine for the whole matching set rather than only the page, and say so
createKPI(el, { grid, tiles }) · computed: 'engine' -
Presenting
A dashboard the reader can rearrange
Four views of one dataset in windows: blow one up to fill the dashboard, fold the rest away, unlock them to move, resize and close by drag or keyboard, and save the arrangement as JSON
createLayout · setInteractive · getLayout -
Presenting
A dashboard from one JSON spec
A grid, a chart over the grid’s own rows, KPI tiles and an html panel, all built from one spec; Show spec prints it back and rebuilds the dashboard from exactly that
createDashboard · dashboard.spec() · dashboard.apply()
Collaboration and governance
What happens when more than one person is looking at the same grid.
-
Collaboration and governance
Collaborative presence
Two windows, two cursors, live selections and edits
grid.presence -
Collaboration and governance
Advisory locking
A peer holding a cell, and what happens if you edit it anyway
presence lock -
Collaboration and governance
Cell comments
Threaded discussion on a cell, with an unresolved count
grid.comments -
Collaboration and governance
Permissions
Column-level visibility and edit rights against a user context
permissions · context -
Collaboration and governance
Redaction
Masking values a viewer must not see
grid.redaction -
Collaboration and governance
Diagnostics and devtools
Frame times, stage rebuilds, store layout, warnings
grid.diagnostics -
Collaboration and governance
Value diagnostics
A setting the grid cannot use, named on the spot instead of ignored
grid.diagnostics.warnings -
Collaboration and governance
Diff and audit mode
Before and after against a baseline, computed only when asked
config.diff · grid.diff
Export and integration
The last mile, which decides whether a grid gets adopted inside an existing app.
-
Export and integration
CSV export
Delimiters, quoting, which rows, and per-cell processing
export.csv() -
Export and integration
Excel export
A styled .xlsx with frozen panes, written with no dependency
export.excel() -
Export and integration
Printing
Virtualisation off for the print pass, then restored
print mode -
Export and integration
A web component
<lattice-grid> in a page with no framework at all
customElements -
Export and integration
Saved views
Naming a configuration and switching between them
grid.views -
Export and integration
htmx integration
A plain server-rendered table, upgraded to a live grid in place
hydrateTable(table, { licence }) -
Export and integration
Coming from another grid API
An existing grid API, unchanged, on a Lattice grid underneath
new Grid(el, { columns, data }) -
Export and integration
Grid state in a URL
Sort, filter and reorder, then share the whole arrangement as a short link
serialiseState() · restoreState() -
Export and integration
Licence and the trial watermark
What an unlicensed grid does, and the loopback exemption
config.licence · grid.licence
Building one of these
Each demo is a column list and a row array. The configuration playground lets you change the options and read back the object that produced the result, the developer guide walks through the same configurations with the code beside them, and the API reference lists every option.