Skip to content

Inner

You may want to position the floating element such that an inner element inside of it is anchored to the reference element. The most common use case for this is a macOS-style select menu, where the selected item pops up directly over the target button.

Usage

There are two modules: inner()inner() and useInnerOffset()useInnerOffset().

  • inner()inner() is a middleware that positions the floating element such that an inner element inside of it is anchored to the reference element.

  • useInnerOffset()useInnerOffset() is an interaction hook that offsets the anchoring upon a wheelwheel event. This allows the floating element’s height to expand, revealing more list items. This is not strictly required but is necessary to match the UX of the macOS-style select menu.

import {inner, useInnerOffset} from '@floating-ui/react';
 
// ...
const {context} = useFloating({
  middleware: [inner()],
});
 
const innerOffset = useInnerOffset(context);
 
const {getReferenceProps, getFloatingProps} = useInteractions([
  innerOffset,
]);
import {inner, useInnerOffset} from '@floating-ui/react';
 
// ...
const {context} = useFloating({
  middleware: [inner()],
});
 
const innerOffset = useInnerOffset(context);
 
const {getReferenceProps, getFloatingProps} = useInteractions([
  innerOffset,
]);

Examples

inner Props

interface Props extends DetectOverflowOptions {
  listRef: React.MutableRefObject<Array<HTMLElement | null>>;
  index: number;
  offset?: number;
  overflowRef?: React.MutableRefObject<SideObject | null>;
  scrollRef?: React.MutableRefObject<HTMLElement | null>;
  referenceOverflowThreshold?: number;
  minItemsVisible?: number;
  onFallbackChange?: (fallback: boolean) => void;
}
interface Props extends DetectOverflowOptions {
  listRef: React.MutableRefObject<Array<HTMLElement | null>>;
  index: number;
  offset?: number;
  overflowRef?: React.MutableRefObject<SideObject | null>;
  scrollRef?: React.MutableRefObject<HTMLElement | null>;
  referenceOverflowThreshold?: number;
  minItemsVisible?: number;
  onFallbackChange?: (fallback: boolean) => void;
}

listRef

Required

default: [][]

An ref containing an array of list items inside the floating element.

const listRef = useRef([]);
 
inner({
  listRef,
});
const listRef = useRef([]);
 
inner({
  listRef,
});

index

Required

default: 00

The index of the list item that should be anchored to the reference element.

const [index, setIndex] = useState(0);
 
inner({
  index,
});
const [index, setIndex] = useState(0);
 
inner({
  index,
});

overflowRef

default: undefinedundefined

A ref containing a SideObjectSideObject. This is used to determine the overflow of the floating element and is required when using the useInnerOffset()useInnerOffset() interaction hook.

const overflowRef = useRef({
  top: 0,
  bottom: 0,
  left: 0,
  right: 0,
});
 
inner({
  overflowRef,
});
const overflowRef = useRef({
  top: 0,
  bottom: 0,
  left: 0,
  right: 0,
});
 
inner({
  overflowRef,
});

scrollRef

default: undefinedundefined

An optional ref containing an HTMLElementHTMLElement. This may be used as the scrolling container instead of the floating element — for instance, to position inner elements as direct children without being interfered by scrolling layout.

referenceOverflowThreshold

default: 00

This determines the distance in pixels of the reference element from the edges of the boundary. If the reference element is too close to the edges of its clipping boundary, onFallbackChangeonFallbackChange will be invoked to use fallback positioning.

inner({
  referenceOverflowThreshold: 20,
});
inner({
  referenceOverflowThreshold: 20,
});

minItemsVisible

default: 44

Specifies the minimum number of items that should be visible before fallback positioning is used. onFallbackChangeonFallbackChange will be invoked if there are less than the number specified visible.

inner({
  minItemsVisible: 10,
});
inner({
  minItemsVisible: 10,
});

offset

default: 00

Determines the offset of the anchoring.

inner({
  offset: 10,
});
inner({
  offset: 10,
});

onFallbackChange

default: no-op

A callback that is invoked with a boolean when positioning should enter fallback mode. You should fallback to middleware that create standard anchor positioning when truetrue.

const [fallback, setFallback] = useState(false);
 
inner({
  onFallbackChange: setFallback,
});
const [fallback, setFallback] = useState(false);
 
inner({
  onFallbackChange: setFallback,
});

useInnerOffset Props

interface Props extends DetectOverflowOptions {
  enabled?: boolean;
  overflowRef: React.MutableRefObject<SideObject | null>;
  scrollRef?: React.MutableRefObject<HTMLElement | null>;
  onChange: (
    offset: number | ((offset: number) => number)
  ) => void;
}
interface Props extends DetectOverflowOptions {
  enabled?: boolean;
  overflowRef: React.MutableRefObject<SideObject | null>;
  scrollRef?: React.MutableRefObject<HTMLElement | null>;
  onChange: (
    offset: number | ((offset: number) => number)
  ) => void;
}

enabled

default: truetrue

Conditionally enable/disable the hook.

useInnerOffset(context, {
  enabled: false,
});
useInnerOffset(context, {
  enabled: false,
});

overflowRef

Required

default: undefinedundefined

A ref containing a SideObjectSideObject. This is used to determine the overflow of the floating element.

const overflowRef = useRef({
  top: 0,
  bottom: 0,
  left: 0,
  right: 0,
});
 
useInnerOffset(context, {
  overflowRef,
});
const overflowRef = useRef({
  top: 0,
  bottom: 0,
  left: 0,
  right: 0,
});
 
useInnerOffset(context, {
  overflowRef,
});

scrollRef

default: undefinedundefined

An optional ref containing an HTMLElementHTMLElement. This may be used as the scrolling container instead of the floating element — for instance, to position inner elements as direct children without being interfered by scrolling layout.

onChange

Required

default: no-op

A callback that is invoked with a new offset upon the wheelwheel event to offset the anchoring, and thus expand the floating element’s height.

const [offset, setOffset] = useState(0);
 
const {context} = useFloating({
  middleware: [
    inner({
      // ...
      offset,
    }),
  ],
});
 
// ...
useInnerOffset(context, {
  onChange: setOffset,
});
const [offset, setOffset] = useState(0);
 
const {context} = useFloating({
  middleware: [
    inner({
      // ...
      offset,
    }),
  ],
});
 
// ...
useInnerOffset(context, {
  onChange: setOffset,
});