Skip to main content

Column panel, toolbar, and saved views

The column panel manages visibility, pinning, and order; the toolbar surfaces grouping, sorting, quick filter, views, and export as opt-in sections; saved views capture the whole layout under application-owned persistence.

Loading interactive example…
React
const [views, setViews] = useState<SavedGridView[]>(loadViews);

<Grid
rowData={rows}
columnDefs={columns}
columnPanel={{ trigger: "toolbar" }}
toolbar={{ grouping: true, sorting: true, quickFilter: true, views: true, export: true }}
savedViews={{
views,
onChange: (next) => {
setViews(next);
localStorage.setItem("grid-views", JSON.stringify(next));
},
}}
/>

Column panel​

const options = {
columnPanel: {
trigger: "rail",
defaultOpen: false,
width: 320,
},
} satisfies GridOptions;

Change trigger to "header", "menu", "footer", or "toolbar". The drawer provides search, show/hide, pinning, pointer/keyboard ordering, bulk visibility, nested groups, and reset. Opt a column out with suppressColumnPanel: true.

Set availability: "pivot" to mount the panel only while pivot mode is on. Since a pivoted panel is the pivot setup and nothing else, this gives an app that manages columns its own way the grid's pivot customizer without the column management drawer:

const options = {
columnPanel: { availability: "pivot" },
} satisfies GridOptions;

Columns with pivot roles (row group, pivot column, aggregate) show removable role chips under their rows, and while pivot mode is on the panel becomes the pivot setup: the column list gives way to three ordered, drag-reorderable role wells.

Toolbar sections​

const options = {
toolbar: {
grouping: true,
sorting: true,
quickFilter: true,
views: true,
export: true,
},
columnPanel: { trigger: "toolbar" },
} satisfies GridOptions;

Every section is independently opt-in. The toolbar observes the grid container and automatically collapses labels and moves secondary commands into an overflow menu as space shrinks — resize the demo's window to see it adapt.

Application-owned saved views​

let views: SavedGridView[] = loadViews();
let activeViewId: string | null = null;

const options = {
toolbar: { views: true },
savedViews: {
views,
activeViewId,
onChange: (nextViews) => {
views = nextViews;
localStorage.setItem("grid-views", JSON.stringify(nextViews));
},
onActiveViewChange: (id) => {
activeViewId = id;
},
},
} satisfies GridOptions;

The application owns persistence; the grid supplies picker and CRUD behavior. A view contains column layout, column-group expanders (which groups are open or closed), grouping, sorting, filters, the quick filter (its text, match mode, match case and filter-or-find behavior), the footer's aggregate scope (None / Current page / Entire dataset), rows pinned at runtime (api.setRowPinned or the row menu's Pin, by row id), and group expansion. The row selection and the sheets list are deliberately not part of a view. A column group is matched by its colId, so give groups an explicit one for their expander state to survive a reload. (The live demo above persists its views to localStorage — shape the grid, open or close a group, save a view, and reload the page.)

Capture and apply view state directly​

const state = api.captureViewState();
localStorage.setItem("orders-view", JSON.stringify(state));

api.applyViewState(state); // exact column restore
api.applyViewState(state, { columns: "merge" }); // retain later columns

Fields added after a view was saved are optional: a view captured before quickFilterSettings, aggregateScope or rowPinning existed applies its quick-filter text alone and leaves the live match settings, aggregate scope and runtime pins as they are. A present rowPinning replaces every runtime pin (an empty list unpins them all); rows pinned by an isRowPinned callback or supplied as pinnedTopRowData / pinnedBottomRowData are configuration, not view state, and are never captured. On a server-side grid without a server aggregation source a saved "all" scope applies as "page".