Tutorial
Build a tooltip from scratch and learn how to use Floating UI’s positioning toolkit.
This page teaches you the fundamentals of Floating UI’s positioning by building a tooltip from scratch. If you just want to start learning about the API, skip to the next section.
Before proceeding
The vanilla package of this library is a JavaScript-based layout engine for “anchor positioning”. It acts as a polyfill for CSS to correctly position absolutely-positioned anchored elements on the document.
If you’re looking for pre-built components or something simple out of the box, you may find other libraries are better suited for your use case.
Setting up
Create a new HTML document with two elements, a <button>
and a tooltip <div>
:
Right now you should see the following (except with the browser’s default styling):
Styling
Let’s give our tooltip some styling:
Here’s the result so far:
Making the tooltip “float”
Your tooltip <div>
is a regular block on the document,
like any other element, which is why it spans the whole width of
the page.
We want it to float on top of the UI though, so it doesn’t disrupt the flow of the document, and should only take up as much size as its contents.
Visit Initial layout to learn more.
Your tooltip is now a “floating” element — it only takes up as much size as it needs to and is overlaid on top of the UI.
Positioning
Inside your module <script>
tag, add the following code:
Above, we call computePosition()
with the button and
tooltip elements as arguments.
It returns a Promise
, so we use the .then()
method which passes in the calculated x
and
y
coordinates for us, which we use to assign
left
and top
styles to the tooltip.
Our tooltip is now centered underneath the button:
Placements
The default placement is 'bottom'
, but you probably want
to place the tooltip anywhere relative to the button. For this,
Floating UI has the placement
option, passed into the
options object as a third argument:
The available base placements are 'top'
, 'right'
,
'bottom'
, 'left'
.
Each of these base placements has an alignment in the form
-start
and -end
. For example, 'right-start'
, or
'bottom-end'
. These allow you to align the tooltip to the
edges of the button, rather than centering it.
Our first problem
These placements are a useful feature themselves, but they don’t
offer much that couldn’t be achieved with raw CSS tricks. Which
brings us to our first problem: what happens if we use a
'top'
placement?
We can’t read the tooltip text because the button happens to be
close to the document boundary. In this case, you could use the
'bottom'
placement, instead of 'top'
. Again, you
could just use CSS for this, although it may become unwieldy
and require extra parent wrapper tags.
Needing to manually handle this for every single tooltip you add in an application can be cumbersome. This is especially true when you want to apply a tooltip or popover to an element anywhere on the page at a whim, and have its position “just work” for any screen size or location, without needing to adjust anything.
This is why you can let Floating UI handle it for you automatically with the adoption of “middleware”.
Middleware
Middleware are how every single feature beyond the basic placement positioning is implemented.
A middleware is a piece of code which runs between the call of
computePosition()
and its eventual return, to modify or
provide data to the consumer.
flip()
modifies the coordinates for us such that it uses
the 'bottom'
placement automatically without us needing to
explicitly specify it.
Now, even though we’ve set placement
to 'top'
,
the tooltip flips to the bottom automatically for us if it can’t
fit on the top. No need to adjust anything manually.
Importantly, it will continue to use the 'top'
placement
at all times if it can, and only fallback to 'bottom'
if
it has to.
Shift middleware
Now, what if we wanted to have more content inside the tooltip?
Oh no, we now have the same problem as before, but on the
opposite axis (x
instead of y
). Because it’s centered
beneath the button, and the tooltip happens to be wider than the
button, it ends up overflowing the viewport edge.
To solve this problem, we have the shift()
middleware:
Now, we can read all the text because the shift()
middleware shifted the tooltip from its bottom centered placement
until it was fully in view.
As you can see, the tooltip lies fully flush with the edge of the
document boundary. To add some whitespace, or padding, the
shift()
middleware accepts an options object:
Now there’s 5px of breathing room between the tooltip and the edge of the boundary.
Offset middleware
We probably also don’t want the tooltip to lie flush with the
button element. For this, we have the offset()
middleware:
This will displace the tooltip 6px from the reference element:
Arrow middleware
Most tooltips have an arrow (or triangle / caret) which points
toward the button. For this, we have the arrow()
middleware, but we first need to add a new element inside of our
tooltip:
Then style it:
Then pass the arrow element into the arrow()
middleware:
Now we need to add dynamic styles to the arrow element. Unlike
other middleware, the arrow()
middleware doesn’t modify
the main x
and y
coordinates. Instead, it
provides data for us to use. We can access this piece of
information provided via middlewareData
.
This contains an arrow
object, referring to the name of the
arrow()
middleware we used:
We now want to use this data to apply the styles.
The styles above will handle the arrow’s position for all placements.
x
is the x-axis offset, only existing if the placement is vertical ('top'
or'bottom'
).y
is the y-axis offset, only existing if the placement is horizontal ('right'
or'left'
).
staticSide
depends on the placement
that gets
chosen. For instance, if the placement is 'bottom'
, then
we want the arrow to be positioned 4px outside of the top of
the tooltip (so we use a negative number).
The reason we use .split('-')[0]
is to also handle aligned
placements, like 'top-start'
rather than only
'top'
.
Functionality
Now that the tooltip has everything positioned, we can add user interactions that will show the tooltip when hovering or focusing the button.
Try hovering or focusing the button:
Misc
To keep the tooltip anchored to the button while scrolling or
resizing, you’ll want to use the autoUpdate
utility.
As for animations, this is up to you to explore when crafting your floating elements!
Complete
You’ve now created a basic accessible tooltip using Floating UI.