For Developers

Collection API

Examples

Basic Example

The ListBox on its own isn't very useful. It registers each item with the model. The ListBox.Item only uses the useListItemRegister hook which handles registration of static items to the model. The ListBox uses useListRenderItems which handles rendering static items as well as Dynamic List example).

  • First
  • Second

Identifying Items

A list item takes an optional data-id property that will be used to identify an item. Without a data-id, the identifier will be the item's index when first registered. The basic example has a data-id attribute that is a string representation of the index. Providing a data-id will override to a value of your choosing. This identifier will be used by other hooks to identify the item for selection, maintaining a cursor, or anything else.

  • First
  • Second

Dynamic Items

The ListBox also handles a dynamic collection of items. Instead of providing each ListBox.Item statically, provide a render function instead. The function is called with an items value that is the same was what's provided to the model. By default, providing items will enable virtualization. This example adds a maxHeight to ensure overflow. Virtualization uses absolute positioning of each item, which could cause problems for popup menus. If your item count is low, pass shouldVirtualize={false} to disable virtualization.

  • Item - 1
  • Item - 2
  • Item - 3
  • Item - 4

Roving Tabindex

The list system also includes a cursor that extends the list. A cursor is mostly used for focusing items. The roving tabindex is a well-supported way to accomplish accessibility requirements for focusing items within a list. This example shows how to use useListRovingFocus. This example uses the ListBox component, but the default ListBox.Item is very basic. We have two options, we can either pass additional functionality via elemPropsHook or by creating a new item using our elemProps hook primitives. Both will be demonstrated. Creating a custom item is recommended if you create a custom component and export it. Using elemPropsHook with ListBox.Item is recommended only for one-off instances.

You can either use the tab key for focus on an item or click on an item and then use the up/down keys to navigation the list. By default, the list is set to wrap navigation using the wrappingNavigationManager. Only a single item in the list is a focus stop that "roves" as the up/down arrows are pressed.

Note: This example doesn't meet accessibility requirements. The list will have to have some type of context. Like "navigation list" or "menu list".

  • First
  • Second
  • Third

Selection

Lists support selection. useSelectionItem is applied to an item which adds an onClick that adds the item to the state.selectedIds. The default selection manager is a single select. This example uses ListBox and creates a custom SelectableItem elemProps hook and component.

Cursor ID:

Selected ID: first

Multiple Selection

The selection manager can be passed directly to the model configuration to handle different selection types. This example passes the multiSelectionManager to handle selecting multiple items.

Cursor ID:

Selected IDs: first,second

Basic Grid

A grid is a two dimensional list. A columnCount config is added to inform how to break up an array of items. A grid is very similar to a list - it receives items as a single dimension list and uses the columnCount to determine keyboard navigation. Grids only support a single orientation.

Wrapping Grid

By default, navigating a grid does not wrap around when the user reaches the end of a row or column. The grid model supports passing in a navigation manager. The collection system supports two types of navigation managers, a non-wrapping navigationManager and the wrappingNavigationManager. The following example passes the wrappingNavigationManager manager to the model. Observe how the cursor wraps around columns and rows when an edge of a column or row is encountered.

Component API

ListBox

ListBox

The ListBox component that offers vertical rendering of a collection in the form of a 2-dimension list. It supports virtualization, rendering only visible items in the DOM while also providing aria attributes to allow screen readers to still navigate virtual lists. The ListBox contains a basic ListBox.Item that renders list items that render correctly with virtualization and adds aria-setsize and aria-posinset for screen readers.

The ListBox is very basic and only adds enough functionality to render correctly. No additional behaviors are added to navigate or select. React Hooks are provided to add this functionality and are used by higher level components like Menu and Menu.Item which utilize ListBox. The ListBox contains two Box elements:

  • Outer Box: Presentational container element responsible for overflow and height. height and maxHeight props will be applied here.
  • Inner Box: The element responsible for the virtual container. Height is controlled by the model and cannot be changed by the developer. All props and ref will be spread to this element.

Layout Component

ListBox supports all props from thelayout component.

Props

Props extend from ul. Changing the as prop will change the element interface.

Props extend from . If a model is passed, props from ListModelConfig are ignored.

NameTypeDescriptionDefault
children ReactNode | ((item: any) => ReactNode)
asReact.ElementType

Optional override of the default element used by the component. Any valid tag or Component. If you provided a Component, this component should forward the ref using React.forwardRefand spread extra props to a root element.

Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care.

ul
refReact.Ref

Optional ref. If the component represents an element, this ref will be a reference to the real DOM element of the component. If as is set to an element, it will be that element. If as is a component, the reference will be to that component (or element if the component uses React.forwardRef).

model

Optional model to pass to the component. This will override the default model created for the component. This can be useful if you want to access to the state and events of the model, or if you have nested components of the same type and you need to override the model provided by React Context.

elemPropsHook(
  model: ,
  elemProps: TProps
) => HTML Attributes

Optional hook that receives the model and all props to be applied to the element. If you use this, it is your responsibility to return props, merging as appropriate. For example, returning an empty object will disable all elemProps hooks associated with this component. This allows finer control over a component without creating a new one.

useListBox

(
  model: ,
  elemProps: {},
  ref: React.Ref
) => {
  style: {
    position: 'relative';
    height:  number | undefined;
  };
}

ListBox.Item

The ListBox.Item is a simple placeholder for listbox items. The functionality of a collection item varies between components. For example, a Tabs.Item and a Menu.Item have shared functionality, but have different behavior. All collection-based components should implement a custom Item subcomponent using the collection-based behavior hooks. The Roving Tabindex example uses the elemPropsHook to provide additional functionality. elemPropsHook is provided on all compound components and is useful in the example to add additional functionality without making a new component.

Layout Component

Item supports all props from thelayout component.

Props

Props extend from li. Changing the as prop will change the element interface.

NameTypeDescriptionDefault
childrenReactNode
asReact.ElementType

Optional override of the default element used by the component. Any valid tag or Component. If you provided a Component, this component should forward the ref using React.forwardRefand spread extra props to a root element.

Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care.

li
refReact.Ref

Optional ref. If the component represents an element, this ref will be a reference to the real DOM element of the component. If as is set to an element, it will be that element. If as is a component, the reference will be to that component (or element if the component uses React.forwardRef).

model

Optional model to pass to the component. This will override the default model created for the component. This can be useful if you want to access to the state and events of the model, or if you have nested components of the same type and you need to override the model provided by React Context.

elemPropsHook(
  model: ,
  elemProps: TProps
) => HTML Attributes

Optional hook that receives the model and all props to be applied to the element. If you use this, it is your responsibility to return props, merging as appropriate. For example, returning an empty object will disable all elemProps hooks associated with this component. This allows finer control over a component without creating a new one.

useListItemRegister

This elemProps hook is the base of all item component hooks. It registers an item with a collection and sets the data-id that is used by other hooks. It should always be the last defined hook when using composeHooks (composeHooks executes hooks right to left and merges props left to right). It is used by ListBox.Item and all *.Item subcomponents.

const useMyItem = composeHooks(
useListItemSelect, // additional hooks go here
useListItemRegister // always last
);
(
  model: ,
  elemProps: {
    data-id: string;
    children: ReactNode;
    index: number;
    disabled: boolean;
    item: <any>;
    virtual: ;
  },
  ref: React.Ref
) => {
  ref: (instance:  | null) => void;
  data-id: string;
  disabled:  boolean | undefined;
  item: null;
  virtual: null;
  aria-setsize:  number | undefined;
  aria-posinset:  number | undefined;
  style: ;
}

Model

useListModel

The List model contains the the state and events necessary to track items, selection, and a cursor. Various hooks can be used for a List model to create common behaviors associated with lists, such as navigating a list with a keyboard, selection (single and multiple), and virtualization.

A list also has a "cursor". A cursor is often represented by focus, but it is not always a 1:1 mapping. Think of the cursor as the focus item within the list. If the list has browser focus, the cursor will map to browser focus. Behaviors such as useListRovingFocus will map the cursor to the active tab stop of the list. For more information, see Roving Tabindex. useListRovingFocus adds keyboard events that map to navigation events. A Navigation Manager is used to map new cursor ids to these events. The ListModel takes an optional navigation configuration to change the default navigation behavior. The default navigation manager is a wrappingNavigationManager meaning the cursor will wrap around the beginning and the ends. The cursor also provides a navigationManager that does not wrap. This is the default navigation for grids.

The cursor also adds the concept of orientation which defaults to 'vertical'. A Tab list is an example of a 'horizontal' list.

const list = useListModel({
// custom handling for selection. single and multi select are provided
selection: mySelectionManager,
// wrapping and non-wrapping navigation are provided
navigation: myNavigationManager,
items: [{ id: '1', text: 'First'}, { id: '2', text: 'Second' }],
getId: item => item.id, // get the unique identifier of your item
})
useListModel (config: ):

useGridModel

The Grid model extends the ListModel and changes some config. For example, the columnCount is required on the grid model's configuration and orientation is removed.

useGridModel (config: ):

NavigationManager

The list and grid models accept a navigation config. If one is not provided, a default will be chosen. It is possible to create a custom navigation manager to hand to the model if the default doesn't work.

NameTypeDescriptionDefault
getFirst

Get the first item in a collection. This will be called when the Home key is pressed for Lists and Ctrl+Home for Grids.

getLast

Get the last item in a collection. This will be called when the End key is pressed for Lists and Ctrl+End for Grids.

getItem

Get an item with the provided id.

getNext

Get the next item after the provided id. This will be called when the Right arrow key is pressed for RTL languages and when the Left arrow is pressed for LTR languages.

getNextRow

For Grids: Get the cell in the next row from the provided id. This will be called when the Down arrow is pressed.

getPrevious

Get the previous item before the provided id. This will be called when the Left arrow key is pressed for RTL languages and when the Right arrow is pressed for LTR languages.

getPreviousRow

For Grids: Get the cell in the previous row from the provided id. This will be called when the Up arrow is pressed.

getFirstOfRow

For Grids: Get the first item in a row. This will be called when the Home key is pressed.

getLastOfRow

For Grids: Get the last item in a row. This will be called when the End key is pressed.

getNextPage

Get the next "page". A "page" is application specific and usually means next visible screen. If the viewport is scrollable, it would scroll so that the last item visible is now the first item visible. This is called when the PageDown key is pressed

getPreviousPage

Get the next "page". A "page" is application specific and usually means previous visible screen. If the viewport is scrollable, it would scroll so that the first item visible is now the last item visible. This is called when the PageUp key is pressed

The navigationManager implements the Navigation Manager interface for lists and grids, but does not wrap when the user hits a boundary of the collection. This is the default navigation manager for grids.

wrappingNavigationManager

The wrappingNavigationManager implements the Navigation Manager interface for lists and grids, and wraps when the user hits a boundary of the collection. Grids will wrap columns, but not rows. This is the default navigation manager for lists.

Selection Manager

SelectionManager

The list and grid models accept a selection config. If one is not provided, singleSelectManager is used. You can provide a custom select manager to suite your needs. A selection manager is an object with a single select method that takes an id and previously selected ids and returns a new set of selected ids.

The collection system provides two selection managers: singleSelectManager and multiSelectionManager.

NameTypeDescriptionDefault
select(
  id: string,
  prevState: 
) => 

Sets a new Selection state based on the current ID passed and the previous state. Each selection manager can implement this differently. For example, a single select manager may unselect all other items and select only the passed in id. A multiselect manager may toggle the passed id.

Hooks

useListItemRegister

useListItemRegister

This elemProps hook is the base of all item component hooks. It registers an item with a collection and sets the data-id that is used by other hooks. It should always be the last defined hook when using composeHooks (composeHooks executes hooks right to left and merges props left to right). It is used by ListBox.Item and all *.Item subcomponents.

const useMyItem = composeHooks(
useListItemSelect, // additional hooks go here
useListItemRegister // always last
);
(
  model: ,
  elemProps: {
    data-id: string;
    children: ReactNode;
    index: number;
    disabled: boolean;
    item: <any>;
    virtual: ;
  },
  ref: React.Ref
) => {
  ref: (instance:  | null) => void;
  data-id: string;
  disabled:  boolean | undefined;
  item: null;
  virtual: null;
  aria-setsize:  number | undefined;
  aria-posinset:  number | undefined;
  style: ;
}

useListItemRovingFocus

useListItemRovingFocus

This elemProps hook is used for cursor navigation by using Roving Tabindex. Only a single item in the collection has a tab stop. Pressing an arrow key moves the tab stop to a different item in the corresponding direction. See the Roving Tabindex example. This elemProps hook should be applied to an *.Item component.

const useMyItem = composeHooks(
useListItemRovingFocus, // adds the roving tabindex support
useListItemRegister
);
(
  model: ,
  elemProps: {
    data-id: string;
  },
  ref: React.Ref
) => {
  onKeyDown: (event: ) => void;
  onFocus: () => void;
  onBlur: () => void;
  onClick: () => void;
  data-focus-id: string;
  tabIndex: number;
  ref:  Ref | undefined;
}

useListItemSelect

useListItemSelect

This elemProps hook adds selection support to a *.Item subcomponent of a collection. It adds a click handler that toggles selection status according to the Selection Manager used.

const useMyItem = composeHooks(
useListItemSelect, // adds selection support to an item
useListItemRegister
);
(
  model: ,
  elemProps: {
    data-id: string;
  },
  ref: React.Ref
) => {
  onClick: (event: <>) => void;
}

useListRenderItems

useListRenderItems

This hook is meant to be used inside the render function of List style components. It is used by ListBox. This hook gives list-based components their static and dynamic APIs to handle list items. This hook should only be used if you want to implement your own List. For example, Tabs.List uses this hook, but Menu.List uses ListBox which uses this hook.

const MyList = createContainer('ul')({
modelHook: useListModel,
})((elemProps, Element, model) => {
return <Element {...elemProps}>{useListRenderItems(model, elemProps.children)}</Element>;
});

Props

NameTypeDescriptionDefault
state{
  UNSTABLE_virtual: {
    virtualItems: [];
    totalSize: number;
    scrollToOffset: (
      index: number,
      options: 
    ) => void;
    scrollToIndex: (
      index: number,
      options: 
    ) => void;
    measure: () => void;
  };
  UNSTABLE_defaultItemHeight: number;
  containerRef: ;
  id: string;
  orientation:  'horizontal' | 'vertical';
  indexRef: ;
  nonInteractiveIds: string[];
  isVirtualized: boolean;
  items: <>[];
}
events{
  registerItem: (data: {
    item: ;
    textValue: string;
  }) => void;
  unregisterItem: (data: {
    id: string;
  }) => void;
  updateItemHeight: (data: {
    value: number;
  }) => void;
}
getId(item: ) => string

useListResetCursorOnBlur

useListResetCursorOnBlur

This elemProps hook resets the cursor when the list looses focus. By default, useListItemRovingFocus will leave the last focused item as the focusable item in the list. Sometimes it is desireable to reset the cursor to the last selected item. For example, Tabs.Item uses this hook to reset the tab stop to the currently selected tab.

const useMyItem = composeHooks(
useListResetCursorOnBlur, // adds the cursor reset to selected for roving tabindex
useListItemRovingFocus,
useListItemRegister
);
(
  model: ,
  elemProps: {},
  ref: React.Ref
) => {
  onKeyDown: (event: ) => void;
  onFocus: () => void;
  onBlur: () => void;
}

useOverflowListItemMeasure

useOverflowListItemMeasure

This elemProps hook measures a list item and reports it to an OverflowListModel. This is used in overflow detection.

const useMyItem = composeHooks(
useOverflowListItemMeasure,
useListRegisterItem,
)
(
  model: ,
  elemProps: {
    data-id: string;
  },
  ref: React.Ref
) => {
  ref: (instance:  | null) => void;
  aria-hidden:  true | undefined;
  style: {};
}

useOverflowListMeasure

useOverflowListMeasure

This elemProps hook measures a list and reports it to an OverflowListModel. This is used in overflow detection.

(
  model: ,
  elemProps: {},
  ref: React.Ref
) => {
  ref: (instance:  | null) => void;
}

useOverflowListTarget

useOverflowListTarget

This elemProps hook measures an overflow list target and reports it to an OverflowListModel. This is used in overflow detection.

(
  model: ,
  elemProps: {},
  ref: React.Ref
) => {
  ref: (instance:  | null) => void;
  aria-hidden: boolean;
  tabIndex: number;
  style: {};
}

Can't Find What You Need?

Check out our FAQ section which may help you find the information you're looking for.

FAQ Section