ARIA ATTRIBUTE•Relationship Attributes
aria-rowspan
Defines the number of rows spanned by a cell or gridcell within a table, grid, or treegrid.
Value Type
Integer (≥1)
Used With
cell, columnheader, rowheader
Default
1 (no spanning)
Overview
The aria-rowspan attribute defines the number of rows spanned by a cell within a table, grid, or treegrid. It's the ARIA equivalent of the HTML rowspan attribute.
Use this attribute when building custom grid components with role="grid" or role="treegrid" that have cells spanning multiple rows. For native HTML tables, use the rowspan attribute instead.
The value must be a positive integer. A value of 1 (the default) means the cell occupies a single row.
Live Demo: Schedule with Spanning Events
Time
Event
Location
9:00 AM
Morning Workshop
aria-rowspan="2"
Room A
10:00 AM
11:00 AM
Coffee Break
Lobby
12:00 PM
Keynote Presentation
aria-rowspan="2"
Main Hall
1:00 PM
Events with aria-rowspan span multiple time slots. Screen readers announce the span, e.g., "Morning Workshop, spans 2 rows".
Code Examples
Basic Usage
<!-- aria-rowspan indicates rows spanned by a cell -->
<table role="grid" aria-rowcount="6">
<thead>
<tr aria-rowindex="1">
<th>Region</th>
<th>Q1</th>
<th>Q2</th>
</tr>
</thead>
<tbody>
<tr aria-rowindex="2">
<!-- Header spans rows 2-4 -->
<th aria-rowspan="3" scope="rowgroup">North America</th>
<td>$1.2M</td>
<td>$1.4M</td>
</tr>
<tr aria-rowindex="3">
<td>$800K</td>
<td>$950K</td>
</tr>
<tr aria-rowindex="4">
<td>$600K</td>
<td>$720K</td>
</tr>
<tr aria-rowindex="5">
<!-- Header spans rows 5-6 -->
<th aria-rowspan="2" scope="rowgroup">Europe</th>
<td>$900K</td>
<td>$1.1M</td>
</tr>
<tr aria-rowindex="6">
<td>$750K</td>
<td>$850K</td>
</tr>
</tbody>
</table>Grid with Spanning Cells
<!-- aria-rowspan in grid with spanning cells -->
<div role="grid" aria-rowcount="5" aria-label="Weekly schedule">
<div role="row" aria-rowindex="1">
<div role="columnheader">Time</div>
<div role="columnheader">Event</div>
<div role="columnheader">Room</div>
</div>
<div role="row" aria-rowindex="2">
<div role="rowheader">9:00 AM</div>
<!-- Event spans 2 rows (9:00-10:00) -->
<div role="gridcell" aria-rowspan="2">
Morning Workshop
</div>
<div role="gridcell" aria-rowspan="2">
Room A
</div>
</div>
<div role="row" aria-rowindex="3">
<div role="rowheader">10:00 AM</div>
<!-- Cells from row 2 continue here -->
</div>
<div role="row" aria-rowindex="4">
<div role="rowheader">11:00 AM</div>
<!-- Event spans 2 rows (11:00-12:00) -->
<div role="gridcell" aria-rowspan="2">
Keynote Speech
</div>
<div role="gridcell" aria-rowspan="2">
Main Hall
</div>
</div>
<div role="row" aria-rowindex="5">
<div role="rowheader">12:00 PM</div>
</div>
</div>Combined with Colspan
<!-- Combining aria-rowspan with aria-colspan -->
<div role="grid" aria-rowcount="4" aria-colcount="4">
<div role="row" aria-rowindex="1">
<div role="columnheader" aria-colindex="1">Header 1</div>
<div role="columnheader" aria-colindex="2">Header 2</div>
<div role="columnheader" aria-colindex="3">Header 3</div>
<div role="columnheader" aria-colindex="4">Header 4</div>
</div>
<div role="row" aria-rowindex="2">
<!-- Cell spans 2 rows and 2 columns -->
<div
role="gridcell"
aria-colindex="1"
aria-rowspan="2"
aria-colspan="2"
>
Large merged cell (2x2)
</div>
<div role="gridcell" aria-colindex="3">Normal cell</div>
<div role="gridcell" aria-colindex="4">Normal cell</div>
</div>
<div role="row" aria-rowindex="3">
<!-- Columns 1-2 occupied by spanning cell above -->
<div role="gridcell" aria-colindex="3">Normal cell</div>
<div role="gridcell" aria-colindex="4">Normal cell</div>
</div>
</div>React Component
// React grid component with rowspan support
import { useMemo } from 'react';
interface GridCell {
content: React.ReactNode;
rowspan?: number;
colspan?: number;
isHeader?: boolean;
}
interface GridRow {
rowIndex: number;
cells: (GridCell | null)[]; // null for cells covered by spans
}
interface AccessibleGridProps {
rows: GridRow[];
totalRows: number;
totalCols: number;
label: string;
}
function AccessibleGrid({ rows, totalRows, totalCols, label }: AccessibleGridProps) {
return (
<div
role="grid"
aria-rowcount={totalRows}
aria-colcount={totalCols}
aria-label={label}
>
{rows.map(row => (
<div key={row.rowIndex} role="row" aria-rowindex={row.rowIndex}>
{row.cells.map((cell, colIdx) => {
if (!cell) return null; // Cell covered by span
const cellRole = cell.isHeader ? 'rowheader' : 'gridcell';
return (
<div
key={colIdx}
role={cellRole}
aria-colindex={colIdx + 1}
aria-rowspan={cell.rowspan}
aria-colspan={cell.colspan}
>
{cell.content}
</div>
);
})}
</div>
))}
</div>
);
}
// Usage: Schedule with spanning events
function ScheduleGrid() {
const rows: GridRow[] = [
{
rowIndex: 1,
cells: [
{ content: 'Time', isHeader: true },
{ content: 'Event', isHeader: true },
{ content: 'Location', isHeader: true },
]
},
{
rowIndex: 2,
cells: [
{ content: '9:00 AM', isHeader: true },
{ content: 'Workshop (2 hrs)', rowspan: 2 },
{ content: 'Room A', rowspan: 2 },
]
},
{
rowIndex: 3,
cells: [
{ content: '10:00 AM', isHeader: true },
null, // Covered by rowspan
null, // Covered by rowspan
]
},
{
rowIndex: 4,
cells: [
{ content: '11:00 AM', isHeader: true },
{ content: 'Lunch Break' },
{ content: 'Cafeteria' },
]
},
];
return (
<AccessibleGrid
rows={rows}
totalRows={4}
totalCols={3}
label="Conference schedule"
/>
);
}
