Floating elements are elements that “float” on top of the UI without disrupting the flow of content. Tooltips are examples of floating element; they are short messages that appear on a page when a user hovers over a specific area. We can use tooltips to create user onboarding flows, send updates and reminders to our users, provide more information about a feature, and more.
Popper has long been one of the most popular JavaScript libraries for creating floating elements. However, a new player is in town: its successor, Floating UI.
Floating UI comes with several upgrades. It is cross-platform compatible and can be used in React and React Native applications. It is smaller than Popper; Popper weighs 3kb, and Floating UI is 600 bytes. It is also tree-shakeable by default, while Popper is not. Floating UI is not just an alternative to Popper but an upgrade with several benefits.
In this article, we will learn about Floating UI and how we can use it to create floating elements.
Floating UI is an extensible, low-level library for creating interactive elements like tooltips, popovers, dropdowns, menus and more.
Floating UI exposes primitives, which we can use to position a floating element next to a given reference element. It also supports the web, React, React Native, WebGL, Canvas, and more.
Run the command below to install Floating UI:
npm install @floating-ui/dom
We can also load Floating UI through a CDN using ESM or UMD format like so:
<script type="module"> import * as FloatingUIDOM from 'https://cdn.skypack.dev/@floating-ui/[email protected]'; </script>
computePosition
functionThe computePosition
function is the heart of Floating UI. It computes the coordinates needed to position the floating element next to its given reference element, which is the element that triggers the floating element.
Let’s build a basic tooltip to see how computePosition
works.
We start by setting up the HTML:
<!DOCTYPE html> <html lang="en"> <body> <button id="button" aria-describedby="tooltip">My button</button> <div id="tooltip" role="tooltip">My tooltip oltip with more content</div> <script src="/index.js" type="module" /> </body> </html>
Next, we style the tooltip and set its position to absolute
so it floats and doesn’t disrupt the flow of the other content.
#tooltip { color: #fff; background: #363636; font-size: 1.2rem; padding: 10px 15px; border-radius: 8px; position: absolute; box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1), 0 3px 3px rgba(0, 0, 0, 0.05); } button { border-radius: 8px; border: none; outline: none; font-size: 1.2rem; cursor: pointer; padding: 10px 15px; color: #fff; background: rgb(48, 19, 129); }
Having set up the structure and styling for the tooltip, let’s work on the functionality:
import {computePosition} from 'https://cdn.skypack.dev/@floating-ui/[email protected]'; const button = document.querySelector('#button'); const tooltip = document.querySelector('#tooltip'); computePosition(button, tooltip).then(({x, y}) => { // Do things with `x` and `y` Object.assign(tooltip.style, { left: `${x}px`, top: `${y}px`, }); });
The button
is the reference element, and the tooltip
is the floating element.
We can change the placement of the tooltip to different positions like so:
computePosition(button, tooltip, { placement: 'top-start', })then(({ x, y }) => { //other code below };
There are 12 core positions we can place elements:
left-start
, left
and left-end
top-start
, top
and top-end
right-start
, right
and right-end
bottom-start
, bottom
and bottom-end
The default position of a floating element is bottom
.
Middleware is a piece of code that runs between the call of computePosition
and its eventual return to modify or provide data to the consumer. It alters the placement and behavior of floating elements.
Middleware is how every single feature beyond the basic placement positioning is implemented.
Floating UI provides several middlewares:
offset
places spacing between the reference element and the floated elementshift
shifts the floated element to ensure its entire content is always in view. It also ensures that the element does not overflow outside the viewport by handling clipping and overflow issuesflip
modifies the coordinates for us, such that the bottom
placement automatically positions the floating element at the bottom if it is too close to the top of the viewport and vice versasize
handles the resizing of the floated elementautoPlacement
automatically chooses the placement of the floated element by selecting the position with the most space availableinline
improves positioning for inline reference elements that span over multiple lines, such as hyperlinksLet’s extend the behavior of the basic tooltip with some of these middlewares:
computePosition(button, tooltip, { placement: "top", middleware: [offset(4), flip(), shift({padding: 5})], }).then(({ x, y }) => { //other code below });
Above, we use offset
to add a 4px spacing between the tooltip and the button.
Besides fixing content clipping issues, the shift
middleware accepts an options object where we define the spacing between the tooltip and the edge of the viewport. We set the spacing to 5px.
The order in which we arrange the middlewares is important; offset
must always be at the beginning of the array.
Currently, the tooltip is always visible. However, it should only show when we hover over the button.
Let’s set up that functionality:
function setUpTooltip() { computePosition(button, tooltip, { placement: "top", middleware: [offset(4), flip(), shift({ padding: 5 })], }).then(({ x, y }) => { Object.assign(tooltip.style, { left: `${x}px`, top: `${y}px`, }); }); } function showTooltip() { tooltip.style.display = "block"; setUpTooltip(); } function hideTooltip() { tooltip.style.display = "none"; }
Above, we move the tooltip logic into a function, setUpTooltip
, so we can call that function when we want the tooltip to show.
We also create two functions, hideTooltip
and showTooltip
. hideTooltip
sets the tooltip’s display to none
. showTooltip
sets the tooltip’s display to block
and class setUpTooltip
.
We want to call hideTooltip
when we hover away from the button and call showTooltip
when we hover over the button:
[ ["mouseenter", showTooltip], ["mouseleave", hideTooltip], ].forEach(([event, listener]) => { button.addEventListener(event, listener); });
Here, we attach the event listeners and the functions to the button. With this, the tooltip will only appear on hover.
We have the final code for the tooltip below:
import { computePosition, flip, shift, offset, } from "https://cdn.skypack.dev/@floating-ui/[email protected]"; const button = document.querySelector("#button"); const tooltip = document.querySelector("#tooltip"); function setUpTooltip() { computePosition(button, tooltip, { placement: "top", middleware: [offset(4), flip(), shift({ padding: 5 })], }).then(({ x, y }) => { Object.assign(tooltip.style, { left: `${x}px`, top: `${y}px`, }); }); } function showTooltip() { tooltip.style.display = "block"; setUpTooltip(); } function hideTooltip() { tooltip.style.display = "none"; } [ ["mouseenter", showTooltip], ["mouseleave", hideTooltip], ["focus", showTooltip], ["blur", hideTooltip], ].forEach(([event, listener]) => { button.addEventListener(event, listener); });
We can easily integrate Floating UI into React applications.
First, we have to install the React library into a React application like so:
npm install @floating-ui/react-dom
Floating UI provides a useFloating
Hook we can use in React applications. Let’s use this Hook to set up the basic tooltip in React:
import { useFloating, shift, offset, flip } from "@floating-ui/react-dom"; export default function App() { const { x, y, reference, floating, strategy } = useFloating({ placement: "right", middleware: [offset(4), flip(), shift({ padding: 5 })], }); return ( <> <button ref={reference}>Button</button> <div id="tooltip" ref={floating} style={{ top: y, left: x }} > Tooltip </div> </> ); }
The useFloating
Hook accepts all of computePosition
‘s options, meaning we can define the placement of a tooltip and add middleware.
In this article, we have learned about floating UI, how it works, its different features, and how to integrate it into React applications.
While Floating UI offers a few benefits over Popper, one thing I would have loved to see is a demo showing how to conditionally display tooltips on hover for React. Sadly, the documentation does not cover that. Also, there is little or no developer content or support available, as this is a new library. So while Floating UI is a great new tool, these are things we should take into account when working with it.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.