Skip to main content
The ICellEditorComp interface allows you to create custom components for editing cell values in AG Grid. Cell editors control how users can modify data in editable cells.

Overview

Create custom cell editors to provide specialized editing experiences such as custom date pickers, color selectors, or complex data entry forms.
const gridOptions = {
  columnDefs: [
    {
      field: 'rating',
      editable: true,
      cellEditor: RatingCellEditor
    }
  ]
};

Interface Definition

interface ICellEditorComp<TData = any, TValue = any, TContext = any> 
  extends ICellEditor<TValue>, IPopupComponent<ICellEditorParams<TData, TValue, TContext>> {
  
  /** Mandatory - Return the final value */
  getValue(): TValue | null | undefined;
  
  /** Optional: Called once after editing is complete */
  isCancelBeforeStart?(): boolean;
  
  /** Optional: Called once after editing is complete */
  isCancelAfterEnd?(): boolean;
  
  /** Optional: Gets called when focus should be put into the editor */
  focusIn?(): void;
  
  /** Optional: If doing full line edit, then gets called when focus is leaving the editor */
  focusOut?(): void;
  
  /** Optional: A hook to perform any necessary operation just after the GUI has been rendered */
  afterGuiAttached?(): void;
  
  /** Optional: If true, editor appears in a popup */
  isPopup?(): boolean;
  
  /** Optional: Gets called with the latest cell editor params every time they update */
  refresh?(params: ICellEditorParams<TData, TValue>): void;
}

ICellEditorParams

value
TValue | null | undefined
Current value of the cell.
init(params) {
  this.value = params.value;
  this.input.value = this.value || '';
}
eventKey
string | null
Key value of key that started the edit, eg ‘Enter’ or ‘F2’ - non-printable characters appear here.
init(params) {
  if (params.eventKey && params.eventKey.length === 1) {
    // User typed a character to start editing
    this.input.value = params.eventKey;
  }
}
column
Column<TValue>
Grid column.
init(params) {
  const columnId = params.column.getId();
}
colDef
ColDef<TData, TValue>
Column definition.
init(params) {
  const field = params.colDef.field;
}
node
IRowNode<TData>
Row node for the cell.
init(params) {
  const rowId = params.node.id;
  const isGroupNode = params.node.group;
}
data
TData
Row data.
init(params) {
  // Access other fields in the row
  const relatedValue = params.data.otherField;
}
rowIndex
number
Editing row index.
init(params) {
  console.log('Editing row at index:', params.rowIndex);
}
cellStartedEdit
boolean
If doing full row edit, this is true if the cell is the one that started the edit (eg it is the cell the user double clicked on, or pressed a key on etc).
afterGuiAttached() {
  if (this.params.cellStartedEdit) {
    // Put focus on this editor
    this.input.focus();
    this.input.select();
  }
}
eGridCell
HTMLElement
A reference to the DOM element representing the grid cell that your component will live inside. Useful if you want to add event listeners or classes at this level.
init(params) {
  params.eGridCell.classList.add('editing');
}

Utility Methods

parseValue
(value: string) => TValue | null | undefined
Utility function to parse a value using the column’s colDef.valueParser.
getValue() {
  const rawValue = this.input.value;
  return this.params.parseValue(rawValue);
}
formatValue
(value: TValue | null | undefined) => string
Utility function to format a value using the column’s colDef.valueFormatter.
init(params) {
  const formatted = params.formatValue(params.value);
  this.input.value = formatted;
}
onKeyDown
(event: KeyboardEvent) => void
Callback to tell grid a key was pressed - useful to pass control key events (tab, arrows etc) back to grid.
init(params) {
  this.input.addEventListener('keydown', (event) => {
    // Let grid handle tab key
    if (event.key === 'Tab') {
      params.onKeyDown(event);
    }
  });
}
stopEditing
(suppressNavigateAfterEdit?: boolean) => void
Callback to tell grid to stop editing the current cell. Call with input parameter true to prevent focus from moving to the next cell after editing stops in case the grid property enterNavigatesVerticallyAfterEdit=true.
onSaveClick() {
  this.params.stopEditing();
}

onCancelClick() {
  this.params.stopEditing(true);
}
validate
() => void
Runs the Editor Validation.
onBlur() {
  this.params.validate();
}

Required Methods

getValue
() => TValue | null | undefined
Mandatory - Return the final value. Called by the grid once after editing is complete.
getValue() {
  return this.input.value;
}

Optional Methods

isCancelBeforeStart
() => boolean
Optional: Gets called once after initialised. If you return true, the editor will not be used and the grid will continue editing. Use this to make a decision on editing inside the init() function.
isCancelBeforeStart() {
  // Only start editing if value is not null
  return this.params.value == null;
}
isCancelAfterEnd
() => boolean
Optional: Gets called once after editing is complete. If your return true, then the new value will not be used. The editing will have no impact on the record.
isCancelAfterEnd() {
  // Cancel if value hasn't changed
  return this.getValue() === this.params.value;
}
afterGuiAttached
() => void
Optional: A hook to perform any necessary operation just after the GUI for this component has been rendered on the screen. This is useful for any logic that requires attachment before executing, such as putting focus on a particular DOM element.
afterGuiAttached() {
  // Focus the input and select all text
  this.input.focus();
  this.input.select();
}
isPopup
() => boolean
Optional: Gets called once after initialised. If you return true, the editor will appear in a popup, so is not constrained to the boundaries of the cell.
isPopup() {
  return true; // Editor will appear in a popup
}
getPopupPosition
() => 'over' | 'under' | undefined
Optional: Gets called once, only if isPopup() returns true. Return “over” if the popup should cover the cell, or “under” if it should be positioned below leaving the cell value visible.
getPopupPosition() {
  return 'under'; // Popup appears below the cell
}
focusIn
() => void
Optional: If doing full line edit, then gets called when focus should be put into the editor.
focusIn() {
  this.input.focus();
}
focusOut
() => void
Optional: If doing full line edit, then gets called when focus is leaving the editor.
focusOut() {
  this.input.blur();
}
refresh
(params: ICellEditorParams<TData, TValue>) => void
Optional: Gets called with the latest cell editor params every time they update.
refresh(params) {
  this.params = params;
  this.input.value = params.value;
}
getValidationElement
(tooltip: boolean) => HTMLElement
Optional: Returns the element to use for validation feedback.Parameters:
  • tooltip: Whether the element is for a tooltip or direct styling
getValidationElement(tooltip) {
  return tooltip ? this.input : this.container;
}
getValidationErrors
() => string[] | null
Optional: The error messages associated with the Editor.
getValidationErrors() {
  const value = this.input.value;
  if (!value) {
    return ['Value is required'];
  }
  if (value.length < 3) {
    return ['Value must be at least 3 characters'];
  }
  return null;
}

Complete Example

class NumericCellEditor {
  init(params) {
    this.params = params;
    
    // Create input
    this.input = document.createElement('input');
    this.input.type = 'number';
    this.input.className = 'ag-input-field-input';
    this.input.value = params.value != null ? params.value : '';
    
    // Handle keyboard navigation
    this.input.addEventListener('keydown', (event) => {
      if (event.key === 'Escape') {
        this.cancelled = true;
        params.stopEditing();
      } else if (event.key === 'Enter') {
        params.stopEditing();
      } else if (event.key === 'Tab') {
        params.onKeyDown(event);
      }
    });
  }
  
  getGui() {
    return this.input;
  }
  
  afterGuiAttached() {
    this.input.focus();
    this.input.select();
  }
  
  getValue() {
    const value = this.input.value;
    return value === '' ? null : Number(value);
  }
  
  isCancelAfterEnd() {
    return this.cancelled;
  }
  
  destroy() {
    // Cleanup if needed
  }
}

// Usage
const gridOptions = {
  columnDefs: [
    {
      field: 'age',
      editable: true,
      cellEditor: NumericCellEditor
    }
  ]
};

Built-in Cell Editors

AG Grid provides several built-in cell editors:
  • 'agTextCellEditor': Simple text input (default)
  • 'agLargeTextCellEditor': Multi-line text area
  • 'agSelectCellEditor': Dropdown select
  • 'agNumberCellEditor': Number input
  • 'agDateCellEditor': Date picker
  • 'agCheckboxCellEditor': Checkbox
  • 'agRichSelectCellEditor': Rich select with search (Enterprise)
{
  field: 'country',
  editable: true,
  cellEditor: 'agSelectCellEditor',
  cellEditorParams: {
    values: ['USA', 'UK', 'Canada', 'Australia']
  }
}

Cell Renderer

Create custom cell renderers

Column Definitions

Configure cell editors in columns

GridApi

Programmatic editing control