DataExplorer
Inspect records with typed filters, multi-sort, grouping, saved views, controlled edits and CSV export.
Explore and edit real rows
Search, add a filter, reorder or pin columns, and export the matching records. Editable cells use a typed editor; this example commits changes to React state. Reload restores the original fixture.
DataExplorer composes DataTable with a complete exploration toolbar. Use DataTable directly when the application supplies its own controls. The dataset explorer adds profiles, linked charts, file/URL snapshot loading and application-owned local view persistence. See Bring your data for source limits and reset behavior.
'use client';
import { useState } from 'react';
import { DataExplorer, dataset, createTableViewState,
type DataTableColumn } from '@quartile/react';
const columns: DataTableColumn[] = [
{ field: 'id', width: 150, pinned: 'left' },
{ field: 'material', width: 140, editable: true },
{ field: 'yield', width: 140, editable: { type: 'number', nullable: true } },
];
export function SampleTable() {
const [rows, setRows] = useState([
{ id: 'S-01', material: 'Ceramic', yield: 0.91 },
{ id: 'S-02', material: 'Alloy', yield: null as number | null },
]);
const [view, setView] = useState(() => createTableViewState({ pageSize: 25 }));
return <DataExplorer data={dataset(rows, { yield: { type: 'quantitative', format: 'percent' } })}
rowKey="id" columns={columns} view={view} onViewChange={setView}
exportFileName="samples.csv"
onCellEdit={({ row: edited, field, value }) => {
setRows((previous) => previous.map((row) =>
row.id === edited.id ? { ...row, [field]: value } : row));
}} />;
}Know which rows a view describes
Shared Selection filters run first, excluding the table's own publisher. Local search and typed filters then narrow those rows, followed by grouping, stable multi-sort and pagination. Search includes configured fields even when their columns are hidden. All local filters combine with AND.
select is enabled. Label the population behind each metric, or derive a common filtered dataset when the whole workspace should follow the same local state.Grouping uses one field and each column's configured aggregation. Edits are disabled for aggregate groups. CSV export includes every matching, sorted result row before pagination and only visible columns; grouped views export groups. It does not mean every original source record.
Typed filter semantics
| Kind | Operations | Meaning |
|---|---|---|
| text | contains / notContains / eq / startsWith | Case-insensitive text matching. |
| number | eq / neq / gt / gte / lt / lte / between | Finite numbers; inclusive range with nullable open ends. |
| date | on / before / after / between | YYYY-MM-DD UTC calendar days; between includes both days. |
| category | in / notIn | Typed scalar identity; null can be selected explicitly. |
| boolean | is | Strict true or false. |
| empty | isEmpty / isNotEmpty | Null, undefined, empty string or nonfinite number. |
Date filters use UTC days: before excludes the selected day, after begins at the next day, and between ends just before the next day after its upper bound. They differ from the local-date convention of synchronous chart range predicates. Local category filters and grouping preserve typed identities. The legacy shared selection path can still equate nominal strings that parse to the same date; use stable non-date-like IDs for linked record selection. The category picker is bounded to 1,000 distinct source options, with saved selections retained.
Save view configuration
view and onViewChange make all controls application-owned. Otherwise defaultView initializes internal state. The versioned state contains search, filters, sort priorities, hidden/order/width/pin preferences, one grouping field and page settings.
import { parseTableViewState, serializeTableViewState } from '@quartile/react';
// In a client event handler; choose your own storage key and access scope.
localStorage.setItem('samples-view-v1', serializeTableViewState(view));
const saved = localStorage.getItem('samples-view-v1');
if (saved) setView(parseTableViewState(saved)); // Catch invalid/stale configurations in your UI.The View control copies and loads JSON; it does not choose persistence or synchronization for you. Saved state contains neither source records nor shared selection. Validate stored input, keep stable column keys, and handle fields removed by a dataset revision. Changing local filters, sort or grouping resets paging.
Controlled editing and export
Editing requires a stable rowKey, onCellEdit and an editable column. The callback receives the original row, field, column key, old and proposed values. Its rowKey is an opaque typed identity when you supply a field name; use the original row's ID to update source records, or provide a rowKey function with your chosen representation. The callback may return a promise: the editor shows pending state and a rejected commit's error. The library never mutates your data or writes a database.
Editors support text, finite number, ISO date, boolean and a typed select. Add validate(value, row) for application rules. Nullable editors can commit null; the boolean editor exposes an Empty choice when nullable. The example stores yield as a fraction even though its display is a percentage.
Pinned columns need explicit numeric widths. Keyboard controls reorder and resize columns; this is not an Excel-style grid with range paste, formulas or collaborative writes.
onExport overrides browser download and receives CSV, rows, columns and view. Raw values are the default; choose csvFormat="formatted" to apply column/field formats and provider locale/time zone. The lower-level tableToCSV accepts mode, schema, locale and timeZone options. It escapes quotes, uses CRLF and prefixes formula-like strings. Formatted export does not reproduce cell JSX, bar-share/sparkline visuals or delta-pill rendering, and onExport.rows still contains result values. See export formatting.
API reference and limits
Also accepts the props of Omit< DataTableProps<R>, | 'data' | 'columns' | 'rowKey' | 'transform' | 'groupBy' | 'sort' | 'defaultSort' | 'onSortChange' | 'sorts' | 'defaultSorts' | 'onSortsChange' | 'manualSort' | 'page' | 'defaultPage' | 'onPageChange' | 'pageSize' | 'limit' >, spread onto the root element.
Frame and state props shared with the charts (5) · ChartStateProps
Filtering, grouping, sorting and CSV generation run synchronously over in-memory rows. Pagination bounds rendered rows, not computation. For worker-backed pages use QueryDataTable; DataExplorer is not automatically a remote query or permission boundary. Read the public table guide for the headless helpers and full contracts.