npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@chainsys/sab-react-grid

v2.0.1

Published

Enterprise TanStack Table component for React: virtualized rows, sorting, filtering, grouping, column resize, Excel/CSV export, layout modes, Tailwind UI, and light/dark theme handling.

Downloads

189

Readme

@chainsys/sab-react-grid

A professional, enterprise-grade data grid for React applications. Built on TanStack Table and TanStack React Virtual, it delivers high-performance tables with sorting, filtering, grouping, column visibility, virtualization, and Excel/CSV export. The UI is Tailwind CSS–based (utility classes and dark: variants), with light / dark / system theme handling that integrates with your app’s toggle or document-level styling.

Package: @chainsys/sab-react-grid
Version: 2.0.1
License: MIT


Features

| Feature | Description | |--------|-------------| | TanStack Table | Full control over sorting, filtering, grouping, pagination, column visibility, order, and sizing | | Row virtualization | Renders only visible rows via @tanstack/react-virtual for smooth scrolling with large datasets | | Layout modes | fit-default, fit-window, or fit-content (content-based column widths) | | Column filters | Text, number, date, datetime, time (Flatpickr), multiselect, checkbox, boolean, dropdown, and radio with operand support (contains, equals, greater than, etc.) | | Filter placeholders | Search icon (🔎︎) for text/number and dropdown filters; calendar icon for date/datetime/time filters (with SVG placeholder when empty) | | Export | Excel (.xlsx) via ExcelJS and CSV via plain Blob from the toolbar Data Export menu | | Grouping | Drag columns into the grouping zone; expand/collapse groups | | Aggregations (group + list footers) | Optional per-column aggregates with Slickgrid-style group footers (under expanded groups) and an optional list footer (<tfoot>) for grand totals | | Column reorder | Drag-and-drop column reordering in the header | | Column visibility | Show/hide columns from the Columns menu | | Sticky headers | Header and filter rows stay fixed while the body scrolls | | Row selection | Opt-in checkbox selector column (header select-all + per-row checkbox) with onRowSelectionChange callback | | Actions column | Row actions (Add, View, Edit, Delete, List, or custom) with configurable icon, label, and navigation URL from meta.actions | | Builtin navigation for actions | Actions can reuse the same built-in router/dialog flow as field navigation (no app callback required) | | Performance | Memoized header, filter, row, and cell components | | Tailwind UI | Toolbar, filters, cells, and chrome styled with Tailwind utilities; optional built-in toolbar light/dark toggle | | Theme API | theme, showThemeToggle, and onThemeChange for controlled or document-driven (system) appearance; root uses data-sab-theme for debugging and CSS hooks | | Tailwind content helper | Subpath export @chainsys/sab-react-grid/tailwind-content so your build scans the package and generates all grid classes (avoids “unstyled” or wrong-mode cells after purge) | | Card styles (fields & actions) | Per-column card/frame and typography via meta.labelStyle, meta.boxStyle, meta.valueStyle; per-action buttonStyle / labelStyle on TableAction | | Frozen columns | Column header menu: Freeze Upto (pin left through selected column) and Unfreeze all columns; optional columnDef.enablePinning: false to exclude a column from freeze | | Field navigation (click & hover) | meta.fieldNavigationInfo: click activates on pointer/keyboard; hover uses a short dwell timer before firing — header glyph + cell data-sab-* attributes |

Card styles (fields & actions)

Use inline React CSS objects on column meta so JSON-driven configs can style cells without custom cell renderers:

| Key | Where it applies | Purpose | |-----|------------------|---------| | labelStyle | Header and filter cells | Chrome for the title/filter row: background, borders, radius (not body “cards”) | | boxStyle | Body value cells | Card / frame: backgroundColor, borderRadius, borders, boxShadow — applied on the cell chrome | | valueStyle | Body value content | Typography / presentation: color, fontWeight, textDecoration — frame keys are stripped and should live in boxStyle |

Actions: each entry in meta.actions can set buttonStyle, labelStyle, className, iconClassName for per-button styling.

// Data column: soft card + bold value
meta: {
  dataType: 'currency',
  boxStyle: { backgroundColor: '#f8fafc', borderRadius: 8, border: '1px solid #e2e8f0' },
  valueStyle: { fontWeight: 600, color: '#0f172a' },
},
// Actions: compact primary button
meta: {
  actions: [
    {
      id: 'edit',
      label: 'Edit',
      icon: 'Edit',
      buttonStyle: { backgroundColor: '#4f46e5', color: '#fff', borderRadius: 6, padding: '4px 10px' },
      labelStyle: { fontWeight: 600 },
    },
  ],
},

Frozen columns

  • Open the column header chevron (⋮) menu on any column that allows pinning.
  • Freeze Upto — pins all visible leaf columns from the left through this column (inclusive). The grid validates the request (for example you cannot freeze through the last visible column; viewport hints may appear as toasts).
  • Unfreeze all columns — clears the custom freeze and returns to normal horizontal scroll.
  • To disable freeze for a specific column (no “Freeze Upto” effect on that column), set TanStack columnDef.enablePinning: false.

Internally the grid may use sticky pinning or a split layout (separate frozen vs scroll panes) depending on state; behavior is the same from the user’s perspective: left columns stay visible while the rest scrolls horizontally.

Field navigation: click vs hover

Configure meta.fieldNavigationInfo on data columns (default DataFormatter / date cells only — custom column.cell is not wrapped). The column header shows a small glyph and tooltip (“field click” vs “field hover”). Data attributes on the cell wrapper help tests and theming: data-sab-field-interaction, data-sab-field-id, data-sab-column-id.

| triggerEvent | When navigation runs | UX notes | |----------------|------------------------|----------| | click | On click (and Enter / Space when focused) | cursor-pointer, role="button", tabIndex={0}; click does not bubble to the row | | hover | After the pointer stays over the value ~400ms (mouseenter → timer; cleared on mouseleave) | Avoids accidental navigation while moving the mouse; native title on the value can show the formatted cell string |

For both modes, handling follows type (router vs dialog) and fieldNavigationBehavior (builtin vs callback). Use onFieldNavigation only for callback; builtin dialog uses renderFieldNavigationDialog or builtinDialogPreset.

Minimal click (builtin router):

meta: {
  fieldNavigationInfo: {
    id: 'region-nav',
    triggerEvent: 'click',
    type: 'router',
    navigationUrl: (row) => `/regions/${(row as { id: string }).id}`,
  },
},

Minimal hover (callback — app handles preview):

<SabReactTable
  data={rows}
  columns={columns}
  onFieldNavigation={(payload) => {
    if (payload.triggerEvent !== 'hover') return
    // open preview / tooltip app using payload.row, payload.cellValue, payload.fieldId
  }}
/>

// column meta:
meta: {
  fieldNavigationInfo: {
    id: 'preview-units',
    triggerEvent: 'hover',
    type: 'dialog',
    fieldNavigationBehavior: 'callback',
  },
},

Installation

npm install @chainsys/sab-react-grid @tanstack/react-table @tanstack/react-virtual react react-dom

Peer dependencies

Install in your application if not already present:

  • react (^18.0.0 or ^19.0.0)
  • react-dom (^18.0.0 or ^19.0.0)
  • @tanstack/react-table (^8.0.0)
  • @tanstack/react-virtual (^3.0.0)
  • flatpickr and react-flatpickr (date/datetime/time filters)
  • tailwindcss (^3.4.0) — recommended whenever you style the app with Tailwind; marked optional in peerDependenciesMeta so non-Tailwind consumers can still install the package, but the grid’s classes require Tailwind in the host build for correct appearance

Dependencies (installed automatically)

  • date-fns – date formatting
  • exceljs – Excel (.xlsx) export (larger files; NPM vulnerabilities mitigated via package overrides)
  • flatpickr, react-flatpickr – date/datetime/time filters

CSV export uses no extra libraries (plain string/Blob).

Tailwind CSS and theming

The component library does not ship a separate CSS bundle: styling is Tailwind utility classes (bg-*, text-*, borders, dark:*, etc.). Your application must run Tailwind v3 in its build and include this package’s sources in Tailwind content, or unused classes are purged and the grid can look unstyled or stuck in light mode while the rest of the app is dark.

Subpath export: tailwind-content

The package publishes @chainsys/sab-react-grid/tailwind-content, which resolves to absolute globs for dist/**/*.{js,mjs,cjs} and src/**/*.{ts,tsx} inside the installed package. Spread it into content so paths work in monorepos and different node_modules layouts.

ESM tailwind.config.js (recommended):

import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
const sabGridContent = require('@chainsys/sab-react-grid/tailwind-content')

export default {
  darkMode: 'selector', // or 'class'; align with how you set `dark` on `<html>` / `:root`
  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}', ...sabGridContent],
}

CommonJS tailwind.config.cjs:

const sabGridContent = require('@chainsys/sab-react-grid/tailwind-content')
module.exports = {
  darkMode: 'selector',
  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}', ...sabGridContent],
}

Important: If your Tailwind config sets content, it replaces any preset’s content entirely. Do not assume a shared preset alone will scan this package—you must merge ...sabGridContent (or equivalent manual globs) into your app’s content array.

Manual globs (only if you cannot use the subpath; more brittle):

content: [
  './index.html',
  './src/**/*.{js,ts,jsx,tsx}',
  './node_modules/@chainsys/sab-react-grid/dist/**/*.{js,mjs,cjs}',
  './node_modules/@chainsys/sab-react-grid/src/**/*.{ts,tsx}',
],

Install tailwindcss, postcss, and autoprefixer in the app and load a global stylesheet with @tailwind directives.

Theme behavior (theme, showThemeToggle, onThemeChange)

| Mode | How it works | |------|----------------| | Controlled | Pass theme="light" or theme="dark" from the same state you use for the rest of the app (e.g. next-themes, Zustand, or useState). The grid follows your value even if <html> differs; it applies Tailwind’s dark class on the grid root when needed. | | Uncontrolled | Omit theme (or use theme="system"). The grid starts in system mode: it observes class / data-theme on documentElement, body, and #root so it tracks <html class="dark">, data-theme="dark", etc. | | Toolbar toggle | Set showThemeToggle to show a sun/moon control. If theme is controlled, you must pass onThemeChange so toolbar clicks update your app state (and typically <html class="dark">). Otherwise the button cannot sync the grid with your app. |

Example (controlled, matches a typical app toggle):

const [dark, setDark] = useState(false)

return (
  <>
    <button type="button" onClick={() => setDark((d) => !d)}>
      Toggle theme
    </button>
    <SabReactTable
      data={data}
      columns={columns}
      theme={dark ? 'dark' : 'light'}
      showThemeToggle
      onThemeChange={(t) => setDark(t === 'dark')}
    />
  </>
)

The grid root exposes data-sab-theme="light" | "dark" for debugging and optional host CSS. Types: SabGridTheme ('light' | 'dark' | 'system'), SabGridResolvedTheme ('light' | 'dark' for callbacks).


Project structure and main files

| Path | Purpose | |------|---------| | src/index.ts | Package entry; re-exports component, formatters, export helpers, and types | | src/SabReactTable.tsx | Main grid component: table UI, toolbar, filter row, virtualization, grouping, export menu | | src/utils/tableFormatters.tsx | DataFormatter, ActionFormatter, date formatting, TableAction / ACTION_ICON_KEYS / ActionIcons | | src/utils/exportUtils.ts | exportTableToExcel (ExcelJS), exportTableToCSV (plain Blob) | | src/utils/tableColumnResizeUtils.ts | resizeColumnsByCellContent, getWidthInPixel, getWidthInPixelTitle for fit-to-content layout | | dist/ | Built output (ESM + CJS + types) | | tailwind-content.cjs | Published alongside dist for Tailwind content globs (exports["./tailwind-content"]) |

Build: npm run build (runs clean then build:esm and build:cjs). The prepublishOnly script runs the same build before publish.
Clean: npm run clean removes dist before build to avoid stale artifacts.


Quick start

import { SabReactTable } from '@chainsys/sab-react-grid'
import type { ColumnDef } from '@tanstack/react-table'

interface Person {
  id: number
  name: string
  email: string
  role: string
}

const columns: ColumnDef<Person, unknown>[] = [
  { id: 'id', accessorKey: 'id', header: 'ID' },
  { id: 'name', accessorKey: 'name', header: 'Name' },
  { id: 'email', accessorKey: 'email', header: 'Email' },
  { id: 'role', accessorKey: 'role', header: 'Role', meta: { dataType: 'text' } },
]

const data: Person[] = [
  { id: 1, name: 'Alice', email: '[email protected]', role: 'Admin' },
  { id: 2, name: 'Bob', email: '[email protected]', role: 'User' },
]

export function MyTable() {
  return (
    <SabReactTable<Person>
      data={data}
      columns={columns}
      title="Users"
      defaultPageSize={50}
      pageSizeOptions={[10, 20, 50, 100]}
      initialLayoutMode="fit-content"
      onSortedDataChange={(rows) => console.log('Filtered/sorted rows', rows)}
      onRowClick={(row) => console.log('Clicked', row)}
    />
  )
}

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | data | TData[] | required | Row data | | columns | ColumnDef<TData, any>[] | required | TanStack Table column definitions | | title | string | 'Table_List' | Table title in the toolbar | | onSortedDataChange | (rows: TData[]) => void | — | Called with filtered/sorted rows (pre-pagination) | | defaultPageSize | number | 50 | Initial page size | | pageSizeOptions | number[] | [10, 20, 50, 100, 500, 1000] | Page size dropdown options | | groupingLabels | Record<string, string> | {} | Display labels for grouping columns | | onRowClick | (row: TData) => void | — | Row click handler | | onActionClick | (actionId, row, action) => void | — | Handler for action buttons; use action.navigate or action.id to route | | onFieldNavigationOverride | (args) => boolean \| void | — | Optional override hook; return true to stop built-in navigation when column meta.fieldNavigationInfo is set | | onFieldNavigation | (payload) => void | — | callback columns only — navigate or app-owned dialog. Not called for builtin + dialog (use renderFieldNavigationDialog) | | routerNavigate | SabRouterNavigateFn | — | Optional override for built-in router navigation; omit when the table is under react-router’s Router (wired automatically) | | routerLocation | { pathname; search; hash } | — | Router location snapshot (only needed when you pass a custom routerNavigate and use preserveLocation) | | renderFieldNavigationDialog | (args) => ReactNode | — | Builtin type: 'dialog' body inside the grid modal; onFieldNavigation is not used for this path | | initialLayoutMode | 'fit-default' \| 'fit-window' \| 'fit-content' | 'fit-content' | Initial layout mode | | theme | SabGridTheme | — | 'light', 'dark', or 'system' (follows document dark / data-theme). Omit for uncontrolled system mode. | | showThemeToggle | boolean | false | Toolbar sun/moon control; pair with onThemeChange when theme is controlled. | | onThemeChange | (theme: SabGridResolvedTheme) => void | — | Fires when the user changes theme via the toolbar; update app state and document class when using controlled theme. | | fillParent | boolean | false | If true, grid uses flex-1 min-h-0 so it fills a flex parent and scrolls within available space | | className | string | — | Extra classes on the root grid card | | getRowId | (row, index) => string | — | Stable row id for selection when rows lack a unique id field. | | onRowSelectionChange | (selection, selectedRows) => void | — | TanStack RowSelectionState and selected data rows when row selection is used. | | toolbarIcons | SabGridToolbarIcons | — | Replace default toolbar glyphs (title badge, command menu, filter toggle, pagination prev/next) |


Column meta

Use the meta property on column definitions to control formatting and filtering:

| Meta key | Type | Description | |----------|------|-------------| | dataType | 'text' \| 'number' \| 'decimal' \| 'currency' \| 'email' \| 'boolean' \| 'date' \| 'datetime' \| 'time' \| 'percentage' \| 'icon-boolean' | Cell formatting and alignment | | labelStyle | React.CSSProperties | Header / filter cell chrome (background, borders) — not body card frame | | boxStyle | React.CSSProperties | Body cell card / frame (background, radius, borders, shadow) | | valueStyle | React.CSSProperties | Body cell content (color, font weight, etc.); frame keys belong in boxStyle | | formatOptions | { decimals?, currencySymbol?, dateFormat?, datetimeFormat?, timeFormat? } | Number, currency, and date formatting | | filterType | 'text' \| 'select' \| 'multiselect' \| 'checkbox' \| 'boolean' \| 'date' \| 'number' \| 'dropdown' \| 'radio' | Filter UI type | | filterOptions | { label: string; value: any }[] | Options for select, multiselect, dropdown, and radio filters | | actions | TableAction[] | Row action buttons (Add, View, Edit, Delete, List, or custom) | | invokeActionClickCallback | boolean | Actions only — false opts into package navigation when an action includes navigationUrl/navigate (unless overridden per action) | | checkboxSelector | boolean | When true, column becomes the row selection checkbox selector (header select-all + per-row checkbox) | | fieldNavigationInfo | TableFieldNavigationInfo | Clickable/hoverable cell: built-in navigation (router URL update) or dialog (see below) | | aggregationFn | AggregationFnOption<any> | Enables group footer aggregates for this column (under expanded groups) | | groupFooter | SabFooterPresentation | Presentation for group footer cells (label + styling such as Avg: / Total:) | | footerAggregationFn | AggregationFnOption<any> | Enables list footer (<tfoot>) aggregates for this column | | footer | SabFooterPresentation | Presentation for list footer cells | | footerScope | 'filtered' \| 'page' | List footer aggregate scope: filtered totals across all filtered rows, page totals current page only |

Filter placeholders

  • Text, number, and dropdown/select filters: search placeholder (🔎︎).
  • Date, datetime, and time filters: calendar icon (SVG) when empty, plus optional text placeholder; Flatpickr for picking date/time.

Actions (from config / JSON)

Actions are fully driven by column meta.actions. Each action can specify:

| Field | Type | Description | |-------|------|-------------| | id | string | Unique key passed to onActionClick | | label | string | Button label (optional for icon-only buttons) | | icon | 'View' \| 'Edit' \| 'Delete' \| 'Add' \| 'List' or lowercase in JSON, or ReactNode | Built-in icon or custom node | | navigate | string \| (row) => string | URL or function; use in onActionClick for routing (e.g. router.push(...)) | | navigationUrl | string \| (row) => string | Preferred alias of navigate (used by built-in action navigation) | | actionNavigationBehavior | 'builtin' \| 'callback' | callback (default): your onActionClick / action.onClick runs. builtin: grid performs built-in router/dialog navigation (same as field navigation). | | navigationType | 'router' \| 'dialog' | Built-in action navigation type (defaults to router) | | preserveLocation | boolean | Built-in router only — keep current URL and store context in location.state (see Field navigation) | | builtinDialogPreset | 'embedCurrentRoute' \| 'fieldContextCard' | Built-in dialog only — package-provided dialog bodies | | onClick | (row) => void | Per-action handler (if not using table onActionClick) | | show | (row) => boolean | Hide button for specific rows | | className | string | Extra CSS class for the button | | buttonStyle | React.CSSProperties | Inline styles on the action <button> | | labelStyle | React.CSSProperties | Inline styles on the label <span> (icon / label modes) |

Built-in icon keys: View, Edit, Delete, Add, List (exported as ACTION_ICON_KEYS). In JSON you can use lowercase ("view", "add", etc.).

Example:

meta: {
  actions: [
    { id: 'add', label: 'Add', icon: 'Add', navigate: '/create' },
    { id: 'view', label: 'View', icon: 'View', navigate: (row) => `/item/${row.id}` },
    { id: 'edit', label: 'Edit', icon: 'Edit', navigate: (row) => `/edit/${row.id}` },
    { id: 'list', label: 'List', icon: 'List', navigate: '/list' },
  ],
}

react-router-dom (built-in navigation)

When you use meta.invokeActionClickCallback: false (package resolves action.navigate) or built-in field navigation with type: 'router', the grid calls react-router’s navigate() for you.

Default: render SabReactTable inside your app’s <Router> / <Routes> tree (add react-router-dom; it is an optional peer). The table detects the router context and wires navigation internally — no routerNavigate prop is required.

Override: pass routerNavigate yourself if you use a custom history, tests, or a table instance that is not under a Router:

import { useNavigate } from 'react-router-dom'

function MyGrid() {
  const navigate = useNavigate()
  return (
    <SabReactTable
      data={rows}
      columns={columns}
      routerNavigate={(to, o) => navigate(to, { replace: o?.replace, state: o?.state })}
    />
  )
}

http(s):// URLs still use a full document navigation (location.assign). Dialog / popup modes are unchanged.

Linked package / monorepo (Vite): if the table is already under BrowserRouter but built-in navigation still warns, the bundler may be resolving two copies of react-router-dom, so the grid’s hooks do not see your router context. Add resolve.dedupe for react-router and react-router-dom (see sab-grid-demo vite.config.ts). Avoid aliasing react-router to a single file — that breaks subpath imports such as react-router/dom.

Optional helpers: getActionClickChannel, type SabInteractionChannel — see column meta.invokeActionClickCallback.


Field navigation (click / hover)

Use column meta.fieldNavigationInfo to mark data cells (default formatter or date cells) as navigable. This is separate from meta.actions. Custom column.cell renderers are not wrapped; action columns take precedence.

Click vs hover (timing, keyboard, data-sab-* attributes, and minimal samples) is documented under Field navigation: click vs hover (see the Features section above).

Builtin vs callback: fieldNavigationBehavior controls whether the grid performs navigation (builtin, default) or calls your app (callback) via onFieldNavigation.

| Field | Type | Description | |-------|------|-------------| | id | string | Stable identifier (telemetry, tests, data-sab-field-id) | | triggerEvent | 'click' \| 'hover' | click: immediate on click + Enter/Space when focused. hover: ~400ms dwell after mouseenter (cleared on mouseleave) before firing | | type | 'router' \| 'dialog' | router: in-app URL (automatic navigate() when under Router). dialog: callbackonFieldNavigation; builtin → grid modal + renderFieldNavigationDialog (not onFieldNavigation) | | navigationUrl | string \| (row) => string | In-app path for router; URL hint for dialog/iframe; normalized for router when needed | | navigate | string \| (row) => string | Deprecated alias of navigationUrl | | fieldNavigationBehavior | 'builtin' \| 'callback' | builtin (default): grid navigates or opens dialog. callback: grid calls onFieldNavigation only | | preserveLocation | boolean | Builtin router only — keep current URL and pass context via location.state only | | builtinDialogPreset | 'embedCurrentRoute' \| 'fieldContextCard' | Builtin dialog only — package-provided dialog bodies | | show | (row) => boolean | Optional row filter |

Builtin router context: The grid passes SAB_FIELD_NAV_CONTEXT_KEYS inside location.state[SAB_FIELD_NAV_ROUTER_STATE_KEY] (not in the URL query string). Read it on the destination route with useLocation().

UI: Interaction hint on the column header. Cells expose data-sab-field-interaction, data-sab-field-id, data-sab-column-id.

Router wiring: Usually omitted — the table uses react-router’s navigate() when rendered under a Router. Pass routerNavigate only to override.

// Builtin router: path only; context in location.state (see SAB_FIELD_NAV_ROUTER_STATE_KEY)
meta: {
  fieldNavigationInfo: {
    id: 'open-region',
    triggerEvent: 'click',
    type: 'router',
    navigationUrl: () => '/regions/detail',
    // preserveLocation: true, // keep URL unchanged; context flows via location.state
  },
},
// Callback dialog: app modal via `onFieldNavigation` (recommended for dialogs)
meta: {
  fieldNavigationInfo: {
    id: 'units-preview',
    triggerEvent: 'hover',
    type: 'dialog',
    fieldNavigationBehavior: 'callback',
  },
},
// Full app control: no grid navigation
meta: {
  fieldNavigationInfo: {
    id: 'custom',
    triggerEvent: 'click',
    type: 'router',
    fieldNavigationBehavior: 'callback',
    navigationUrl: (row) => `/items/${(row as { id: string }).id}`,
  },
},

Aggregations (group footer + list footer)

Aggregations are opt-in per column and rendered as:

  • Group footer rows (under expanded groups): enable with column meta.aggregationFn (or TanStack column.aggregationFn), and optionally style with meta.groupFooter.
  • List footer row (<tfoot>): enable with column meta.footerAggregationFn, optionally style with meta.footer, and control scope with meta.footerScope ('filtered' vs 'page').

Example:

import type { ColumnDef } from '@tanstack/react-table'

type Row = { id: string; amount: number; region: string }

const columns: ColumnDef<Row, unknown>[] = [
  { id: 'region', accessorKey: 'region', header: 'Region' },
  {
    id: 'amount',
    accessorKey: 'amount',
    header: 'Amount',
    meta: {
      dataType: 'currency',
      aggregationFn: 'sum',
      groupFooter: { label: 'Total:', labelClassName: 'font-semibold' },
      footerAggregationFn: 'sum',
      footerScope: 'filtered',
      footer: { label: 'Total:', labelClassName: 'font-semibold' },
    },
  },
]

Row selection (checkbox selector column)

Row selection is enabled when you add a checkbox selector column:

  • Preferred: set column meta.checkboxSelector: true
  • Legacy: a column id ending in '_checkbox_selector' is treated as a selector

When enabled, the grid renders a header select-all checkbox and per-row checkboxes, and calls onRowSelectionChange(selection, selectedRows).


Example: filters and formatting

const columns: ColumnDef<Person, unknown>[] = [
  { id: 'id', accessorKey: 'id', header: 'ID', meta: { dataType: 'number' } },
  { id: 'name', accessorKey: 'name', header: 'Name', meta: { dataType: 'text' } },
  { id: 'email', accessorKey: 'email', header: 'Email', meta: { dataType: 'email' } },
  {
    id: 'role',
    accessorKey: 'role',
    header: 'Role',
    meta: {
      dataType: 'text',
      filterType: 'select',
      filterOptions: [
        { label: 'Admin', value: 'Admin' },
        { label: 'User', value: 'User' },
      ],
    },
  },
]

Export

  • Excel: exportTableToExcel(data, filename?, tableTitle?) – uses ExcelJS; suitable for larger files. NPM audit issues from transitive deps are addressed via overrides in package.json (minimatch).
  • CSV: exportTableToCSV(data, filename?) – plain string/Blob; no ExcelJS.

Both are available from the toolbar Data Export menu and as standalone imports.


Exports

| Export | Description | |--------|-------------| | SabReactTable | Main grid component | | SabReactTableProps, LayoutMode, SabGridTheme, SabGridResolvedTheme, OnActionClickHandler, OnFieldNavigationOverrideHandler | Component, layout, and theme types | | DataFormatter, ActionFormatter | Default cell and action formatters | | formatDateValue, renderDateCell, isIsoDateLike | Date formatting helpers | | ActionIcons, ACTION_ICON_KEYS, TableAction, ActionConfig, ActionIconKey | Action types and built-in icons | | FieldNavigationCellWrapper, resolveFieldNavigate, buildFieldNavigationPayload, buildBuiltinRouterNavigateOptions, buildInternalFieldNavigationContext, isFieldNavigationMetaPlausible, normalizeNavigationPath, SAB_FIELD_NAV_CONTEXT_KEYS, SAB_FIELD_NAV_ROUTER_STATE_KEY, TableFieldNavigationInfo, FieldNavigationType, FieldNavigationTriggerEvent, FieldNavigationPayload | Field navigation config and helpers | | FieldNavigationDialogRenderArgs, SabRouterNavigateFn | Router/dialog integration types | | exportTableToExcel, exportTableToCSV | Standalone export helpers | | resizeColumnsByCellContent, getWidthInPixel, getWidthInPixelTitle | Column resize utilities | | ColumnResizeDef, ResizeColumnsByCellContentParams | Resize utility types | | DateFormatOptions | Date format options type | | ColumnDef, RowSelectionState | Re-exported from @tanstack/react-table |

Subpaths: @chainsys/sab-react-grid/tailwind-content — CommonJS module exporting a string[] of glob paths for Tailwind content (see Tailwind CSS and theming).



License

MIT