Skip to content


Adds focus-managed indexed navigation via arrow keys to a list of items within the floating element.

import {
} from '@floating-ui/react-dom-interactions';
// ...
const {context} = useFloating();
const {getReferenceProps, getFloatingProps, getItemProps} =
    useListNavigation(context, {
      // props

Spread the getItemProps to each list item.


interface Props {
  listRef: React.MutableRefObject<Array<HTMLElement | null>>;
  activeIndex: number | null;
  onNavigate: (index: number | null) => void;
  enabled?: boolean;
  selectedIndex?: number | null;
  loop?: boolean;
  nested?: boolean;
  rtl?: boolean;
  virtual?: boolean;
  allowEscape?: boolean;
  orientation?: 'vertical' | 'horizontal' | 'both';
  focusItemOnOpen?: 'auto' | boolean;
  focusItemOnHover?: boolean;
  openOnArrowKeyDown?: boolean;
  disabledIndices?: Array<number>;


default: empty list

A ref that holds an array of list items. You can assign each item in the array by its index like so:

const options = ['one', 'two', 'three'];
const listRef = useRef([]);
return, index) => (
    ref={(node) => {
      listRef.current[index] = node;


default: null

The currently active item index, which may or may not be selected.

In a <Select />, this is the item that's currently highlighted (focused) but not selected. The user may have intent to select this item.


default: no-op

Callback invoked when the user navigates, passed in the current activeIndex.

const [activeIndex, setActiveIndex] = useState(null);
useListNavigation(context, {
  onNavigate: setActiveIndex,


default: true

Conditionally enable/disable the hook.

useListNavigation(context, {
  enabled: false,


default: null

The currently selected item index, which may or may not be active.

In a <Select />, this is the item shown in the trigger button.


default: false

Whether to restart from the beginning or end if the user has navigated to the boundary of the list.

useListNavigation(context, {
  loop: true,


default: false

If the list is nested within another one (e.g. a nested submenu), the navigation semantics change.

useListNavigation(context, {
  nested: true,


default: false

Whether the direction of the floating element's navigation is in RTL layout.

useListNavigation(context, {
  rtl: true,


default: false

Whether the focus is virtual (using aria-activedescendant).

Use this if you need focus to remain on the reference element (such as an input), but allow arrow keys to navigate list items. This is common in autocomplete listbox components.

useListNavigation(context, {
  virtual: true,

Your virtually-focused list items must have a unique id set on them.


Determines whether focus can escape the list, such that nothing is selected after navigating beyond the boundary of the list. In some autocomplete/combobox components, this may be desired, as screen readers will return to the input.

loop must be true

useListNavigation(context, {
  loop: true,
  allowEscape: true,


default: 'vertical'

The orientation in which navigation occurs.

useListNavigation(context, {
  orientation: 'horizontal',


default: 'auto'

Whether to focus the item upon opening the floating element. 'auto' infers what to do based on the input type (keyboard vs. pointer), while a boolean value will force the value.

useListNavigation(context, {
  focusItemOnOpen: true,


default: true

Whether hovering an item synchronizes the focus.


default: true

Whether pressing an arrow key on the navigation's main axis opens the floating element.

useListNavigation(context, {
  openOnArrowKeyDown: false,


default: openOnArrowKeyDown ? undefined : []

By default elements with either a disabled or aria-disabled attribute are skipped in the list navigation – however, this requires the items to be rendered.

This prop allows you to manually specify indices which should be disabled, overriding the default logic.

useListNavigation(context, {
  disabledIndices: [0, 3],