Middleware

shift

Keeps the floating element within the available viewport boundary.

shift nudges the floating element along its placement axis to keep it visible inside the boundary. Use it to preserve the chosen placement while adjusting coordinates to prevent clipping against viewport edges.

Type

function shift(options?: ShiftOptions): Middleware;

interface ShiftOptions {
  mainAxis?: boolean;
  crossAxis?: boolean;
  limiter?: {
    fn: (state: MiddlewareState) => Coords;
    options?: unknown;
  };
  padding?: Padding;
  boundary?: Boundary;
  rootBoundary?: RootBoundary;
  elementContext?: ElementContext;
  altBoundary?: boolean;
}

Options

NameTypeDefaultNotes
mainAxisbooleantrueAllows shifting along the placement's cross-direction to prevent clipping against side edges.
crossAxisbooleanfalseAllows shifting along the placement direction (e.g. moving closer to or further from the anchor).
limiter{ fn, options? }undefinedConstrains shifting so the panel never slides beyond the anchor edge (e.g. limitShift()).
paddingPadding0Inset padding from the clipping viewport boundary.
boundaryBoundary"clippingAncestors"Clipping boundary element or rect.
rootBoundaryRootBoundary"viewport"Root boundary context ("viewport" or "document").
altBoundarybooleanfalseWhen true, checks boundaries of the floating element instead of the anchor.

Returns

shift returns a Middleware object with name: "shift". It writes { x: number, y: number } adjustments to middlewareData.value.shift.

Details

Pipeline Placement

shift runs after flip:

  1. offset creates separation.
  2. flip picks the placement side with adequate room.
  3. shift adjusts coordinates within that side to prevent the floating panel from overflowing the viewport boundaries.

Running shift after flip ensures that your surface only slides sideways after confirming that the current side fits.

Example

<script setup lang="ts">
import { ref } from "vue";
import { useFloatingNode, usePosition } from "v-float";

const anchorEl = ref<HTMLElement | null>(null);
const floatingEl = ref<HTMLElement | null>(null);

const node = useFloatingNode({ anchorEl, floatingEl });
usePosition(node, {
  placement: "bottom-start",
  middlewares: {
    offset: 8,
    flip: true,
    shift: { padding: 12, crossAxis: false },
  },
});
</script>

<template>
  <button ref="anchorEl">Anchor</button>
  <div v-if="node.open" ref="floatingEl">
    Shifted floating content stays within viewport padding
  </div>
</template>

See Also

  • flip - Chooses alternate placements when space is constrained
  • offset - Adds spacing before shift checks run
  • size - Resizes the floating element when shifting is insufficient
  • Keep Content in View - Guide to collision and viewport management

Released under the MIT License. Copyright © 2026