use Typeahead Provides a matching callback that can be used to focus an item as
the user types, used in tandem with useListNavigation ()
.
import { useTypeahead } from ' @floating-ui/react ' ;
This is useful for creating a menu with typeahead support, where
the user can type to focus an item and then immediately select
it, especially if it contains a large number of items.
See FloatingList
for creating composable
children API components.
Note This Hook should not be used for typeable < input />
elements.
It is intended to be used on menu buttons for a dropdown menu or select menu,
rather than a combobox that is searchable.
This Hook returns event handler props.
To use it, pass it the context
object returned from
useFloating ()
, and then feed its result into the
useInteractions ()
array. The returned prop getters are
then spread onto the elements for rendering.
useListNavigation ()
is responsible for synchronizing the
index for focus.
function App () {
const [ activeIndex , setActiveIndex ] = useState ( null ) ;
const { refs , floatingStyles , context } = useFloating ({
open : true ,
}) ;
const items = [ ' one ' , ' two ' , ' three ' ];
const listRef = useRef ( items ) ;
const typeahead = useTypeahead ( context , {
listRef ,
activeIndex ,
onMatch : setActiveIndex ,
}) ;
const { getReferenceProps , getFloatingProps , getItemProps } =
useInteractions ( [ typeahead ] ) ;
return (
<>
< div ref ={ refs . setReference } { ... getReferenceProps () }>
Reference element
</ div >
< div
ref ={ refs . setFloating }
style ={ floatingStyles }
{ ... getFloatingProps () }
>
{ items . map (( item , index ) => (
< div
key ={ item }
// Make these elements focusable using a roving tabIndex.
tabIndex ={ activeIndex === index ? 0 : - 1 }
{ ... getItemProps () }
>
{ item }
</ div >
)) }
</ div >
</>
) ;
}
interface UseTypeaheadProps {
listRef : React . MutableRefObject < Array < string | null >>;
activeIndex : number | null ;
onMatch ? ( index : number ) : void ;
enabled ?: boolean ;
resetMs ?: number ;
ignoreKeys ?: Array < string >;
selectedIndex ?: number | null ;
onTypingChange ? ( isTyping : boolean ) : void ;
findMatch ?:
| null
| ((
list : Array < string | null >,
typedString : string ,
) => string | null | undefined ) ;
}
Required
default: empty list
A ref which contains an array of strings whose indices match the
HTML elements of the list.
const listRef = useRef ( [ ' one ' , ' two ' , ' three ' ] ) ;
useTypeahead ( context , {
listRef ,
}) ;
You can derive these strings when assigning the node if the
strings are not available up front:
// Array<HTMLElement | null> for `useListNavigation`
const listItemsRef = useRef ( [] ) ;
// Array<string | null> for `useTypeahead`
const listContentRef = useRef ( [] ) ;
< li
ref ={ ( node ) => {
listItemsRef . current [ index ] = node ;
listContentRef . current [ index ] = node ?. textContent ?? null ;
} }
/>
Disabled items can be represented by null
values in the
array at the relevant index, and will be skipped.
Required
default: null
The currently active index. This specifies where the typeahead
starts.
const [ activeIndex , setActiveIndex ] = useState ( null ) ;
useTypeahead ( context , {
activeIndex ,
}) ;
default: no-op
Callback invoked with the matching index if found as the user
types.
const [ isOpen , setIsOpen ] = useState ( false ) ;
const [ activeIndex , setActiveIndex ] = useState ( null ) ;
const [ selectedIndex , setSelectedIndex ] = useState ( null ) ;
useTypeahead ( context , {
onMatch : isOpen ? setActiveIndex : setSelectedIndex ,
}) ;
default: true
Conditionally enable/disable the Hook.
useTypeahead ( context , {
enabled : false ,
}) ;
default: lowercase finder
If you’d like to implement custom finding logic (for example
fuzzy search ), you can use this callback.
useTypeahead ( context , {
findMatch : ( list , typedString ) =>
list . find (
( itemString ) =>
itemString ?. toLowerCase () . indexOf ( typedString ) === 0 ,
) ,
}) ;
default: 750
Debounce timeout which will reset the transient string as the
user types.
useTypeahead ( context , {
resetMs : 500 ,
}) ;
default: []
Optional keys to ignore.
useTypeahead ( context , {
ignoreKeys : [ ' I ' , ' G ' , ' N ' , ' O ' , ' R ' , ' E ' ],
}) ;
default: null
The currently selected index, if available.
const [ selectedIndex , setSelectedIndex ] = useState ( null ) ;
useTypeahead ( context , {
selectedIndex ,
}) ;
default: no-op
Callback invoked with the typing state as the user types.
useTypeahead ( context , {
onTypingChange ( isTyping ) {
// ...
} ,
}) ;