Loading Developer Playground

Loading ...

Skip to main content
ARIA ATTRIBUTERelationship Attributes

aria-rowcount

Defines the total number of rows in a table, grid, or treegrid when not all rows are present in the DOM.

Value Type
Integer
Used With
table, grid, treegrid
Special Value
-1 = unknown count

Overview

The aria-rowcount attribute defines the total number of rows in a table, grid, or treegrid. It's essential for virtual scrolling implementations where only a subset of rows are rendered in the DOM.

When all rows are present, browsers calculate the count automatically. But for large datasets with thousands of rows, rendering everything hurts performance. Use aria-rowcount to tell assistive technologies the true total.

Use aria-rowcount="-1" when the total is unknown (infinite scroll, lazy loading).

Live Demo: Virtual Scrolling Table

Showing rows 2 - 6 of 100
IDNameStatusProgress
002Item 2Pending20%
003Item 3Complete30%
004Item 4In Progress40%
005Item 5Pending50%
006Item 6Complete60%

aria-rowcount="101" (100 data rows + 1 header) tells screen readers the full table size. Each row uses aria-rowindex to indicate its position.

Code Examples

Basic Usage

<!-- aria-rowcount indicates total rows when not all are in DOM -->

<!-- Table with 1000 total rows, only 10 visible -->
<table role="grid" aria-rowcount="1000" aria-label="Customer list">
  <thead>
    <tr aria-rowindex="1">
      <th>ID</th>
      <th>Name</th>
      <th>Email</th>
    </tr>
  </thead>
  <tbody>
    <!-- Showing rows 50-59 of 1000 -->
    <tr aria-rowindex="50">
      <td>050</td>
      <td>Customer 50</td>
      <td>customer50@example.com</td>
    </tr>
    <tr aria-rowindex="51">
      <td>051</td>
      <td>Customer 51</td>
      <td>customer51@example.com</td>
    </tr>
    <!-- ... more visible rows ... -->
  </tbody>
</table>

<!-- Screen reader announces: "Table with 1000 rows" -->

Virtual Scrolling

<!-- Virtual scrolling grid with many rows -->

<div 
  role="grid" 
  aria-rowcount="10000"
  aria-colcount="5"
  aria-label="Large dataset"
>
  <!-- Header row -->
  <div role="row" aria-rowindex="1">
    <div role="columnheader">ID</div>
    <div role="columnheader">Name</div>
    <div role="columnheader">Date</div>
    <div role="columnheader">Amount</div>
    <div role="columnheader">Status</div>
  </div>
  
  <!-- Only render visible rows (e.g., rows 500-510) -->
  <div role="row" aria-rowindex="500">
    <div role="gridcell">500</div>
    <div role="gridcell">Transaction 500</div>
    <div role="gridcell">2024-01-15</div>
    <div role="gridcell">$1,250.00</div>
    <div role="gridcell">Complete</div>
  </div>
  
  <div role="row" aria-rowindex="501">
    <div role="gridcell">501</div>
    <div role="gridcell">Transaction 501</div>
    <div role="gridcell">2024-01-15</div>
    <div role="gridcell">$890.00</div>
    <div role="gridcell">Pending</div>
  </div>
  
  <!-- ... rows 502-510 ... -->
</div>

Unknown Row Count

<!-- When total row count is unknown -->

<!-- Use aria-rowcount="-1" for dynamic/unknown totals -->
<table role="grid" aria-rowcount="-1" aria-label="Search results">
  <thead>
    <tr aria-rowindex="1">
      <th>Result</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <!-- Rows load as user scrolls -->
    <tr aria-rowindex="2">
      <td>Result 1</td>
      <td>Description 1</td>
    </tr>
    <tr aria-rowindex="3">
      <td>Result 2</td>
      <td>Description 2</td>
    </tr>
  </tbody>
</table>

<!-- -1 indicates "count unknown" to assistive technology -->
<!-- Use when implementing infinite scroll or lazy loading -->

React Component

// React virtual list with aria-rowcount
import { useState, useCallback, useRef, useEffect } from 'react';

interface VirtualListProps<T> {
  items: T[];
  totalItems: number;
  itemHeight: number;
  containerHeight: number;
  renderItem: (item: T, index: number) => React.ReactNode;
  label: string;
}

function VirtualList<T>({
  items,
  totalItems,
  itemHeight,
  containerHeight,
  renderItem,
  label
}: VirtualListProps<T>) {
  const [scrollTop, setScrollTop] = useState(0);
  const containerRef = useRef<HTMLDivElement>(null);
  
  const visibleCount = Math.ceil(containerHeight / itemHeight);
  const startIndex = Math.floor(scrollTop / itemHeight);
  const endIndex = Math.min(startIndex + visibleCount + 1, items.length);
  
  const visibleItems = items.slice(startIndex, endIndex);
  
  const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
    setScrollTop(e.currentTarget.scrollTop);
  }, []);

  return (
    <div
      ref={containerRef}
      role="grid"
      aria-rowcount={totalItems}
      aria-label={label}
      style={{ height: containerHeight, overflowY: 'auto' }}
      onScroll={handleScroll}
    >
      {/* Spacer for items above viewport */}
      <div style={{ height: startIndex * itemHeight }} />
      
      {visibleItems.map((item, idx) => {
        const actualIndex = startIndex + idx;
        return (
          <div
            key={actualIndex}
            role="row"
            aria-rowindex={actualIndex + 1} // 1-based
            style={{ height: itemHeight }}
          >
            {renderItem(item, actualIndex)}
          </div>
        );
      })}
      
      {/* Spacer for items below viewport */}
      <div style={{ height: (items.length - endIndex) * itemHeight }} />
    </div>
  );
}

// Usage
function DataGrid() {
  const [data, setData] = useState<string[]>([]);
  const [totalCount, setTotalCount] = useState(-1); // Unknown initially
  
  useEffect(() => {
    // Fetch total count and initial data
    fetchData().then(result => {
      setData(result.items);
      setTotalCount(result.total);
    });
  }, []);

  return (
    <VirtualList
      items={data}
      totalItems={totalCount}
      itemHeight={50}
      containerHeight={400}
      label="Data grid"
      renderItem={(item, index) => (
        <div role="gridcell">{item}</div>
      )}
    />
  );
}

Related Attributes

Specifications & Resources