Loading Developer Playground

Loading ...

Skip to main content
ARIA ATTRIBUTERelationship Attributes

aria-flowto

Identifies the next element (or elements) in an alternate reading order of content, overriding the default DOM order.

Value Type
ID Reference (List)
Global Attribute
Yes
Common Use
Multi-column layouts, branching

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

1. Story Startflowto: 3

Breaking news begins here in the top of column one. The story continues below in the same column...

3. Story Continuesflowto: 2

...the breaking news story concludes here. After finishing, the reader moves to the sports section.

2. Sports Sectionflowto: 4

Sports coverage starts here. In the logical reading order, this comes after the main story.

4. Sports Continuesend

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.

Related Attributes

Specifications & Resources