HelpBot Assistant

How can I help you?

Column Pinning (Frozen) in Angular Grid Component

19 Mar 202624 minutes to read

The Syncfusion® Angular Grid component enables pinning (freezing) columns to lock them in place on the left, right, or a fixed position, ensuring they remain visible during horizontal scrolling. This feature ensures that essential data, such as identifiers or action buttons, remains accessible while navigating large datasets.

Frozen columns require careful configuration to avoid rendering issues. Ensure they are within the grid’s viewport and test rendering across different screen sizes for responsiveness.

Freeze multiple columns

The Syncfusion® Angular Grid allows freezing multiple columns using the frozenColumns property, which specifies the number of columns to freeze on the left side. This ensures that the specified columns remain fixed while the remaining columns can be scrolled horizontally.

The following example sets the frozenColumns property to “2”, freezing the first two columns on the left side of the grid.

import { data } from './datasource';
import { Component, OnInit, ViewChild } from '@angular/core';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { EditService, FreezeService, GridComponent, GridModule, SelectionService, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { NumericTextBoxAllModule, NumericTextBoxComponent, RatingAllModule } from '@syncfusion/ej2-angular-inputs';

@Component({
  imports: [
      GridModule,
      NumericTextBoxAllModule,
      RatingAllModule,
      ButtonModule
      ],
  providers: [FreezeService, SelectionService, EditService, ToolbarService],
  standalone: true,
    selector: 'app-root',
    template: `<div style="display: flex">
    <label style="padding: 5px 5px 5px 0">
      Change the frozen columns:
    </label>
    <ejs-numerictextbox
      id="frozenColumns"
      #frozenColumns
      min="0"
      max="3"
      [validateDecimalOnType]="true"
      decimals="0"
      format="n"
      value="2"
      width="100px"
    ></ejs-numerictextbox>
    <div> 
      <button style="margin-left:5px" ejs-button (click)="frozenColumnFn()">
        Update
      </button>
    </div>
  </div>
  <ejs-grid #grid style="padding: 5px 5px" [dataSource]='data' height=290 [frozenColumns]='2' [allowSelection]='false'  [enableHover]='false'>
    <e-columns>
      <e-column field='OrderID' headerText='Order ID' textAlign='Right' width=90></e-column>
      <e-column field='CustomerID' headerText='Customer ID' width=100></e-column>
      <e-column field='OrderDate' headerText='Order Date' width=100 format='yMd' textAlign='Right'></e-column>
      <e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
      <e-column field='ShipName' headerText='Ship Name' width=130></e-column>
      <e-column field='ShipAddress' headerText='Ship Address' width=140></e-column>
      <e-column field='ShipCity' headerText='Ship City' width=100></e-column>
      <e-column field='ShipCountry' headerText='Ship Country' width=100></e-column>
      <e-column field='ShipRegion' headerText='Ship Region' width=80></e-column>
      <e-column field='ShipPostalCode' headerText='Ship Postal Code' width=110></e-column>
      <e-column field='Freight' headerText='Freight' width=80></e-column>
    </e-columns>
  </ejs-grid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
    @ViewChild('grid')
    public grid?: GridComponent;
    @ViewChild('frozenColumns')
    public frozenColumns?: NumericTextBoxComponent;
  
    ngOnInit(): void {
      this.data = data;
    }
  
    frozenColumnFn() {
      (this.grid as GridComponent).frozenColumns = (this.frozenColumns as NumericTextBoxComponent).value;
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

  • Frozen columns must not be set outside the grid’s viewport.
  • The Grid supports column virtualization with frozen columns, improving performance for large datasets.
  • Frozen columns are supported only for columns visible in the current view.
  • Both frozenColumns and frozenRows properties can be used together.
  • When column virtualization and frozen columns are enabled, horizontal scrolling via touch pad gestures (e.g., two-finger swipe) is not supported. Use the horizontal scrollbar instead.

Freeze particular columns

The Grid provides a straightforward way to freeze individual columns on the left side using the isFrozen property in the column definition. When this property is set to true, the chosen column is locked at the leftmost position of the Grid and remains visible while scrolling horizontally.

The following example demonstrates freezing a particular column using the isFrozen property. Initially, the “OrderID” column is frozen. The change event of the DropDownList component allows dynamically changing which column is frozen. The selected column’s isFrozen property is modified using the getColumnByField method, and the refreshColumns method updates the display.

import { data } from './datasource';
import { Component, OnInit, ViewChild } from '@angular/core';
import { ButtonModule, CheckBoxModule, RadioButtonModule, SwitchModule } from '@syncfusion/ej2-angular-buttons';
import { ChangeEventArgs, DropDownListAllModule } from '@syncfusion/ej2-angular-dropdowns';
import { EditService, FreezeService, GridComponent, GridModule, SelectionService, ToolbarService } from '@syncfusion/ej2-angular-grids';

@Component({
  imports: [ GridModule, DropDownListAllModule, ButtonModule, CheckBoxModule, RadioButtonModule, SwitchModule],
  providers: [FreezeService, SelectionService, EditService, ToolbarService],
  standalone: true,
  selector: 'app-root',
  template: `
    <div style="display:flex;">
    <label style="padding: 5px 5px 5px 0">Change the frozen column: </label>
    <ejs-dropdownlist
      id="dropdown"
      #dropdown
      index="0"
      width="120"
      [fields]="field"
      [dataSource]="ddlData"
      (change)="columnChange($event)"
      width="100px"
    ></ejs-dropdownlist>
  </div>  
  <ejs-grid #grid id="grid" style="padding: 5px 5px" [dataSource]='data' height=290 [allowSelection]='false'  [enableHover]='false'>
    <e-columns>
      <e-column field='OrderID' headerText='Order ID' textAlign='Right' width=90></e-column>
      <e-column field='CustomerID' headerText='Customer ID' width=100 [isFrozen]="true"></e-column>
      <e-column field='OrderDate' headerText='Order Date' width=100 format='yMd' textAlign='Right'></e-column>
      <e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
      <e-column field='ShipName' headerText='Ship Name' width=130></e-column>
      <e-column field='ShipCity' headerText='Ship City' width=100></e-column>
      <e-column field='ShipCountry' headerText='Ship Country' width=100></e-column>
      <e-column field='ShipRegion' headerText='Ship Region' width=80></e-column>
      <e-column field='ShipPostalCode' headerText='Ship Postal Code' width=110></e-column>
      <e-column field='Freight' headerText='Freight' width=80></e-column>
    </e-columns>
  </ejs-grid>`
})
export class AppComponent implements OnInit {

  public data?: object[];
  @ViewChild('grid')
  public grid?: GridComponent;
  public field?: object;
  public ddlData?: object[] ;

  ngOnInit(): void {  
    this.data = data;
    this.field= { text: 'text', value: 'value' };
    this.ddlData= [
      { text: 'OrderID', value: 'OrderID' },
      { text: 'CustomerID', value: 'CustomerID' },
      { text: 'OrderDate', value: 'OrderDate' },
      { text: 'EmployeeID', value: 'EmployeeID' },
      { text: 'ShipName', value: 'ShipName' },
      { text: 'ShipCity', value: 'ShipCity' },
      { text: 'ShipCountry', value: 'ShipCountry' },
      { text: 'ShipRegion', value: 'ShipRegion' },
      { text: 'ShipPostalCode', value: 'ShipPostalCode' },
      { text: 'Freight', value: 'Freight' },
    ]
  }

  columnChange(args: ChangeEventArgs) {
    
    const selectedColumn = (this.grid as GridComponent).getColumnByField(args.value as string);

    // Iterate through all columns and unfreeze any previously frozen columns
    ((this.grid as GridComponent).columns as { isFrozen: boolean; }[]).forEach((column) => {
      if (column.isFrozen) {
          column.isFrozen = false;
      }
  });

    // Freeze the newly selected column, if it exists
    if (selectedColumn) {
      selectedColumn.isFrozen = true;
    }

    // Refresh the columns
    (this.grid as GridComponent).refreshColumns();
  }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Freeze direction

The Grid supports different freeze directions, which determine where frozen columns appear. By setting the freeze property in a column definition, columns can be pinned to the left, right, or fixed in place. This makes it easy to keep important columns visible while scrolling and gives flexibility when designing grids with multiple frozen sections.

The Grid supports three freeze direction types:

Direction Description Behavior
Left Freezes columns on the left side Frozen columns remain fixed on the left while other columns scroll horizontally
Right Freezes columns on the right side Frozen columns remain fixed on the right while other columns scroll horizontally
Fixed Locks columns at a specific position Frozen columns stay fixed in their position, creating a locked section between scrollable areas

In the following example, the “ShipCountry” column is frozen on the left side, the “CustomerID” column is frozen on the right side and the “Freight” column is frozen on the fixed of the content table. Additionally, the column.freeze property to Left, Right and Fixed based on the selected column by utilizing the change event of the DropDownList component.

import { data } from './datasource';
import { Component, OnInit, ViewChild } from '@angular/core';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { DropDownListAllModule, DropDownListComponent } from '@syncfusion/ej2-angular-dropdowns';
import { EditService, freezeDirection, FreezeService, GridComponent, GridModule, SelectionService, ToolbarService } from '@syncfusion/ej2-angular-grids';

@Component({
  imports: [ 
      GridModule,
      ButtonModule,
      DropDownListAllModule
      ],
  providers: [FreezeService, SelectionService, EditService, ToolbarService],
  standalone: true,
    selector: 'app-root',
    template: `
    <div style="display: flex">
    <label style="padding: 5px 5px 5px 0"> Change column: </label>
    <ejs-dropdownlist
      id="columnDropDown"
      #columnDropDown
      index="7"
      [dataSource]="columnData"
      [fields]="fields"
      width="100px"
    ></ejs-dropdownlist>
    <label style="padding: 5px 5px 5px 0; margin-left:5px">
      Change freeze direction:
    </label>
    <ejs-dropdownlist
      id="directionDropDown"
      #directionDropDown
      index="0"
      [dataSource]="directionData"
      [fields]="fields"
      width="80px"
    ></ejs-dropdownlist>
      <div>
        <button style="margin-left:5px" ejs-button (click)="freezeDirectionFn()">Update</button>
      </div>
    </div>
    <ejs-grid #grid id='grid' style="padding: 5px 5px" [dataSource]='data' height='290' [enableHover]='false'>
        <e-columns>
          <e-column field='OrderID' headerText='Order ID' width='90' textAlign='Right'></e-column>
          <e-column field='Freight' headerText='Freight' width='90' format='C2' textAlign='Right' freeze='Fixed'></e-column>
          <e-column field='CustomerID' headerText='Customer ID' width='100' freeze='Right'></e-column>
          <e-column field='OrderDate' headerText='Order Date' width='100' format="yMd" textAlign='Right'></e-column>
          <e-column field='ShipName' headerText='Ship Name' width='100'></e-column>
          <e-column field='ShipAddress' headerText='Ship Address' width='120'></e-column>
          <e-column field='ShipCity' headerText='Ship City' width='110'></e-column>
          <e-column field='ShipCountry' headerText='Ship Country' width='100' freeze='Left'></e-column>
        </e-columns>
    </ejs-grid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
    @ViewChild('grid')
    public grid?: GridComponent;
    @ViewChild('columnDropDown')
    public columnDropDown?: DropDownListComponent;
    @ViewChild('directionDropDown')
    public directionDropDown?: DropDownListComponent;
    public fields: object = { text: 'name', value: 'id' };
    public columnData?: object[]
    public directionData?: object[];

    ngOnInit(): void {
      this.data = data;
      this.columnData=[
        { id: 'OrderID', name: 'Order ID' },
        { id: 'Freight', name: 'Freight' },
        { id: 'CustomerID', name: 'Customer ID' },
        { id: 'OrderDate', name: 'Order Date' },
        { id: 'ShipName', name: 'Ship Name' },
        { id: 'ShipAddress', name: 'Ship Address' },
        { id: 'ShipCity', name: 'Ship City' },
        { id: 'ShipCountry', name: 'Ship Country' },
      ];
      this.directionData= [
        { id: 'Left', name: 'Left' },
        { id: 'Right', name: 'Right' },
        { id: 'Fixed', name: 'Fixed' },
      ];
    }

    freezeDirectionFn() {
      (this.grid as GridComponent).getColumnByField((this.columnDropDown as DropDownListComponent).value as string).freeze=(this.directionDropDown as DropDownListComponent).value as freezeDirection
      (this.grid as GridComponent).refreshColumns();
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Freeze Direction is not compatible with the isFrozen and frozenColumns properties.

Customize frozen line color

The visual separator between frozen and scrollable columns (the “frozen line”) can be customized to match application design requirements. Custom CSS styles can be applied to change the border color for left, right, and fixed frozen columns. The Grid applies specific CSS classes to frozen column borders based on their freeze direction. These classes can be targeted to customize the frozen line appearance.

For left frozen columns:

.e-grid .e-leftfreeze.e-freezeleftborder {
    border-right-color: rgb(198, 30, 204);
}

For right frozen columns:

.e-grid .e-rightfreeze.e-freezerightborder {
    border-left-color: rgb(19, 228, 243);
}

For fixed frozen columns:

Specify both left and right borders:

.e-grid .e-fixedfreeze.e-freezeleftborder{
    border-left-color: rgb(9, 209, 9); 
}

.e-grid .e-fixedfreeze.e-freezerightborder{
    border-right-color: rgb(10, 224, 10);
}

The following example demonstrates changing the default frozen line color using CSS.

import { data } from './datasource';
import { Component, OnInit } from '@angular/core';
import { EditService, FreezeService, GridModule, SelectionService, ToolbarService } from '@syncfusion/ej2-angular-grids';

@Component({
  imports: [ GridModule,],
  providers: [FreezeService, SelectionService, EditService, ToolbarService],
  standalone: true,
    selector: 'app-root',
    template: `<ejs-grid [dataSource]='data' height=315 [allowSelection]='false' [enableHover]='false'>
    <e-columns>
      <e-column field='OrderID' headerText='Order ID' textAlign='Right' width=90></e-column>
      <e-column field='CustomerID' headerText='Customer ID' width=100 freeze='Left'></e-column>
      <e-column field='OrderDate' headerText='Order Date' width=100 format='yMd' textAlign='Right'></e-column>
      <e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
      <e-column field='ShipName' headerText='Ship Name' width=130></e-column>
      <e-column field='ShipAddress' headerText='Ship Address' width=140 freeze='Fixed'></e-column>
      <e-column field='ShipCity' headerText='Ship City' width=100></e-column>
      <e-column field='ShipCountry' headerText='Ship Country' width=100 freeze='Right'></e-column>
      <e-column field='ShipRegion' headerText='Ship Region' width=80></e-column>
      <e-column field='ShipPostalCode' headerText='Ship Postal Code' width=110></e-column>
      <e-column field='Freight' headerText='Freight' width=80></e-column>
    </e-columns>
  </ejs-grid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
  
    ngOnInit(): void {
      this.data=data
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Render custom editors in frozen columns

Custom edit controls, such as a DatePicker, can be rendered inside frozen columns during editing. This is achieved using the edit property of the column definition, which accepts an object with custom editor methods.

Custom editor methods:

Method Description Purpose
create Creates and returns the editor element Initializes the custom editor control (e.g., DatePicker)
read Retrieves the value from the editor Extracts the current value from the editor when saving
write Writes the value to the editor Sets the initial or updated value in the editor
destroy Destroys the editor instance Cleans up resources when editing is complete

The following example demonstrates rendering a DatePicker component in the frozen “OrderDate” column. The DatePicker allows date selection during edit mode and properly integrates with the grid’s editing life cycle.

import { data } from './datasource';
import { Component, OnInit } from '@angular/core';
import { EditService, FreezeService, GridModule, PageService, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { DatePicker } from '@syncfusion/ej2-calendars';

@Component({
  imports: [GridModule],
  providers: [FreezeService, PageService, EditService, ToolbarService],
  standalone: true,
    selector: 'app-root',
    template: `
    <ejs-grid [dataSource]='data' height='250px' [frozenColumns]='2' [editSettings]='editSettings' [toolbar]='toolbar' allowPaging='true' [pageSettings]='pageSettings'>
      <e-columns>
        <e-column field='OrderID' headerText='Order ID' textAlign='Right' width=90 isPrimaryKey="true"></e-column>
        <e-column field='OrderDate' headerText='Order Date' width=100 format='yMd' [edit]='datePickerParams'></e-column>
        <e-column field='CustomerID' headerText='Customer ID' width=100 freeze='Left'></e-column>
        <e-column field='ShipCity' headerText='Ship City' width=100></e-column>
      </e-columns>
    </ejs-grid>`
})
export class AppComponent implements OnInit {
  public data?: object[];
  public pageSettings?: Object;
  public toolbar?: string[];
  public editSettings?: Object;
  public datePickerParams?: Object;
  public datePickerObj!: DatePicker;
  ngOnInit(): void {
    this.data=data;
    this.pageSettings = { pageCount: 5 };
    this.toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
    this.editSettings = {allowEditing: true, allowAdding: true, allowDeleting: true };
    this.datePickerParams = {
      create: this.createDatePicker,
      read: this.readDatePicker,
      destroy: this.destroyDatePicker,
      write: this.writeDatePicker,
    };
  }
  public createDatePicker = (): HTMLElement => {
    return document.createElement('input');
  };
  public readDatePicker = (): Date => {
    return this.datePickerObj.value;
  };
  public destroyDatePicker = (): void => {
    this.datePickerObj.destroy();
  };
  public writeDatePicker = (args:any): void => {
    this.datePickerObj = new DatePicker({
      value: new Date(args.rowData[args.column.field]),
      floatLabelType: 'Never',
    });
    this.datePickerObj.appendTo(args.element);
  };
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Deprecated methods

The Grid’s frozen column implementation has evolved to use a unified table architecture instead of separate tables for different freeze sections. This change improves performance and simplifies the API. The following methods from the previous architecture are now deprecated in favor of new methods with CSS class-based selectors.

Deprecated Method Current Method Explanation
getMovableRows() getRows() with CSS selector Previous: gridInstance.getMovableRows()[0].querySelectorAll('.e-unfreeze')
Current: gridInstance.getRows()[0].querySelectorAll('.e-unfreeze')

The previous architecture used separate tables for left, right, and movable contents, returning only movable rows. The current architecture combines them into one table, returning all rows. Use the e-unfreeze class to select movable (non-frozen) rows.
getFrozenRightRows() getRows() with CSS selector Previous: gridInstance.getFrozenRightRows()[0].querySelectorAll('.e-rightfreeze')
Current: gridInstance.getRows()[0].querySelectorAll('.e-rightfreeze')

The previous architecture returned only rows from the right freeze table. The current architecture returns all rows. Use the e-rightfreeze class to select right-frozen rows.
getMovableRowByIndex()
getFrozenRowByIndex()
getFrozenRightRowByIndex()
getRowByIndex() with CSS selector Previous: Three separate methods for different table sections
Current: gridInstance.getRowByIndex(1).querySelectorAll('.e-unfreeze')

The current architecture methods return the same table row based on the given index. Use CSS class selectors to target specific cell types:
e-leftfreeze - Left-frozen cells
e-unfreeze - Movable cells
e-rightfreeze - Right-frozen cells
getMovableCellFromIndex()
getFrozenRightCellFromIndex()
getCellFromIndex() Previous: Separate methods for movable and right-frozen cells
Current: gridInstance.getCellFromIndex(1, 1)

The current architecture allows selecting any cell using a single method, simplifying cell selection and retrieval regardless of freeze status.
getMovableDataRows()
getFrozenRightDataRows()
getFrozenDataRows()
getDataRows() with CSS selector Previous: Separate methods for each table section
Current: gridInstance.getDataRows()[0].querySelectorAll('.e-unfreeze')

The current approach returns entire viewport data rows for all sections together. Extract specific cells using:
e-leftfreeze - Left-frozen cells
e-unfreeze - Movable cells
e-rightfreeze - Right-frozen cells
getMovableColumnHeaderByIndex()
getFrozenRightColumnHeaderByIndex()
getFrozenLeftColumnHeaderByIndex()
getColumnHeaderByIndex() Previous: Separate methods for each header section
Current: gridInstance.getColumnHeaderByIndex(1)

The current approach returns the same results but through a unified method that works across all freeze sections.

When a validation message is displayed in the frozen part (Left, Right, Fixed) of the table, scrolling is prevented until the validation message is cleared.

Limitations

The following features are not supported when using frozen columns:

  • Detail Template
  • Hierarchy Grid
  • Autofill