DEV Community

Cover image for React.js ~Testing Accesibility of Fragment Refs~
Ogasawara Kakeru
Ogasawara Kakeru

Posted on

React.js ~Testing Accesibility of Fragment Refs~

Testing Accessibility with Fragment Refs

In my previous post, I introduced Fragment Refs. A reader pointed out that removing wrapper elements is nice, but accessibility needs careful testing. So let's actually test it.

1. What I want to check

Fragment Refs let you control a group of sibling DOM nodes without a wrapper element. Because the DOM structure stays flat, I want to check two things:

  • Does focus() move focus to the element I expected?
  • Does the tab order stay natural?

I did not test with a screen reader in this post.

2. Test setup

import { Fragment, useRef } from 'react';

function Sections({ posts }) {
  const fragmentRef = useRef(null);

  return (
    <>
      <button onClick={() => fragmentRef.current.focus()}>
        Focus the first heading
      </button>

      <Fragment ref={fragmentRef}>
        {posts.map((post) => (
          <h2 key={post.id} tabIndex={0}>
            {post.title}
          </h2>
        ))}
      </Fragment>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

I tested with React 19.3.0-canary-17eca7b0-20261006 in jsdom.

3. Results

Check Result
focus() target The first <h2> (focusLast() moves focus to the last one)
Tab order Natural: button → h2 → h2 → h2, following DOM order

The DOM stays flat (<button>…</button><h2>…</h2><h2>…</h2>…), so no wrapper element appears in the tab order.

4. Pitfalls I found

  • Elements without tabIndex can't receive focus, so focus() skips them. If no child is focusable, nothing happens, and no error or warning is shown.
  • When the fragment has zero children, focus() does nothing and doesn't throw.
  • tabIndex={0} on a heading adds it to the tab order even though it isn't interactive. If you only need programmatic focus, use tabIndex={-1}: focus() still works, and the headings drop out of the tab order.

5. Conclusion

Fragment Refs keep the DOM flat, and in my tests focus() moved focus to the first focusable sibling and the tab order followed DOM order. The main risk is silent failure: if no child is focusable, focus() does nothing and reports nothing. So when the siblings are interactive, check that at least one child can actually receive focus. Screen reader behavior is not covered here and needs separate testing.

Top comments (0)