Skip to main content
Frozen columns remain visible on the left side (or right side in RTL mode) of the grid while other columns scroll horizontally. This is useful for keeping important columns like IDs or names always visible.

Basic Usage

Mark columns as frozen using the frozen property:
import { DataGrid, type Column } from 'react-data-grid';

interface Row {
  id: number;
  name: string;
  email: string;
  department: string;
  salary: number;
}

const columns: Column<Row>[] = [
  { key: 'id', name: 'ID', width: 80, frozen: true },
  { key: 'name', name: 'Name', width: 200, frozen: true },
  { key: 'email', name: 'Email', width: 250 },
  { key: 'department', name: 'Department', width: 200 },
  { key: 'salary', name: 'Salary', width: 150 }
];

function MyGrid() {
  return <DataGrid columns={columns} rows={rows} />;
}
With the above configuration:
  • ID and Name columns stay fixed on the left
  • Email, Department, and Salary scroll horizontally
  • A shadow appears on the edge of the last frozen column to indicate the freeze boundary

Multiple Frozen Columns

You can freeze multiple consecutive columns:
const columns: Column<Row>[] = [
  { key: 'id', name: 'ID', frozen: true },
  { key: 'name', name: 'Name', frozen: true },
  { key: 'status', name: 'Status', frozen: true },
  { key: 'col1', name: 'Column 1' },
  { key: 'col2', name: 'Column 2' },
  // ... more scrollable columns
];
Frozen columns must be consecutive and start from the beginning of the columns array. You cannot freeze columns in the middle or at the end.
const columns: Column<Row>[] = [
  { key: 'id', name: 'ID', frozen: true },
  { key: 'name', name: 'Name', frozen: true },
  { key: 'email', name: 'Email' }, // Not frozen
  { key: 'phone', name: 'Phone' }  // Not frozen
];
Frozen columns are at the start and consecutive.

Frozen Column Shadow

The grid automatically adds a visual shadow to indicate the boundary between frozen and scrollable columns:
// From src/DataGrid.tsx - shadow is automatically rendered
{lastFrozenColumnIndex > -1 && (
  <div
    className={frozenColumnShadowClassname}
    style={{
      gridColumnStart: lastFrozenColumnIndex + 2,
      insetInlineStart: totalFrozenColumnWidth
    }}
  />
)}
The shadow provides a visual cue showing where the frozen area ends.
You can customize the shadow appearance using CSS:
.rdg-frozen-column-shadow {
  box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
}

/* Dark mode */
.rdg-dark .rdg-frozen-column-shadow {
  box-shadow: 2px 0 5px rgba(0, 0, 0, 0.3);
}

RTL (Right-to-Left) Support

In RTL mode, frozen columns are pinned to the right side:
<DataGrid
  columns={columns}
  rows={rows}
  direction="rtl"
/>
When direction="rtl" is set:
  • Frozen columns appear on the right side
  • Scrollbar moves to the left
  • Column resize handles appear on the left edge
  • The frozen column shadow appears on the left side of frozen columns

Frozen Columns with Selection

The SelectColumn can be frozen like any other column:
import { DataGrid, SelectColumn, type Column } from 'react-data-grid';

const columns: Column<Row>[] = [
  { ...SelectColumn, frozen: true },
  { key: 'id', name: 'ID', frozen: true },
  { key: 'name', name: 'Name' },
  // ... more columns
];
import { useState } from 'react';
import { DataGrid, SelectColumn } from 'react-data-grid';

function MyGrid() {
  const [selectedRows, setSelectedRows] = useState<ReadonlySet<number>>(new Set());
  
  const columns = [
    { ...SelectColumn, frozen: true },
    { key: 'id', name: 'ID', width: 80, frozen: true },
    { key: 'name', name: 'Name', width: 200, frozen: true },
    { key: 'email', name: 'Email', width: 250 },
    { key: 'department', name: 'Department', width: 200 }
  ];
  
  return (
    <DataGrid
      columns={columns}
      rows={rows}
      rowKeyGetter={(row) => row.id}
      selectedRows={selectedRows}
      onSelectedRowsChange={setSelectedRows}
    />
  );
}
The selection checkbox stays visible while scrolling horizontally.

Frozen Columns and Features

Resizable Frozen Columns

Frozen columns can be resized:
const columns: Column<Row>[] = [
  {
    key: 'id',
    name: 'ID',
    width: 80,
    frozen: true,
    resizable: true
  },
  {
    key: 'name',
    name: 'Name',
    width: 200,
    frozen: true,
    resizable: true
  }
];

Sortable Frozen Columns

Frozen columns support sorting:
const columns: Column<Row>[] = [
  {
    key: 'id',
    name: 'ID',
    frozen: true,
    sortable: true
  },
  {
    key: 'name',
    name: 'Name',
    frozen: true,
    sortable: true
  }
];

Editable Frozen Columns

Frozen columns can be editable:
import { renderTextEditor } from 'react-data-grid';

const columns: Column<Row>[] = [
  {
    key: 'name',
    name: 'Name',
    frozen: true,
    renderEditCell: renderTextEditor
  }
];
All column features work with frozen columns:
  • Resizing
  • Sorting
  • Editing
  • Custom renderers
  • Cell spanning
  • Column classes

Frozen Columns in TreeDataGrid

In TreeDataGrid, group columns are automatically frozen:
import { TreeDataGrid, type Column } from 'react-data-grid';

const columns: Column<Row>[] = [
  { key: 'country', name: 'Country' }, // Auto-frozen when used in groupBy
  { key: 'city', name: 'City' },       // Auto-frozen when used in groupBy
  { key: 'name', name: 'Name' },
  { key: 'value', name: 'Value' }
];

function MyTreeGrid() {
  const [expandedGroupIds, setExpandedGroupIds] = useState<ReadonlySet<unknown>>(new Set());
  
  return (
    <TreeDataGrid
      columns={columns}
      rows={rows}
      groupBy={['country', 'city']} // These columns are automatically frozen
      rowGrouper={rowGrouper}
      expandedGroupIds={expandedGroupIds}
      onExpandedGroupIdsChange={setExpandedGroupIds}
    />
  );
}
From the source code (src/TreeDataGrid.tsx):
for (const [index, column] of columns.entries()) {
  if (rawGroupBy.includes(column.key)) {
    groupBy.push(column.key);
    columns[index] = {
      ...column,
      frozen: true, // Automatically frozen
      renderCell: () => null,
      renderGroupCell: column.renderGroupCell ?? renderToggleGroup,
      editable: false
    };
  }
}
Columns used in groupBy are automatically marked as frozen: true.

Performance Considerations

Frozen columns are always rendered, even with column virtualization enabled:
// From src/hooks/useViewportColumns.ts (conceptual)
const viewportColumns = [
  ...frozenColumns,           // Always included
  ...visibleScrollableColumns // Only visible ones
];
This means:
  • Frozen columns do not affect horizontal scroll performance
  • More frozen columns = slightly more initial render work
  • Generally, 1-5 frozen columns have negligible impact
  • Avoid freezing 10+ columns as they bypass virtualization
Best Practices:
  • Freeze 1-3 key columns (ID, name, status)
  • Avoid freezing many wide columns
  • Use frozen columns for data users need constant access to
  • Consider responsive design: fewer frozen columns on mobile

Styling Frozen Columns

Apply custom styles to frozen columns:
const columns: Column<Row>[] = [
  {
    key: 'id',
    name: 'ID',
    frozen: true,
    cellClass: 'frozen-cell',
    headerCellClass: 'frozen-header'
  }
];
.frozen-cell {
  background-color: #f9f9f9;
  font-weight: 600;
}

.frozen-header {
  background-color: #e8e8e8;
  font-weight: bold;
}

/* Dark mode */
.rdg-dark .frozen-cell {
  background-color: #2a2a2a;
}

.rdg-dark .frozen-header {
  background-color: #1a1a1a;
}
Make frozen columns more visually distinct:
.frozen-cell {
  background-color: #f0f7ff;
  border-right: 2px solid #0066cc;
  position: sticky;
  z-index: 1;
}

.frozen-header {
  background-color: #e6f2ff;
  border-right: 2px solid #0066cc;
  font-weight: 700;
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.5px;
}

Accessibility

Frozen columns maintain proper ARIA attributes:
// The grid automatically sets appropriate aria-colindex values
<div
  role="gridcell"
  aria-colindex={1} // Correct index regardless of scroll position
>
  Cell content
</div>
Screen readers announce frozen columns correctly when navigating the grid.

API Reference

Column Property

frozen

frozen?: boolean
Default: false Description: Determines whether the column is frozen (pinned). Frozen columns remain visible during horizontal scrolling. Requirements:
  • Frozen columns must be consecutive
  • Frozen columns must start from the beginning of the columns array
  • In RTL mode, frozen columns are pinned to the right
Usage:
const columns: Column<Row>[] = [
  { key: 'id', name: 'ID', frozen: true },
  { key: 'name', name: 'Name', frozen: true },
  { key: 'email', name: 'Email' }
];

DataGrid Props

direction

direction?: 'ltr' | 'rtl'
Default: 'ltr' Description: Text direction of the grid. In RTL mode, frozen columns are pinned to the right side. Effects:
  • 'ltr': Frozen columns on left, scrollbar on right
  • 'rtl': Frozen columns on right, scrollbar on left

Build docs developers (and LLMs) love