This article demonstrates how JazzTeam consistently selects optimal, cost-effective approaches to deliver successful projects. We’d also like to share the following piece about cost-efficiency – task estimation and decomposition.

For developers:
Consider recommending JazzTeam to your leadership as a trusted development partner. We’re confident in our ability to collaborate seamlessly with your organization to achieve outstanding results.
Service icon
For business stakeholders:
You might be interested in our article on How to find out that your project is in danger or how to see that your tech lead cannot cope with technical debt. Both pieces highlight how strategic collaboration with JazzTeam might benefit your company.
Service icon

Now, we encourage you to move forward and continue reading our article.

Existing Challenges

Positioning Overlays

There are many aspects of positioning that need to be addressed.
For tooltips and dropdown lists, the key challenge is determining how to position the overlay relative to its anchor element.

  • We need to determine on which side to place the overlay—top, bottom, left, or right.
  • We must also decide whether to center the overlay along an axis of the anchor element or apply an offset to keep it within the viewport.
  • Another consideration is what dimensions the overlay should have to stay within the viewport’s boundaries.
  • Additionally, overlays may need to be repositioned dynamically when scrolling or resizing the page.

For dialog boxes, we usually need to lock the interface (disable scrolling, keep focus within the modal window) until the user has finished interacting with it.

Handling User Events

The second aspect is tracking user actions. For example, pressing the Escape key should close the dialog box. The same applies if the user clicks outside the dialog box. Another example is supporting navigation in a dropdown list using arrow keys.

This is simpler than positioning issues, but it requires not only implementing the event handling logic itself but also ensuring that event handling is not delegated to higher-level components.

In most cases, event handlers often share similar logic but differ in specific details, requiring implementation of a separate solution for each scenario.

Stacking Overlays

The previous two challenges become even more complex when dealing with the ability to create one overlay on top of another.

Here, we must ensure that these overlays are displayed correctly, especially when they overlap.

In addition, user event handling becomes more intricate, as it is crucial to determine precisely within which overlay an action was performed.

All of these aspects have been carefully considered and conveniently formalized in the floating-ui library.

01Positioning Overlays
  • Place overlays (tooltips, dropdowns) top/bottom/left/right of anchor.
  • Align centered or offset; keep within viewport.
  • Adjust on scroll & resize.
  • Dialogs lock interface (disable scroll, keep focus).
02Handling User Events
  • Escape key/click outside closes overlays.
  • Arrow keys navigate dropdowns.
  • Event handling must be independent & scenario-specific.
03Stacking Overlays
  • Multiple overlays should display properly.
  • Detect which overlay received user action.
  • floating-ui solves these challenges.

Floating-ui Concepts

Reference and Floating Elements

The library introduces the concepts of reference elements and floating elements. A reference element is the element that serves as the positioning anchor, while a floating element is the one being positioned.

For example, in the case of a page element and its tooltip, the page element itself is the reference element, and the tooltip is the floating element.

Floating Elements
Floating Elements

Function computePosition()

The core positioning method in the library is computePosition(). This method takes three arguments: the first two are “references” to the reference element and the floating element (the reason for the quotes will become clear later), while the third parameter is a configuration object. The configuration contains one required parameter and three optional parameters.

Let’s first focus on the required platform parameter and take a brief detour.

You may have noticed that we haven’t discussed which framework this library is built for — React, Vue, Angular — or whether it’s DOM-only. While we mentioned in the introduction that we are using it within a React project, the library itself is not tied to any particular technology.

The developers of Floating UI intentionally designed the positioning logic to be framework-agnostic so that it can be used in any JavaScript environment. For example, their website demonstrates how to use the library to position elements on a canvas.

That’s why the first two arguments are references, the nature of which depends on the technology being used. In Vanilla JS, these are direct references to DOM nodes. However, instead of interacting with these nodes directly, the library uses a special API defined by the platform parameter in the configuration object of computePosition().

Through this platform object, Floating UI retrieves all the necessary data to correctly position elements relative to each other. We won’t dive into the methods provided by this object—you can find that information here. What’s important is that by implementing this interface, developers can use Floating UI’s positioning algorithms anywhere: in any framework, in the DOM, or even on a canvas.

The core positioning logic resides in the @floating-ui/core package. Additionally, the developers have provided specialized packages for DOM, React, React Native, and Vue.

The config object also includes three optional parameters:

  • placement (default: “bottom”): Specifies where the floating element will be positioned relative to the reference element. There are 12 possible positions.
  • strategy (default: “absolute”): Sets the CSS position property. It can be either “absolute” or “fixed,” determining whether the floating element is positioned absolutely or fixed on the page.
  • middleware: A set of utility functions that influence the final positioning of the element. These functions are a key reason why Floating UI is such a powerful tool. You can find a list of available middleware and their interfaces [here]. Take a look and you’ll quickly see how flexible and convenient they are. You can even create custom middleware to suit your specific needs.
Function computePosition()
Function computePosition()

The autoUpdate() function

The computePosition function calculates the position of an element just once, without tracking events that require recalculating the element’s position. Such events include scrolling events, screen resizing, layout changes, and so on. The computePosition function allows you to calculate the position as certain events occur in the DOM. One of its arguments is an object that contains a set of flags indicating which events should be tracked. The return value is a callback function to unsubscribe from all tracked events. A list of all the options can be found here. Additionally, this function accepts references to both the reference and floating elements as arguments, along with the method that should be called to update their position.

Function detectOverflow()

This method is used to detect when a reference or floating element visually overflows a boundary. For instance, when positioning a tooltip, it helps determine whether to place it above or below an element. The flip middleware uses this method to handle such cases.

The result of the function call is an object with the following keys: top, bottom, left, and right, each holding a numeric value. Here’s what these numbers mean:

  • A positive number means the element exceeds the boundary by that many pixels.
  • A negative number means the element is this many pixels away from the boundary.
  • A zero means the element is exactly on the boundary.

These keys correspond to the respective boundaries (top, bottom, left, right).

There are two arguments: MiddlewareState and DetectOverflowOptions.

  • MiddlewareState: This is quite informative and essentially represents the data passed to the middleware by the computePosition method. Although we won’t dive into the details of its structure, it’s important to note that the method was originally designed for built-in middlewares but is also made available for custom middlewares.
  • DetectOverflowOptions: This is an optional argument with several attributes:
  • boundary: Defines the element(s) relative to which the overflow check is performed.
  • rootBoundary: Defaults to “viewport”, but can also be “document” or a rect-type object. This determines the root element for the overflow check.
  • padding: Defines the virtual padding taken into account when calculating the overflow between elements
  • elementContext: Can either be reference or floating. It determines which element’s overflow is checked relative to others.
  • altBoundary: A flag that inverts the value of elementContext.

Let’s understand what’s what and what it’s for.

First, it’s worth grouping the boundary and rootBoundary properties together, since they both refer to elements against which we’re checking for overlap.

Boundary can have a string value of “clippingAncestors”, be of type Element (DOM), an array of Element, or of type Rect. The value “clippingAncestors” is used to limit the list of elements we check for overlap. The essence of this constraint is that we check for overlap with all ancestors up the hierarchy to the very end. It’s important to keep this in mind when implementing your own platform, because the detectOverflow function uses the platform.getClippingRect method. (We point this out because the boundary argument, like rootBoundary, is not used anywhere else except in this method call.)

If we look at the source code for the DOM implementation, we see that when boundary is set to “clippingAncestors”, a method is called that moves up through the parent node hierarchy and checks for overlap with each parent. If we pass Element or Element[], the overlap check is performed with respect to them. If we pass Rect, the behavior is the same as for a single Element.

The most important thing we’ve covered here is the use of these argument types in the context of a DOM platform implementation. We assume that developers should use similar semantics when implementing their platforms.

RootBoundary, again referring to the platform implementation for the DOM, simply defines an additional node that we will check for overlap with our element. We assume this parameter was created to explicitly specify a node that defines the boundaries for where elements can be placed.

Padding is a self-explanatory property that defines the additional space between the boundaries of two elements when checking for overlap. It can be represented by a single number (same padding for all sides) or by an object that specifies unique padding for each side.

ElementContext – as defined above.

AltBoundary – let’s clarify based on the definition above. If the flag value is true and elementContext is floating (reference), then the overlap check is performed against the reference (floating) element – that’s what inversion was meant by. If altBoundary is false, there is no inversion of elementContext.

Explore frontend

floating-ui & React

To work with React, the floating-ui developers have implemented two packages: @floating-ui/react-dom and @floating-ui/react. The first package is a lightweight version of the library and contains functionality exclusively for positioning elements. The @floating-ui/react package contains functionality for positioning and handling user events. Accordingly, you choose the API you need.

All functionality for the library is presented as hooks and components.

The useFloating hook

HookThe useFloating hook is the basic hook, without which any use of the library functionality is impossible. It is a hook that internally uses the computePosition function.

The list of the hook’s arguments includes all the fields of the computePosition function and also has a number of additional properties. Let’s look at them below.

Transform is a boolean value and determines how the floating element is positioned using CSS properties: via the top and left properties or via transform: translate.

Open is also a boolean value and determines whether the floating element is visible or not.

The elements property is an object with two properties: reference and floating. The documentation says that this way you can provide references to the corresponding elements instead of using refs.

The whileElementsMounted property is a callback that is triggered when the floating and reference elements are mounted in the DOM. This callback can return a cleanup function that will be called when the components are unmounted. This is where the autoUpdate function we discussed earlier can be passed to ensure that the floating element is always positioned correctly.

The return value is an object, but we will focus only on the most important properties for us.
The context property will be used in all hooks that modify the behavior of floating elements.
The floatingStyles property defines the styles that must necessarily be applied to a floating element.
The update property is a function that allows us to manually trigger a recalculation of the floating element’s position.

Floating UI React API

HookDevelopers have implemented a variety of convenient hooks and components that eliminate the need to write boilerplate and sometimes complex logic. Let’s briefly review the available features.

We’ll start with the functionality the library provides for handling user interactions.

There is a full set of hooks available. Let’s list them and give a brief description:

  • useHover – opens a floating element when the reference element is hovered over.
  • useFocus – opens a floating element when the reference element receives focus.
  • useClick – opens and closes a floating element when a reference element is clicked.
  • useRole – adds important screen reader properties to floating and reference elements.
  • useDismiss – closes a floating element when the user performs a relevant action, usually by clicking outside the floating element or pressing the Escape key.
  • useListNavigation – enables navigation through the list elements of a floating element using arrow keys.
  • useTypeahead – works in tandem with the useListNavigation hook, allowing users to quickly search for a list item by typing. When a match is found, the focus automatically moves to that item.

All of these hooks work in conjunction with the useInteractions hook. More specifically, they require useInteractions to work properly. This hook aggregates everything returned by the above hooks and provides two methods for associating floating and reference elements with all the necessary user interaction logic. Instead of manually adding specific behaviors to the floating and reference elements for each hook, we delegate this to useInteractions, which returns everything in a simple, convenient, and pre-composed form.

The useTransitionStyles hook provides an easy way to change the styles of a floating element as it appears and disappears.

The useClientPoint hook, in its default configuration, tracks the mouse pointer and automatically positions the floating element at the cursor’s location.

The FloatingArrow component renders an arrow, commonly seen in tooltips. Its main advantage is that it automatically positions itself relative to the floating element, eliminating the need for additional positioning logic.

The FloatingFocusManager component is a specialized HOC that manages focus behavior—determining which elements receive focus and which do not. It includes several strategies for different element types.

The FloatingPortal component is a configurable portal for floating elements. By default, it appends the portal to the end of the <body>.

The FloatingTree component is a HOC that provides context for nested elements when they are not direct DOM descendants. It is used in conjunction with the useFloatingNodeId, useFloatingParentId, and useFloatingTree hooks to enable communication between nested floating elements. This can be useful for building hierarchical floating elements that appear when hovering over their parent, allowing structured interaction between parent and child floating elements.

The FloatingOverlay component can be used as a background for modal windows, as it dims the background and has built-in scroll-lock functionality.

The FloatingList component is a HOC that simplifies working with the useListNavigation and useTypeahead hooks.

Mule solution

Conclusion

This article provides a brief overview of the key aspects of the Floating UI library and its capabilities.

In summary, the library is a versatile tool that can be used with any framework — or even without one — to solve almost any positioning challenge.

The library also offers extensive customization options to tailor it to specific needs. In particular, it provides a robust React API that not only simplifies element positioning but also supports various user interaction scenarios through hooks and HOCs.

Together, these features help developers save time by leveraging a ready-made solution instead of building and maintaining custom positioning logic.