VitalGrid Concepts Guide
This guide explains the core ideas behind VitalGrid in simple terms.
This guide explains the core ideas behind VitalGrid in simple terms.
It is for people who want to understand how VitalGrid is put together so they can use it correctly and extend it safely.
If you just want to see code, start with:
- Getting Started
- Then come back here when you want to know why things are structured this way
What VitalGrid provides
VitalGrid is a ready-to-use table system that:
- Uses TanStack React Table under the hood
- Supports large datasets with virtualization
- Supports drag and drop for rows and columns
- Supports dynamic columns driven by your backend
- Works with any backend through a clean data source interface
- Gives you composable React components to build your own table UI
To make this work without getting messy, we follow a few core concepts:
- Factory
- Hook
- Provider
- Compound components
- Data source (VitalGridDataSource)
- Columns (VitalGridColumn)
- Fields (cell types)
- Feature flags and capabilities
The rest of this guide walks through each of these.
1. Factory: createVitalGrid
The factory is your starting point.
You call it once, outside of React components:
- Configure global features (sorting, resizing, virtualization, etc.)
- Register field types (text, number, status, etc.)
Example (conceptual):
const VitalGrid = createVitalGrid({
features: {
sorting: true,
filtering: true,
columnResizing: true,
virtualization: true,
views: true,
},
fields: {
text: createTextField(),
number: createNumberField(),
// your custom fields...
},
});The factory returns:
useVitalGrid– the main hook for runtime configurationProvider– context provider for your grid- A set of UI components (Root, Toolbar, Header, Body, Columns, etc.)
- Helpers like
createColumnDef(depending on your entrypoint)
Key points:
- The factory is pure configuration.
- No data fetching here.
- No React hooks here.
- You can create more than one factory if you like (for different apps or setups).
2. Hook: VitalGrid.useVitalGrid
Inside your React component, you call the factory's hook.
Here you pass runtime values:
- Your data source (backend adapter)
- Your rows and columns (based on the data source)
- Any per-instance options
Conceptually:
function MyGrid() {
const dataSource = useMyDataSource();
const { grid, VitalGridProvider } = VitalGrid.useVitalGrid({
dataSource,
data: dataSource.rows.current,
columns: dataSource.columns.current.map((column) =>
VitalGrid.createColumnDef(column)
),
});
return (
<VitalGridProvider
grid={grid}
workspaceId="my-workspace"
height={600}
>
{/* UI components go here */}
</VitalGridProvider>
);
}What useVitalGrid does for you:
- Wires up TanStack React Table
- Applies feature flags
- Respects what the data source says it supports
- Exposes a
gridobject that contains:table(the TanStack table)features(final enabled features)fields(field registry)localPreferenceManager(for widths etc.)
Key points:
- This is where you pass
dataSource. - You do not pass
dataSourceto the factory. - This split keeps code clean and testable.
3. Provider: VitalGridProvider
VitalGridProvider wraps your grid UI.
It:
- Stores the table object in context
- Shares features, workspace/view IDs, height, scroll container, etc.
- Powers all the compound components
Conceptual layout:
<VitalGridProvider
grid={grid}
workspaceId="my-workspace"
viewId="main-view"
height={600}
>
<VitalGrid.Root height={600}>
<VitalGrid.Toolbar showViewControls={grid.features.views} />
<VitalGrid.Header />
<VitalGrid.Body />
</VitalGrid.Root>
</VitalGridProvider>Key points:
- You almost always:
- Call
useVitalGrid - Wrap with
VitalGridProvider - Render compound components inside
- Call
4. Compound components
VitalGrid exposes a set of React components under the factory namespace.
Common ones:
RootToolbarandToolbar.SlotColumns,Columns.Left,Columns.RightHeaderBodyBodyCell
These are:
- Thin wrappers around the table and context
- Made to be easy to compose
- A way to express "what my grid looks like" in JSX
Example:
<VitalGrid.Root height={600}>
<VitalGrid.Toolbar showViewControls={grid.features.views}>
<VitalGrid.Toolbar.Slot name="left">
<Button onClick={onRefresh}>Refresh</Button>
</VitalGrid.Toolbar.Slot>
<VitalGrid.Toolbar.Slot name="right">
<Button onClick={onExport}>Export</Button>
</VitalGrid.Toolbar.Slot>
</VitalGrid.Toolbar>
<VitalGrid.Columns>
<VitalGrid.Columns.Left>
<VitalGrid.Columns.Select />
<VitalGrid.Columns.Drag />
</VitalGrid.Columns.Left>
<VitalGrid.Columns.Right>
<VitalGrid.Columns.Add />
</VitalGrid.Columns.Right>
</VitalGrid.Columns>
<VitalGrid.Header />
<VitalGrid.Body />
</VitalGrid.Root>Key points:
- They do not know about Convex or any backend.
- They talk to the table and context only.
- You pick and arrange them to fit your UI.
5. Data source: VitalGridDataSource
This is the contract between VitalGrid and your backend.
A data source:
-
Wraps your real API (Convex, REST, GraphQL, localStorage, etc.)
-
Exposes a simple shape:
rows:current: T[]create,update,delete
columns:current: VitalGridColumn[]create,update,delete
views(optional):save,load,all
capabilities:supportedFeatures(what this backend can do)
getRowId(optional):- How to read the row's ID (e.g.
_id,id)
- How to read the row's ID (e.g.
VitalGrid includes built-in data sources:
- Local storage (
useLocalStorageDataSource) - Dexie/IndexedDB (
useDexieDataSource)
Note: The docs-site includes a Convex demonstration example, but Convex is not part of the core library. You can implement your own data source adapter for any backend by implementing the VitalGridDataSource interface.
Key points:
- All backend-specific logic lives here.
useVitalGridand components treat the data source as a black box.- To support a new backend, implement this interface and plug it in.
6. Columns: VitalGridColumn
VitalGridColumn is the standard column shape the grid understands.
It is used by:
VitalGridDataSource.columns.currentcreateColumnDef- Header, body, and system column logic
Typical fields:
id: string- Unique ID
- Also used as accessor key
header: string- Label shown in header
type: string- Field type key (links to a field definition)
settings?: object- Extra config passed to the field component
- Optional:
width,order,pinned,hidden, etc.
Key points:
- All grid internals use
VitalGridColumn. - Backend-specific column types are converted to
VitalGridColumnin the data source layer. - Do not leak backend-specific shapes into your UI code.
7. Fields: cell types and value mapping
Fields define how a column behaves at the cell level.
A field:
- Knows how to render a value
- Knows how to edit a value
- Can map between stored values and UI values with simple helpers
Important idea:
toDisplayValue:- Turns the stored cell value into something the component uses.
toPersistenceValue:- Turns user input back into the stored value.
Examples:
- Text field:
- Stored: string
- Display: string
- Date field:
- Stored: ISO string
- Display: Date object or formatted string in the UI
- Status field:
- Stored: status key
- Display: colored badge
Key points:
- Field logic is UI-level.
- Do not put backend-specific logic in your fields.
- Backend mapping belongs in the data source.
8. Feature flags and capabilities
VitalGrid has features like:
- Sorting
- Filtering
- Column resizing
- Column/row reordering (drag and drop)
- Virtualization
- Views
- Selection
- Add-column controls
These are controlled in two steps:
- Factory config:
- You choose what you want:
const VitalGrid = createVitalGrid({
features: {
sorting: true,
filtering: true,
columnResizing: true,
columnReordering: true,
rowReordering: true,
virtualization: true,
views: true,
selection: true,
addColumn: true,
},
});- Data source capabilities:
- Each data source declares what it supports.
- VitalGrid combines:
- factory features
- data source capabilities
- Result is
grid.features:- Only features that are enabled and supported.
Key points:
- You do not need to manually hide features the backend cannot handle.
- Use
grid.featuresin your UI (for example, to show view controls only whenviewsis true).
Putting it together
When you build with VitalGrid, think in this order:
- Choose or implement a
VitalGridDataSourcefor your backend. - Create a factory with
createVitalGrid:- Set features.
- Register fields.
- In your component:
- Call
VitalGrid.useVitalGrid({ dataSource, data, columns }). - Wrap with
VitalGridProvider. - Use the compound components (
Root,Toolbar,Header,Body,Columns, etc.).
- Call
- Let:
- The data source handle backend details.
VitalGridColumndescribe columns.- Fields handle cell behavior.
- Features and capabilities drive what is turned on.
If you follow these concepts, you get:
- A table that is:
- Virtualized
- Drag and drop ready
- Column-dynamic
- Backend-agnostic
- With a clear place to put every piece of logic.