aria-setsize
Defines the number of items in the current set of listitems or treeitems when not all items are present in the DOM.
Overview
The aria-setsize attribute defines the total number of items in the current set. It works together with aria-posinset to help assistive technologies convey position information.
When the total size is unknown (infinite scroll, lazy loading), use aria-setsize="-1" to indicate the size cannot be determined.
Each nested level in a tree structure has its own setsize, referring to the number of siblings at that level.
Live Demo: Dynamic Set Size
- Item 11 of 25
aria-setsize="25" aria-posinset="1"
- Item 22 of 25
aria-setsize="25" aria-posinset="2"
- Item 33 of 25
aria-setsize="25" aria-posinset="3"
- Item 44 of 25
aria-setsize="25" aria-posinset="4"
- Item 55 of 25
aria-setsize="25" aria-posinset="5"
(Showing first 5 items of 25)
aria-setsize tells screen readers the total number of items. When you change the value, screen readers will announce "item X of 25" accordingly.
Code Examples
Basic Usage
<!-- aria-setsize defines total items in a set -->
<!-- Showing 3 of 50 results -->
<ul role="listbox" aria-label="Search results">
<li role="option" aria-posinset="1" aria-setsize="50">
Result 1 of 50
</li>
<li role="option" aria-posinset="2" aria-setsize="50">
Result 2 of 50
</li>
<li role="option" aria-posinset="3" aria-setsize="50">
Result 3 of 50
</li>
</ul>
<!-- Screen reader announces "item 1 of 50" for first item -->Unknown Size
<!-- When total size is unknown, use aria-setsize="-1" -->
<!-- Infinite scroll / lazy loading -->
<ul role="listbox" aria-label="Feed items">
<li role="option"
aria-posinset="1"
aria-setsize="-1">
Post 1
</li>
<li role="option"
aria-posinset="2"
aria-setsize="-1">
Post 2
</li>
<li role="option"
aria-posinset="3"
aria-setsize="-1">
Post 3
</li>
<!-- More items load as user scrolls -->
</ul>
<!-- -1 indicates "size unknown" to assistive technology -->Dynamic Content
<!-- Dynamically updated set size -->
<div role="listbox" aria-label="Shopping cart items">
<!-- Items can be added/removed dynamically -->
<div role="option"
aria-posinset="1"
aria-setsize="3">
Product A - $29.99
<button aria-label="Remove Product A">Remove</button>
</div>
<div role="option"
aria-posinset="2"
aria-setsize="3">
Product B - $49.99
<button aria-label="Remove Product B">Remove</button>
</div>
<div role="option"
aria-posinset="3"
aria-setsize="3">
Product C - $19.99
<button aria-label="Remove Product C">Remove</button>
</div>
</div>
<!-- When item removed, update all aria-posinset
and aria-setsize values -->Tree Structure
<!-- aria-setsize in tree structure -->
<ul role="tree" aria-label="Project files">
<!-- Root level has 3 items -->
<li role="treeitem"
aria-posinset="1"
aria-setsize="3"
aria-expanded="true">
src/
<ul role="group">
<!-- Nested level has 4 items -->
<li role="treeitem"
aria-posinset="1"
aria-setsize="4">
index.ts
</li>
<li role="treeitem"
aria-posinset="2"
aria-setsize="4">
utils.ts
</li>
<li role="treeitem"
aria-posinset="3"
aria-setsize="4">
types.ts
</li>
<li role="treeitem"
aria-posinset="4"
aria-setsize="4">
components/
</li>
</ul>
</li>
<li role="treeitem"
aria-posinset="2"
aria-setsize="3">
package.json
</li>
<li role="treeitem"
aria-posinset="3"
aria-setsize="3">
README.md
</li>
</ul>
<!-- Each level has its own setsize -->React Components
// React components with aria-setsize
import { useState, useMemo } from 'react';
// Dynamic list with automatic setsize
function DynamicList({ items, label }) {
return (
<ul role="listbox" aria-label={label}>
{items.map((item, index) => (
<li
key={item.id}
role="option"
aria-posinset={index + 1}
aria-setsize={items.length}
>
{item.content}
</li>
))}
</ul>
);
}
// Paginated/filtered list with true total
function FilteredList({
visibleItems,
totalCount,
startIndex,
label
}) {
// totalCount might be -1 if unknown
const setSize = totalCount === -1 ? -1 : totalCount;
return (
<ul role="listbox" aria-label={label}>
{visibleItems.map((item, index) => (
<li
key={item.id}
role="option"
aria-posinset={startIndex + index + 1}
aria-setsize={setSize}
>
{item.content}
</li>
))}
</ul>
);
}
// Tree with automatic position tracking
function TreeNode({ item, position, siblingCount, children }) {
const [expanded, setExpanded] = useState(false);
const childCount = children?.length || 0;
return (
<li
role="treeitem"
aria-posinset={position}
aria-setsize={siblingCount}
aria-expanded={childCount > 0 ? expanded : undefined}
>
<button onClick={() => setExpanded(!expanded)}>
{item.name}
</button>
{expanded && childCount > 0 && (
<ul role="group">
{children.map((child, idx) => (
<TreeNode
key={child.id}
item={child}
position={idx + 1}
siblingCount={childCount}
children={child.children}
/>
))}
</ul>
)}
</li>
);
}
// Shopping cart with dynamic updates
function ShoppingCart({ items, onRemove }) {
return (
<div role="listbox" aria-label="Cart items">
{items.map((item, index) => (
<div
key={item.id}
role="option"
aria-posinset={index + 1}
aria-setsize={items.length}
>
<span>{item.name} - ${item.price}</span>
<button
onClick={() => onRemove(item.id)}
aria-label={`Remove ${item.name} from cart`}
>
Remove
</button>
</div>
))}
</div>
);
}
