---
title: "SSRM Row Selection"
enterprise: true
framework: react
version: "36.1.0"
---

# SSRM Row Selection

Selecting rows and groups in the Server-Side Row Model is supported. Configure the `selection` grid option as described in [Row selection](https://www.ag-grid.com/react-data-grid/row-selection/). Some SSRM-specific considerations are detailed below.

> **Note**
>
> Server-Side Row Selection requires [Row IDs](https://www.ag-grid.com/react-data-grid/server-side-model-configuration/#providing-row-ids) to be supplied to grid.

## Filters and Selection

The selected rows are preserved when the grid is sorted or filtered and are displayed as selected when scrolled into view. Select a row, apply a column filter so that the selected row is in the filter results and it will appear as selected in the filter results. If a selected row doesn’t match the applied filter, it will still be selected when the filter is removed.

#### Click Selection

```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 {
  AutoGroupColumnDef,
  ColDef,
  ColGroupDef,
  GetRowIdFunc,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IServerSideDatasource,
  ModuleRegistry,
  NumberFilterModule,
  RowModelType,
  RowSelectionOptions,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  RowGroupingModule,
  ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { FakeServer } from "./fakeServer";
import { IOlympicDataWithId } from "./interfaces";

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

const modules = [
  RowGroupingModule,
  ServerSideRowModelModule,
  TextFilterModule,
  NumberFilterModule,
];

const getServerSideDatasource: (server: any) => IServerSideDatasource = (
  server: any,
) => {
  return {
    getRows: (params) => {
      console.log("[Datasource] - rows requested by grid: ", params.request);
      const response = server.getData(params.request);
      // adding delay to simulate real server call
      setTimeout(() => {
        if (response.success) {
          // call the success callback
          params.success({
            rowData: response.rows,
            rowCount: response.lastRow,
          });
        } else {
          // inform the grid request failed
          params.fail();
        }
      }, 200);
    },
  };
};

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

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "athlete", filter: "agTextColumnFilter" },
    { field: "country", filter: "agTextColumnFilter" },
    { field: "sport", filter: "agTextColumnFilter" },
    { field: "gold", aggFunc: "sum", filter: "agNumberColumnFilter" },
    { field: "silver", aggFunc: "sum", filter: "agNumberColumnFilter" },
    { field: "bronze", aggFunc: "sum", filter: "agNumberColumnFilter" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      floatingFilter: true,
      flex: 1,
      minWidth: 120,
    };
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      flex: 1,
      minWidth: 180,
    };
  }, []);
  const getRowId = useCallback((params) => {
    if (params.data.id != null) {
      return "leaf-" + params.data.id;
    }
    const rowGroupCols = params.api.getRowGroupColumns();
    const rowGroupColIds = rowGroupCols.map((col) => col.getId()).join("-");
    const thisGroupCol = rowGroupCols[params.level];
    return (
      "group-" +
      rowGroupColIds +
      "-" +
      (params.parentKeys || []).join("-") +
      params.data[thisGroupCol.getColDef().field as keyof IOlympicDataWithId]
    );
  }, []);
  const rowSelection = useMemo<
    RowSelectionOptions | "single" | "multiple"
  >(() => {
    return {
      mode: "multiRow",
    };
  }, []);

  const onGridReady = useCallback((params: GridReadyEvent) => {
    fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: IOlympicDataWithId[]) => {
        // assign a unique ID to each data item
        data.forEach(function (item: any, index: number) {
          item.id = index;
        });
        // setup the fake server with entire dataset
        const fakeServer = new FakeServer(data);
        // create datasource with a reference to the fake server
        const datasource = getServerSideDatasource(fakeServer);
        // register the datasource with the grid
        params.api!.setGridOption("serverSideDatasource", datasource);
      });
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div style={gridStyle}>
          <AgGridReact<IOlympicDataWithId>
            columnDefs={columnDefs}
            defaultColDef={defaultColDef}
            autoGroupColumnDef={autoGroupColumnDef}
            getRowId={getRowId}
            rowModelType={"serverSide"}
            rowSelection={rowSelection}
            suppressAggFuncInHeader={true}
            onGridReady={onGridReady}
          />
        </div>
      </div>
    </AgGridProvider>
  );
};

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

[Live example: Click Selection](https://www.ag-grid.com/examples/server-side-model-selection/click-selection/reactFunctionalTs)

## Selecting All Rows

When using SSRM, the `'filtered'` or `'currentPage'` settings for `rowSelection.selectAll` are considered invalid. The grid will behave as though `rowSelection.selectAll = 'all'`, which is the default value. The example below demonstrates this.

#### Bulk Selection

```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 {
  AutoGroupColumnDef,
  ColDef,
  ColGroupDef,
  GetRowIdFunc,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IServerSideDatasource,
  ModuleRegistry,
  NumberFilterModule,
  PaginationModule,
  RowModelType,
  RowSelectionOptions,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  RowGroupingModule,
  RowGroupingPanelModule,
  ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { FakeServer } from "./fakeServer";
import { IOlympicDataWithId } from "./interfaces";

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

const modules = [
  PaginationModule,
  RowGroupingModule,
  ServerSideRowModelModule,
  RowGroupingPanelModule,
  TextFilterModule,
  NumberFilterModule,
];

const getServerSideDatasource: (server: any) => IServerSideDatasource = (
  server: any,
) => {
  return {
    getRows: (params) => {
      console.log("[Datasource] - rows requested by grid: ", params.request);
      const response = server.getData(params.request);
      // adding delay to simulate real server call
      setTimeout(() => {
        if (response.success) {
          // call the success callback
          params.success({
            rowData: response.rows,
            rowCount: response.lastRow,
          });
        } else {
          // inform the grid request failed
          params.fail();
        }
      }, 200);
    },
  };
};

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

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "country", enableRowGroup: true, rowGroup: true, hide: true },
    { field: "year", enableRowGroup: true },
    { field: "sport", enableRowGroup: true, filter: "agTextColumnFilter" },
    { field: "gold", aggFunc: "sum", filter: "agNumberColumnFilter" },
    { field: "silver", aggFunc: "sum", filter: "agNumberColumnFilter" },
    { field: "bronze", aggFunc: "sum", filter: "agNumberColumnFilter" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      floatingFilter: true,
      flex: 1,
      minWidth: 120,
    };
  }, []);
  const getRowId = useCallback((params) => {
    if (params.data.id != null) {
      return "leaf-" + params.data.id;
    }
    const rowGroupCols = params.api.getRowGroupColumns();
    const rowGroupColIds = rowGroupCols.map((col) => col.getId()).join("-");
    const thisGroupCol = rowGroupCols[params.level];
    return (
      "group-" +
      rowGroupColIds +
      "-" +
      (params.parentKeys || []).join("-") +
      params.data[thisGroupCol.getColDef().field as keyof IOlympicDataWithId]
    );
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      field: "athlete",
      flex: 1,
      minWidth: 240,
    };
  }, []);
  const rowSelection = useMemo<
    RowSelectionOptions | "single" | "multiple"
  >(() => {
    return {
      mode: "multiRow",
      selectAll: "all",
    };
  }, []);

  const onGridReady = useCallback((params: GridReadyEvent) => {
    fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: IOlympicDataWithId[]) => {
        // assign a unique ID to each data item
        data.forEach(function (item: any, index: number) {
          item.id = index;
        });
        // setup the fake server with entire dataset
        const fakeServer = new FakeServer(data);
        // create datasource with a reference to the fake server
        const datasource = getServerSideDatasource(fakeServer);
        // register the datasource with the grid
        params.api!.setGridOption("serverSideDatasource", datasource);
      });
  }, []);

  const onSelectAllChanged = useCallback(() => {
    gridRef.current!.api.setGridOption("rowSelection", {
      mode: "multiRow",
      selectAll: document.querySelector<HTMLSelectElement>("#input-select-all")!
        .value as any,
    });
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div className="example-wrapper">
          <div className="example-header">
            <label>
              <span>Select All:</span>
              <select id="input-select-all" onChange={onSelectAllChanged}>
                <option>all</option>
                <option>filtered</option>
                <option>currentPage</option>
              </select>
            </label>
          </div>

          <div style={gridStyle}>
            <AgGridReact<IOlympicDataWithId>
              ref={gridRef}
              columnDefs={columnDefs}
              defaultColDef={defaultColDef}
              getRowId={getRowId}
              autoGroupColumnDef={autoGroupColumnDef}
              rowGroupPanelShow={"always"}
              rowModelType={"serverSide"}
              rowSelection={rowSelection}
              suppressAggFuncInHeader={true}
              pagination={true}
              paginationPageSize={20}
              onGridReady={onGridReady}
            />
          </div>
        </div>
      </div>
    </AgGridProvider>
  );
};

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

[Live example: Bulk Selection](https://www.ag-grid.com/examples/server-side-model-selection/select-all/reactFunctionalTs)

> **Note**
>
> When using header checkbox selection with the server-side row model, you should **not** use the api functions `getSelectedNodes()` or `getSelectedRows()`. See the [API Reference](https://www.ag-grid.com/react-data-grid/server-side-model-selection/#api-reference) section below for suggested alternatives.

## Group Selection

> **Note**
>
> When using group selection with the server-side row model, you should **not** use the api functions `getSelectedNodes()` or `getSelectedRows()`. See the [API Reference](https://www.ag-grid.com/react-data-grid/server-side-model-selection/#api-reference) section below for suggested alternatives.

### Transactions

When adding a new row via transaction, the new row will be treated as if it conforms to its parent's selection. Therefore, if the parent row was selected the new row will be treated as selected upon creation. For indeterminate groups, this will follow the last non-indeterminate state. Note the following:

- When clicking the `Add new Aggressive` button, the new row is unselected
- After selecting the `Aggressive` group, new rows created by the `Add new Aggressive` button will be selected.
- After toggling one of the child rows of the `Aggressive` group, new rows follow the group's previous selection state.

#### Transactions Example

```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 {
  AutoGroupColumnDef,
  ColDef,
  ColGroupDef,
  GetRowIdFunc,
  GridApi,
  GridOptions,
  GridReadyEvent,
  HighlightChangesModule,
  IServerSideGetRowsParams,
  IsServerSideGroupOpenByDefault,
  ModuleRegistry,
  RowModelType,
  RowSelectionOptions,
  ServerSideTransaction,
  ServerSideTransactionResult,
  enableDevValidations,
} from "ag-grid-community";
import {
  RowGroupingModule,
  ServerSideRowModelApiModule,
  ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { createRowOnServer, data } from "./data";
import { FakeServer } from "./fakeServer";

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

const modules = [
  HighlightChangesModule,
  RowGroupingModule,
  ServerSideRowModelModule,
  ServerSideRowModelApiModule,
];

function getServerSideDatasource(server: any) {
  return {
    getRows: (params: IServerSideGetRowsParams) => {
      const response = server.getData(params.request);
      // adding delay to simulate real server call
      setTimeout(() => {
        if (response.success) {
          // call the success callback
          params.success({
            rowData: response.rows,
            rowCount: response.lastRow,
          });
        } else {
          // inform the grid request failed
          params.fail();
        }
      }, 300);
    },
  };
}

function logResults(
  transaction: ServerSideTransaction,
  result?: ServerSideTransactionResult,
) {
  console.log(
    "[Example] - Applied transaction:",
    transaction,
    "Result:",
    result,
  );
}

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

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "portfolio", hide: true, rowGroup: true },
    { field: "book" },
    { field: "previous" },
    { field: "current" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      flex: 1,
      minWidth: 100,
      enableCellChangeFlash: true,
    };
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      minWidth: 220,
      field: "tradeId",
    };
  }, []);
  const isServerSideGroupOpenByDefault = useCallback((params) => {
    return (
      params.rowNode.key === "Aggressive" || params.rowNode.key === "Hybrid"
    );
  }, []);
  const getRowId = useCallback((params) => {
    if (params.level === 0) {
      return params.data.portfolio;
    }
    return String(params.data.tradeId);
  }, []);
  const rowSelection = useMemo<
    RowSelectionOptions | "single" | "multiple"
  >(() => {
    return {
      mode: "multiRow",
      groupSelects: "descendants",
    };
  }, []);

  const onGridReady = useCallback((params: GridReadyEvent) => {
    // setup the fake server
    const server = FakeServer(data);
    // create datasource with a reference to the fake server
    const datasource = getServerSideDatasource(server);
    // register the datasource with the grid
    params.api.setGridOption("serverSideDatasource", datasource);
  }, []);

  const createOneAggressive = useCallback(() => {
    // NOTE: real applications would be better served listening to a stream of changes from the server instead
    const serverResponse: any = createRowOnServer(
      "Aggressive",
      "Aluminium",
      "GL-1",
    );
    if (!serverResponse.success) {
      console.warn("Nothing has changed on the server");
      return;
    }
    if (serverResponse.newGroupCreated) {
      // if a new group had to be created, reflect in the grid
      const transaction = {
        route: [],
        add: [{ portfolio: "Aggressive" }],
      };
      const result =
        gridRef.current!.api.applyServerSideTransaction(transaction);
      logResults(transaction, result);
    } else {
      // if the group already existed, add rows to it
      const transaction = {
        route: ["Aggressive"],
        add: [serverResponse.newRecord],
      };
      const result =
        gridRef.current!.api.applyServerSideTransaction(transaction);
      logResults(transaction, result);
    }
  }, [createRowOnServer]);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div className="example-wrapper">
          <div style={{ marginBottom: "5px" }}>
            <button onClick={createOneAggressive}>Add new 'Aggressive'</button>
          </div>

          <div style={gridStyle}>
            <AgGridReact
              ref={gridRef}
              columnDefs={columnDefs}
              defaultColDef={defaultColDef}
              autoGroupColumnDef={autoGroupColumnDef}
              isServerSideGroupOpenByDefault={isServerSideGroupOpenByDefault}
              getRowId={getRowId}
              rowModelType={"serverSide"}
              rowSelection={rowSelection}
              onGridReady={onGridReady}
            />
          </div>
        </div>
      </div>
    </AgGridProvider>
  );
};

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

[Live example: Transactions Example](https://www.ag-grid.com/examples/server-side-model-selection/group-selects-children-transactions/reactFunctionalTs)

## API Reference

The following API functions exist for the Server-Side Row Model when using [Row Selection](https://www.ag-grid.com/react-data-grid/row-selection-api-reference/#grid-selection-api). These can be used to get the currently selected rows and nodes if all of the selected rows have been loaded by the grid. These cannot be used while using `rowSelection.groupSelects` is `'descendants'` or `'filteredDescendants'`, or when any select all functionality is required.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `getSelectedNodes` | `Function` |  |  | Returns an unsorted list of selected nodes. Getting the underlying node (rather than the data) is useful when working with tree / aggregated data, as the node can be traversed. Module: [`RowSelectionModule`](https://www.ag-grid.com/react-data-grid/modules/). |
| `getSelectedRows` | `Function` |  |  | Returns an unsorted list of selected rows (i.e. row data that you provided). Module: [`RowSelectionModule`](https://www.ag-grid.com/react-data-grid/modules/). |

When using selection where all selected rows may not have been loaded, it is instead advised to use `api.getServerSideSelectionState` and `api.setServerSideSelectionState`, as these functions can be used to identify selected rows without having ever loaded the rows.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `getServerSideSelectionState` | `Function` |  |  | Returns an object containing rules matching the selected rows in the SSRM. If `rowSelection.groupSelects` is `'self'` the returned object will be flat, and will conform to `IServerSideSelectionState`. If `rowSelection.groupSelects` is `'descendants'` or `'filteredDescendants'` the returned object will be hierarchical, and will conform to `IServerSideGroupSelectionState`. Module: [`ServerSideRowModelApiModule`](https://www.ag-grid.com/react-data-grid/modules/). |
| `setServerSideSelectionState` | `Function` |  |  | Set the rules matching the selected rows in the SSRM. If `rowSelection.groupSelects` is `'self'` the param will be flat, and should conform to `IServerSideSelectionState`. If `rowSelection.groupSelects` is `'descendants'` or `'filteredDescendants'` the param will be hierarchical, and should conform to `IServerSideGroupSelectionState`. Module: [`ServerSideRowModelApiModule`](https://www.ag-grid.com/react-data-grid/modules/). |

### Selection API

The below snippet demonstrates how to set all nodes as selected, except for the row which has the ID `group-country-year-United States`, and the row with the ID `group-country-year-United States2004`.

```jsx
gridApi.setServerSideSelectionState({
    selectAll: true,
    toggledNodes: ['group-country-year-United States', 'group-country-year-United States2004'],
});
```

In the example below, note the following;

- The above snippet has been included inside of the `firstDataRendered` callback, which sets the initial selection state.
- The `Save Selection` button has been configured to store the result of `api.getServerSideSelectionState()`, it also logs the saved state to the console when clicked.
- The `Load Selection` button can subsequently re-apply the previously saved selection rules using `api.setServerSideSelectionState(state)`.

#### Select All API

```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 {
  AutoGroupColumnDef,
  ColDef,
  ColGroupDef,
  GetRowIdFunc,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IServerSideDatasource,
  IServerSideSelectionState,
  IsServerSideGroupOpenByDefault,
  ModuleRegistry,
  NumberFilterModule,
  RowModelType,
  RowSelectionOptions,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  RowGroupingModule,
  RowGroupingPanelModule,
  ServerSideRowModelApiModule,
  ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { FakeServer } from "./fakeServer";
import { IOlympicDataWithId } from "./interfaces";

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

const modules = [
  RowGroupingModule,
  ServerSideRowModelModule,
  ServerSideRowModelApiModule,
  RowGroupingPanelModule,
  TextFilterModule,
  NumberFilterModule,
];

let selectionState: IServerSideSelectionState = {
  selectAll: false,
  toggledNodes: [],
};

const getServerSideDatasource: (server: any) => IServerSideDatasource = (
  server: any,
) => {
  return {
    getRows: (params) => {
      console.log("[Datasource] - rows requested by grid: ", params.request);
      const response = server.getData(params.request);
      // adding delay to simulate real server call
      setTimeout(() => {
        if (response.success) {
          // call the success callback
          params.success({
            rowData: response.rows,
            rowCount: response.lastRow,
          });
        } else {
          // inform the grid request failed
          params.fail();
        }
      }, 200);
    },
  };
};

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

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "country", enableRowGroup: true, rowGroup: true, hide: true },
    { field: "year", enableRowGroup: true, rowGroup: true, hide: true },
    { field: "athlete", hide: true },
    { field: "sport", enableRowGroup: true, filter: "agTextColumnFilter" },
    { field: "gold", aggFunc: "sum", filter: "agNumberColumnFilter" },
    { field: "silver", aggFunc: "sum", filter: "agNumberColumnFilter" },
    { field: "bronze", aggFunc: "sum", filter: "agNumberColumnFilter" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      floatingFilter: true,
      flex: 1,
      minWidth: 120,
    };
  }, []);
  const getRowId = useCallback((params) => {
    if (params.data.id != null) {
      return "leaf-" + params.data.id;
    }
    const rowGroupCols = params.api.getRowGroupColumns();
    const rowGroupColIds = rowGroupCols.map((col) => col.getId()).join("-");
    const thisGroupCol = rowGroupCols[params.level];
    return (
      "group-" +
      rowGroupColIds +
      "-" +
      (params.parentKeys || []).join("-") +
      params.data[thisGroupCol.getColDef().field as keyof IOlympicDataWithId]
    );
  }, []);
  const isServerSideGroupOpenByDefault = useCallback((params) => {
    return (
      params.rowNode.key === "United States" ||
      String(params.rowNode.key) === "2004"
    );
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      field: "athlete",
      flex: 1,
      minWidth: 240,
    };
  }, []);
  const rowSelection = useMemo<
    RowSelectionOptions | "single" | "multiple"
  >(() => {
    return {
      mode: "multiRow",
    };
  }, []);

  const onGridReady = useCallback((params: GridReadyEvent) => {
    fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: IOlympicDataWithId[]) => {
        // assign a unique ID to each data item
        data.forEach(function (item: any, index: number) {
          item.id = index;
        });
        // setup the fake server with entire dataset
        const fakeServer = new FakeServer(data);
        // create datasource with a reference to the fake server
        const datasource = getServerSideDatasource(fakeServer);
        // register the datasource with the grid
        params.api!.setGridOption("serverSideDatasource", datasource);
      });
  }, []);

  const onFirstDataRendered = useCallback((params) => {
    params.api.setServerSideSelectionState({
      selectAll: true,
      toggledNodes: [
        "group-country-year-United States",
        "group-country-year-United States2004",
      ],
    });
  }, []);

  const saveSelectionState = useCallback(() => {
    selectionState =
      gridRef.current!.api.getServerSideSelectionState() as IServerSideSelectionState;
    console.log(JSON.stringify(selectionState, null, 2));
  }, [selectionState]);

  const loadSelectionState = useCallback(() => {
    gridRef.current!.api.setServerSideSelectionState(selectionState);
  }, [selectionState]);

  const clearSelectionState = useCallback(() => {
    gridRef.current!.api.setServerSideSelectionState({
      selectAll: false,
      toggledNodes: [],
    });
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div className="example-wrapper">
          <div style={{ marginBottom: "5px" }}>
            <button onClick={saveSelectionState}>Save Selection</button>
            <button onClick={loadSelectionState}>Load Selection</button>
            <button onClick={clearSelectionState}>Clear Selection</button>
          </div>

          <div style={gridStyle}>
            <AgGridReact<IOlympicDataWithId>
              ref={gridRef}
              columnDefs={columnDefs}
              defaultColDef={defaultColDef}
              getRowId={getRowId}
              isServerSideGroupOpenByDefault={isServerSideGroupOpenByDefault}
              autoGroupColumnDef={autoGroupColumnDef}
              rowGroupPanelShow={"always"}
              rowModelType={"serverSide"}
              rowSelection={rowSelection}
              suppressAggFuncInHeader={true}
              onGridReady={onGridReady}
              onFirstDataRendered={onFirstDataRendered}
            />
          </div>
        </div>
      </div>
    </AgGridProvider>
  );
};

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

[Live example: Select All API](https://www.ag-grid.com/examples/server-side-model-selection/api-select-all/reactFunctionalTs)

### Group Selection API

The below snippet demonstrates how to set all nodes as selected, except in the case of the "United States" group, whose only selected child is the row with the ID `group-country-year-United States2004`.

```jsx
gridApi.setServerSideSelectionState({
    // this root level config can be used to determine a global select-all
    selectAllChildren: true,
    // all of the top level group nodes which do not conform with the select all value will have an entry here
    // including indeterminate nodes
    toggledNodes: [{
        // as this is a group node with toggledNodes, this node will be marked as indeterminate
        nodeId: 'group-country-year-United States',
        // selectAllChildren can be used to determine whether this group's children are all selected
        selectAllChildren: false,
        toggledNodes: [{
            // this group node has no toggledNodes, and so it will respect its own `selectAllChildren` property along
            // with its descendants.
            nodeId: 'group-country-year-United States2004',
            selectAllChildren: true,
        }],
    }],
});
```

> **Note**
>
> The state being used here conforms to `IServerSideGroupSelectionState`, not `IServerSideSelectionState`. Invalid states will be ignored.

In the example below, note the following;

- The above snippet has been included inside of the `firstDataRendered` callback, which sets the initial selection state.
- The `Save Selection` button has been configured to store the result of `api.getServerSideSelectionState()`, it also logs the saved state to the console when clicked.
- The `Load Selection` button can subsequently re-apply the previously saved selection rules using `api.setServerSideSelectionState(state)`.

#### Group Selection API

```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 {
  AutoGroupColumnDef,
  ColDef,
  ColGroupDef,
  GetRowIdFunc,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IServerSideDatasource,
  IServerSideGroupSelectionState,
  IsServerSideGroupOpenByDefault,
  ModuleRegistry,
  NumberFilterModule,
  RowModelType,
  RowSelectionOptions,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  RowGroupingModule,
  RowGroupingPanelModule,
  ServerSideRowModelApiModule,
  ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { FakeServer } from "./fakeServer";
import { IOlympicDataWithId } from "./interfaces";

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

const modules = [
  RowGroupingModule,
  ServerSideRowModelModule,
  ServerSideRowModelApiModule,
  RowGroupingPanelModule,
  TextFilterModule,
  NumberFilterModule,
];

let selectionState: IServerSideGroupSelectionState = {
  selectAllChildren: false,
  toggledNodes: [],
};

const getServerSideDatasource: (server: any) => IServerSideDatasource = (
  server: any,
) => {
  return {
    getRows: (params) => {
      console.log("[Datasource] - rows requested by grid: ", params.request);
      const response = server.getData(params.request);
      // adding delay to simulate real server call
      setTimeout(() => {
        if (response.success) {
          // call the success callback
          params.success({
            rowData: response.rows,
            rowCount: response.lastRow,
          });
        } else {
          // inform the grid request failed
          params.fail();
        }
      }, 200);
    },
  };
};

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

  const [columnDefs, setColumnDefs] = useState<ColDef[]>([
    { field: "country", enableRowGroup: true, rowGroup: true, hide: true },
    { field: "year", enableRowGroup: true, rowGroup: true, hide: true },
    { field: "athlete", hide: true },
    { field: "sport", enableRowGroup: true, filter: "agTextColumnFilter" },
    { field: "gold", aggFunc: "sum", filter: "agNumberColumnFilter" },
    { field: "silver", aggFunc: "sum", filter: "agNumberColumnFilter" },
    { field: "bronze", aggFunc: "sum", filter: "agNumberColumnFilter" },
  ]);
  const defaultColDef = useMemo<ColDef>(() => {
    return {
      floatingFilter: true,
      flex: 1,
      minWidth: 120,
    };
  }, []);
  const getRowId = useCallback((params) => {
    if (params.data.id != null) {
      return "leaf-" + params.data.id;
    }
    const rowGroupCols = params.api.getRowGroupColumns();
    const rowGroupColIds = rowGroupCols.map((col) => col.getId()).join("-");
    const thisGroupCol = rowGroupCols[params.level];
    return (
      "group-" +
      rowGroupColIds +
      "-" +
      (params.parentKeys || []).join("-") +
      params.data[thisGroupCol.getColDef().field as keyof IOlympicDataWithId]
    );
  }, []);
  const isServerSideGroupOpenByDefault = useCallback((params) => {
    return (
      params.rowNode.key === "United States" ||
      String(params.rowNode.key) === "2004"
    );
  }, []);
  const autoGroupColumnDef = useMemo<AutoGroupColumnDef>(() => {
    return {
      field: "athlete",
      flex: 1,
      minWidth: 240,
    };
  }, []);
  const rowSelection = useMemo<
    RowSelectionOptions | "single" | "multiple"
  >(() => {
    return {
      mode: "multiRow",
      groupSelects: "descendants",
    };
  }, []);

  const onGridReady = useCallback((params: GridReadyEvent) => {
    fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .then((resp) => resp.json())
      .then((data: IOlympicDataWithId[]) => {
        // assign a unique ID to each data item
        data.forEach(function (item: any, index: number) {
          item.id = index;
        });
        // setup the fake server with entire dataset
        const fakeServer = new FakeServer(data);
        // create datasource with a reference to the fake server
        const datasource = getServerSideDatasource(fakeServer);
        // register the datasource with the grid
        params.api!.setGridOption("serverSideDatasource", datasource);
      });
  }, []);

  const onFirstDataRendered = useCallback((params) => {
    params.api.setServerSideSelectionState({
      selectAllChildren: true,
      toggledNodes: [
        {
          nodeId: "group-country-year-United States",
          selectAllChildren: false,
          toggledNodes: [
            {
              nodeId: "group-country-year-United States2004",
              selectAllChildren: true,
            },
          ],
        },
      ],
    });
  }, []);

  const saveSelectionState = useCallback(() => {
    selectionState =
      gridRef.current!.api.getServerSideSelectionState() as IServerSideGroupSelectionState;
    console.log(JSON.stringify(selectionState, null, 2));
  }, [selectionState]);

  const loadSelectionState = useCallback(() => {
    gridRef.current!.api.setServerSideSelectionState(selectionState);
  }, [selectionState]);

  const clearSelectionState = useCallback(() => {
    gridRef.current!.api.setServerSideSelectionState({
      selectAllChildren: false,
      toggledNodes: [],
    });
  }, []);

  return (
    <AgGridProvider modules={modules}>
      <div style={containerStyle}>
        <div className="example-wrapper">
          <div style={{ marginBottom: "5px" }}>
            <button onClick={saveSelectionState}>Save Selection</button>
            <button onClick={loadSelectionState}>Load Selection</button>
            <button onClick={clearSelectionState}>Clear Selection</button>
          </div>

          <div style={gridStyle}>
            <AgGridReact<IOlympicDataWithId>
              ref={gridRef}
              columnDefs={columnDefs}
              defaultColDef={defaultColDef}
              getRowId={getRowId}
              isServerSideGroupOpenByDefault={isServerSideGroupOpenByDefault}
              autoGroupColumnDef={autoGroupColumnDef}
              rowModelType={"serverSide"}
              rowSelection={rowSelection}
              rowGroupPanelShow={"always"}
              suppressAggFuncInHeader={true}
              onGridReady={onGridReady}
              onFirstDataRendered={onFirstDataRendered}
            />
          </div>
        </div>
      </div>
    </AgGridProvider>
  );
};

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

[Live example: Group Selection API](https://www.ag-grid.com/examples/server-side-model-selection/api-group-selects-children/reactFunctionalTs)

## Tree Data

Row selection is also supported when using tree data. See this documented on the [SSRM Tree Data](https://www.ag-grid.com/react-data-grid/server-side-model-tree-data/#selection-with-tree-data) page.
