-
Notifications
You must be signed in to change notification settings - Fork 0
State Persistence
📝 Generated from
docs/features/state-persistence.md. Edit it there; changes made in the wiki are overwritten.
Save and restore the grid's configuration (sorting, filtering, column order, etc.) to provide a consistent experience for returning users.
You can pre-configure the grid status on mount using the initialState prop. Every part is optional, including each field of columns.
<DataGrid
rows={rows}
columns={columns}
initialState={{
sorting: {
sortModel: [{ field: 'name', sort: 'asc' }]
},
filter: {
filterModel: { items: [{ field: 'age', operator: '>', value: 30 }] }
},
columns: {
columnVisibilityModel: { age: false }
},
density: { density: 'compact' }
}}
/>initialState is read once, when the grid mounts. It seeds the grid's own (uncontrolled) state, so it has no effect on a model you also pass as a prop (sortModel, filterModel, paginationModel, …), and the density prop wins over initialState.density.
OpenGridX provides a built-in hook to simplify localStorage persistence.
import { DataGrid, useGridStateStorage } from '@opencorestack/opengridx';
export default function MyGrid() {
const { initialState, onStateChange, clearState } = useGridStateStorage('my-app-storage-key');
return (
<>
<button type="button" onClick={clearState}>Forget saved layout</button>
<DataGrid
rows={rows}
columns={columns}
initialState={initialState}
onStateChange={onStateChange}
/>
</>
);
}Writes are debounced (debounceMs, default 300 ms), and a pending write is flushed when the component unmounts. When the browser blocks storage (cookie blocking, sandboxed iframes), the hook falls back to no persistence instead of throwing.
Options (pass an object instead of the key string):
| Option | Type | Default | Description |
|---|---|---|---|
key |
string |
— | Storage key. |
debounceMs |
number |
300 |
Delay before a state change is written. |
include |
(keyof GridState)[] |
all | Only persist these parts, e.g. ['sorting', 'columns']. |
storage |
{ getItem, setItem, removeItem } |
window.localStorage |
Any Storage-like object (e.g. sessionStorage). |
clearState() removes the saved state and cancels any pending write, so the old state is not written back afterwards. It does not reset the mounted grid, and the grid's next state change is saved again; remount the grid (for example with a new key) to start from defaults.
useGridStateStorage is client-only. It reads storage in its initial render, so on the server initialState is empty while the browser's first render already has the saved state: when saved state exists, React reports a hydration mismatch (the sort arrows, column order or page differ). Render the persisted grid only after mount, for example:
function PersistedGrid() {
const { initialState, onStateChange } = useGridStateStorage('my-app-storage-key');
return <DataGrid rows={rows} columns={columns} initialState={initialState} onStateChange={onStateChange} />;
}
export default function Page() {
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
return mounted ? <PersistedGrid /> : <GridPlaceholder />;
}In Next.js, dynamic(() => import('./PersistedGrid'), { ssr: false }) does the same.
The grid reads initialState only when it mounts. If the key can change while the grid stays on screen (a per-user or per-view key), remount the grid with the key so it starts from the new key's saved state:
const storageKey = `grid-${userId}`;
const { initialState, onStateChange } = useGridStateStorage(storageKey);
<DataGrid key={storageKey} rows={rows} columns={columns} initialState={initialState} onStateChange={onStateChange} />Without the remount the grid keeps its current state, and its next change is saved under the new key.
The hook reads storage during the first render. On the server there is no storage, so the server HTML uses the default state while the client's first render uses the saved one, and React reports a hydration mismatch. If users can have saved state, render the persisted grid on the client only (for example after mount, or with Next.js dynamic(..., { ssr: false })). The migration guide, §26 has a complete example.
Use the onStateChange callback to listen for modifications to the grid state and save them to localStorage or a database. It fires once on mount and then whenever the state's value changes — re-rendering the grid with equal (even inline) props does not fire it, so storing the state in parent React state is safe.
<DataGrid
rows={rows}
columns={columns}
onStateChange={(state) => {
localStorage.setItem('grid-state', JSON.stringify(state));
}}
/>The following features support state persistence (they appear in the onStateChange payload and are restored from initialState):
-
Sorting:
sortModel -
Filtering:
filterModel -
Pagination:
paginationModel -
Columns:
columnVisibilityModel,columnOrder,pinnedColumns,columnWidths -
Density:
density
Only your own columns appear in the column state: the grid's synthetic grouping column (added by groupingColDef) is never included.
OpenGridX 3.2.2 · MIT · This wiki is generated from docs/ on every push to main. To fix a page, open a PR against the source file.
Start here
Components
- DataGrid
- Header
- Row
- Cell
- Toolbar
- Pagination
- Filter Panel
- Tooltip
- Column Visibility
- Column Grouping
- Column Resizing
- Empty State
- Error Overlay
- Aggregation Footer
Features
- Virtualization
- Filtering & Search
- Sorting & Pagination
- Custom Pagination
- Editing & Reordering
- Row Selection
- Clipboard
- Pinning
- State Persistence
- Aggregation & Pivot
- Tree Data & Grouping
- Cell Spanning
- Master-Detail
- Keyboard & Accessibility
- List View
- Infinite Scroll
- Data Source
- Loading States
- Toolbar Customization
- Export (CSV, Excel, JSON, Print)
- PDF Export
Customization
Upgrading
Contributing
- Contributing
- Testing
- Roadmap
- DataGrid orchestration
- GridRowMeta
- useGridControlledState
- useGridRowPipeline
- useGridColumns
- useGridVirtualization
- useGridVisibleRows
- useGridScrollSync
- useGridStateSnapshot