---
title: "Fill Handle"
enterprise: true
framework: react
version: "36.1.0"
---

# Fill Handle

When working with cell selection, a Fill Handle allows you to run operations on cells as you adjust the size of the range.

## Enabling the Fill Handle

To enable the Fill Handle, set `cellSelection.handle` to `{ mode: 'fill' }` in the `gridOptions` as shown below:

```jsx
const cellSelection = useMemo(() => { 
	return {
        handle: {
            mode: 'fill',
        }
    };
}, []);

<AgGridReact cellSelection={cellSelection} />
```

The example below demonstrates the [default behaviour](#default-fill-handle) with the minimal configuration above:

#### Fill Handle

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  CellSelectionOptions,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { CellSelectionModule } from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  CellSelectionModule,
];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 150 },
    { field: "age", maxWidth: 90 },
    { field: "country", minWidth: 150 },
    { field: "year", maxWidth: 90 },
    { field: "date", minWidth: 150 },
    { field: "sport", minWidth: 150 },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
    { field: "total" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
      editable: true,
      cellDataType: false,
    };
  }, []);
  const cellSelection = useMemo<boolean | CellSelectionOptions>(() => {
    return {
      handle: { mode: "fill" },
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/small-olympic-winners.json",
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicData>
            rowData={data}
            loading={loading}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            cellSelection={cellSelection}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Fill Handle](https://www.ag-grid.com/examples/cell-selection-fill-handle/fill-handle/reactFunctionalTs)

## Default Fill Handle

The default Fill Handle behaviour will be as close as possible to other spreadsheet applications. Note the following:

### Single Cell

- When a single cell is selected and the range is increased, the value of that cell will be copied to the cells added to the range.
- When a single cell containing a **number** value is selected and the range is increased while pressing the `⌥ Alt` key, that value will be incremented (or decremented if dragging to the left or up) by the value of one until all new cells have been filled.

### Multi Cell

- When a range of numbers is selected and that range is extended, the Grid will detect the linear progression of the selected items and fill the extra cells with calculated values.
- When a range of strings or a mix of strings and numbers are selected and that range is extended, the range items will be copied in order until all new cells have been properly filled.
- When a range of numbers is selected and the range is increased while pressing the `⌥ Alt` key, the behaviour will be the same as when a range of strings or mixed values is selected.

### Range Reduction

- When reducing the size of the range, cells that are no longer part of the range will be cleared (set to `null`). If your column uses a `valueParser`, it will receive an empty string (`''`) as the new value.

#### Fill Handle

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  CellSelectionOptions,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { CellSelectionModule } from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  CellSelectionModule,
];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 150 },
    { field: "age", maxWidth: 90 },
    { field: "country", minWidth: 150 },
    { field: "year", maxWidth: 90 },
    { field: "date", minWidth: 150 },
    { field: "sport", minWidth: 150 },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
    { field: "total" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
      editable: true,
      cellDataType: false,
    };
  }, []);
  const cellSelection = useMemo<boolean | CellSelectionOptions>(() => {
    return {
      handle: { mode: "fill" },
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/small-olympic-winners.json",
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicData>
            rowData={data}
            loading={loading}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            cellSelection={cellSelection}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Fill Handle](https://www.ag-grid.com/examples/cell-selection-fill-handle/fill-handle/reactFunctionalTs)

### Preventing Range Reduction

Reducing a range selection with the Fill Handle will clear cell contents by default, as can be observed in the [cell reduction](#range-reduction) example above.

If this behaviour for decreasing selection needs to be prevented, the flag `cellSelection.handle.suppressClearOnFillReduction` should be set to `true`.

#### Fill Handle - Range Reduction

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  CellSelectionOptions,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { CellSelectionModule } from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  CellSelectionModule,
];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 150 },
    { field: "age", maxWidth: 90 },
    { field: "country", minWidth: 150 },
    { field: "year", maxWidth: 90 },
    { field: "date", minWidth: 150 },
    { field: "sport", minWidth: 150 },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
    { field: "total" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
      editable: true,
      cellDataType: false,
    };
  }, []);
  const cellSelection = useMemo<boolean | CellSelectionOptions>(() => {
    return {
      handle: {
        mode: "fill",
        suppressClearOnFillReduction: true,
      },
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/small-olympic-winners.json",
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicData>
            rowData={data}
            loading={loading}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            cellSelection={cellSelection}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Fill Handle - Range Reduction](https://www.ag-grid.com/examples/cell-selection-fill-handle/fill-handle-reduction/reactFunctionalTs)

## Fill Handle Axis

By default, the Fill Handle can be dragged horizontally or vertically. If you wish to restrict the permitted direction of dragging to either horizontal or vertical, set `cellSelection.handle.direction` to either `x` for horizontal or `y` for vertical.

```jsx
const cellSelection = useMemo(() => { 
	return {
        handle: {
            mode: 'fill',
            direction: 'x', // Fill Handle can only be dragged horizontally
        }
    };
}, []);

<AgGridReact cellSelection={cellSelection} />
```

#### Fill Handle - Direction

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import "./styles.css";
import {
  CellSelectionOptions,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { CellSelectionModule } from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  CellSelectionModule,
];

const GridExample = () => {
  const gridRef = useRef<AgGridReact<IOlympicData>>(null);
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 150 },
    { field: "age", maxWidth: 90 },
    { field: "country", minWidth: 150 },
    { field: "year", maxWidth: 90 },
    { field: "date", minWidth: 150 },
    { field: "sport", minWidth: 150 },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
    { field: "total" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
      editable: true,
      cellDataType: false,
    };
  }, []);
  const cellSelection = useMemo<boolean | CellSelectionOptions>(() => {
    return {
      handle: {
        mode: "fill",
        direction: "x",
      },
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/small-olympic-winners.json",
  );

  const fillHandleAxis = useCallback((direction: "x" | "y" | "xy") => {
    const buttons = Array.prototype.slice.call(
      document.querySelectorAll(".ag-fill-direction"),
    );
    const button = document.querySelector(".ag-fill-direction." + direction)!;
    buttons.forEach((btn) => {
      btn.classList.remove("selected");
    });
    button.classList.add("selected");
    gridRef.current!.api.setGridOption("cellSelection", {
      handle: {
        mode: "fill",
        direction,
      },
    });
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div className="example-wrapper">
          <div style={{ marginBottom: "5px" }}>
            <label>Axis: </label>
            <button
              className="ag-fill-direction xy"
              onClick={() => fillHandleAxis("xy")}
            >
              xy
            </button>
            <button
              className="ag-fill-direction x selected"
              onClick={() => fillHandleAxis("x")}
            >
              x only
            </button>
            <button
              className="ag-fill-direction y"
              onClick={() => fillHandleAxis("y")}
            >
              y only
            </button>
          </div>

          <div style={gridStyle}>
            <AgGridReact<IOlympicData>
              ref={gridRef}
              rowData={data}
              loading={loading}
              columnDefs={columnDefs}
              defaultColDef={defaultColDef}
              cellSelection={cellSelection}
            />
          </div>
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Fill Handle - Direction](https://www.ag-grid.com/examples/cell-selection-fill-handle/fill-handle-direction/reactFunctionalTs)

## Double-Click Fill

When the fill handle direction is `'y'` or `'xy'`, double-clicking on the fill handle will perform a fill operation on all cells below the selected cells. Similarly, when the fill handle direction is `'x'`, double-clicking on the fill handle will perform a fill operation on all cells to the right of the selected cells.

This is enabled by default when the fill handle is enabled and does not require separate configuration.

## Fill Handle Events

When using the fill handle the grid will fire the `fillStart` event before it starts filling cells and the `fillEnd` event when all cells have been filled. See the Custom User Function example below for an example of using these events.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `fillStart` | `FillStartEvent` |  |  | Fill operation has started. |
| `fillEnd` | `FillEndEvent` |  |  | Fill operation has ended. |

## Custom User Function

Often there is a need to use a custom method to fill values instead of simply copying values or increasing number values using linear progression. In these scenarios, the `cellSelection.handle.setFillValue` callback should be used.

```jsx
const [columnDefs, setColumnDefs] = useState([
    { field: 'country' },
    { field: 'year' },
    { field: 'sport' },
    { field: 'total' }
]);
const cellSelection = useMemo(() => { 
	return {
        handle: {
            mode: 'fill',
            setFillValue: (params) => 'Custom Fill Value',
        }
    };
}, []);

<AgGridReact
    columnDefs={columnDefs}
    cellSelection={cellSelection}
/>
```

### FillOperationParams

Properties available on the `FillOperationParams&lt;TData = any, TContext = any&gt;` interface.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `event` | [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) |  |  | The mouse event for the fill operation. |
| `values` | `any[]` |  |  | The values that have been processed by the fill operation. |
| `rowNode` | [`IRowNode`](https://www.ag-grid.com/react-data-grid/row-object/) |  |  | The RowNode of the current cell being changed. |
| `column` | [`Column`](https://www.ag-grid.com/react-data-grid/column-object/) |  |  | The Column of the current cell being changed. |
| `initialValues` | `any[]` |  |  | The values that were present before processing started. |
| `initialNonAggregatedValues` | `any[]` |  |  | The values that were present before processing, without the aggregation function. |
| `initialFormattedValues` | `any[]` |  |  | The values that were present before processing, after being formatted by their value formatter |
| `currentIndex` | `number` |  |  | The index of the current processed value. |
| `currentCellValue` | `any` |  |  | The value of the cell being currently processed by the Fill Operation. |
| `direction` | `'up' \| 'down' \| 'left' \| 'right'` |  |  | The direction of the Fill Operation. |
| `api` | [`GridApi`](https://www.ag-grid.com/react-data-grid/grid-api/) |  |  | The grid api. |
| `context` | [`TContext`](https://www.ag-grid.com/react-data-grid/typescript-generics/#context-tcontext) |  |  | Application context as set on `gridOptions.context`. |

> **Note**
>
> If a `setFillValue` callback is provided, the fill handle will always run it. If the current values are not relevant to the `setFillValue` function that was provided, `false` should be returned to allow the grid to process the values as it normally would.

The example below will use the custom `setFillValue` for the **Day of the week** column, but it will use the default operation for any other column.

#### Custom Fill Operation

```tsx
("use client");

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  CellSelectionOptions,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  FillEndEvent,
  FillStartEvent,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { CellSelectionModule } from "ag-grid-enterprise";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  CellSelectionModule,
];

const daysList = [
  "Sunday",
  "Monday",
  "Tuesday",
  "Wednesday",
  "Thursday",
  "Friday",
  "Saturday",
];

function createRowData(rowData: any[]) {
  const currentDate = new Date();
  const currentYear = currentDate.getFullYear();
  for (let i = 0; i < rowData.length; i++) {
    const dt = new Date(
      getRandom(currentYear - 10, currentYear + 10),
      getRandom(0, 12),
      getRandom(1, 25),
    );
    rowData[i].dayOfTheWeek = daysList[dt.getDay()];
  }
  return rowData;
}

var getRandom = function (start: number, finish: number) {
  return Math.floor(window.agRandom() * (finish - start) + start);
};

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [rowData, setRowData] = useState<any[]>();
  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 150 },
    { headerName: "Day of the Week", field: "dayOfTheWeek", minWidth: 180 },
    { field: "age", maxWidth: 90 },
    { field: "country", minWidth: 150 },
    { field: "year", maxWidth: 90 },
    { field: "date", minWidth: 150 },
    { field: "sport", minWidth: 150 },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
      editable: true,
      cellDataType: false,
    };
  }, []);
  const cellSelection = useMemo<boolean | CellSelectionOptions>(() => {
    return {
      handle: {
        mode: "fill",
        setFillValue(params) {
          const hasNonDayValues = params.initialValues.some(function (val) {
            return daysList.indexOf(val) === -1;
          });
          if (hasNonDayValues) {
            return false;
          }
          const lastValue = params.values[params.values.length - 1];
          const idxOfLast = daysList.indexOf(lastValue);
          const nextDay = daysList[(idxOfLast + 1) % daysList.length];
          console.log("Custom Fill Operation -> Next Day is:", nextDay);
          return nextDay;
        },
      },
    };
  }, []);

  const onGridReady = useCallback((params: GridReadyEvent) => {
    fetch("https://www.ag-grid.com/example-assets/small-olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: any[]) => {
        setRowData(createRowData(data));
      });
  }, []);

  const onFillStart = useCallback((event: FillStartEvent) => {
    console.log("Fill Start", event);
  }, []);

  const onFillEnd = useCallback((event: FillEndEvent) => {
    console.log("Fill End", event);
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact
            rowData={rowData}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            cellSelection={cellSelection}
            onGridReady={onGridReady}
            onFillStart={onFillStart}
            onFillEnd={onFillEnd}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Custom Fill Operation](https://www.ag-grid.com/examples/cell-selection-fill-handle/custom-fill-operation/reactFunctionalTs)

### Skipping Columns in the Fill Operation

The example below will use the custom `setFillValue` to prevent values in the **Country** column from being altered by the Fill Handle.

> **Note**
>
> When the `setFillValue` function returns `params.currentCellValue` that value is not added to the `params.values` list. This allows users to skip any cells in the Fill Handle operation.

#### Skipping Columns

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  CellSelectionOptions,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { CellSelectionModule } from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  CellSelectionModule,
];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 150 },
    { field: "age", maxWidth: 90 },
    { field: "country", minWidth: 150 },
    { field: "year", maxWidth: 90 },
    { field: "date", minWidth: 150 },
    { field: "sport", minWidth: 150 },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
      editable: true,
      cellDataType: false,
    };
  }, []);
  const cellSelection = useMemo<boolean | CellSelectionOptions>(() => {
    return {
      handle: {
        mode: "fill",
        suppressClearOnFillReduction: true,
        setFillValue(params) {
          if (params.column.getColId() === "country") {
            return params.currentCellValue;
          }
          return params.values[params.values.length - 1];
        },
      },
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/small-olympic-winners.json",
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicData>
            rowData={data}
            loading={loading}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            cellSelection={cellSelection}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Skipping Columns](https://www.ag-grid.com/examples/cell-selection-fill-handle/skipping-columns/reactFunctionalTs)

> **Warning**
>
> Non editable cells will **not** be changed by the Fill Handle, so there is no need to add custom logic to skip columns that aren't editable.

## Read Only Edit

When the grid is in [Read Only Edit](https://www.ag-grid.com/react-data-grid/value-setters/#read-only-edit) mode the Fill Handle will not update the data inside the grid. Instead the grid fires `cellEditRequest` events allowing the application to process the update request.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `cellEditRequest` | `CellEditRequestEvent` |  |  | Value has changed after editing. Only fires when `readOnlyEdit=true`. |

The example below will show how to update cell value combining the Fill Handle with `readOnlyEdit=true`.

#### Fill Handle - ReadOnlyEdit

```tsx
("use client");

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
  CellEditRequestEvent,
  CellSelectionOptions,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GetRowIdFunc,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { CellSelectionModule } from "ag-grid-enterprise";
import { IOlympicDataWithId } from "./interfaces";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  CellSelectionModule,
];

let rowImmutableStore: any[];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
  const [rowData, setRowData] = useState<IOlympicDataWithId[]>();
  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 160 },
    { field: "age" },
    { field: "country", minWidth: 140 },
    { field: "year" },
    { field: "date", minWidth: 140 },
    { field: "sport", minWidth: 160 },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
    { field: "total" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
      editable: true,
      cellDataType: false,
    };
  }, []);
  const cellSelection = useMemo<boolean | CellSelectionOptions>(() => {
    return {
      handle: {
        mode: "fill",
      },
    };
  }, []);
  const getRowId = useCallback((params) => String(params.data.id), []);

  const onGridReady = useCallback((params: GridReadyEvent) => {
    fetch("https://www.ag-grid.com/example-assets/small-olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: IOlympicDataWithId[]) => {
        data.forEach((item, index) => (item.id = index));
        rowImmutableStore = data;
        params.api.setGridOption("rowData", rowImmutableStore);
      });
  }, []);

  const onCellEditRequest = useCallback(
    (event: CellEditRequestEvent) => {
      const data = event.data;
      const field = event.colDef.field;
      const newValue = event.newValue;
      const oldItem = rowImmutableStore.find((row) => row.id === data.id);
      if (!oldItem || !field) {
        return;
      }
      const newItem = { ...oldItem };
      newItem[field] = newValue;
      console.log("onCellEditRequest, updating " + field + " to " + newValue);
      rowImmutableStore = rowImmutableStore.map((oldItem) =>
        oldItem.id == newItem.id ? newItem : oldItem,
      );
      setRowData(rowImmutableStore);
    },
    [rowImmutableStore],
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicDataWithId>
            rowData={rowData}
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            cellSelection={cellSelection}
            readOnlyEdit={true}
            getRowId={getRowId}
            onGridReady={onGridReady}
            onCellEditRequest={onCellEditRequest}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Fill Handle - ReadOnlyEdit](https://www.ag-grid.com/examples/cell-selection-fill-handle/read-only-edit/reactFunctionalTs)

## Suppressing the Fill Handle

The Fill Handle can be disabled on a per column basis by setting the column definition property `suppressFillHandle` to `true`.

In the example below, please note that the Fill Handle is disabled in the **Country** and **Date** columns.

#### Suppress Fill Handle

```tsx
"use client";

import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import "./styles.css";
import {
  CellSelectionOptions,
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  ModuleRegistry,
  NumberEditorModule,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { CellSelectionModule } from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";

if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

const modules = [
  NumberEditorModule,
  TextEditorModule,
  ClientSideRowModelModule,
  CellSelectionModule,
];

const GridExample = () => {
  const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
  const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", minWidth: 150 },
    { field: "age", maxWidth: 90 },
    { field: "country", minWidth: 150, suppressFillHandle: true },
    { field: "year", maxWidth: 90 },
    { field: "date", minWidth: 150, suppressFillHandle: true },
    { field: "sport", minWidth: 150 },
    { field: "gold" },
    { field: "silver" },
    { field: "bronze" },
    { field: "total" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
      editable: true,
      cellDataType: false,
    };
  }, []);
  const cellSelection = useMemo<boolean | CellSelectionOptions>(() => {
    return {
      handle: { mode: "fill" },
    };
  }, []);

  const { data, loading } = useFetchJson<IOlympicData>(
    "https://www.ag-grid.com/example-assets/small-olympic-winners.json",
  );

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div className="example-wrapper">
          <div style={gridStyle}>
            <AgGridReact<IOlympicData>
              rowData={data}
              loading={loading}
              columnDefs={columnDefs}
              defaultColDef={defaultColDef}
              cellSelection={cellSelection}
            />
          </div>
        </div>
      </div>
    </AgGridProvider>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(
  <StrictMode>
    <GridExample />
  </StrictMode>,
);
```

[Live example: Suppress Fill Handle](https://www.ag-grid.com/examples/cell-selection-fill-handle/suppress-fill-handle/reactFunctionalTs)

## API Reference

Here you can find a full list of configuration options available when the handle options are in `'fill'` mode.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `mode` | `'fill'` |  |  | 'fill' |
| `suppressClearOnFillReduction` | `boolean` |  | `false` | Set this to `true` to prevent cell values from being cleared when the Range Selection is reduced by the Fill Handle. |
| `direction` | `'x' \| 'y' \| 'xy'` |  | `'xy'` | Set to `'x'` to force the fill handle direction to horizontal, or set to `'y'` to force the fill handle direction to vertical. |
| `setFillValue` | `Function` |  |  | Callback to fill values instead of simply copying values or increasing number values using linear progression. |
