aria-flowto
Identifies the next element (or elements) in an alternate reading order of content, overriding the default DOM order.
Overview
The aria-flowto attribute identifies the next element(s) in an alternate reading order. It allows authors to define a reading sequence that differs from the DOM order, which is useful for complex visual layouts.
The value is a space-separated list of ID references. When multiple IDs are provided, assistive technologies may offer users a choice of which element to navigate to next.
Note: Browser and AT support for aria-flowto varies. Always ensure your content is accessible via the default DOM order as a fallback.
Visual Demo: Custom Reading Flow
Newspaper-Style Layout
The DOM order is: 1 → 2 → 3 → 4, but aria-flowto creates: 1 → 3 (continue column 1) → 2 → 4
Breaking news begins here in the top of column one. The story continues below in the same column...
...the breaking news story concludes here. After finishing, the reader moves to the sports section.
Sports coverage starts here. In the logical reading order, this comes after the main story.
Sports section concludes here. This is the end of the logical reading flow.
aria-flowto creates a logical reading path: 1 → 3 → 2 → 4. Screen readers that support this attribute allow users to follow the intended narrative flow rather than the visual column order.
Code Examples
Basic Usage
<!-- aria-flowto defines alternate reading order -->
<!-- Multi-column layout with custom flow -->
<article>
<section id="intro" aria-flowto="sidebar-note">
<h2>Introduction</h2>
<p>This section flows to the sidebar note...</p>
</section>
<aside id="sidebar-note" aria-flowto="main-content">
<p>Important sidebar note that should be read
between intro and main content.</p>
</aside>
<section id="main-content">
<h2>Main Content</h2>
<p>After the sidebar, continue here...</p>
</section>
</article>
<!-- Allows AT users to follow a logical flow that
differs from DOM order -->Multiple Flow Targets
<!-- Multiple flow targets -->
<!-- Reader can choose which path to follow -->
<div id="decision-point" aria-flowto="option-a option-b option-c">
<h2>Choose Your Path</h2>
<p>From here, you can go to any of three sections.</p>
</div>
<section id="option-a" aria-flowto="conclusion">
<h3>Path A</h3>
<p>Content for path A...</p>
</section>
<section id="option-b" aria-flowto="conclusion">
<h3>Path B</h3>
<p>Content for path B...</p>
</section>
<section id="option-c" aria-flowto="conclusion">
<h3>Path C</h3>
<p>Content for path C...</p>
</section>
<section id="conclusion">
<h3>Conclusion</h3>
<p>All paths lead here.</p>
</section>
<!-- Space-separated IDs offer multiple next elements -->Newspaper Layout
<!-- Newspaper-style column layout -->
<div class="newspaper-layout">
<div id="col1-top" aria-flowto="col1-bottom">
<h2>Breaking News</h2>
<p>First part of the article starts here in
the top of column 1...</p>
</div>
<div id="col2-top" aria-flowto="col2-bottom">
<h2>Sports</h2>
<p>Sports section begins here...</p>
</div>
<div id="col1-bottom" aria-flowto="col2-top">
<p>...article continues and concludes here.</p>
</div>
<div id="col2-bottom">
<p>...sports coverage ends here.</p>
</div>
</div>
<!-- Visual layout:
[col1-top] [col2-top]
[col1-bottom] [col2-bottom]
Reading order: col1-top -> col1-bottom ->
col2-top -> col2-bottom -->React Components
// React components with aria-flowto
import { useId } from 'react';
// Multi-column article with custom flow
function NewspaperArticle({ sections }) {
const ids = sections.map(() => useId());
return (
<article className="newspaper-layout">
{sections.map((section, index) => {
const nextIndex = index + 1;
const flowTo = nextIndex < sections.length
? ids[nextIndex]
: undefined;
return (
<section
key={ids[index]}
id={ids[index]}
aria-flowto={flowTo}
>
{section.content}
</section>
);
})}
</article>
);
}
// Branching content with multiple flow options
function BranchingContent({
decisionPoint,
branches,
conclusion
}) {
const decisionId = useId();
const branchIds = branches.map(() => useId());
const conclusionId = useId();
return (
<div>
<section
id={decisionId}
aria-flowto={branchIds.join(' ')}
>
{decisionPoint}
<p>Choose a path below:</p>
<ul>
{branches.map((branch, i) => (
<li key={i}>
<a href={`#${branchIds[i]}`}>{branch.title}</a>
</li>
))}
</ul>
</section>
{branches.map((branch, i) => (
<section
key={branchIds[i]}
id={branchIds[i]}
aria-flowto={conclusionId}
>
<h2>{branch.title}</h2>
{branch.content}
</section>
))}
<section id={conclusionId}>
{conclusion}
</section>
</div>
);
}
// Interactive story with flow navigation
function InteractiveStory({ nodes }) {
const nodeIds = Object.fromEntries(
Object.keys(nodes).map(key => [key, useId()])
);
return (
<div className="story">
{Object.entries(nodes).map(([key, node]) => {
const flowTargets = node.nextNodes
?.map(n => nodeIds[n])
.join(' ');
return (
<section
key={key}
id={nodeIds[key]}
aria-flowto={flowTargets}
>
<h2>{node.title}</h2>
<p>{node.content}</p>
{node.nextNodes && (
<nav aria-label="Continue to">
{node.nextNodes.map(nextKey => (
<a
key={nextKey}
href={`#${nodeIds[nextKey]}`}
>
Go to {nodes[nextKey].title}
</a>
))}
</nav>
)}
</section>
);
})}
</div>
);
}
// Usage
function Article() {
return (
<NewspaperArticle
sections={[
{ content: <><h2>Headline</h2><p>Opening...</p></> },
{ content: <p>Continuation of the story...</p> },
{ content: <p>Conclusion of the article.</p> },
]}
/>
);
}Browser Support Considerations
Important: Support for aria-flowto varies across assistive technologies:
- • NVDA and JAWS have limited support
- • VoiceOver on macOS/iOS may not announce flow relationships
- • Some screen readers ignore this attribute entirely
Best practice: Always ensure content is logically ordered in the DOM and can be understood without aria-flowto. Use it as an enhancement, not a requirement.

