# How to Use the Astryx Table Component with Column Settings and Data Virtualization

> Master the Astryx Table component. Learn to integrate column settings and data virtualization for high-performance, interactive tables. Unlock efficient data display.

- Repository: [Meta/astryx](https://github.com/facebook/astryx)
- Tags: how-to-guide
- Published: 2026-08-05

---

**Build interactive, high-performance tables in Astryx by composing the built-in column settings plugin with third-party virtualization libraries through the Table's plugin architecture.**

The Astryx design system provides a composable **Table** component designed for extensibility through plugin hooks. For complex data applications, two critical capabilities are **column settings** (runtime column visibility, reordering, and grouping) and **data virtualization** (rendering only visible rows for large datasets). Both integrate seamlessly via the `plugins` prop and render-prop APIs in `facebook/astryx`.

## Understanding Astryx Table Plugin Architecture

The Table component in [`packages/core/src/Table/Table.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/Table.tsx) accepts a `plugins` object that intercepts and transforms rendering at key lifecycle points. This architecture keeps core Table code lean while enabling powerful extensions without internal modifications.

Key design principles from the source:

- **Plugins are pure functions** that receive table state and return transformed state or JSX
- **State hooks are separate from plugin factories**—`useTableColumnSettingsState` manages state, `useTableColumnSettings` creates the plugin
- **Render props like `renderBody`** allow complete override of table regions for virtualization

## Column Settings: Hide, Reorder, and Group Columns

Column settings in Astryx are implemented as a first-class plugin. The implementation spans two files in `packages/core/src/Table/plugins/columnSettings/`:

- [`useTableColumnSettingsState.tsx`](https://github.com/facebook/astryx/blob/main/useTableColumnSettingsState.tsx) — state management for active keys, order, and groups
- [`useTableColumnSettings.tsx`](https://github.com/facebook/astryx/blob/main/useTableColumnSettings.tsx) — plugin factory that consumes state and filters columns

### Setting Up Column Settings State

The `useTableColumnSettingsState` hook initializes and manages column configuration:

```tsx
import {useTableColumnSettingsState} from '@astryxdesign/core/Table/plugins/columnSettings';

const columnState = useTableColumnSettingsState({
  columns: allColumns,
  activeColumnKeys: ['id', 'name', 'email'],
  onChangeActiveColumnKeys: (keys) => console.log('active columns →', keys),
  columnGroups: [
    {group: 'Info', keys: ['id', 'name']},
    {group: 'Contact', keys: ['email']},
  ],
});

```

The returned `columnState` object includes helpers for building UI controls:

| Property | Purpose |
|----------|---------|
| `toggleColumn(key)` | Show/hide a column |
| `isColumnVisible(key)` | Check visibility state |
| `reset()` | Restore initial configuration |
| `columnSettingsConfig` | Object passed to plugin factory |

### Creating and Applying the Plugin

Feed the state configuration into the plugin factory, then pass to Table:

```tsx
import {Table} from '@astryxdesign/core';
import {useTableColumnSettings} from '@astryxdesign/core/Table/plugins/columnSettings';

const columnSettingsPlugin = useTableColumnSettings(columnState.columnSettingsConfig);

<Table
  columns={allColumns}
  data={data}
  plugins={{columnSettings: columnSettingsPlugin}}
/>

```

The plugin automatically:

- Filters out columns not in `activeColumnKeys`
- Orders columns according to `activeColumnKeys` array sequence
- Preserves grouping metadata for header rendering

## Data Virtualization: Rendering Large Datasets

Astryx does not ship a dedicated virtualization plugin in core, but [`packages/core/src/Table/BaseTable.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/BaseTable.tsx) explicitly reserves extension points for virtualization with comments referencing "sticky shadows, virtualization." The `renderBody` prop provides the integration mechanism.

### Integration with react-virtual

Use any virtualization library via `renderBody`. This example uses `@tanstack/react-virtual`:

```tsx
import {Table} from '@astryxdesign/core';
import {useVirtual} from '@tanstack/react-virtual';
import {useRef} from 'react';

function VirtualizedTable({columns, data}) {
  const parentRef = useRef(null);
  
  const rowVirtualizer = useVirtual({
    size: data.length,
    parentRef,
    estimateSize: () => 40,
    overscan: 5,
  });

  return (
    <Table
      columns={columns}
      renderBody={() => (
        <div ref={parentRef} style={{overflow: 'auto', height: '400px'}}>
          <div style={{height: rowVirtualizer.totalSize, position: 'relative'}}>
            {rowVirtualizer.virtualItems.map((virtualRow) => (
              <div
                key={virtualRow.index}
                style={{
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  width: '100%',
                  transform: `translateY(${virtualRow.start}px)`,
                }}
              >
                <Table.Row row={data[virtualRow.index]} />
              </div>
            ))}
          </div>
        </div>
      )}
    />
  );
}

```

Critical integration points:

- **Do not pass `data` directly** to Table when virtualizing—render only the visible slice
- **Reuse `Table.Row`** from [`packages/core/src/Table/TableRow.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/TableRow.tsx) to maintain consistent cell rendering with column settings
- **Measure container height** and set `estimateSize` based on your actual row height

### Combining Column Settings with Virtualization

Both features compose naturally since they operate on different rendering layers:

```tsx
<Table
  columns={allColumns}
  plugins={{columnSettings: columnSettingsPlugin}}
  renderBody={() => {
    // Virtualization renders only visible rows
    // Column settings plugin already filtered/reordered columns
    const visibleColumns = columnState.activeColumnKeys
      .map(key => allColumns.find(col => col.key === key))
      .filter(Boolean);
    
    return (
      <div ref={parentRef} style={{overflow: 'auto', height: '400px'}}>
        <div style={{height: rowVirtualizer.totalSize, position: 'relative'}}>
          {rowVirtualizer.virtualItems.map((virtualRow) => (
            <div
              key={virtualRow.index}
              style={{
                position: 'absolute',
                top: 0,
                left: 0,
                width: '100%',
                transform: `translateY(${virtualRow.start}px)`,
              }}
            >
              <Table.Row
                row={data[virtualRow.index]}
                columns={visibleColumns}
              />
            </div>
          ))}
        </div>
      </div>
    );
  }}
/>

```

The column settings plugin processes columns before virtualization, so virtual rows render only the active subset.

## File Reference

| File | Path | Purpose |
|------|------|---------|
| [`Table.tsx`](https://github.com/facebook/astryx/blob/main/Table.tsx) | [`packages/core/src/Table/Table.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/Table.tsx) | Core component with `plugins` and `renderBody` props |
| [`BaseTable.tsx`](https://github.com/facebook/astryx/blob/main/BaseTable.tsx) | [`packages/core/src/Table/BaseTable.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/BaseTable.tsx) | Low-level logic with virtualization extension points |
| [`useTableColumnSettings.tsx`](https://github.com/facebook/astryx/blob/main/useTableColumnSettings.tsx) | [`packages/core/src/Table/plugins/columnSettings/useTableColumnSettings.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/plugins/columnSettings/useTableColumnSettings.tsx) | Plugin factory for column filtering/reordering |
| [`useTableColumnSettingsState.tsx`](https://github.com/facebook/astryx/blob/main/useTableColumnSettingsState.tsx) | [`packages/core/src/Table/plugins/columnSettings/useTableColumnSettingsState.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/plugins/columnSettings/useTableColumnSettingsState.tsx) | State management hook |
| [`TableRow.tsx`](https://github.com/facebook/astryx/blob/main/TableRow.tsx) | [`packages/core/src/Table/TableRow.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/TableRow.tsx) | Row rendering component for custom implementations |

## Summary

- **Column settings are built-in**: Combine `useTableColumnSettingsState` with `useTableColumnSettings`, then pass via `plugins={{columnSettings: ...}}`
- **Virtualization requires integration**: Use `renderBody` to override table body rendering with any virtual-scroll library
- **Composition is explicit**: State hooks, plugin factories, and render props are separate concerns, making behavior predictable and testable
- **Internal components are reusable**: `Table.Row` ensures cell rendering stays consistent with Astryx styling when building custom virtualized bodies

## Frequently Asked Questions

### Does Astryx Table include a built-in virtualization plugin?

No. The core package in `facebook/astryx` does not ship a virtualization plugin, but [`BaseTable.tsx`](https://github.com/facebook/astryx/blob/main/BaseTable.tsx) contains explicit extension points and the `renderBody` prop enables integration with any virtual-scroll library. This design choice keeps bundle size minimal for users who don't need virtualization.

### How does the column settings plugin affect row data?

It doesn't. The column settings plugin operates solely on the `columns` array, filtering and reordering before headers and cells render. Row data flows through unchanged, which means virtualization and other row-level transformations remain unaffected.

### Can I persist column settings across sessions?

Yes. The `useTableColumnSettingsState` hook accepts `activeColumnKeys` and `onChangeActiveColumnKeys` props. Save `activeColumnKeys` to `localStorage` or a backend, then rehydrate on mount. The hook's `reset()` method restores the initial configuration when needed.

### What happens if `activeColumnKeys` includes a key not in `columns`?

The plugin silently ignores invalid keys. Only columns present in both the `columns` array and `activeColumnKeys` are rendered. This defensive behavior prevents crashes when column schemas evolve independently of saved preferences.