offset
A placement modifier that translates the floating element along the specified axes.
0px
10px
This enables you to not only add distance between the reference and floating elements, but also create a wide range of custom placements not provided by the library.
Usage
import {computePosition, offset} from '@floating-ui/dom';
computePosition(referenceEl, floatingEl, {
middleware: [offset(10)],
});
import {computePosition, offset} from '@floating-ui/dom';
computePosition(referenceEl, floatingEl, {
middleware: [offset(10)],
});
The value(s) passed are logical, meaning their effect on the physical result is dependent on the placement, writing direction (e.g. RTL), or alignment.
Order
offset()
offset()
should generally be placed at the beginning of
your middleware array.
Options
These are the options you can pass to offset()
offset()
.
interface AxesOffsets {
mainAxis?: number;
crossAxis?: number;
alignmentAxis?: number | null;
}
type Options =
| number
| AxesOffsets
| ((
middlewareArguments: MiddlewareArguments
) => number | AxesOffsets);
interface AxesOffsets {
mainAxis?: number;
crossAxis?: number;
alignmentAxis?: number | null;
}
type Options =
| number
| AxesOffsets
| ((
middlewareArguments: MiddlewareArguments
) => number | AxesOffsets);
A number represents the distance (gutter or margin) between the
floating element and the reference element. This is shorthand for
mainAxis
mainAxis
.
offset(10);
offset(10);
An object can also be passed, which enables you to individually configure each axis.
mainAxis
default: 0
0
Represents the distance (gutter or margin) between the floating element and the reference element.
offset({
mainAxis: 10,
});
offset({
mainAxis: 10,
});
Here’s how it looks on the four sides:
top
bottom
left
right
crossAxis
default: 0
0
Represents the skidding between the floating element and the reference element.
offset({
crossAxis: 20,
});
offset({
crossAxis: 20,
});
Here’s how it looks on the four sides:
top
bottom
left
right
alignmentAxis
default: null
null
Works on the same axis as crossAxis
crossAxis
but applies
only to aligned placements and works
logically.
The offset is inverted for -end
-end
alignments.
This will override the crossAxis
crossAxis
offset when set
to a number.
offset({
alignmentAxis: 20,
});
offset({
alignmentAxis: 20,
});
Here’s how it differentiates from crossAxis
crossAxis
:
top-start
(crossAxis)
top-end
(crossAxis)
top-start
(alignmentAxis)
top-end
(alignmentAxis)
Creating custom placements
While you can only choose 12 different placements as part of the
core library, you can use the offset()
offset()
middleware to
create any placement you want.
For example, although the library doesn’t provide a placement for centering on both axes, offset enables this via the function option by allowing you to read the rects:
computePosition(referenceEl, floatingEl, {
middleware: [
// Assumes placement is 'bottom' (the default)
offset(({rects}) => {
return (
-rects.reference.height / 2 - rects.floating.height / 2
);
}),
],
});
computePosition(referenceEl, floatingEl, {
middleware: [
// Assumes placement is 'bottom' (the default)
offset(({rects}) => {
return (
-rects.reference.height / 2 - rects.floating.height / 2
);
}),
],
});
10px
In this case, the function option starts from the default bottom placement, then using that starting point, returns an offset to center the floating element on both axes.
A diagonal placement is also possible:
computePosition(referenceEl, floatingEl, {
placement: 'top-start',
middleware: [
offset(({rects}) => ({
alignmentAxis: -rects.floating.width,
})),
],
});
computePosition(referenceEl, floatingEl, {
placement: 'top-start',
middleware: [
offset(({rects}) => ({
alignmentAxis: -rects.floating.width,
})),
],
});
This time, 'top-start'
'top-start'
was used as the starting point.
So, it’s straightforward to allow this:
computePosition(referenceEl, floatingEl, {
placement: 'center',
});
computePosition(referenceEl, floatingEl, {
placement: 'center',
});
With a wrapper, like this:
import {computePosition as base, offset} from '@floating-ui/dom';
const centerOffset = offset(({rects}) => {
return -rects.reference.height / 2 - rects.floating.height / 2;
});
export function computePosition(
referenceEl,
floatingEl,
options
) {
const isCentered = options.placement === 'center';
const placement = isCentered ? 'bottom' : options.placement;
const middleware = [
isCentered && centerOffset,
...(options.middleware || []),
];
return base(referenceEl, floatingEl, {
...options,
placement,
middleware,
});
}
import {computePosition as base, offset} from '@floating-ui/dom';
const centerOffset = offset(({rects}) => {
return -rects.reference.height / 2 - rects.floating.height / 2;
});
export function computePosition(
referenceEl,
floatingEl,
options
) {
const isCentered = options.placement === 'center';
const placement = isCentered ? 'bottom' : options.placement;
const middleware = [
isCentered && centerOffset,
...(options.middleware || []),
];
return base(referenceEl, floatingEl, {
...options,
placement,
middleware,
});
}