Search Docs

Search through documentation...

OpenStatus Logo

UI Components

See the /infinite example for a live demo with all components in action.

data-table with filters and timeline chart

DataTableInfinite

The main infinite scroll table component. Located at src/app/infinite/data-table-infinite.tsx.

<DataTableInfinite
  columns={columns}
  data={flatData}
  totalRows={totalDBRowCount}
  filterRows={filterDBRowCount}
  totalRowsFetched={totalFetched}
  defaultColumnFilters={defaultColumnFilters}
  defaultColumnSorting={sort ? [sort] : undefined}
  defaultColumnVisibility={defaultColumnVisibility}
  filterFields={filterFields}
  sheetFields={sheetFields}
  schema={filterSchema.definition}
  meta={metadata}
  isFetching={isFetching}
  isLoading={isLoading}
  fetchNextPage={fetchNextPage}
  hasNextPage={hasNextPage}
  fetchPreviousPage={fetchPreviousPage}
  refetch={refetch}
  renderSheetTitle={(props) => props.row?.original.pathname}
  getRowId={(row) => row.uuid}
  chartData={chartData}
  chartDataColumnId="date"
/>

DataTableFilterControls

The left sidebar with accordion-based filter controls. Renders the appropriate UI component for each filter type:

  • Checkbox — multi-select with search and count display
  • Slider — dual min/max range inputs
  • Input — text search with debounce
  • Timerange — date range picker with preset shortcuts

Toggle with Cmd+B. For responsive layouts, DataTableFilterControlsDrawer provides a mobile-friendly drawer alternative.

DataTableFilterCommand

The command palette for text-based filtering. Opens with Cmd+K.

Supports filter syntax:

  • Regular: host:API
  • Union: regions:ams,gru
  • Range: latency:100-500
  • Quoted: host:"API Server" (for values with space)
<DataTableFilterCommand schema={filterSchema.definition} tableId="my-table" />

Want AI-powered natural language queries in your command palette? Install the data-table-filter-command-ai block for an enhanced version that translates free-form text into structured filters. See AI Filters.

DataTableFilterAICommand

A drop-in replacement for DataTableFilterCommand that adds natural language query support. It includes everything from the standard command palette (structured key:value syntax, autocomplete, history) plus:

  • AI inference — type a free-form query like "5xx errors in the last hour" and press Enter. The input is sent to your AI endpoint, which streams back structured filters.
  • TextShimmer — while the AI is processing, the closed command bar displays the query with a shimmer animation so the user knows it's working.
  • Sparkles indicator — an AI: badge in the footer and a sparkles icon on the infer suggestion hint that the input is being treated as natural language.

The component automatically detects whether the input is structured (host:API) or natural language (show me slow requests) based on whether it matches known field names.

<DataTableFilterAICommand
  schema={filterSchema.definition}
  tableSchema={tableSchema.definition}
  api="/api/ai/filters"
  tableId="my-table"
/>
PropTypeDescription
schemaSchemaDefinitionBYOS schema for parsing/serializing filter values
tableSchemaTableSchemaDefinitionTable schema for AI context generation
apistringAPI endpoint that streams AI filter results
tableIdstringUnique ID for localStorage namespacing (default: "default")

For full setup including the API route and provider configuration, see AI Filters.

DataTableSheetDetails

data-table sheet detail drawer

Row detail drawer. Opens when a row is selected. Supports keyboard navigation between rows.

<DataTableSheetDetails
  title={<>{row?.original.pathname}</>}
  titleClassName="font-mono"
>
  <DataTableSheetContent fields={sheetFields} />
</DataTableSheetDetails>

DataTableFloatingBar

A fixed bottom bar that appears when rows are selected, providing a slot for bulk action buttons. Dismisses when no rows are selected.

  • Shows "{n} selected" count with a deselect button
  • Cmd+Shift+X hotkey to deselect all rows
  • Uses a render prop that receives the selected rows and table instance
<DataTableFloatingBar<ColumnSchema>>
  {({ rows, table }) => (
    <Button
      variant="outline"
      size="sm"
      onClick={() => {
        const json = JSON.stringify(
          rows.map((r) => r.original),
          null,
          2,
        );
        navigator.clipboard.writeText(json);
      }}
    >
      Copy {rows.length} rows
    </Button>
  )}
</DataTableFloatingBar>

Pass it via the floatingBarSlot prop on DataTableInfinite:

<DataTableInfinite
  floatingBarSlot={
    <DataTableFloatingBar<ColumnSchema>>
      {({ rows }) => <MyBulkActions rows={rows} />}
    </DataTableFloatingBar>
  }
  // ...other props
/>

Cell Components

Built-in cell renderers (used automatically by generateColumns):

  • DataTableCellText — plain text with overflow tooltip
  • DataTableCellCode — monospace
  • DataTableCellNumber — formatted with optional unit
  • DataTableCellBar — the number over a bar filled to its share of minmax
  • DataTableCellHeatmap — the number over a cell tinted by that same share
  • DataTableCellGauge — the number beside a circular gauge
  • DataTableCellTimestamp — relative time with absolute tooltip
  • DataTableCellBadge — colored chip
  • DataTableCellBoolean — checkmark / dash
  • DataTableCellStar — filled yellow star / outlined muted star
  • DataTableCellStatusCode — HTTP status styling
  • DataTableCellLevelIndicator — severity dot

Each takes an optional color (a hex string). Rather than passing it per cell, set colorMap on the column's display config — generateColumns and the sheet look the row's value up in it and pass the result through as color.

Filter Functions

Columns produced by generateColumns carry their filterFn as a function, resolved from the table schema's declared semantics. They need no registration — the same interpretation runs on the client, in the in-memory routes, and in SQL.

Registration is only needed for columns you write by hand that refer to a filter function by name. In TanStack Table v9 that registration lives on the feature set, not on the table config:

// lib/table/features.ts
import {
  filterFn_arrIncludes,
  filterFn_arrIncludesSome,
  filterFn_equals,
  filterFn_includesString,
  filterFn_inNumberRange,
  filterFn_weakEquals,
  tableFeatures,
} from "@tanstack/react-table";
import { arrSome, inDateRange } from "@/lib/table/filterfns";
 
export const dataTableFeatures = tableFeatures({
  // ...features and row models
  filterFns: {
    // The six names `filterFn: 'auto'` resolves to. Register all of them —
    // see the note below. `inDateRange` is the sixth, supplied further down.
    includesString: filterFn_includesString,
    inNumberRange: filterFn_inNumberRange,
    equals: filterFn_equals,
    arrIncludes: filterFn_arrIncludes,
    weakEquals: filterFn_weakEquals,
    // Not auto-resolved: registered because columns reference it by name.
    arrIncludesSome: filterFn_arrIncludesSome,
    // Ours. `inDateRange` deliberately shadows the v9 built-in of the same
    // name, so it also serves `'auto'` for Date columns.
    inDateRange,
    arrSome,
  },
});
  • inDateRange — matches dates within [start, end] range
  • arrSome — matches if row value is in the filter array

filterFn defaults to 'auto', which resolves by data type to includesString, inNumberRange, equals, arrIncludes, inDateRange, or weakEquals. Unlike v8, an unregistered name does not fall back — the column simply stops filtering (and warns in development) — so features.ts registers that whole set.

Extending TanStack Table Types

v9 threads a TFeatures type parameter through every table type, including the declaration-merged meta interfaces:

import "@tanstack/react-table";
 
declare module "@tanstack/react-table" {
  interface TableMeta<
    in out TFeatures extends TableFeatures,
    in out TData extends RowData,
  > {
    getRowClassName?: (row: Row<TFeatures, TData>) => string;
  }
 
  interface ColumnMeta<
    in out TFeatures extends TableFeatures,
    in out TData extends RowData,
    TValue extends CellData = CellData,
  > {
    headerClassName?: string;
    cellClassName?: string;
    label?: string;
    kind?: string;
  }
}

The v8 FilterFns augmentation is gone — filter functions are registered in the filterFns slot on tableFeatures() instead (see Filter Functions).