Loading Developer Playground

Loading ...

Skip to main content
ARIA ATTRIBUTERelationship Attributes

aria-posinset

Defines an element's number or position in the current set of listitems or treeitems when not all items are present in the DOM.

Value Type
Integer (≥1)
Used With
option, treeitem, listitem, menuitem
Paired With
aria-setsize

Overview

The aria-posinset attribute defines an element's position (1-based index) within a set. It's typically used with aria-setsize to tell assistive technologies the total number of items.

This is essential for paginated lists, virtual scrolling, and any scenario where not all items are rendered in the DOM but users need to understand their position in the complete set.

Screen readers typically announce this as "item X of Y" when users navigate to the element.

Live Demo: Paginated List

Page 1 of 10 (50 total items)
  • Search Result 11 of 50

    aria-posinset="1" aria-setsize="50"

  • Search Result 22 of 50

    aria-posinset="2" aria-setsize="50"

  • Search Result 33 of 50

    aria-posinset="3" aria-setsize="50"

  • Search Result 44 of 50

    aria-posinset="4" aria-setsize="50"

  • Search Result 55 of 50

    aria-posinset="5" aria-setsize="50"

Each item has aria-posinset indicating its position and aria-setsize indicating the total. Screen readers announce "item 1 of 50".

Code Examples

Basic Usage

<!-- aria-posinset defines position within a set -->

<!-- Paginated list showing items 6-10 of 50 -->
<ul role="listbox" aria-label="Search results">
  <li role="option" aria-posinset="6" aria-setsize="50">
    Result 6
  </li>
  <li role="option" aria-posinset="7" aria-setsize="50">
    Result 7
  </li>
  <li role="option" aria-posinset="8" aria-setsize="50">
    Result 8
  </li>
  <li role="option" aria-posinset="9" aria-setsize="50">
    Result 9
  </li>
  <li role="option" aria-posinset="10" aria-setsize="50">
    Result 10
  </li>
</ul>

<!-- Screen reader: "Result 6, 6 of 50" -->

Virtual Scrolling

<!-- Virtual scrolling list with aria-posinset -->

<div role="listbox" aria-label="All users">
  <!-- Only visible items rendered, but position maintained -->
  
  <div role="option" 
       aria-posinset="101" 
       aria-setsize="5000">
    User 101
  </div>
  
  <div role="option" 
       aria-posinset="102" 
       aria-setsize="5000">
    User 102
  </div>
  
  <div role="option" 
       aria-posinset="103" 
       aria-setsize="5000">
    User 103
  </div>
  
  <!-- ... more visible items ... -->
</div>

<!-- Enables screen reader users to understand position 
     in large datasets even when items aren't all rendered -->

Tree Items

<!-- Tree items with aria-posinset -->

<ul role="tree" aria-label="File system">
  <li role="treeitem" 
      aria-posinset="1" 
      aria-setsize="3"
      aria-expanded="true">
    Documents
    <ul role="group">
      <li role="treeitem" 
          aria-posinset="1" 
          aria-setsize="2">
        Work
      </li>
      <li role="treeitem" 
          aria-posinset="2" 
          aria-setsize="2">
        Personal
      </li>
    </ul>
  </li>
  
  <li role="treeitem" 
      aria-posinset="2" 
      aria-setsize="3">
    Downloads
  </li>
  
  <li role="treeitem" 
      aria-posinset="3" 
      aria-setsize="3">
    Pictures
  </li>
</ul>

<!-- Each level has its own position counting -->

React Components

// React components with aria-posinset
import { useState, useMemo } from 'react';

// Paginated list with position info
interface PaginatedListProps<T> {
  items: T[];
  totalItems: number;
  currentPage: number;
  pageSize: number;
  renderItem: (item: T, position: number, total: number) => React.ReactNode;
  label: string;
}

function PaginatedList<T>({
  items,
  totalItems,
  currentPage,
  pageSize,
  renderItem,
  label
}: PaginatedListProps<T>) {
  const startIndex = (currentPage - 1) * pageSize;

  return (
    <ul role="listbox" aria-label={label}>
      {items.map((item, index) => {
        const position = startIndex + index + 1;
        return (
          <li
            key={index}
            role="option"
            aria-posinset={position}
            aria-setsize={totalItems}
          >
            {renderItem(item, position, totalItems)}
          </li>
        );
      })}
    </ul>
  );
}

// Virtual list with position tracking
function VirtualList({
  items,
  totalItems,
  visibleStartIndex,
  renderItem
}) {
  return (
    <div role="listbox" aria-label="Virtual list">
      {items.map((item, index) => {
        const position = visibleStartIndex + index + 1;
        return (
          <div
            key={position}
            role="option"
            aria-posinset={position}
            aria-setsize={totalItems}
          >
            {renderItem(item, position)}
          </div>
        );
      })}
    </div>
  );
}

// Tree item component
function TreeItem({ 
  item, 
  position, 
  siblingCount, 
  children 
}) {
  const [isExpanded, setIsExpanded] = useState(false);

  return (
    <li
      role="treeitem"
      aria-posinset={position}
      aria-setsize={siblingCount}
      aria-expanded={children ? isExpanded : undefined}
    >
      <button onClick={() => setIsExpanded(!isExpanded)}>
        {item.name}
      </button>
      
      {isExpanded && children && (
        <ul role="group">
          {children}
        </ul>
      )}
    </li>
  );
}

// Usage
function SearchResults() {
  const [page, setPage] = useState(1);
  const pageSize = 10;
  const totalResults = 250;
  const results = fetchResults(page, pageSize);

  return (
    <div>
      <PaginatedList
        items={results}
        totalItems={totalResults}
        currentPage={page}
        pageSize={pageSize}
        label="Search results"
        renderItem={(item, pos, total) => (
          <span>{item.title} (item {pos} of {total})</span>
        )}
      />
      
      <nav aria-label="Pagination">
        <button 
          onClick={() => setPage(p => p - 1)}
          disabled={page === 1}
        >
          Previous
        </button>
        <button 
          onClick={() => setPage(p => p + 1)}
          disabled={page * pageSize >= totalResults}
        >
          Next
        </button>
      </nav>
    </div>
  );
}

Related Attributes

Specifications & Resources