Skip to content

Platform

Anchors’ core is essentially a bunch of mathematical calculations.

These calculations are pure and agnostic, allowing Anchors to work on any platform that can execute JavaScript.

To make it work with a given platform, methods are used to allow it to hook into measurement APIs, for instance, to measure the bounding box of a given element.

Possible platforms other than the DOM include React Native, Canvas/WebGL, etc.

This is Floating UI running in a pure <canvas /><canvas /> element!

Check it out on CodeSandbox.

Custom object

If you’re building a platform from scratch, e.g. your own tiny custom DOM platform, you’ll be using the @floating-ui/core package — see Methods.

If you’re extending or customizing the existing DOM methods, and are using @floating-ui/dom, this is accessible via the platform import:

import {platform} from '@floating-ui/dom';
 
computePosition(referenceEl, floatingEl, {
  platform: {
    ...platform,
    // Overwrite the methods above with your own.
  },
});
import {platform} from '@floating-ui/dom';
 
computePosition(referenceEl, floatingEl, {
  platform: {
    ...platform,
    // Overwrite the methods above with your own.
  },
});

Shadow DOM fix

There’s an unfortunate platform gap regarding offsetParent where the spec says to hide the actual CSS offsetParent inside shadow DOM hierarchies, notable when using web components. This causes the position to be wrong when a positioned parent has a shadowed child floating element. Since Chrome 109, the offsetParent property now matches the spec, as well as Safari and Firefox for quite some time, which causes the positioning issue.

In many cases, using the 'fixed''fixed' strategy instead of 'absolute''absolute' will fix this positioning issue, but in case it does not, you can add the following “ponyfill”:

npm i composed-offset-position
npm i composed-offset-position
import {platform} from '@floating-ui/dom';
import {offsetParent} from 'composed-offset-position';
 
computePosition(referenceEl, floatingEl, {
  platform: {
    ...platform,
    getOffsetParent: (element) =>
      platform.getOffsetParent(element, offsetParent),
  },
});
import {platform} from '@floating-ui/dom';
import {offsetParent} from 'composed-offset-position';
 
computePosition(referenceEl, floatingEl, {
  platform: {
    ...platform,
    getOffsetParent: (element) =>
      platform.getOffsetParent(element, offsetParent),
  },
});

Concepts

The library works largely with a RectRect:

interface Rect {
  width: number;
  height: number;
  x: number;
  y: number;
}
interface Rect {
  width: number;
  height: number;
  x: number;
  y: number;
}

This data can come from anywhere, and the library will perform the right computations. xx and yy represent the coordinates of the element relative to another one.

import {computePosition} from '@floating-ui/core';
 
computePosition(referenceElement, floatingElement, {
  platform: {
    // ...
  },
});
import {computePosition} from '@floating-ui/core';
 
computePosition(referenceElement, floatingElement, {
  platform: {
    // ...
  },
});

Methods

A platformplatform is a plain object consisting of 3 required and 7 optional methods. These methods allow the platform to interface with Floating UI’s logic.

Each of these methods can be either async or sync. This enables support of platforms whose measurement APIs are async, like React Native.

Required methods

getElementRects

Takes in the elements and the positioning strategystrategy and returns the element RectRect objects.

function getElementRects({reference, floating, strategy}) {
  return {
    reference: {width: 0, height: 0, x: 0, y: 0},
    floating: {width: 0, height: 0, x: 0, y: 0},
  };
}
function getElementRects({reference, floating, strategy}) {
  return {
    reference: {width: 0, height: 0, x: 0, y: 0},
    floating: {width: 0, height: 0, x: 0, y: 0},
  };
}

reference

The xx and yy values of a reference RectRect should be its coordinates relative to the floating element’s offsetParent element if required rather than the viewport.

floating

Both xx and yy are not relevant initially, so you can set these both of these to 00.

getDimensions

Returns the dimensions of an element.

function getDimensions(element) {
  return {width: 0, height: 0};
}
function getDimensions(element) {
  return {width: 0, height: 0};
}

getClippingRect

Returns the RectRect (relative to the viewport) whose outside bounds will clip the given element. For instance, the viewport itself.

function getClippingRect({element, boundary, rootBoundary}) {
  return {
    width: 0,
    height: 0,
    x: 0,
    y: 0,
  };
}
function getClippingRect({element, boundary, rootBoundary}) {
  return {
    width: 0,
    height: 0,
    x: 0,
    y: 0,
  };
}

Optional methods

Depending on the platform you’re working with, these may or may not be necessary.

convertOffsetParentRelativeRectToViewportRelativeRect

This function will take a RectRect that is relative to a given offsetParentoffsetParent element and convert its xx and yy values such that it is instead relative to the viewport.

function convertOffsetParentRelativeRectToViewportRelativeRect({
  rect,
  offsetParent,
  strategy,
}) {
  return rect;
}
function convertOffsetParentRelativeRectToViewportRelativeRect({
  rect,
  offsetParent,
  strategy,
}) {
  return rect;
}

getOffsetParent

Returns the offsetParent of a given element. The following four properties are what is accessed on an offsetParent.

function getOffsetParent(element, polyfill) {
  return {
    clientWidth: 0,
    clientHeight: 0,
    clientLeft: 0,
    clientTop: 0,
  };
}
function getOffsetParent(element, polyfill) {
  return {
    clientWidth: 0,
    clientHeight: 0,
    clientLeft: 0,
    clientTop: 0,
  };
}

The polyfillpolyfill parameter exists only for @floating-ui/dom and is optional to fix the Shadow DOM bug.

getDocumentElement

Returns the document element.

function getDocumentElement(element) {
  return {};
}
function getDocumentElement(element) {
  return {};
}

getClientRects

Returns an array of ClientRectClientRects.

function getClientRects(element) {
  return [];
}
function getClientRects(element) {
  return [];
}

isElement

Determines if the current value is an element.

function isElement(value) {
  return true;
}
function isElement(value) {
  return true;
}

isRTL

Determines if an element is in RTL layout.

function isRTL(element) {
  return false;
}
function isRTL(element) {
  return false;
}

getScale

Determines the scale of an element.

function getScale(element) {
  return {x: 1, y: 1};
}
function getScale(element) {
  return {x: 1, y: 1};
}

Usage

All these methods are passed to platformplatform:

import {computePosition} from '@floating-ui/core';
 
computePosition(referenceEl, floatingEl, {
  platform: {
    // Required
    getElementRects,
    getDimensions,
    getClippingRect,
 
    // Optional
    convertOffsetParentRelativeRectToViewportRelativeRect,
    getOffsetParent,
    getDocumentElement,
    getClientRects,
    isElement,
    isRTL,
    getScale,
  },
});
import {computePosition} from '@floating-ui/core';
 
computePosition(referenceEl, floatingEl, {
  platform: {
    // Required
    getElementRects,
    getDimensions,
    getClippingRect,
 
    // Optional
    convertOffsetParentRelativeRectToViewportRelativeRect,
    getOffsetParent,
    getDocumentElement,
    getClientRects,
    isElement,
    isRTL,
    getScale,
  },
});