---
title: "SSRM Configuration"
enterprise: true
framework: vue
version: "36.1.0"
---

# SSRM Configuration

This section covers the Server-Side Cache and configurations available in the Server-Side Row Model.

As many of the configurations available in the Server-Side Row Model relate to the Server-Side Cache it is important to understand how the grid organises data obtained from the server into caches.

## Server-Side Cache

The grid arranges rows into blocks which are in turn stored in a cache as illustrated below:

![serverSideCache](https://www.ag-grid.com/_astro/serverSideCache.BdouedRv.png)

Fig 1. Server-side Cache

There is a cache containing the top-level rows (i.e. on the root node) and for each individual [Row Grouping](https://www.ag-grid.com/vue-data-grid/server-side-model-grouping/) level. When the grid initialises, it will retrieve an initial number (as per configuration) of blocks containing rows, and as the user scrolls down more blocks will be loaded.

Several [Configurations](https://www.ag-grid.com/vue-data-grid/server-side-model-api-reference/), such as `cacheBlockSize` and `maxBlocksInCache`, are expressed in terms of the Server-Side Cache.

The example below demonstrates some configurations. Note the following:

- The grid property `cacheBlockSize = 50`. This sets the block size to 50, thus rows are read back 50 at a time.
- The grid property `maxBlocksInCache = 2`. This means the grid will keep two blocks in memory only. To see this in action, scroll past row 100 (which will require a third block to be loaded), then quickly scroll back to the start and you will observe the first block needs to be reloaded.

#### Cache Configurations

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IServerSideDatasource,
  IServerSideGetRowsRequest,
  ModuleRegistry,
  RowModelType,
  enableDevValidations,
} from "ag-grid-community";
import { ServerSideRowModelModule } from "ag-grid-enterprise";
import { IOlympicDataWithId } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([ServerSideRowModelModule]);

function createServerSideDatasource(server: any): IServerSideDatasource {
  return {
    getRows: (params) => {
      console.log("[Datasource] - rows requested by grid: ", params.request);
      // get data for request from our fake server
      const response = server.getData(params.request);
      // simulating real server call with a 500ms delay
      setTimeout(() => {
        if (response.success) {
          // supply rows for requested block to grid
          params.success({
            rowData: response.rows,
            rowCount: response.lastRow,
          });
        } else {
          params.fail();
        }
      }, 500);
    },
  };
}

function createFakeServer(allData: any[]) {
  return {
    getData: (request: IServerSideGetRowsRequest) => {
      // take a slice of the total rows for requested block
      const rowsForBlock = allData.slice(request.startRow, request.endRow);
      // here we are pretending we don't know the last row until we reach it!
      const lastRow = getLastRowIndex(request, rowsForBlock);
      return {
        success: true,
        rows: rowsForBlock,
        lastRow: lastRow,
      };
    },
  };
}

function getLastRowIndex(request: IServerSideGetRowsRequest, results: any[]) {
  if (!results) return undefined;
  const currentLastRow = (request.startRow || 0) + results.length;
  // if on or after the last block, work out the last row, otherwise return 'undefined'
  return currentLastRow < (request.endRow || 0) ? currentLastRow : undefined;
}

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowBuffer="rowBuffer"
      :rowModelType="rowModelType"
      :cacheBlockSize="cacheBlockSize"
      :maxBlocksInCache="maxBlocksInCache"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicDataWithId> | null>(null);
    const columnDefs = ref<ColDef[]>([
      { field: "id", maxWidth: 80 },
      { field: "athlete", minWidth: 220 },
      { field: "country", minWidth: 200 },
      { field: "year" },
      { field: "sport", minWidth: 200 },
      { field: "gold" },
      { field: "silver" },
      { field: "bronze" },
    ]);
    const defaultColDef = ref<ColDef>({
      flex: 1,
      minWidth: 100,
      sortable: false,
    });
    const rowBuffer = ref(0);
    const rowModelType = ref<RowModelType>("serverSide");
    const cacheBlockSize = ref(50);
    const maxBlocksInCache = ref(2);
    const rowData = ref<IOlympicDataWithId[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => {
        // adding row id to data
        let idSequence = 0;
        data.forEach(function (item: any) {
          item.id = idSequence++;
        });
        // setup the fake server with entire dataset
        const fakeServer = createFakeServer(data);
        // create datasource with a reference to the fake server
        const datasource = createServerSideDatasource(fakeServer);
        // register the datasource with the grid
        params.api!.setGridOption("serverSideDatasource", datasource);
      };

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowBuffer,
      rowModelType,
      cacheBlockSize,
      maxBlocksInCache,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Cache Configurations](https://www.ag-grid.com/examples/server-side-model-configuration/cache-configurations/vue3)

## Block Loading Debounce

It may be desirable to scroll through the entire dataset without the need for intermediate blocks to be loaded.

The example below shows how debouncing block loading can be achieved. Note the following:

- The response from the server sets the `rowCount` property so that the vertical scrollbars bounds are set such that the entire dataset can be scrolled through.
- `blockLoadDebounceMillis = 1000` - loading of blocks is delayed by `1000ms`. This allows for skipping over blocks when scrolling to advanced positions.
- The grid property `debug = true`. This means the browser's dev console will show loading block details.

#### Block Loading Debounce

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IServerSideDatasource,
  IServerSideGetRowsRequest,
  ModuleRegistry,
  RowModelType,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { IOlympicDataWithId } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ColumnsToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  ServerSideRowModelModule,
]);

function createServerSideDatasource(server: any): IServerSideDatasource {
  return {
    getRows: (params) => {
      console.log("[Datasource] - rows requested by grid: ", params.request);
      // get data for request from our fake server
      const response = server.getData(params.request);
      // simulating real server call with a 100ms delay
      setTimeout(() => {
        if (response.success) {
          // supply rows for requested block to grid
          params.success({
            rowData: response.rows,
            rowCount: response.lastRow,
          });
        } else {
          params.fail();
        }
      }, 100);
    },
  };
}

function createFakeServer(allData: any[]) {
  return {
    getData: (request: IServerSideGetRowsRequest) => {
      // take a slice of the total rows for requested block
      const rowsForBlock = allData.slice(request.startRow, request.endRow);
      // when row count is known and 'blockLoadDebounceMillis' is set it is possible to
      // quickly skip over blocks while scrolling
      const lastRow = allData.length;
      return {
        success: true,
        rows: rowsForBlock,
        lastRow: lastRow,
      };
    },
  };
}

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowModelType="rowModelType"
      :blockLoadDebounceMillis="blockLoadDebounceMillis"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicDataWithId> | null>(null);
    const columnDefs = ref<ColDef[]>([
      { field: "id", maxWidth: 80 },
      { field: "athlete", minWidth: 220 },
      { field: "country", minWidth: 200 },
      { field: "year" },
      { field: "sport", minWidth: 200 },
      { field: "gold" },
      { field: "silver" },
      { field: "bronze" },
    ]);
    const defaultColDef = ref<ColDef>({
      flex: 1,
      minWidth: 100,
      sortable: false,
    });
    const rowModelType = ref<RowModelType>("serverSide");
    const blockLoadDebounceMillis = ref(1000);
    const rowData = ref<IOlympicDataWithId[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => {
        // adding row id to data
        let idSequence = 0;
        data.forEach(function (item: any) {
          item.id = idSequence++;
        });
        // setup the fake server with entire dataset
        const fakeServer = createFakeServer(data);
        // create datasource with a reference to the fake server
        const datasource = createServerSideDatasource(fakeServer);
        // register the datasource with the grid
        params.api!.setGridOption("serverSideDatasource", datasource);
      };

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowModelType,
      blockLoadDebounceMillis,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Block Loading Debounce](https://www.ag-grid.com/examples/server-side-model-configuration/block-load-debounce/vue3)

## Providing Additional Data

It is possible to supply extra data to the grid outside of the datasource lifecycle. This can be used to populate the grid with data before scrolling, provide hierarchical data, or provide additional blocks.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `applyServerSideRowData` | `Function` |  |  | Applies row data to a server side store. New rows will overwrite rows at the same index in the same way as if provided by a datasource success callback. Module: [`ServerSideRowModelApiModule`](https://www.ag-grid.com/vue-data-grid/modules/). |

The example below demonstrates that the grid can be populated with data outside of the datasource flow. Note the following:

- The first loading row never displays, as the first 100 rows are loaded by default
- 100 rows are provided by default, ignoring the `cacheBlockSize` property
- The loading of these additional rows bypasses the `blockLoadDebounceMillis` and `maxConcurrentDatasourceRequests` properties.

#### Additional Row Data

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IServerSideDatasource,
  IServerSideGetRowsRequest,
  ModuleRegistry,
  RowModelType,
  enableDevValidations,
} from "ag-grid-community";
import {
  ServerSideRowModelApiModule,
  ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ServerSideRowModelModule,
  ServerSideRowModelApiModule,
]);

function createServerSideDatasource(server: any): IServerSideDatasource {
  return {
    getRows: (params) => {
      console.log(
        "[Datasource] - rows requested by grid: startRow = " +
          params.request.startRow +
          ", endRow = " +
          params.request.endRow,
      );
      // get data for request from our fake server
      const response = server.getData(params.request);
      // simulating real server call with a 1000ms delay
      setTimeout(() => {
        if (response.success) {
          // supply rows for requested block to grid
          params.success({
            rowData: response.rows,
            rowCount: response.lastRow,
          });
        } else {
          params.fail();
        }
      }, 1000);
    },
  };
}

function createFakeServer(allData: any[]) {
  return {
    getData: (
      request: Pick<IServerSideGetRowsRequest, "startRow" | "endRow">,
    ) => {
      // in this simplified fake server all rows are contained in an array
      const requestedRows = allData.slice(request.startRow, request.endRow);
      return {
        success: true,
        rows: requestedRows,
        lastRow: allData.length,
      };
    },
  };
}

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowModelType="rowModelType"
      :maxBlocksInCache="maxBlocksInCache"
      :cacheBlockSize="cacheBlockSize"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<ColDef[]>([
      { headerName: "Index", valueGetter: "node.rowIndex", minWidth: 100 },
      { field: "athlete", minWidth: 150 },
      { field: "country", minWidth: 150 },
      { field: "year" },
      { field: "sport", minWidth: 120 },
      { field: "gold" },
      { field: "silver" },
      { field: "bronze" },
    ]);
    const defaultColDef = ref<ColDef>({
      flex: 1,
      minWidth: 80,
      sortable: false,
    });
    const rowModelType = ref<RowModelType>("serverSide");
    const maxBlocksInCache = ref(0);
    const cacheBlockSize = ref(50);
    const rowData = ref<IOlympicData[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => {
        // setup the fake server with entire dataset
        const fakeServer = createFakeServer(data);
        // create datasource with a reference to the fake server
        const datasource = createServerSideDatasource(fakeServer);
        // register the datasource with the grid
        params.api!.setGridOption("serverSideDatasource", datasource);
        const initialData = fakeServer.getData({
          startRow: 0,
          endRow: 100,
        });
        params.api!.applyServerSideRowData({
          successParams: {
            rowData: initialData.rows,
            rowCount: initialData.lastRow,
          },
        });
      };

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowModelType,
      maxBlocksInCache,
      cacheBlockSize,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Additional Row Data](https://www.ag-grid.com/examples/server-side-model-configuration/additional-row-data/vue3)

## Initial Scroll Position

When using the server-side row model the initial scroll position of the grid can be set. This is achieved by calling `api.ensureIndexVisible()` after setting the data source to the grid.

> **Note**
>
> It is important that the `serverSideInitialRowCount` property is set to a value that is greater than the sum of the row index provided to `api.ensureIndexVisible()` and the number of rows displayed in the grid's viewport.

This is demonstrated in the example below, note the following:

- The `serverSideInitialRowCount` property has been set to provide an initial length to the vertical scrollbars.
- After the datasource has been set `api.ensureIndexVisible(5000, 'top')` is called, causing the grid to scroll down to row `5000`.

#### Initial Scroll Position

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IServerSideDatasource,
  IServerSideGetRowsRequest,
  ModuleRegistry,
  RowModelType,
  ScrollApiModule,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ScrollApiModule,
  ColumnsToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  ServerSideRowModelModule,
]);

function createServerSideDatasource(server: any): IServerSideDatasource {
  return {
    getRows: (params) => {
      console.log(
        "[Datasource] - rows requested by grid: startRow = " +
          params.request.startRow +
          ", endRow = " +
          params.request.endRow,
      );
      // get data for request from our fake server
      const response = server.getData(params.request);
      // simulating real server call with a 1000ms delay
      setTimeout(() => {
        if (response.success) {
          // supply rows for requested block to grid
          params.success({
            rowData: response.rows,
            rowCount: response.lastRow,
          });
        } else {
          params.fail();
        }
      }, 1000);
    },
  };
}

function createFakeServer(allData: any[]) {
  return {
    getData: (request: IServerSideGetRowsRequest) => {
      // in this simplified fake server all rows are contained in an array
      const requestedRows = allData.slice(request.startRow, request.endRow);
      return {
        success: true,
        rows: requestedRows,
        lastRow: allData.length,
      };
    },
  };
}

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowModelType="rowModelType"
      :serverSideInitialRowCount="serverSideInitialRowCount"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicData> | null>(null);
    const columnDefs = ref<ColDef[]>([
      { headerName: "Index", valueGetter: "node.rowIndex", minWidth: 100 },
      { field: "athlete", minWidth: 150 },
      { field: "country", minWidth: 150 },
      { field: "year" },
      { field: "sport", minWidth: 120 },
      { field: "gold" },
      { field: "silver" },
      { field: "bronze" },
    ]);
    const defaultColDef = ref<ColDef>({
      flex: 1,
      minWidth: 80,
      sortable: false,
    });
    const rowModelType = ref<RowModelType>("serverSide");
    const serverSideInitialRowCount = ref(5500);
    const rowData = ref<IOlympicData[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => {
        // setup the fake server with entire dataset
        const fakeServer = createFakeServer(data);
        // create datasource with a reference to the fake server
        const datasource = createServerSideDatasource(fakeServer);
        // register the datasource with the grid
        params.api!.setGridOption("serverSideDatasource", datasource);
        // scroll the grid down until row 5000 is at the top of the viewport
        params.api!.ensureIndexVisible(5000, "top");
      };

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowModelType,
      serverSideInitialRowCount,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Initial Scroll Position](https://www.ag-grid.com/examples/server-side-model-configuration/initial-scroll-position/vue3)

## Providing Row IDs

Some features of the server-side row model require Row IDs in order to be enabled or to enhance their behaviour, these features include [Row Selection](https://www.ag-grid.com/vue-data-grid/server-side-model-selection/), [Transactions](https://www.ag-grid.com/vue-data-grid/server-side-model-updating-transactions/) and [Refreshing Data](https://www.ag-grid.com/vue-data-grid/server-side-model-updating-refresh/).

The server-side row model requires unique Row IDs in order to identify rows after new data loads. For example, if a sort or filter is applied which results in new rows being loaded, the grid needs to be able to identify the previously known rows.

Row IDs are provided by the application using the `getRowId()` callback:

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `getRowId` | `GetRowIdFunc` |  |  | Provide a pure function that returns a string ID to uniquely identify a given row. This enables the grid to work optimally with data changes and updates. [Initial](https://www.ag-grid.com/vue-data-grid/grid-interface/#initial-grid-options). |

When implementing `getRowId()` you must ensure the rows have unique Row IDs across the entire data set. Using an ID that is provided in the data such as a database Primary Key is recommended.

### Supplying Unique Group IDs

When grouping there may not be an easy way to get unique Row IDs from the data for group levels. This is because a group row doesn't always correspond to one Row in the store.

To handle this scenario, the grid provides `parentKeys` and `level` properties in the `GetRowIdParams` supplied to `getRowId()`.

These can be used to create unique group IDs as shown below:

```ts
<ag-grid-vue
    :getRowId="getRowId"
    /* other grid options ... */>
</ag-grid-vue>

this.getRowId = params => {
    const parentKeysJoined = (params.parentKeys || []).join('-');
    if (params.data.id != null) {
        return parentKeysJoined + params.data.id;
    }
    const rowGroupCols = params.api.getRowGroupColumns();
    const thisGroupCol = rowGroupCols[params.level];
    return parentKeysJoined + params.data[thisGroupCol.getColDef().field];
};
```

## Grand Total Row

A grand total row can be displayed at the top or bottom of the grid, even on flat grids without row grouping. See [Grand Total Row](https://www.ag-grid.com/vue-data-grid/server-side-model-grouping/#grand-total-row) for full details and examples.

## Debug Info

When using the server-side row model it can be helpful to gather the state of each block. This can be gathered by using `api.getCacheBlockState()` or alternatively you can enable `debug: true` in the grid properties to see this output logged to the console whenever blocks are loaded.

In the example below, note the following:

- The grid option `debug: true` has been enabled
- When scrolling through the grid, block information is logged to the console

#### Cache Block State

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IServerSideDatasource,
  IServerSideGetRowsRequest,
  ModuleRegistry,
  RowModelType,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { IOlympicDataWithId } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  ColumnsToolPanelModule,
  ColumnMenuModule,
  ContextMenuModule,
  ServerSideRowModelModule,
]);

function createServerSideDatasource(server: any): IServerSideDatasource {
  return {
    getRows: (params) => {
      console.log("[Datasource] - rows requested by grid: ", params.request);
      // get data for request from our fake server
      const response = server.getData(params.request);
      // simulating real server call with a 500ms delay
      setTimeout(() => {
        if (response.success) {
          // supply rows for requested block to grid
          params.success({
            rowData: response.rows,
            rowCount: response.lastRow,
          });
        } else {
          params.fail();
        }
      }, 500);
    },
  };
}

function createFakeServer(allData: any[]) {
  return {
    getData: (request: IServerSideGetRowsRequest) => {
      // take a slice of the total rows for requested block
      const rowsForBlock = allData.slice(request.startRow, request.endRow);
      // here we are pretending we don't know the last row until we reach it!
      const lastRow = getLastRowIndex(request, rowsForBlock);
      return {
        success: true,
        rows: rowsForBlock,
        lastRow: lastRow,
      };
    },
  };
}

function getLastRowIndex(request: IServerSideGetRowsRequest, results: any[]) {
  if (!results) return undefined;
  const currentLastRow = (request.startRow || 0) + results.length;
  // if on or after the last block, work out the last row, otherwise return 'undefined'
  return currentLastRow < (request.endRow || 0) ? currentLastRow : undefined;
}

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowModelType="rowModelType"
      :maxBlocksInCache="maxBlocksInCache"
      :debug="true"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi<IOlympicDataWithId> | null>(null);
    const columnDefs = ref<ColDef[]>([
      { field: "id", maxWidth: 80 },
      { field: "athlete", minWidth: 220 },
      { field: "country", minWidth: 200 },
      { field: "year" },
      { field: "sport", minWidth: 200 },
      { field: "gold" },
      { field: "silver" },
      { field: "bronze" },
    ]);
    const defaultColDef = ref<ColDef>({
      flex: 1,
      minWidth: 100,
      sortable: false,
    });
    const rowModelType = ref<RowModelType>("serverSide");
    const maxBlocksInCache = ref(2);
    const rowData = ref<IOlympicDataWithId[]>(null);

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;

      const updateData = (data) => {
        // adding row id to data
        let idSequence = 0;
        data.forEach(function (item: any) {
          item.id = idSequence++;
        });
        // setup the fake server with entire dataset
        const fakeServer = createFakeServer(data);
        // create datasource with a reference to the fake server
        const datasource = createServerSideDatasource(fakeServer);
        // register the datasource with the grid
        params.api!.setGridOption("serverSideDatasource", datasource);
      };

      fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
        .then((resp) => resp.json())
        .then((data) => updateData(data));
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowModelType,
      maxBlocksInCache,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Cache Block State](https://www.ag-grid.com/examples/server-side-model-configuration/cache-block-state/vue3)
