npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-roving-focus

v0.0.7

Published

Flexible roving focus for React with support for any fixed or responsive layout.

Readme

react-roving-focus

Flexible roving focus (aka roving tabindex) for React with support for any fixed or responsive layout.

Storybook npm version bundlephobia MIT License

Examples

Refer to the Storybook for various layout examples:

How it works

Unlike traditional roving tabindex implementations, react-roving-focus determines navigation order using the rendered size and position of elements, rather than row or column indices. This enables keyboard navigation across any 1D or 2D layout (fixed or responsive) without any configuration.

Keyboard Support

Use the following keys to move focus between elements:

| Key | Function | | :---------------------------- | :--------------------------------------------------------- | | Tab | Move to next <RovingFocusGroup> or focusable element | | Shift + Tab | Move to previous <RovingFocusGroup> or focusable element | | (left arrow) | Move focus to element on the left | | (right arrow) | Move focus to element on the right | | (up arrow) | Move focus to element above | | (down arrow) | Move focus to element below | | Home (or fn + on Mac) | Move focus to first element in the <RovingFocusGroup> | | End (or fn + on Mac) | Move focus to last element in the <RovingFocusGroup> |

Performance

To minimize re-renders, each element maintains its own tabIndex state via the useRovingFocus() hook. The <RovingFocusGroup> updates individual tabIndex state values in response to element registration, unregistration, focus changes, and enabled/disabled state changes.

The <RovingFocusGroup> determines the 'first' and 'last' elements during registration and unregistration. To track accessibility state changes, a MutationObserver monitors the disabled and aria-disabled attributes of all registered elements.

When an arrow key is pressed, the 'next' element is calculated using multi-level filtering and simple distance calculations for efficiency. Element positions are intentionally not cached since layout changes (particularly those driven by state) may not be automatically detectable.

Performance has been stress tested using various layouts with 10,000 focusable elements.

Installation

npm install react-roving-focus
# or
pnpm add react-roving-focus
# or
bun add react-roving-focus

Usage

Wrap a group of focusable elements in a <RovingFocusGroup> and use the useRovingFocus() hook to control the tabIndex of each element.

import { RovingFocusGroup, useRovingFocus } from 'react-roving-focus';

function ExampleGroup() {
  return (
    <RovingFocusGroup>
      <div>
        <ExampleItem>1</ExampleItem>
        <ExampleItem>2</ExampleItem>
        <ExampleItem>3</ExampleItem>
      </div>
    </RovingFocusGroup>
  );
}

function ExampleItem({ children }: { children: React.ReactNode }) {
  const { ref, tabIndex } = useRovingFocus<HTMLButtonElement>();

  return (
    <button ref={ref} tabIndex={tabIndex}>
      {children}
    </button>
  );
}

With an existing ref

If a focusable element has an existing ref, provide it to the useRovingFocus() hook.

import { useRef } from 'react';
import { useRovingFocus } from 'react-roving-focus';

function ExampleItem({ children }: { children: React.ReactNode }) {
  const buttonRef = useRef<HTMLButtonElement>(null);
  const { tabIndex } = useRovingFocus({ ref: buttonRef });

  return (
    <button ref={buttonRef} tabIndex={tabIndex}>
      {children}
    </button>
  );
}

With disabled elements

To disable a focusable element, set the disabled or aria-disabled attribute on the focusable element.

import { useRovingFocus } from 'react-roving-focus';

function ExampleItem({ children }: { children: React.ReactNode }) {
  const { ref, tabIndex } = useRovingFocus<HTMLButtonElement>();

  return (
    <button ref={ref} tabIndex={tabIndex} disabled>
      {children}
    </button>
  );
}

Alternatively, set disabled: true in the useRovingFocus() hook.

import { useRovingFocus } from 'react-roving-focus';

function ExampleItem({ children }: { children: React.ReactNode }) {
  const { ref, tabIndex } = useRovingFocus<HTMLButtonElement>({
    disabled: true,
  });

  return (
    <button ref={ref} tabIndex={tabIndex}>
      {children}
    </button>
  );
}