How to Use the Astryx Table Component with Column Settings and Data Virtualization
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 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—
useTableColumnSettingsStatemanages state,useTableColumnSettingscreates the plugin - Render props like
renderBodyallow 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— state management for active keys, order, and groupsuseTableColumnSettings.tsx— plugin factory that consumes state and filters columns
Setting Up Column Settings State
The useTableColumnSettingsState hook initializes and manages column configuration:
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:
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
activeColumnKeysarray 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 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:
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
datadirectly to Table when virtualizing—render only the visible slice - Reuse
Table.Rowfrompackages/core/src/Table/TableRow.tsxto maintain consistent cell rendering with column settings - Measure container height and set
estimateSizebased on your actual row height
Combining Column Settings with Virtualization
Both features compose naturally since they operate on different rendering layers:
<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 |
packages/core/src/Table/Table.tsx |
Core component with plugins and renderBody props |
BaseTable.tsx |
packages/core/src/Table/BaseTable.tsx |
Low-level logic with virtualization extension points |
useTableColumnSettings.tsx |
packages/core/src/Table/plugins/columnSettings/useTableColumnSettings.tsx |
Plugin factory for column filtering/reordering |
useTableColumnSettingsState.tsx |
packages/core/src/Table/plugins/columnSettings/useTableColumnSettingsState.tsx |
State management hook |
TableRow.tsx |
packages/core/src/Table/TableRow.tsx |
Row rendering component for custom implementations |
Summary
- Column settings are built-in: Combine
useTableColumnSettingsStatewithuseTableColumnSettings, then pass viaplugins={{columnSettings: ...}} - Virtualization requires integration: Use
renderBodyto 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.Rowensures 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →