---
id: "packages/active-grid"
title: "active-grid"
description: "A styled, standalone React data grid with filtering, sorting, paging, virtualization, editing and export, in client and server mode."
url: "/packages/active-grid"
locale: "en"
revision: "5f3222ff87acb043"
---

# active-grid

`@workspace/active-grid` is a React data grid with its own kernel, its own plain-CSS stylesheet and
its own internal controls. It runs on React and React DOM alone: no framework, no design system, no
Tailwind and no provider is required. Your application gives it columns, data (or a function that
fetches data), a presentation and a translator.

**Example: Orders in client mode**

Client: Runs entirely in your browser. Source: `examples/grid/client-orders.tsx`.

```tsx
// apps/docs/examples/grid/client-orders.tsx
'use client';

import '@workspace/active-grid/styles.css';

import {useMemo} from 'react';
import {generateOrders, type Order} from '@/lib/examples/orders';

import {
  composePresentationContext,
  toIanaTimeZone,
} from '@workspace/active-format';
import {
  ActiveGrid,
  createColumnHelper,
  englishGridTranslator,
  type GridColumnDef,
} from '@workspace/active-grid';

// The presentation: language, notation and the time zone the date filter
// needs for its relative presets and calendar.
const presentation = composePresentationContext(
  'en-GB',
  null,
  toIanaTimeZone('Europe/Amsterdam'),
);

const STATUS = [
  {label: 'Pending', value: 'pending'},
  {label: 'Processing', value: 'processing'},
  {label: 'Shipped', value: 'shipped'},
  {label: 'Delivered', value: 'delivered'},
  {label: 'Cancelled', value: 'cancelled'},
];

const ch = createColumnHelper<Order>();
const columns: GridColumnDef<Order>[] = [
  ch.accessor('id', {header: 'Order', meta: {filterType: 'text'}}),
  ch.accessor('customer', {header: 'Customer', meta: {filterType: 'text'}}),
  ch.accessor('category', {
    header: 'Category',
    meta: {filterType: 'select'},
  }),
  ch.accessor('quantity', {header: 'Qty', type: 'number'}),
  ch.accessor('total', {
    header: 'Total',
    type: 'currency',
    meta: {currencyCode: 'EUR', filterType: 'number'},
  }),
  ch.accessor('orderDate', {header: 'Ordered', type: 'date'}),
  ch.accessor('status', {
    header: 'Status',
    type: 'badge',
    meta: {
      filterType: 'select',
      filterOptions: STATUS,
      statusTones: {
        pending: 'warning',
        processing: 'info',
        shipped: 'info',
        delivered: 'success',
        cancelled: 'danger',
      },
    },
  }),
];

/** Client mode: all 500 rows are in the browser; the grid sorts, filters and pages them there. */
export default function ClientOrders() {
  const data = useMemo(() => generateOrders(500), []);
  return (
    <ActiveGrid<Order>
      gridId="docs-client-orders"
      mode="client"
      data={data}
      columns={columns}
      height={420}
      locale={presentation}
      t={englishGridTranslator}
      enableRowSelection
      pagination={{enabled: true, pageSizeOptions: [10, 25, 50]}}
      toolbar={{search: {enabled: true}, showColumnChooser: true}}
    />
  );
}
```

## What it does

- **Two modes.** Client mode sorts, filters and pages rows that are in the browser. Server mode asks
  your `fetchFn` for one block of rows at a time, with the sorting, filters and search to apply.
- **Columns.** Resize, reorder, pin, hide, a column chooser, auto-size; column types for text,
  numbers, amounts, percentages, dates, badges and booleans.
- **Filtering.** Text, number, date, select and boolean filters with operator presets, a global
  search, server-side facets, and an opt-in AND/OR filter builder.
- **Rows.** Selection (also "select all" in server mode), pinning, expanding with detail panels,
  grouping with aggregation.
- **Editing.** Cell, row and batch modes with validation, and undo and redo.
- **Export.** CSV, JSON, Excel, PDF and the clipboard, as data or as presentation.
- **Keyboard and direction.** Cell-level keyboard navigation with roving focus; right to left with
  logical pinning; any language `Intl` formats.

## What it does not do

- **No data access.** In server mode the server is yours: the grid sends a request and renders the
  answer. It has no query language, no database adapter and no authorization.
- **No language of its own.** Every word goes through the translator you pass (`t`);
  `englishGridTranslator` is the package's English, which you pass on purpose. The presentation
  (`locale`) is required.
- **No brand.** A neutral baseline; your application maps the `--grid-*` tokens to its own
  (see [Host styling](/packages/active-grid/host-styling)).

## What your application provides

| You provide | Where |
| --- | --- |
| the presentation and the translator | `locale` and `t`, per grid or once on `<GridDefaults>` |
| the stylesheet, once | `import '@workspace/active-grid/styles.css'` |
| the token mapping to your brand | your global CSS (optional) |
| in server mode: the server and `fetchFn` | a route of your own; the grid sends its request shape |
| a link component, export fonts, a sink | `<GridDefaults ui={{linkComponent}}>`, export options, `diagnostics` |
| the optional export libraries | `xlsx` for Excel, `jspdf` and `jspdf-autotable` for PDF |

## Dependencies

`@workspace/active-format`, `@workspace/active-virtual` and `@workspace/active-helpers` (owners
that come along with it), `@base-ui/react`, `clsx`, `date-fns`, `@date-fns/tz`, `lucide-react` and
`react-day-picker`. `react` and `react-dom` 19 are peers.

## Next

- [Getting started](/packages/active-grid/getting-started)
- [Client, server and simulation](/packages/active-grid/client-server-and-simulation): the three
  kinds of examples, with a real server route.
