---
title: "Full Width Rows"
framework: javascript
version: "36.1.0"
---

# Full Width Rows

Under normal operation, AG Grid will render each row as a horizontal list of cells. Each cell in the row will correspond to one column definition. It is possible to switch this off and allow you to provide one component to span the entire width of the grid and not use columns. This is useful if you want to embed a complex component inside the grid instead of rendering a list of cells. This technique can be used for displaying panels of information.

> **Note**
>
> See [Master / Detail](https://www.ag-grid.com/javascript-data-grid/master-detail/) to include full width rows as a child of another row.

## Example of Full Width Rows

Below shows an example using full width. The following can be noted:

- The rows for countries France, Italy and Peru have full width components instead of cells.
- Sorting and filtering all work as if the data was displayed as normal.

#### Simple Full Width

```ts
import {
  ClientSideRowModelModule,
  GridApi,
  GridOptions,
  ICellRendererComp,
  ICellRendererParams,
  IsFullWidthRowParams,
  ModuleRegistry,
  RowHeightParams,
  TextFilterModule,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import { getData } from "./data";
import { FullWidthCellRenderer } from "./fullWidthCellRenderer";

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

ModuleRegistry.registerModules([TextFilterModule, ClientSideRowModelModule]);

class CountryCellRenderer implements ICellRendererComp {
  eGui!: HTMLElement;

  init(params: ICellRendererParams) {
    const flag = `<img border="0" width="15" height="10" src="https://www.ag-grid.com/example-assets/flags/${params.data.code}.png">`;

    const eTemp = document.createElement("div");
    eTemp.innerHTML = `<span style="cursor: default;">${flag} ${params.value}</span>`;
    this.eGui = eTemp.firstElementChild as HTMLElement;
  }

  getGui() {
    return this.eGui;
  }

  refresh(params: ICellRendererParams): boolean {
    return false;
  }
}

let gridApi: GridApi;

const gridOptions: GridOptions = {
  columnDefs: [
    { field: "name", cellRenderer: CountryCellRenderer },
    { field: "continent" },
    { field: "language" },
  ],
  defaultColDef: {
    flex: 1,
    filter: true,
  },
  rowData: getData(),
  getRowHeight: (params: RowHeightParams) => {
    // return 100px height for full width rows
    if (isFullWidth(params.data)) {
      return 100;
    }
  },
  isFullWidthRow: (params: IsFullWidthRowParams) => {
    return isFullWidth(params.rowNode.data);
  },
  // see AG Grid docs cellRenderer for details on how to build cellRenderers
  fullWidthCellRenderer: FullWidthCellRenderer,
};

function isFullWidth(data: any) {
  // return true when country is Peru, France or Italy
  return ["Peru", "France", "Italy"].indexOf(data.name) >= 0;
}

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

[Live example: Simple Full Width](https://www.ag-grid.com/examples/full-width-rows/simple-full-width/typescript)

## Understanding Full Width

A `fullWidth` (full width) component takes up the entire width of the grid. A full width component:

- is not impacted by horizontal scrolling.
- is the width of the grid, regardless of what columns are present.
- is not impacted by pinned sections of the grid, will span left and right pinned areas regardless.
- does not participate in the navigation, [Cell Selection](https://www.ag-grid.com/javascript-data-grid/cell-selection/) (AG Grid Enterprise) or [Context Menu](https://www.ag-grid.com/javascript-data-grid/context-menu/) (AG Grid Enterprise) of the main grid.

To use `fullWidth`, you must:

1. Implement the `isFullWidthRow(params)` callback, to tell the grid which rows should be treated as `fullWidth`.
2. Provide a `fullWidthCellRenderer`, to tell the grid what `cellRenderer` to use when doing `fullWidth` rendering.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `fullWidthCellRenderer` | `any` |  |  | Provide your own cell renderer component to use for full width rows. |

The cell renderer can be any AG Grid cell renderer. Refer to [Cell Rendering](https://www.ag-grid.com/javascript-data-grid/component-cell-renderer/) on how to build cell renderers. The cell renderer for `fullWidth` has one difference to normal cell renderers: the parameters passed are missing the value and column information as the cell renderer is not tied to a particular column. Instead you should use the `data` parameter, which represents the value for the entire row.

The `isFullWidthRow(params)` callback receives a `params` object containing the `rowNode` as its input and should return `true` to use `fullWidth` or `false` to render as normal.

## Sorting and Filtering

Sorting and Filtering are NOT impacted by full width; full width is a rendering time feature. The sorting and filtering applied to the data is done before rendering and is not impacted.

## Detailed Full Width Example

The example below demonstrates full width with pinned rows and columns. The example's data is minimalistic to focus on how full width impacts rows. For demonstration, the pinned rows are shaded blue (with full width a darker shade of blue) and unpinned full width rows are green.

The following points should be noted:

- Full width can be applied to any row, including pinned rows. The example demonstrates full width in pinned top, pinned bottom and body rows.
- Full width rows can be of any height, which is specified in the usual way using the `getRowHeight(params)` callback. The example sets body `fullWidth` rows to 75px.
- The pinned full width rows are not impacted by either vertical or horizontal scrolling.
- The unpinned full width rows are impacted by vertical scrolling only, and not horizontal scrolling.
- The full width rows span the entire grid, including the pinned left and pinned right sections.
- The full width rows are the width of the grid, despite the grid requiring horizontal scrolling to show the cells.
- The example is showing a flat list of data. There is no grouping or parent / child relationships between the full width and normal rows.
- The buttons log to the developer console.

#### Basic Full Width

```ts
import {
  ClientSideRowModelModule,
  ColDef,
  ColumnApiModule,
  GridApi,
  GridOptions,
  IsFullWidthRowParams,
  ModuleRegistry,
  PinnedRowModule,
  RowHeightParams,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import { FullWidthCellRenderer } from "./fullWidthCellRenderer";

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

ModuleRegistry.registerModules([
  ColumnApiModule,
  PinnedRowModule,
  ClientSideRowModelModule,
]);

const rowData = createData(100, "body");

function getColumnDefs() {
  const columnDefs: ColDef[] = [];
  alphabet().forEach((letter) => {
    const colDef: ColDef = {
      headerName: letter,
      field: letter,
      width: 150,
    };
    if (letter === "A") {
      colDef.pinned = "left";
    }
    if (letter === "Z") {
      colDef.pinned = "right";
    }
    columnDefs.push(colDef);
  });
  return columnDefs;
}

let gridApi: GridApi;

const gridOptions: GridOptions = {
  columnDefs: getColumnDefs(),
  rowData,
  enableRowPinning: true,
  isRowPinned: (node) => {
    if ([51, 52, 53].includes(node.rowIndex!)) {
      return "top";
    }

    if ([96, 97, 98].includes(node.rowIndex!)) {
      return "bottom";
    }
    return null;
  },
  isFullWidthRow: (params: IsFullWidthRowParams) => {
    // in this example, we check the fullWidth attribute that we set
    // while creating the data. what check you do to decide if you
    // want a row full width is up to you, as long as you return a boolean
    // for this method.
    return params.rowNode.data.fullWidth;
  },
  // see AG Grid docs cellRenderer for details on how to build cellRenderers
  // this is a simple function cellRenderer, returns plain HTML, not a component
  fullWidthCellRenderer: FullWidthCellRenderer,
  getRowHeight: (params: RowHeightParams) => {
    // you can have normal rows and full width rows any height that you want
    const isBodyRow = params.node.rowPinned === undefined;
    const isFullWidth = params.node.data.fullWidth;
    if (isBodyRow && isFullWidth) {
      return 75;
    }
  },
};

function alphabet() {
  return "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
}

function createData(count: number, prefix: string) {
  const rowData = [];
  for (let i = 0; i < count; i++) {
    const item: any = {};
    // mark every third row as full width. how you mark the row is up to you,
    // in this example the example code (not the grid code) looks at the
    // fullWidth attribute in the isFullWidthRow() callback. how you determine
    // if a row is full width or not is totally up to you.
    item.fullWidth = i % 3 === 2;
    // put in a column for each letter of the alphabet
    alphabet().forEach((letter) => {
      item[letter] = prefix + " (" + letter + "," + i + ")";
    });
    rowData.push(item);
  }
  return rowData;
}

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

[Live example: Basic Full Width](https://www.ag-grid.com/examples/full-width-rows/basic-full-width/typescript)

## Embedded Full Width Rows

By default, Full Width Rows remain in place while the grid is scrolled horizontally. However, this may be undesirable for some applications which need to horizontally scroll the full-width rows together with the rest of the rows.

In order to have Full Width Rows scroll like normal rows, set `embedFullWidthRows=true` in the gridOptions.

The example below demonstrates the behaviour when Full Width Rows are embedded in the same container as regular rows. Note the following:

- A different instance of the Full Width Cell Renderer is created for each one of the following sections: **Pinned Left**, **Pinned Right**, **Non Pinned**.
- Full Width Rows in the **non pinned** section take the whole width of the section and scroll horizontally.
- Full Width Rows in the **pinned** sections take the whole width of the section.
- The renderer can hide a pinned section by returning `null` from `getGui()`. When a pinned section is hidden, the **non pinned** section expands to fill the available space.
- In the example below, the **left pinned** section is hidden for every 4th full-width row, and the **right pinned** section is hidden for every 2nd full-width row.
- The buttons log to the developer console.

#### Embedded Full Width Rows

```ts
import {
  ClientSideRowModelModule,
  ColDef,
  ColumnApiModule,
  GridApi,
  GridOptions,
  IsFullWidthRowParams,
  ModuleRegistry,
  RowHeightParams,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import { FullWidthCellRenderer } from "./fullWidthCellRenderer";

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

ModuleRegistry.registerModules([ColumnApiModule, ClientSideRowModelModule]);

const rowData = createData(100, "body");

function getColumnDefs() {
  const columnDefs: ColDef[] = [];
  alphabet().forEach((letter) => {
    const colDef: ColDef = {
      headerName: letter,
      field: letter,
      width: 100,
    };
    if (letter === "A" || letter === "B") {
      colDef.pinned = "left";
    }
    if (letter === "Z" || letter === "Y") {
      colDef.pinned = "right";
    }
    columnDefs.push(colDef);
  });
  return columnDefs;
}

let gridApi: GridApi;

const gridOptions: GridOptions = {
  columnDefs: getColumnDefs(),
  rowData: rowData,
  embedFullWidthRows: true,
  isFullWidthRow: (params: IsFullWidthRowParams) => {
    // in this example, we check the fullWidth attribute that we set
    // while creating the data. what check you do to decide if you
    // want a row full width is up to you, as long as you return a boolean
    // for this method.
    return params.rowNode.data.fullWidth;
  },
  // see AG Grid docs cellRenderer for details on how to build cellRenderers
  // this is a simple function cellRenderer, returns plain HTML, not a component
  fullWidthCellRenderer: FullWidthCellRenderer,
  getRowHeight: (params: RowHeightParams) => {
    // you can have normal rows and full width rows any height that you want
    const isBodyRow = params.node.rowPinned === undefined;
    const isFullWidth = params.node.data.fullWidth;
    if (isBodyRow && isFullWidth) {
      return 75;
    }
  },
};

function alphabet() {
  return "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
}

function createData(count: number, prefix: string) {
  const rowData = [];
  for (let i = 0; i < count; i++) {
    const item: any = {};
    // mark every third row as full width. how you mark the row is up to you,
    // in this example the example code (not the grid code) looks at the
    // fullWidth attribute in the isFullWidthRow() callback. how you determine
    // if a row is full width or not is totally up to you.
    item.fullWidth = i % 3 === 2;
    // put in a column for each letter of the alphabet
    alphabet().forEach((letter) => {
      item[letter] = prefix + " (" + letter + "," + i + ")";
    });
    rowData.push(item);
  }
  return rowData;
}

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

[Live example: Embedded Full Width Rows](https://www.ag-grid.com/examples/full-width-rows/embedded-full-width/typescript)

## Full Width Keyboard Navigation

When using full width rows, the full width cell renderer is responsible for implementing support for keyboard navigation among its focusable elements. This is why by default, focusing a grid cell with a full width cell renderer will focus the entire cell instead of any of the elements inside the full width cell renderer.

Adding support for keyboard navigation and focus requires a custom `suppressKeyboardEvent` function in grid options. See [Suppress Keyboard Events](https://www.ag-grid.com/javascript-data-grid/keyboard-navigation/#suppress-keyboard-events).

An example of this is shown below, enabling keyboard navigation through the full width cell elements when pressing `⇥ Tab` and `⇧ Shift`+`⇥ Tab`:

- Click on the `United Kingdom` row, press the `⇥ Tab` a few times and notice that the full width `France` row can be tabbed into, along with the button, link and textbox. At the end of the cell elements, the tab focus moves to the next cell in the next row
- Use `⇧ Shift`+`⇥ Tab` to navigate in the reverse direction

The `suppressKeyboardEvent` callback is used to capture tab events and determine if the user is tabbing forward or backwards. It also suppresses the default behaviour of moving to the next cell if tabbing within the child elements.

If the focus is at the beginning or the end of the cell children and moving out of the cell, the keyboard event is not suppressed, so focus can move between the children elements. Also, when moving backwards, the focus needs to be manually set while preventing the default behaviour of the keyboard press event.

#### Full Width Keyboard Navigation

```ts
import {
  ClientSideRowModelModule,
  GridApi,
  GridOptions,
  IsFullWidthRowParams,
  ModuleRegistry,
  SuppressKeyboardEventParams,
  TextFilterModule,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";
import { getData } from "./data";
import { FullWidthCellRenderer } from "./fullWidthCellRenderer";

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

ModuleRegistry.registerModules([TextFilterModule, ClientSideRowModelModule]);

const GRID_CELL_CLASSNAME = "ag-full-width-row";

function getAllFocusableElementsOf(el: HTMLElement) {
  return Array.from<HTMLElement>(
    el.querySelectorAll(
      'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
    ),
  ).filter((focusableEl) => {
    return focusableEl.tabIndex !== -1;
  });
}

function getEventPath(event: Event): HTMLElement[] {
  const path: HTMLElement[] = [];
  let currentTarget: any = event.target;

  while (currentTarget) {
    path.push(currentTarget);
    currentTarget = currentTarget.parentElement;
  }

  return path;
}

/**
 * Capture whether the user is tabbing forwards or backwards and suppress keyboard event if tabbing
 * outside of the children
 */
function suppressKeyboardEvent({ event }: SuppressKeyboardEventParams<any>) {
  const { key, shiftKey } = event;
  const path = getEventPath(event);
  const isTabForward = key === "Tab" && shiftKey === false;
  const isTabBackward = key === "Tab" && shiftKey === true;

  let suppressEvent = false;

  // Handle cell children tabbing
  if (isTabForward || isTabBackward) {
    const eGridCell = path.find((el) => {
      if (el.classList === undefined) return false;
      return el.classList.contains(GRID_CELL_CLASSNAME);
    });

    if (!eGridCell) {
      return suppressEvent;
    }

    const focusableChildrenElements = getAllFocusableElementsOf(eGridCell);
    const lastCellChildEl =
      focusableChildrenElements[focusableChildrenElements.length - 1];
    const firstCellChildEl = focusableChildrenElements[0];

    // Suppress keyboard event if tabbing forward within the cell and the current focused element is not the last child
    if (isTabForward && focusableChildrenElements.length > 0) {
      const isLastChildFocused =
        lastCellChildEl && document.activeElement === lastCellChildEl;
      if (!isLastChildFocused) {
        suppressEvent = true;
      }
    }
    // Suppress keyboard event if tabbing backwards within the cell, and the current focused element is not the first child
    else if (isTabBackward && focusableChildrenElements.length > 0) {
      const cellHasFocusedChildren =
        eGridCell.contains(document.activeElement) &&
        eGridCell !== document.activeElement;

      // Manually set focus to the last child element if cell doesn't have focused children
      if (!cellHasFocusedChildren) {
        lastCellChildEl.focus();
        // Cancel keyboard press, so that it doesn't focus on the last child and then pass through the keyboard press to
        // move to the 2nd last child element
        event.preventDefault();
      }

      const isFirstChildFocused =
        firstCellChildEl && document.activeElement === firstCellChildEl;
      if (!isFirstChildFocused) {
        suppressEvent = true;
      }
    }
  }

  return suppressEvent;
}

let gridApi: GridApi;

const gridOptions: GridOptions = {
  columnDefs: [
    { field: "name" },
    { field: "continent" },
    { field: "language" },
  ],
  defaultColDef: {
    flex: 1,
    filter: true,
    suppressKeyboardEvent,
  },
  rowData: getData(),
  isFullWidthRow: (params: IsFullWidthRowParams) => {
    return isFullWidth(params.rowNode.data);
  },
  // see AG Grid docs cellRenderer for details on how to build cellRenderers
  fullWidthCellRenderer: FullWidthCellRenderer,
};

function isFullWidth(data: any) {
  // return true when country is Peru, France or Italy
  return ["Peru", "France", "Italy"].includes(data.name);
}

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

[Live example: Full Width Keyboard Navigation](https://www.ag-grid.com/examples/full-width-rows/full-width-keyboard-navigation/typescript)
