adev/src/content/guide/aria/grid.md
A grid enables users to navigate two-dimensional data or interactive elements using directional arrow keys, Home, End, and Page Up/Down. Grids work for data tables, calendars, spreadsheets, and layout patterns that group related interactive elements.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/grid/src/overview/basic/app/app.ts"> <docs-code header="TS" path="adev/src/content/examples/aria/grid/src/overview/basic/app/app.ts"/> <docs-code header="HTML" path="adev/src/content/examples/aria/grid/src/overview/basic/app/app.html"/> <docs-code header="CSS" path="adev/src/content/examples/aria/grid/src/overview/basic/app/app.css"/> </docs-code-multifile>Grids work well for data or interactive elements organized in rows and columns where users need keyboard navigation in multiple directions.
Use grids when:
Avoid grids when:
<table> instead)Use a grid for interactive tables where users need to navigate between cells using arrow keys. This example shows a basic data table with keyboard navigation.
<docs-tab-group> <docs-tab label="Basic"> <docs-code-multifile preview hideCode path="adev/src/content/examples/aria/grid/src/table/basic/app/app.ts"> <docs-code header="TS" path="adev/src/content/examples/aria/grid/src/table/basic/app/app.ts"/> <docs-code header="HTML" path="adev/src/content/examples/aria/grid/src/table/basic/app/app.html"/> <docs-code header="CSS" path="adev/src/content/examples/aria/grid/src/table/basic/app/app.css"/> </docs-code-multifile> </docs-tab> <docs-tab label="Retro"> <docs-code-multifile preview hideCode path="adev/src/content/examples/aria/grid/src/table/retro/app/app.ts"> <docs-code header="TS" path="adev/src/content/examples/aria/grid/src/table/retro/app/app.ts"/> <docs-code header="HTML" path="adev/src/content/examples/aria/grid/src/table/retro/app/app.html"/> <docs-code header="CSS" path="adev/src/content/examples/aria/grid/src/table/retro/app/app.css"/> </docs-code-multifile> </docs-tab> </docs-tab-group>Apply the ngGrid directive to the table element, ngGridRow to each row, and ngGridCell to each cell.
Calendars are a common use case for grids. This example shows a month view where users navigate dates using arrow keys.
<docs-tab-group> <docs-tab label="Basic"> <docs-code-multifile preview hideCode path="adev/src/content/examples/aria/grid/src/calendar/basic/app/app.ts"> <docs-code header="TS" path="adev/src/content/examples/aria/grid/src/calendar/basic/app/app.ts"/> <docs-code header="HTML" path="adev/src/content/examples/aria/grid/src/calendar/basic/app/app.html"/> <docs-code header="CSS" path="adev/src/content/examples/aria/grid/src/calendar/basic/app/app.css"/> </docs-code-multifile> </docs-tab> <docs-tab label="Material"> <docs-code-multifile preview hideCode path="adev/src/content/examples/aria/grid/src/calendar/material/app/app.ts"> <docs-code header="TS" path="adev/src/content/examples/aria/grid/src/calendar/material/app/app.ts"/> <docs-code header="HTML" path="adev/src/content/examples/aria/grid/src/calendar/material/app/app.html"/> <docs-code header="CSS" path="adev/src/content/examples/aria/grid/src/calendar/material/app/app.css"/> </docs-code-multifile> </docs-tab> <docs-tab label="Retro"> <docs-code-multifile preview hideCode path="adev/src/content/examples/aria/grid/src/calendar/retro/app/app.ts"> <docs-code header="TS" path="adev/src/content/examples/aria/grid/src/calendar/retro/app/app.ts"/> <docs-code header="HTML" path="adev/src/content/examples/aria/grid/src/calendar/retro/app/app.html"/> <docs-code header="CSS" path="adev/src/content/examples/aria/grid/src/calendar/retro/app/app.css"/> </docs-code-multifile> </docs-tab> </docs-tab-group>Users can activate a date by pressing Enter or Space when focused on a cell.
Use a layout grid to group interactive elements and reduce tab stops. This example shows a grid of pill buttons.
<docs-tab-group> <docs-tab label="Basic"> <docs-code-multifile preview hideCode path="adev/src/content/examples/aria/grid/src/pill-list/basic/app/app.ts"> <docs-code header="TS" path="adev/src/content/examples/aria/grid/src/pill-list/basic/app/app.ts"/> <docs-code header="HTML" path="adev/src/content/examples/aria/grid/src/pill-list/basic/app/app.html"/> <docs-code header="CSS" path="adev/src/content/examples/aria/grid/src/pill-list/basic/app/app.css"/> </docs-code-multifile> </docs-tab> <docs-tab label="Material"> <docs-code-multifile preview hideCode path="adev/src/content/examples/aria/grid/src/pill-list/material/app/app.ts"> <docs-code header="TS" path="adev/src/content/examples/aria/grid/src/pill-list/material/app/app.ts"/> <docs-code header="HTML" path="adev/src/content/examples/aria/grid/src/pill-list/material/app/app.html"/> <docs-code header="CSS" path="adev/src/content/examples/aria/grid/src/pill-list/material/app/app.css"/> </docs-code-multifile> </docs-tab> <docs-tab label="Retro"> <docs-code-multifile preview hideCode path="adev/src/content/examples/aria/grid/src/pill-list/retro/app/app.ts"> <docs-code header="TS" path="adev/src/content/examples/aria/grid/src/pill-list/retro/app/app.ts"/> <docs-code header="HTML" path="adev/src/content/examples/aria/grid/src/pill-list/retro/app/app.html"/> <docs-code header="CSS" path="adev/src/content/examples/aria/grid/src/pill-list/retro/app/app.css"/> </docs-code-multifile> </docs-tab> </docs-tab-group>Instead of tabbing through each button, users navigate with arrow keys and only one button receives tab focus.
Enable selection with [enableSelection]="true" and configure how focus and selection interact.
<table
ngGrid
[enableSelection]="true"
[selectionMode]="'explicit'"
[multi]="true"
[focusMode]="'roving'"
>
<tr ngGridRow>
<td ngGridCell>Cell 1</td>
<td ngGridCell>Cell 2</td>
</tr>
</table>
Selection modes:
follow: Focused cell is automatically selectedexplicit: Users select cells with Space or clickFocus modes:
roving: Focus moves to cells using tabindex (better for simple grids)activedescendant: Focus stays on grid container, aria-activedescendant indicates active cell (better for virtual scrolling)Angular Aria provides component harnesses for testing grid components. Here is an example of how to use the harnesses in a component test:
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {HarnessLoader} from '@angular/cdk/testing';
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
import {GridHarness} from '@angular/aria/grid/testing';
import {MyGridComponent} from './my-grid'; // Your component
describe('MyGridComponent', () => {
let fixture: ComponentFixture<MyGridComponent>;
let loader: HarnessLoader;
beforeEach(async () => {
TestBed.configureTestingModule({
imports: [MyGridComponent],
});
fixture = TestBed.createComponent(MyGridComponent);
await fixture.whenStable();
loader = TestbedHarnessEnvironment.loader(fixture);
});
it('should read cell values and focus cells', async () => {
const grid = await loader.getHarness(GridHarness);
// Get all cells text in a 2D array organized by rows
const cellTexts = await grid.getCellTextByIndex();
expect(cellTexts).toEqual([
['Cell 1.1', 'Cell 1.2'],
['Cell 2.1', 'Cell 2.2'],
]);
// Get a specific cell by text
const cells = await grid.getCells({text: 'Cell 1.1'});
expect(cells.length).toBe(1);
const cell = cells[0];
// Verify cell state
expect(await cell.isSelected()).toBe(true);
expect(await cell.isActive()).toBe(true);
// Focus the cell
await cell.focus();
expect(await cell.isFocused()).toBe(true);
});
});
For detailed API documentation, inspect the following API references: