flip
A visibility optimizer that changes the placement of the floating element in order to keep it in view, with the ability to flip to any placement.
Default
This is useful to prevent the floating element from overflowing on the main axis of its placement.
Usage
import {computePosition, flip} from '@floating-ui/dom';
computePosition(referenceEl, floatingEl, {
middleware: [flip()],
});
import {computePosition, flip} from '@floating-ui/dom';
computePosition(referenceEl, floatingEl, {
middleware: [flip()],
});
Options
These are the options you can pass to flip()
flip()
.
interface Options extends DetectOverflowOptions {
mainAxis?: boolean;
crossAxis?: boolean;
fallbackAxisSideDirection?: 'none' | 'start' | 'end';
flipAlignment?: boolean;
fallbackPlacements?: Array<Placement>;
fallbackStrategy?: 'bestFit' | 'initialPlacement';
}
interface Options extends DetectOverflowOptions {
mainAxis?: boolean;
crossAxis?: boolean;
fallbackAxisSideDirection?: 'none' | 'start' | 'end';
flipAlignment?: boolean;
fallbackPlacements?: Array<Placement>;
fallbackStrategy?: 'bestFit' | 'initialPlacement';
}
mainAxis
default: true
true
This is the main axis in which overflow is checked to perform a flip. By disabling this, it will ignore overflow.
y
-axis for'top'
'top'
and'bottom'
'bottom'
placementsx
-axis for'left'
'left'
and'right'
'right'
placements
flip({
mainAxis: false,
});
flip({
mainAxis: false,
});
crossAxis
default: true
true
This is the cross axis in which overflow is checked to perform a
flip, the axis perpendicular to mainAxis
mainAxis
. By
disabling this, it will ignore overflow.
flip({
crossAxis: false,
});
flip({
crossAxis: false,
});
fallbackAxisSideDirection
default: 'none'
'none'
Whether to allow fallback to the opposite axis if no placements along the preferred placement axis fit, and if so, which side direction along that axis to choose. If necessary, it will fallback to the other direction.
'none'
'none'
signals that no fallback to the opposite axis should take place.'start'
'start'
represents'top'
'top'
or'left'
'left'
.'end'
'end'
represents'bottom'
'bottom'
or'right'
'right'
.
For instance, by default, if the initial placement
placement
is set to 'right'
'right'
, then the placements to try (in order)
are:
['right', 'left']
['right', 'left']
On a narrow viewport, it’s possible or even likely that neither of these will fit.
By specifying a string other than 'none'
'none'
, you allow
placements along the perpendicular axis of the initial placement
to be tried. The direction determines which side of placement is
tried first:
flip({
fallbackAxisSideDirection: 'start',
});
flip({
fallbackAxisSideDirection: 'start',
});
The above results in: ['right', 'left', 'top', 'bottom']
['right', 'left', 'top', 'bottom']
.
flip({
fallbackAxisSideDirection: 'end',
});
flip({
fallbackAxisSideDirection: 'end',
});
The above results in: ['right', 'left', 'bottom', 'top']
['right', 'left', 'bottom', 'top']
.
As an example, if you’d like a tooltip that has a placement of
'right'
'right'
to be placed on top on mobile (assuming it doesn’t
fit), then you’d use 'start'
'start'
. For an interactive popover,
you likely want to use 'end'
'end'
so it’s placed on the bottom,
closer to the user’s fingers.
In each of the following demos, the placement
placement
is
'right'
'right'
.
Notice that it can overflow.
Notice that it prefers top
if it doesn’t fit.
Notice that it prefers bottom
if it doesn’t fit.
Combining with shift
If shift()
shift()
is in use in the middleware array, you may
desire to disable crossAxis
crossAxis
overflow checking,
which will allow shift()
shift()
to perform its work without
falling back to the opposite axis (therefore preserving the
original axis as best as possible):
const middleware = [
flip({
fallbackAxisSideDirection: 'start',
crossAxis: false,
}),
shift(),
];
const middleware = [
flip({
fallbackAxisSideDirection: 'start',
crossAxis: false,
}),
shift(),
];
If your placement is not edge aligned, disabling it is usually a good choice.
flipAlignment
default: true
true
When an alignment is specified, e.g. 'top-start'
'top-start'
instead
of just 'top'
'top'
, this will flip to 'top-end'
'top-end'
if
start
start
doesn’t fit.
flip({
flipAlignment: false,
});
flip({
flipAlignment: false,
});
When using this with the shift()
shift()
middleware, ensure
flip()
flip()
is placed before shift()
shift()
in your
middleware array. This ensures the flipAlignment
flipAlignment
logic can act before shift()
shift()
’s does.
fallbackPlacements
default: [oppositePlacement]
[oppositePlacement]
This describes an explicit array of placements to try if the
initial placement
placement
doesn’t fit on the axes in which
overflow is checked.
flip({
fallbackPlacements: ['right', 'bottom'],
});
flip({
fallbackPlacements: ['right', 'bottom'],
});
In the above example, if placement
placement
is set to
'top'
'top'
, then the placements to try (in order) are:
['top', 'right', 'bottom']
['top', 'right', 'bottom']
fallbackStrategy
default: 'bestFit'
'bestFit'
When no placements fit, then you’ll want to decide what happens.
'bestFit'
'bestFit'
will use the placement which fits best on the
checked axes. 'initialPlacement'
'initialPlacement'
will use the initial
placement
placement
specified.
flip({
fallbackStrategy: 'initialPlacement',
});
flip({
fallbackStrategy: 'initialPlacement',
});
…detectOverflowOptions
All of detectOverflow
’s options
can be passed. For instance:
flip({
padding: 5, // 0 by default
});
flip({
padding: 5, // 0 by default
});
Deriving options from state
You can derive the options from the middleware lifecycle state:
flip((state) => ({
padding: state.rects.reference.width,
}));
flip((state) => ({
padding: state.rects.reference.width,
}));
Final placement
The placement returned from the function is always the final one, not necessarily the one you passed in as the “preferred” one.
computePosition(referenceEl, floatingEl, {
placement: 'bottom',
middleware: [flip()],
}).then(({placement}) => {
console.log(placement); // 'top' or 'bottom'
});
computePosition(referenceEl, floatingEl, {
placement: 'bottom',
middleware: [flip()],
}).then(({placement}) => {
console.log(placement); // 'top' or 'bottom'
});
Conflict with autoPlacement
flip()
flip()
and autoPlacement()
autoPlacement()
cannot be used together
inside the same middleware array, make sure you choose only one
of them to use.
The reason is they both try to perform work on the placement but with opposing strategies. Therefore, they will continually try to change the result or work of the other one, leading to a reset loop.
flip()
flip()
— Uses a fallback “no space” strategy. Ensures the preferred placement is kept unless there is no space left.autoPlacement()
autoPlacement()
— Uses a primary “most space” strategy. Always chooses the placement with the most space available.