React
This package provides React bindings for @floating-ui/dom
, a
library that provides anchor positioning, and also interaction
primitives to build floating UI components.
This allows you to create components such as tooltips, popovers, dropdown menus, hover cards, modal dialogs, select menus, comboboxes, and more.
Library goals
The goal of this library is to provide building blocks to create your own floating UI components, handling difficult parts like accessibility and positioning for you, but not offering pre-built components. In theory, this allows you to build any type of component you desire, since you can add your own custom logic on top.
If you’re looking for flexibility and are comfortable building your own components, this library is hopefully what you’re looking for. If instead, you’re looking for something simpler and ready-made, you will likely find other libraries better suited for your use case. React component libraries that use Floating UI behind the scenes for positioning include Radix UI, Mantine, Ariakit, among others.
Install
The following package provides all modules:
npm install @floating-ui/react
npm install @floating-ui/react
If you only want positioning, without any interactions necessary, the following package is smaller in size. The above is a superset and uses this one as a dependency.
npm install @floating-ui/react-dom
npm install @floating-ui/react-dom
This documentation refers to the latest version of the library. If you find a feature is missing locally, make sure you have upgraded to the latest version:
Usage
There are two main parts to creating floating elements:
- Positioning — available for both packages
- Interactions — available for
@floating-ui/react
Both of these sections should be read in order before reading the
API docs for the hooks and components in the left navigation bar
(useHover
, useFocus
, etc.)
Positioning
useFloating()
useFloating()
is the main hook of each package.
import {useFloating} from '@floating-ui/react';
function App() {
const {x, y, strategy, refs} = useFloating();
return (
<>
<button ref={refs.setReference}>Button</button>
<div
ref={refs.setFloating}
style={{
position: strategy,
top: y ?? 0,
left: x ?? 0,
width: 'max-content',
}}
>
Tooltip
</div>
</>
);
}
import {useFloating} from '@floating-ui/react';
function App() {
const {x, y, strategy, refs} = useFloating();
return (
<>
<button ref={refs.setReference}>Button</button>
<div
ref={refs.setFloating}
style={{
position: strategy,
top: y ?? 0,
left: x ?? 0,
width: 'max-content',
}}
>
Tooltip
</div>
</>
);
}
This will position the floating Tooltip
element at the bottom
center of the Button
element by default.
x
x
andy
y
are the positioning coordinates. These values arenull
null
initially, before the layout effect has fired (such as during SSR).strategy
strategy
is the positioning strategy,'absolute'
'absolute'
(default) or'fixed'
'fixed'
.refs.setReference
refs.setReference
andrefs.setFloating
refs.setFloating
are function refs that get called with the elements and update the position when they change (such as with conditional rendering), unlike with plain refs.
Return value
The hook
returns all the values from computePosition
,
plus some extras to work with React. This includes data about the
final placement and middleware data which are useful when
rendering.
Options
The hook accepts all the
options from computePosition
,
which allows you to customize the position. Here’s an example:
import {
useFloating,
offset,
flip,
shift,
} from '@floating-ui/react';
// Inside your component
useFloating({
placement: 'right',
middleware: [offset(10), flip(), shift()],
});
import {
useFloating,
offset,
flip,
shift,
} from '@floating-ui/react';
// Inside your component
useFloating({
placement: 'right',
middleware: [offset(10), flip(), shift()],
});
Middleware can alter the
positioning from the basic placement
placement
, act as
visibility optimizers, or provide data to use.
The docs for the middleware that were passed are available here:
All of these are re-exported from the base @floating-ui/dom
library.
Updating
The position is only calculated once on render, or when the
reference
reference
or floating
floating
elements change.
To ensure the floating element remains anchored to its reference
element in a variety of scenarios without detaching — such as
when scrolling or resizing the page — you can pass the
autoUpdate
utility to the
whileElementsMounted
whileElementsMounted
prop:
import {useFloating, autoUpdate} from '@floating-ui/react';
// Inside your component
useFloating({
whileElementsMounted: autoUpdate,
});
import {useFloating, autoUpdate} from '@floating-ui/react';
// Inside your component
useFloating({
whileElementsMounted: autoUpdate,
});
To pass options to autoUpdate
:
useFloating({
whileElementsMounted(...args) {
const cleanup = autoUpdate(...args, {animationFrame: true});
// Important! Always return the cleanup function.
return cleanup;
},
});
useFloating({
whileElementsMounted(...args) {
const cleanup = autoUpdate(...args, {animationFrame: true});
// Important! Always return the cleanup function.
return cleanup;
},
});
autoUpdate performance
autoUpdate
is expensive because it re-renders the host
component the useFloating()
useFloating()
hook lives in on every single
scroll or resize event. Therefore you should be mindful of the
following:
- Try not to call
useFloating()
useFloating()
in a big complex parent component, especially if it renders a list. Instead, create a new child component that only renders out the reference and floating elements. This will keep the high frequency re-renders scoped to the cheap child component. - Ensure you are using conditional rendering for the floating
element, not an opacity/visibility/display style. If you are
using CSS to hide it, instead of conditional rendering, use an
effect to register and clean up
autoUpdate
instead of thewhileElementsMounted
whileElementsMounted
prop.
Manual updating
While autoUpdate
covers most cases where the position of the
floating element must be updated, it does not cover every single
one possible due to performance/platform limitations.
The hook returns an update()
update()
function to update the
position at will:
const {update} = useFloating();
<Panel onResize={update} />;
const {update} = useFloating();
<Panel onResize={update} />;
Refs
To access the DOM elements, you can either access the refs:
const {refs} = useFloating();
// Inside an effect or event handler:
refs.reference.current;
refs.floating.current;
const {refs} = useFloating();
// Inside an effect or event handler:
refs.reference.current;
refs.floating.current;
Or the elements directly:
const {elements} = useFloating();
// During render, unlike the refs:
elements.reference;
elements.floating;
const {elements} = useFloating();
// During render, unlike the refs:
elements.reference;
elements.floating;
External elements can be synchronized like so, if they live outside the component:
function MyComponent({referenceEl, floatingEl}) {
const {refs} = useFloating();
useLayoutEffect(() => {
refs.setReference(referenceEl);
refs.setFloating(floatingEl);
}, [refs, referenceEl, floatingEl]);
}
function MyComponent({referenceEl, floatingEl}) {
const {refs} = useFloating();
useLayoutEffect(() => {
refs.setReference(referenceEl);
refs.setFloating(floatingEl);
}, [refs, referenceEl, floatingEl]);
}
Effects
Positioning is done in an async function, which means the position is ready during a microtask, after layout effects are executed. This means initially, the floating element is situated at the top-left (0, 0) of its offset container — so calling DOM methods that cause side-effects like scrolling will result in unexpected behavior.
The hook returns an isPositioned
isPositioned
boolean that lets you
know if the floating element has been positioned:
const [isOpen, setIsOpen] = useState(false);
const {isPositioned} = useFloating({
// Synchronize `isPositioned` with an `open` state.
open: isOpen,
});
// Each time the floating element opens, we want to focus and
// scroll some element into view.
useLayoutEffect(() => {
if (isPositioned) {
someElement.focus();
someElement.scrollIntoView();
}
}, [isPositioned]);
const [isOpen, setIsOpen] = useState(false);
const {isPositioned} = useFloating({
// Synchronize `isPositioned` with an `open` state.
open: isOpen,
});
// Each time the floating element opens, we want to focus and
// scroll some element into view.
useLayoutEffect(() => {
if (isPositioned) {
someElement.focus();
someElement.scrollIntoView();
}
}, [isPositioned]);
The open
open
option accepts a boolean that represents
the open/close state of the floating element. This ensures you
can wait each time it opens when the host component does not
unmount, which is necessary in cases where the reference element
relocates on the page.
Arrow
The arrow
module exported from this package allows refs in
addition to elements:
import {arrow} from '@floating-ui/react';
// Inside your component
const arrowRef = useRef(null);
useFloating({
middleware: [
arrow({
element: arrowRef,
}),
],
});
import {arrow} from '@floating-ui/react';
// Inside your component
const arrowRef = useRef(null);
useFloating({
middleware: [
arrow({
element: arrowRef,
}),
],
});
If you need your arrow to be reactive to updates (e.g. showing
or hiding the arrow with conditional rendering while the floating
element is open), you should use state instead. Alternatively,
you can use visibility: hidden
visibility: hidden
CSS to hide it and keep
using a plain ref.
For details on creating an arrow element, see the
arrow
middleware page.
Testing
When testing your components, ensure you flush microtasks
immediately after the floating element renders. This will avoid
the act
warning.
import {act} from '@testing-library/react';
test('something', async () => {
render(<Tooltip open />);
await act(async () => {}); // Flush microtasks.
// Position state is ready by this line.
});
import {act} from '@testing-library/react';
test('something', async () => {
render(<Tooltip open />);
await act(async () => {}); // Flush microtasks.
// Position state is ready by this line.
});
You may use this a lot, so you can create a custom function:
const waitForPosition = () => act(async () => {});
test('something', async () => {
render(<Tooltip open />);
await waitForPosition();
expect(screen.queryByRole('tooltip')).toBeInTheDocument();
});
const waitForPosition = () => act(async () => {});
test('something', async () => {
render(<Tooltip open />);
await waitForPosition();
expect(screen.queryByRole('tooltip')).toBeInTheDocument();
});
Narrow reference type
Because the reference
reference
callback ref accepts a
virtual element, you may need to narrow
the type when performing DOM operations on the ref:
const {refs} = useFloating<HTMLButtonElement>();
// @floating-ui/react
// refs.domReference.current is now of type HTMLButtonElement
// @floating-ui/react-dom
// refs.reference.current is now of type HTMLButtonElement
const {refs} = useFloating<HTMLButtonElement>();
// @floating-ui/react
// refs.domReference.current is now of type HTMLButtonElement
// @floating-ui/react-dom
// refs.reference.current is now of type HTMLButtonElement
In the full package, it is narrowed on refs.domReference
refs.domReference
,
as the
position can be separated.
Variable freshness
When using React state and middleware, variables inside function options aren’t fresh. This means that if you use a variable inside a function option, it will always be the same value, even if the variable changes.
const [value, setValue] = useState(0);
// Fresh:
offset(value);
// Not fresh:
offset(() => value);
const [value, setValue] = useState(0);
// Fresh:
offset(value);
// Not fresh:
offset(() => value);
You should instead use a ref:
// Fresh:
offset(() => valueRef.current);
// Fresh:
offset(() => valueRef.current);
To update the position when the ref changes, you can call
update()
update()
manually.
This is where the compatibility with the leaner
@floating-ui/react-dom
package ends. The following docs now
only apply to @floating-ui/react
.
View examples, or read below to understand the basics.
Interactions


To add interactions, such as the ability to only show a floating element while hovering over its reference element, the hook must first accept the following two options:
open
open
— a boolean that represents whether the floating element is currently rendered.onOpenChange
onOpenChange
— a state setter called when the open state changes.
import {useFloating} from '@floating-ui/react';
function App() {
const [isOpen, setIsOpen] = useState(false);
const {x, y, strategy, refs} = useFloating({
open: isOpen,
onOpenChange: setIsOpen,
});
return (
<>
<button ref={refs.setReference}>Button</button>
{isOpen && (
<div
ref={refs.setFloating}
style={{
position: strategy,
top: y ?? 0,
left: x ?? 0,
width: 'max-content',
}}
>
Tooltip
</div>
)}
</>
);
}
import {useFloating} from '@floating-ui/react';
function App() {
const [isOpen, setIsOpen] = useState(false);
const {x, y, strategy, refs} = useFloating({
open: isOpen,
onOpenChange: setIsOpen,
});
return (
<>
<button ref={refs.setReference}>Button</button>
{isOpen && (
<div
ref={refs.setFloating}
style={{
position: strategy,
top: y ?? 0,
left: x ?? 0,
width: 'max-content',
}}
>
Tooltip
</div>
)}
</>
);
}
Note that floating components do not always require “anchor
positioning” — for instance a modal dialog centered in the
viewport. So the x
x
, y
y
and
strategy
strategy
values returned from the hook can be safely
ignored.
Interaction hooks
useInteractions()
useInteractions()
is a separate hook that accepts an array
of interaction hooks and returns prop getters.
Each interaction hook accepts the context
context
object which
gets returned from useFloating()
useFloating()
as its first argument:
import {
useFloating,
useInteractions,
useHover,
useFocus,
} from '@floating-ui/react';
// Inside your component
const {refs, context} = useFloating();
const hover = useHover(context);
const focus = useFocus(context);
const {getReferenceProps, getFloatingProps} = useInteractions([
hover,
focus,
]);
import {
useFloating,
useInteractions,
useHover,
useFocus,
} from '@floating-ui/react';
// Inside your component
const {refs, context} = useFloating();
const hover = useHover(context);
const focus = useFocus(context);
const {getReferenceProps, getFloatingProps} = useInteractions([
hover,
focus,
]);
This API enables each of the hooks to be fully tree-shakeable and opt-in. The navigation bar on the left explains them in detail.
Prop getters
The prop getters are used to add event listeners, among other
functionality, to the reference and floating elements. When
called, they return an object of props like onFocus
onFocus
.
<>
<button ref={refs.setReference} {...getReferenceProps()}>
My button
</button>
<div
ref={refs.setFloating}
style={{
position: strategy,
left: x ?? 0,
top: y ?? 0,
width: 'max-content',
}}
{...getFloatingProps()}
>
My tooltip
</div>
</>
<>
<button ref={refs.setReference} {...getReferenceProps()}>
My button
</button>
<div
ref={refs.setFloating}
style={{
position: strategy,
left: x ?? 0,
top: y ?? 0,
width: 'max-content',
}}
{...getFloatingProps()}
>
My tooltip
</div>
</>
All custom event listener props, such as onClick
onClick
,
onKeyDown
onKeyDown
and more you pass to the element should be
specified inside the prop getter. They perform merging of their
own internal event listeners and your own without overriding
them.
// ❌ Your `onClick` can be overridden:
<div
onClick={() => {
// Potentially overwritten by the props below.
}}
{...getReferenceProps()}
/>
// ❌ Your `onClick` can be overridden:
<div
onClick={() => {
// Potentially overwritten by the props below.
}}
{...getReferenceProps()}
/>
// ✅ Merging works inside `getReferenceProps()`:
<div
{...getReferenceProps({
onClick() {
// Will not be overwritten.
},
})}
/>
// ✅ Merging works inside `getReferenceProps()`:
<div
{...getReferenceProps({
onClick() {
// Will not be overwritten.
},
})}
/>
You may find passing all props through the prop getter helps you
remember to prevent overriding event handlers, but is not
currently required unless the value is a function event handler
that starts with on
.
getItemProps
const {getItemProps} = useInteractions([]);
const {getItemProps} = useInteractions([]);
This is an optional prop getter that is only used when dealing
with a list inside your floating element (see
useListNavigation
).
Data
The context object contains a data ref with some information available.
const {context} = useFloating();
useEffect(() => {
console.log(context.dataRef.current);
}, [context]);
const {context} = useFloating();
useEffect(() => {
console.log(context.dataRef.current);
}, [context]);
Currently, only two built-in values are set:
interface ContextData {
// Which event caused the floating element to open.
openEvent?: MouseEvent | PointerEvent | FocusEvent;
// Whether the user is typing currently when `useTypeahead`
// is in use.
typing?: boolean;
// Add support for custom data to pass around.
[key: string]: any;
}
interface ContextData {
// Which event caused the floating element to open.
openEvent?: MouseEvent | PointerEvent | FocusEvent;
// Whether the user is typing currently when `useTypeahead`
// is in use.
typing?: boolean;
// Add support for custom data to pass around.
[key: string]: any;
}
Changing the positioning reference while retaining events
By default, the refs.setReference
refs.setReference
element is both the
events and position reference. The
refs.setPositionReference
refs.setPositionReference
callback ref allows you to
separate the position to another element (either real or
virtual):
const {refs} = useFloating();
return (
<>
<button ref={refs.setReference} {...getReferenceProps()}>
Event reference
</button>
<button ref={refs.setPositionReference}>
Position reference
</button>
</>
);
const {refs} = useFloating();
return (
<>
<button ref={refs.setReference} {...getReferenceProps()}>
Event reference
</button>
<button ref={refs.setPositionReference}>
Position reference
</button>
</>
);
Multiple floating elements on a single reference element
import {useMergeRefs} from '@floating-ui/react';
import {useMergeRefs} from '@floating-ui/react';
Refs can be merged with the useMergeRefs
hook, and props can be merged by calling one of the getters
inside of the other:
const {refs: tooltipRefs} = useFloating();
const {refs: menuRefs} = useFloating();
const {getReferenceProps: getTooltipReferenceProps} =
useInteractions([]);
const {getReferenceProps: getMenuReferenceProps} =
useInteractions([]);
const ref = useMergeRefs([
tooltipRefs.setReference,
menuRefs.setReference,
]);
const props = getTooltipReferenceProps(getMenuReferenceProps());
return (
<button ref={ref} {...props}>
Common reference
</button>
);
const {refs: tooltipRefs} = useFloating();
const {refs: menuRefs} = useFloating();
const {getReferenceProps: getTooltipReferenceProps} =
useInteractions([]);
const {getReferenceProps: getMenuReferenceProps} =
useInteractions([]);
const ref = useMergeRefs([
tooltipRefs.setReference,
menuRefs.setReference,
]);
const props = getTooltipReferenceProps(getMenuReferenceProps());
return (
<button ref={ref} {...props}>
Common reference
</button>
);
Disabled elements
Disabled elements don’t fire events, so tooltips attached to
disabled buttons don’t show. Avoid using the
disabled
disabled
prop, and make the button visually disabled
instead. This ensures you won’t need any wrapper tags and makes
the tooltip accessible to all users.