---
title: "Master / Detail - Detail Height"
enterprise: true
framework: javascript
version: "36.1.0"
---

# Master / Detail - Detail Height

This section shows how the detail height can be customised to suit application requirements.

## Detail Height Options

The default height of each detail section (ie the row containing the Detail Grid in the master) is fixed at `300px`. The height does not change based on how much data there is to display in the detail section.

To change the height of the details section from the default you have the following options:

- [Fixed Height](https://www.ag-grid.com/javascript-data-grid/master-detail-height/#fixed-height): a custom fixed height can be provided for all detail sections instead of the default `300px`.
- [Auto Height](https://www.ag-grid.com/javascript-data-grid/master-detail-height/#auto-height): detail sections can auto-size to fit based off the contents.
- [Dynamic Height](https://www.ag-grid.com/javascript-data-grid/master-detail-height/#dynamic-height): different heights can be provided for each detail section.

## Fixed Height

Use the grid property `detailRowHeight` to set a fixed height for each detail row.

```js
const gridOptions = {
    // statically fix row height for all detail grids
    detailRowHeight: 200,

    // other grid options ...
}
```

The following example sets a fixed row height for all detail rows.

#### Fixed Detail Row Height

```ts
import {
  ClientSideRowModelModule,
  FirstDataRenderedEvent,
  GridApi,
  GridOptions,
  IDetailCellRendererParams,
  ModuleRegistry,
  RowApiModule,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  MasterDetailModule,
} from "ag-grid-enterprise";
import { IAccount } from "./interfaces";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  RowApiModule,
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  MasterDetailModule,
  ColumnMenuModule,
  ContextMenuModule,
]);

let gridApi: GridApi<IAccount>;

const gridOptions: GridOptions<IAccount> = {
  columnDefs: [
    // group cell renderer needed for expand / collapse icons
    { field: "name", cellRenderer: "agGroupCellRenderer" },
    { field: "account" },
    { field: "calls" },
    { field: "minutes", valueFormatter: "x.toLocaleString() + 'm'" },
  ],
  defaultColDef: {
    flex: 1,
  },
  masterDetail: true,
  detailRowHeight: 200,
  detailCellRendererParams: {
    detailGridOptions: {
      columnDefs: [
        { field: "callId" },
        { field: "direction" },
        { field: "number", minWidth: 150 },
        { field: "duration", valueFormatter: "x.toLocaleString() + 's'" },
        { field: "switchCode", minWidth: 150 },
      ],
      defaultColDef: {
        flex: 1,
      },
    },
    getDetailRowData: (params) => {
      params.successCallback(params.data.callRecords);
    },
  } as IDetailCellRendererParams<IAccount, ICallRecord>,
  alwaysShowVerticalScroll: true,
  onFirstDataRendered: onFirstDataRendered,
};

function onFirstDataRendered(params: FirstDataRenderedEvent) {
  // arbitrarily expand a row for presentational purposes
  setTimeout(() => {
    params.api.getDisplayedRowAtIndex(1)!.setExpanded(true);
  }, 0);
}

const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);

fetch("https://www.ag-grid.com/example-assets/master-detail-data.json")
  .then((response) => response.json())
  .then((data: IAccount[]) => {
    gridApi!.setGridOption("rowData", data);
  });
```

[Live example: Fixed Detail Row Height](https://www.ag-grid.com/examples/master-detail-height/fixed-detail-row-height/typescript)

## Auto Height

Set grid property `detailRowAutoHeight=true` to have the detail grid dynamically change its height to fit its rows.

```js
const gridOptions = {
    // dynamically set row height for all detail grids
    detailRowAutoHeight: true,

    // other grid options ...
}
```

#### Auto Height

```ts
import {
  ClientSideRowModelModule,
  FirstDataRenderedEvent,
  GridApi,
  GridOptions,
  IDetailCellRendererParams,
  ModuleRegistry,
  RowApiModule,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import { MasterDetailModule } from "ag-grid-enterprise";
import { IAccount } from "./interfaces";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  RowApiModule,
  ClientSideRowModelModule,
  MasterDetailModule,
]);

let gridApi: GridApi<IAccount>;

const gridOptions: GridOptions<IAccount> = {
  columnDefs: [
    // group cell renderer needed for expand / collapse icons
    { field: "name", cellRenderer: "agGroupCellRenderer" },
    { field: "account" },
    { field: "calls" },
    { field: "minutes", valueFormatter: "x.toLocaleString() + 'm'" },
  ],
  defaultColDef: {
    flex: 1,
  },
  masterDetail: true,
  detailRowAutoHeight: true,
  detailCellRendererParams: {
    detailGridOptions: {
      columnDefs: [
        { field: "callId" },
        { field: "direction" },
        { field: "number", minWidth: 150 },
        { field: "duration", valueFormatter: "x.toLocaleString() + 's'" },
        { field: "switchCode", minWidth: 150 },
      ],
      defaultColDef: {
        flex: 1,
      },
    },
    getDetailRowData: (params) => {
      params.successCallback(params.data.callRecords);
    },
  } as IDetailCellRendererParams<IAccount, ICallRecord>,
  alwaysShowVerticalScroll: true,
  onFirstDataRendered: onFirstDataRendered,
};

function onFirstDataRendered(params: FirstDataRenderedEvent) {
  // arbitrarily expand a row for presentational purposes
  setTimeout(() => {
    params.api.getDisplayedRowAtIndex(1)!.setExpanded(true);
  }, 0);
}

const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);

fetch("https://www.ag-grid.com/example-assets/master-detail-data.json")
  .then((response) => response.json())
  .then((data: IAccount[]) => {
    gridApi!.setGridOption("rowData", data);
  });
```

[Live example: Auto Height](https://www.ag-grid.com/examples/master-detail-height/auto-height/typescript)

Note that when using Auto Height, the Detail Grid will have a minimum height of 150px for the rows section. See [Min Height with Auto Height](https://www.ag-grid.com/javascript-data-grid/grid-size/#min-height-with-auto-height) for more information on how to change this.

> **Note**
>
> When using Auto Height feature, the Detail Grid will render all of its rows all the time. [Row Virtualisation](https://www.ag-grid.com/javascript-data-grid/dom-virtualisation/) will not happen. This means if the Detail Grid has many rows, it could slow down your application and could result in stalling the browser.
>
> Do not use Auto Height if you have many rows (eg 100+) in the Detail Grids. To know if this is a concern for your grid and dataset, try it out and check the performance.

### Auto Height with Custom Detail

If you are providing your own [Detail Cell Renderer](https://www.ag-grid.com/javascript-data-grid/master-detail-custom-detail/), set `detailRowAutoHeight: true` in the master-level gridOptions and ensure the content nested inside the detail cell renderer component sets a height value.

Here is an example of Auto Height being used with a Custom Detail Cell Renderer:

#### Auto Height with Custom Detail

```ts
import {
  ClientSideRowModelModule,
  FirstDataRenderedEvent,
  GridApi,
  GridOptions,
  IDetailCellRendererParams,
  ModuleRegistry,
  RowApiModule,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import { MasterDetailModule } from "ag-grid-enterprise";
import { IAccount } from "./interfaces";

export class DetailCellRenderer {
  eGui: HTMLDivElement | undefined;

  init() {
    this.eGui = document.createElement("div");
    //additional content shown in detail
    const panel = document.createElement("div");

    // Notice: the height is set
    panel.style =
      "height:100px; background-color:lightblue; padding: 15px; font-weight: bold; ";
    panel.innerText = "Optional element content";

    // button to toggle optional content visibility
    const btn = document.createElement("button");
    btn.innerText = "Show Optional Element";

    btn.style = "margin:10px";
    btn.addEventListener("click", function (p: any) {
      //add your own condition here based on application logic - this only checks the number of children shown
      if (p.target.parentElement.children.length === 1) {
        p.target.parentElement.appendChild(panel);
        p.target.innerText = "Hide Optional Element";
      } else {
        p.target.parentElement.removeChild(panel);
        p.target.innerText = "Show Optional Element";
      }
    });

    this.eGui.appendChild(btn);
  }

  getGui() {
    return this.eGui;
  }

  refresh() {
    return false;
  }
}

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  RowApiModule,
  ClientSideRowModelModule,
  MasterDetailModule,
]);

let gridApi: GridApi<IAccount>;

const gridOptions: GridOptions<IAccount> = {
  columnDefs: [
    // group cell renderer needed for expand / collapse icons
    { field: "name", cellRenderer: "agGroupCellRenderer" },
    { field: "account" },
    { field: "calls" },
    { field: "minutes", valueFormatter: "x.toLocaleString() + 'm'" },
  ],
  defaultColDef: {
    flex: 1,
  },
  masterDetail: true,
  detailRowAutoHeight: true,
  detailCellRendererParams: {
    detailGridOptions: {
      columnDefs: [
        { field: "callId" },
        { field: "direction" },
        { field: "number", minWidth: 150 },
        { field: "duration", valueFormatter: "x.toLocaleString() + 's'" },
        { field: "switchCode", minWidth: 150 },
      ],
      defaultColDef: {
        flex: 1,
      },
    },
    getDetailRowData: (params) => {
      params.successCallback(params.data.callRecords);
    },
  } as IDetailCellRendererParams<IAccount, ICallRecord>,
  onFirstDataRendered: onFirstDataRendered,

  detailCellRenderer: DetailCellRenderer,
};

function onFirstDataRendered(params: FirstDataRenderedEvent) {
  // arbitrarily expand a row for presentational purposes
  setTimeout(() => {
    params.api.getDisplayedRowAtIndex(1)!.setExpanded(true);
  }, 0);
}

const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);

fetch("https://www.ag-grid.com/example-assets/master-detail-data.json")
  .then((response) => response.json())
  .then((data: IAccount[]) => {
    gridApi!.setGridOption("rowData", data);
  });
```

[Live example: Auto Height with Custom Detail](https://www.ag-grid.com/examples/master-detail-height/custom-detail-auto-height/typescript)

## Dynamic Height

Use the callback `getRowHeight(params)` to set height for each row individually. This is a specific use of the callback that is explained in more detail in [Get Row Height](https://www.ag-grid.com/javascript-data-grid/row-height/#getrowheight-callback)

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `getRowHeight` | `GetRowHeight` |  |  | Callback version of property `rowHeight` to set height for each row individually. Function should return a positive number of pixels, or return `null`/`undefined` to use the default row height. |

Note that this callback gets called for **all rows** in the Master Grid, not just rows containing Detail Grids. If you do not want to set row heights explicitly for other rows simply return `undefined / null` and the grid will ignore the result for that particular row.

```js
const gridOptions = {
    // dynamically assigning detail row height
    getRowHeight: params => {
        const isDetailRow = params.node.detail;
        // for all rows that are not detail rows, return nothing
        if (!isDetailRow) { return undefined; }

        // otherwise return height based on number of rows in detail grid
        const detailPanelHeight = params.data.children.length * 50;
        return detailPanelHeight;
    },

    // other grid options ...
}
```

The following example demonstrates dynamic detail row heights:

#### Dynamic Detail Row Height

```ts
import {
  ClientSideRowModelModule,
  FirstDataRenderedEvent,
  GridApi,
  GridOptions,
  IDetailCellRendererParams,
  ModuleRegistry,
  RenderApiModule,
  RowApiModule,
  RowHeightParams,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import {
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  MasterDetailModule,
} from "ag-grid-enterprise";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  RenderApiModule,
  RowApiModule,
  ClientSideRowModelModule,
  ColumnsToolPanelModule,
  MasterDetailModule,
  ColumnMenuModule,
  ContextMenuModule,
]);

let gridApi: GridApi;

const gridOptions: GridOptions = {
  columnDefs: [
    // group cell renderer needed for expand / collapse icons
    { field: "name", cellRenderer: "agGroupCellRenderer" },
    { field: "account" },
    { field: "calls" },
    { field: "minutes", valueFormatter: "x.toLocaleString() + 'm'" },
  ],
  defaultColDef: {
    flex: 1,
  },
  masterDetail: true,
  detailCellRendererParams: {
    detailGridOptions: {
      columnDefs: [
        { field: "callId" },
        { field: "direction" },
        { field: "number" },
        { field: "duration", valueFormatter: "x.toLocaleString() + 's'" },
        { field: "switchCode" },
      ],
      defaultColDef: {
        flex: 1,
      },
      onGridReady: (params) => {
        // using auto height to fit the height of the the detail grid
        params.api.setGridOption("domLayout", "autoHeight");
      },
    },
    getDetailRowData: (params) => {
      params.successCallback(params.data.callRecords);
    },
  } as IDetailCellRendererParams<IAccount, ICallRecord>,
  getRowHeight: (params: RowHeightParams) => {
    if (params.node && params.node.detail) {
      const offset = 80;
      const allDetailRowHeight =
        params.data.callRecords.length *
        params.api.getSizesForCurrentTheme().rowHeight;
      const gridSizes = params.api.getSizesForCurrentTheme();
      return (
        allDetailRowHeight +
        ((gridSizes && gridSizes.headerHeight) || 0) +
        offset
      );
    }
  },
  alwaysShowVerticalScroll: true,
  onFirstDataRendered: onFirstDataRendered,
};

function onFirstDataRendered(params: FirstDataRenderedEvent) {
  // arbitrarily expand a row for presentational purposes
  setTimeout(() => {
    params.api.getDisplayedRowAtIndex(1)!.setExpanded(true);
  }, 0);
}

const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);

fetch(
  "https://www.ag-grid.com/example-assets/master-detail-dynamic-row-height-data.json",
)
  .then((response) => response.json())
  .then(function (data) {
    gridApi!.setGridOption("rowData", data);
  });
```

[Live example: Dynamic Detail Row Height](https://www.ag-grid.com/examples/master-detail-height/dynamic-detail-row-height/typescript)
