Skip to main content

Column Pinning

Pin columns to the left edge of the grid so they remain visible while the user scrolls horizontally through the remaining columns.

Live Demo

Scroll horizontally — the Name column stays pinned
Live
Name
Email
Department
Salary
Status
Age
Start Date
Alice Johnson
alice@example.com
Engineering
$50,000
Active
25
2024-01-15
Bob Smith
bob@example.com
Marketing
$53,500
Draft
26
2024-02-15
Carol Lee
carol@example.com
Sales
$57,000
Archived
27
2024-03-15
David Kim
david@example.com
Finance
$60,500
Active
28
2024-04-15
Eve Torres
eve@example.com
Operations
$64,000
Draft
29
2024-05-15
Frank Wu
frank@example.com
Engineering
$67,500
Archived
30
2024-06-15
Grace Park
grace@example.com
Marketing
$71,000
Active
31
2024-07-15
Henry Adams
henry@example.com
Sales
$74,500
Draft
32
2024-08-15
Irene Costa
irene@example.com
Finance
$78,000
Archived
33
2024-09-15
Jack Rivera
jack@example.com
Operations
$81,500
Active
34
2024-10-15
Try it in your framework

The demo above uses Radix UI for styling. To see this feature with your framework's design system (Fluent UI, Material UI, Vuetify, PrimeNG, etc.), click "Open in online demo" below the demo.

Quick Example

import { OGrid } from '@alaarab/ogrid-react-radix';
import type { IColumnDef } from '@alaarab/ogrid-react-radix';

interface Person {
id: number;
name: string;
email: string;
department: string;
salary: number;
status: string;
age: number;
startDate: string;
}

const columns: IColumnDef<Person>[] = [
{ columnId: 'name', name: 'Name', pinned: 'left', defaultWidth: 160 },
{ columnId: 'email', name: 'Email', defaultWidth: 220 },
{ columnId: 'department', name: 'Department', defaultWidth: 160 },
{
columnId: 'salary',
name: 'Salary',
type: 'numeric',
defaultWidth: 120,
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
},
{ columnId: 'status', name: 'Status', defaultWidth: 120 },
{ columnId: 'age', name: 'Age', type: 'numeric', defaultWidth: 80 },
{ columnId: 'startDate', name: 'Start Date', defaultWidth: 130 },
];

function App() {
return (
<OGrid
columns={columns}
data={people}
getRowId={(p) => p.id}
defaultPageSize={10}
/>
);
}
Switching UI libraries

The OGrid component has the same props across all React UI packages. To switch, just change the import:

  • Radix (lightweight, default): from '@alaarab/ogrid-react-radix'
  • Fluent UI (Microsoft 365 / SPFx): from '@alaarab/ogrid-react-fluent' — wrap in <FluentProvider>
  • Material UI (MUI v7): from '@alaarab/ogrid-react-material' — wrap in <ThemeProvider>

The "Name" column stays fixed on the left while all other columns scroll normally.

How It Works

Set pinned: 'left' on any IColumnDef to pin it. The grid renders pinned columns with CSS position: sticky and a calculated left offset so multiple pinned columns stack correctly.

{ columnId: 'name', name: 'Name', pinned: 'left' }

Pinned columns:

  • Use sticky positioning so they stay visible during horizontal scroll.
  • Stack left-to-right in the order they appear in the columns array.
  • Work with all grid features: sorting, filtering, editing, cell selection, and context menu.

With Row Selection

When rowSelection is 'single' or 'multiple', OGrid inserts a checkbox column at the far left. Pinned columns appear after the checkbox column, and both stay visible during scroll.

<OGrid
columns={columns}
data={rows}
getRowId={(r) => r.id}
rowSelection="multiple"
/>

With Column Groups

Pinning works inside column groups. Set pinned: 'left' on the leaf IColumnDef items within a group. The group header will span correctly over the pinned columns.

const columns = [
{
headerName: 'Identity',
children: [
{ columnId: 'id', name: 'ID', pinned: 'left' },
{ columnId: 'name', name: 'Name', pinned: 'left' },
],
},
{ columnId: 'email', name: 'Email' },
];

Persisting Pin State

Pin state can be saved and restored via the Grid API, just like column widths and sort order:

const gridRef = useRef<IOGridApi<Row>>(null);

// Save pin state (e.g. to localStorage)
const state = gridRef.current.getColumnState();
// state.pinnedColumns → { name: 'left' }
localStorage.setItem('gridState', JSON.stringify(state));

// Restore pin state on mount
const saved = JSON.parse(localStorage.getItem('gridState'));
gridRef.current.applyColumnState(saved);

onColumnPinned Callback

Listen for pin changes to persist state automatically:

<OGrid
columns={columns}
data={rows}
getRowId={(r) => r.id}
ref={gridRef}
onColumnPinned={(columnId, pinned) => {
// pinned is 'left', 'right', or null (unpinned)
const state = gridRef.current.getColumnState();
localStorage.setItem('gridState', JSON.stringify(state));
}}
/>

Props Reference

TypeFieldValuesDescription
IColumnMetapinned'left' | 'right'Pin column to left or right edge
IOGridPropsonColumnPinned(columnId, pinned) => voidCalled when a column is pinned or unpinned
IGridColumnStatepinnedColumnsRecord<string, 'left' | 'right'>Pinned columns in saved state
IColumnMetaminWidthnumberMinimum width in pixels (useful for pinned columns)
IColumnMetadefaultWidthnumberInitial width in pixels
tip

Pin identifying columns like "Name" or "ID" so users always know which row they are looking at, even when scrolling through many columns.