Floating UI logo

Floating UI

用于 Tooltip、Popover、Dropdown 等浮层元素定位的底层 JavaScript 工具库,Popper.js 的官方继任者

ReactHeadless UI浮层定位Popper

Floating UI 是专注于浮层元素定位的 JavaScript 库,采用中间件管道架构,提供 React、Vue、DOM 多框架绑定,是众多 Headless UI 库的底层定位依赖。

Floating UI

用于 Tooltip、Popover、Dropdown 等浮层元素定位的底层 JavaScript 工具库,Popper.js 的官方继任者。

技术简介说明

Floating UI 是一个专注于浮层元素(floating elements)定位的 JavaScript 库,是 Popper.js 的官方继任者。它解决了一个核心问题:如何将一个元素(如 tooltip、popover、dropdown menu)精确地定位在另一个元素(reference element)旁边,同时处理边界溢出、视口碰撞和动态调整等复杂场景。

与 Headless 组件库不同,Floating UI 不做组件封装——它只提供纯定位逻辑。这意味着你可以将它与任何框架(React、Vue、Svelte、Vanilla JS)和任何样式方案搭配使用。这种"只做一件事并做好"的设计哲学使其成为众多 Headless UI 库(如 Radix UI、Headless UI、React Aria)的底层依赖。

Floating UI 由 Popper.js 原作者 Atomiks 和 mantained byFloating UI 团队重新设计,从零重写以解决 Popper.js 在架构上的局限性。它采用中间件(middleware)管道架构,每个中间件负责一个定位关注点(翻转、偏移、翻转检测等),具有极高的可扩展性。

基本信息

  • 官网https://floating-ui.com
  • GitHubhttps://github.com/floating-ui/floating-ui
  • License:MIT
  • 最新版本
    • @floating-ui/react v0.27.19(2026.3)
    • @floating-ui/dom v1.7.6(2026.3)
    • @floating-ui/core v1.7.5
    • @floating-ui/vue v2.0.0(2026.6)
  • npm 周下载量:4000 万+(所有包合计)
  • GitHub Stars:32,600+
  • 主要维护者:Atomiks(原创作者)、Floating UI 团队
  • 包体积@floating-ui/core ~5 KB gzipped;@floating-ui/react ~8 KB gzipped
  • TypeScript 支持:完整类型定义,内置 TS 编写

快速上手

安装

Floating UI 提供多种平台包,根据你的框架选择:

# DOM(Vanilla JS / 任意框架)
npm install @floating-ui/dom
 
# React
npm install @floating-ui/react
 
# Vue
npm install @floating-ui/vue
 
# React DOM(轻量版,不含交互逻辑)
npm install @floating-ui/react-dom

基础配置

DOM 版本

import { computePosition } from '@floating-ui/dom'
 
const reference = document.querySelector('#button')
const floating = document.querySelector('#tooltip')
 
computePosition(reference, floating, {
  placement: 'bottom',
  middleware: [offset(10), flip(), shift()],
}).then(({ x, y }) => {
  Object.assign(floating.style, {
    left: `${x}px`,
    top: `${y}px`,
  })
})

React 版本

import { useState } from 'react'
import {
  useFloating,
  useInteractions,
  useHover,
  useFocus,
  useDismiss,
  useRole,
  offset,
  flip,
  shift,
} from '@floating-ui/react'
 
function Tooltip() {
  const [isOpen, setIsOpen] = useState(false)
  const { refs, floatingStyles, context } = useFloating({
    open: isOpen,
    onOpenChange: setIsOpen,
    placement: 'bottom',
    middleware: [offset(8), flip(), shift()],
  })
 
  const hover = useHover(context)
  const focus = useFocus(context)
  const dismiss = useDismiss(context)
  const role = useRole(context, { role: 'tooltip' })
 
  const { getReferenceProps, getFloatingProps } = useInteractions([
    hover, focus, dismiss, role,
  ])
 
  return (
    <>
      <button ref={refs.setReference} {...getReferenceProps()}>
        悬停查看提示
      </button>
      {isOpen && (
        <div
          ref={refs.setFloating}
          style={floatingStyles}
          {...getFloatingProps()}
        >
          提示信息
        </div>
      )}
    </>
  )
}

最小示例

一个完整的 Tooltip 示例:

import { useState } from 'react'
import {
  useFloating,
  useHover,
  useInteractions,
  offset,
  flip,
  shift,
} from '@floating-ui/react'
 
function App() {
  const [open, setOpen] = useState(false)
  const { refs, floatingStyles } = useFloating({
    open,
    onOpenChange: setOpen,
    middleware: [offset(10), flip(), shift()],
  })
 
  const { getReferenceProps, getFloatingProps } = useInteractions([
    useHover({ open, onOpenChange: setOpen }),
  ])
 
  return (
    <>
      <button ref={refs.setReference} {...getReferenceProps()}>
        目标元素
      </button>
      {open && (
        <div
          ref={refs.setFloating}
          style={{ ...floatingStyles, background: '#333', color: '#fff', padding: '6px 12px', borderRadius: 6 }}
          {...getFloatingProps()}
        >
          浮层内容
        </div>
      )}
    </>
  )
}

核心概念与架构

三层架构

┌─────────────────────────────────────┐
│        @floating-ui/react           │  ← React 交互层(hooks、交互、无障碍)
├─────────────────────────────────────┤
│        @floating-ui/dom             │  ← DOM 层(平台适配、自动更新)
├─────────────────────────────────────┤
│        @floating-ui/core            │  ← 核心层(纯定位算法、中间件管道)
└─────────────────────────────────────┘
  • Core:纯函数,接收位置数据和中间件列表,返回计算后的坐标。无 DOM 依赖,可用于任何 JavaScript 环境。
  • DOM:为浏览器环境提供平台适配,包括 getBoundingClientRect 读取、ResizeObserver/IntersectionObserver 自动更新。
  • React / Vue:框架绑定层,提供 hooks 和组件封装。

中间件(Middleware)管道

核心定位流程:

reference 元素位置
  → offset()    // 偏移
  → flip()      // 自动翻转方向
  → shift()     // 平移避免溢出
  → size()      // 动态调整尺寸
  → arrow()     // 箭头定位
  → 最终坐标 (x, y)

每个中间件接收前一个中间件的输出,修改后传递给下一个。你可以编写自定义中间件来扩展行为。

交互 Hooks(React)

React 版本提供一套交互 hooks 来处理常见的浮层行为:

Hook职责
useHover鼠标悬停打开/关闭
useFocus键盘焦点打开/关闭
useClick点击切换打开/关闭
useDismiss点击外部/Escape 关闭
useRole自动注入 ARIA 角色属性
useListNavigation键盘方向键导航列表项
useTypeahead输入字符快速定位列表项
useInteractions合并所有交互 hooks 的属性

核心特性

  • 中间件架构:可插拔的定位管道,支持 offset、flip、shift、size、arrow 等内置中间件,也可自定义扩展
  • 框架无关核心@floating-ui/core 是纯函数,无 DOM 依赖,可适配任何框架
  • 多框架绑定:官方提供 React、Vue、DOM(Vanilla JS)三种绑定
  • 自动更新:内置 autoUpdate 监听 reference 元素的尺寸/位置变化,自动重新计算浮层位置
  • 完整的交互系统:React 版本提供 8 个交互 hooks,覆盖悬停、点击、焦点、键盘导航、角色注入等场景
  • 无障碍支持useRole hook 自动注入正确的 ARIA 属性和键盘交互
  • 虚拟元素支持:支持虚拟 reference 元素(如鼠标坐标定位的上下文菜单)
  • Shadow DOM 兼容:正确处理 Shadow DOM 内的浮层定位
  • TypeScript 原生:全部使用 TypeScript 编写,提供完整类型定义
  • 极致性能:核心包仅 ~5 KB gzipped,按需引入中间件,零不必要的开销

生态图

Floating UI 生态
│
├── 核心层
│   ├── @floating-ui/core        # 纯定位算法
│   ├── @floating-ui/utils       # 共享工具函数
│   └── @floating-ui/dom         # DOM 平台绑定
│
├── 框架绑定
│   ├── @floating-ui/react       # React 完整交互层
│   ├── @floating-ui/react-dom   # React 轻量定位层
│   └── @floating-ui/vue         # Vue 绑定
│
├── 社区生态
│   ├── headlessui-float         # Headless UI + Floating UI 桥接
│   ├── svelte-floating-ui       # Svelte 社区绑定
│   └── solid-floating-ui        # Solid 社区绑定
│
└── 作为底层依赖的库
    ├── Radix UI (Primitives)    # Tooltip、Popover、DropdownMenu 等
    ├── Headless UI (v2+)        # 内置锚点定位
    ├── React Aria               # 部分定位场景
    ├── shadcn/ui                # 通过 Radix 间接使用
    ├── Tippy.js                 # Tooltip 库
    └── Mantine                  # Popover 等组件

适用场景

  • Tooltip 提示框:最常见的浮层场景,Floating UI 提供完整的悬停/焦点/延迟控制
  • Dropdown Menu 下拉菜单:结合 useListNavigationuseTypeahead 实现完整的键盘导航菜单
  • Popover 弹出框:复杂浮层内容(表单、卡片)的定位和交互管理
  • Context Menu 右键菜单:使用虚拟元素(鼠标坐标)定位上下文菜单
  • Autocomplete / Combobox 自动补全:输入框下方的建议列表定位
  • Date Picker 日期选择器:日历浮层的定位和方向翻转
  • 自定义定位逻辑:任何需要将一个元素定位在另一个元素旁边的场景

开发与工程化

项目结构

Floating UI 采用 monorepo 结构:

floating-ui/
├── packages/
│   ├── core/          # 核心定位算法
│   ├── dom/           # DOM 平台适配
│   ├── react/         # React 完整绑定
│   ├── react-dom/     # React 轻量绑定
│   ├── vue/           # Vue 绑定
│   └── utils/         # 共享工具
├── .changeset/        # 版本管理
└── pnpm-workspace.yaml

开发命令

pnpm install        # 安装依赖
pnpm build          # 构建所有包
pnpm test           # 运行测试
pnpm lint           # 代码检查

自定义中间件开发

import type { Middleware } from '@floating-ui/core'
 
// 自定义中间件示例:限制浮层不超出特定容器
function myCustomMiddleware(): Middleware {
  return {
    name: 'myCustom',
    fn(state) {
      const { placement, rects, middlewareData } = state
      // 自定义逻辑
      return {
        data: { /* 可被后续中间件访问 */ },
        x: 0,  // x 轴偏移修正
        y: 0,  // y 轴偏移修正
      }
    },
  }
}

CI/CD 集成

  • Changesets 管理版本发布
  • GitHub Actions 自动测试和发布
  • Visual Regression Testing 确保定位精度

性能与安全

性能特点

  • 极致轻量@floating-ui/core gzipped 仅 ~5 KB,@floating-ui/react ~8 KB
  • Tree-shakable:所有中间件按需引入
  • 零依赖:核心无外部依赖
  • 高效重计算:使用 ResizeObserver + requestAnimationFrame 实现 60fps 更新
  • 批处理更新:React 版本合并多次位置更新,减少不必要的渲染

性能优化建议

  1. 使用 @floating-ui/react-dom:如果不需要交互逻辑(如纯展示 tooltip),使用轻量版
  2. 合理使用 autoUpdate:只在需要动态更新时启用,静态定位可手动计算一次
  3. 缓存中间件实例:避免在渲染函数中创建新的中间件数组
  4. 使用 whileElementsMounted 选项:控制自动更新的触发条件

安全注意事项

  • Floating UI 只处理定位,不涉及 HTML 内容渲染,XSS 风险由上层应用控制
  • useDismiss 的点击外部检测会正确处理 pointer-events: none 的元素
  • Shadow DOM 场景下需确保浮层和 reference 在同一 shadow tree 中

技术对比

特性Floating UIPopper.jsTippy.jsCSS position: fixed
定位能力✅ 完整✅ 完整✅ 完整(基于 Floating UI)⚠️ 基础
包体积~5 KB (core)~12 KB~15 KB0
框架绑定React / Vue / DOMDOM 为主DOM 为主通用
交互逻辑✅ 完整 hooks 体系✅ 内置
自定义扩展✅ 中间件管道✅ Modifier⚠️ 有限
维护状态✅ 活跃❌ 停止维护✅ 维护中(基于 Floating UI)-
无障碍✅ React hooks✅ 内置
虚拟元素

何时不需要 Floating UI

  • 纯 CSS 可解决的简单定位(如 position: absolute + 固定方向)
  • 不需要碰撞检测和动态调整的静态浮层
  • 已经通过 Headless UI 库间接使用(Radix UI、Headless UI 等已内置)

最佳实践

1. 合理选择包

需要交互逻辑?
├── 是 → @floating-ui/react(hooks 完整版)
└── 否 → @floating-ui/react-dom(纯定位,更轻量)
         或 → @floating-ui/dom(Vanilla JS)

2. 中间件最佳顺序

// 推荐顺序
middleware: [
  offset(8),    // 先偏移
  flip(),       // 再翻转
  shift(),      // 再平移
  size(),       // 调整尺寸
  arrow(),      // 最后箭头
]

3. 正确处理异步内容

// 浮层内容可能异步加载导致尺寸变化
const { refs, floatingStyles } = useFloating({
  middleware: [
    offset(8),
    flip(),
    shift(),
    size({
      apply({ rects, availableHeight, elements }) {
        Object.assign(elements.floating.style, {
          maxHeight: `${availableHeight}px`,
        })
      },
    }),
  ],
})

4. 与 Headless UI 库搭配使用

当 Headless UI 库的定位能力不足时,可以搭配 Floating UI:

npm install @floating-ui/react headlessui-float
import { useFloat } from 'headlessui-float/react'
import { Menu } from '@headlessui/react'
 
// Headless UI 组件 + Floating UI 定位

5. 性能优化

// 避免每次渲染创建新中间件数组
const middleware = useMemo(() => [
  offset(8),
  flip(),
  shift(),
], [])
 
const { refs, floatingStyles } = useFloating({
  middleware,
})

6. 正确处理销毁

// useFloating 自动处理清理,无需手动调用
// autoUpdate 在组件卸载时自动停止观察

技术局限与边界

已知限制

  1. 不处理内容渲染:Floating UI 只管定位,不管浮层内容的渲染、样式和无障碍
  2. 不包含组件封装:不提供 &lt;Tooltip&gt;&lt;Popover&gt; 等现成组件,需要自己组合
  3. 学习曲线:中间件系统和交互 hooks 需要一定时间理解
  4. 动画支持有限:浮层进出动画需要自己实现(可搭配 Framer Motion 等)
  5. 不处理滚动容器:跨滚动容器的定位需要额外配置

不适用场景

  • 需要完整组件封装的场景 → 使用 Radix UI、Headless UI 等
  • 简单的静态定位 → 使用 CSS 即可
  • 非浏览器环境(如 React Native)→ 使用 React Native 专属定位方案

与 Popper.js 的迁移注意事项

  • Floating UI 是 Popper.js 的完全重写,API 完全不同
  • 中间件对应 Popper 的 Modifier,但设计更简洁
  • createPopper() 对应 computePosition() + autoUpdate()
  • 详细迁移指南:https://floating-ui.com/docs/migration

学习资源

2026 年现状

版本状态

  • @floating-ui/react v0.27.19(2026 年 3 月)— 持续维护中
  • @floating-ui/dom v1.7.6 — 稳定
  • @floating-ui/vue v2.0.0(2026 年 6 月)— 新大版本
  • @floating-ui/core v1.7.5 — 稳定

发展趋势

  1. 性能持续优化:每个版本都在减小包体积和提升运行时性能
  2. React 19 兼容:已完全适配 React 19,修复了相关兼容性问题
  3. Vue 生态扩展@floating-ui/vue 2.0 正式发布,Vue 绑定日趋成熟
  4. 无障碍增强:持续改进 iOS/Safari/VoiceOver 等场景的无障碍支持
  5. Shadow DOM 支持:加强对 Shadow DOM 场景的处理

社区活跃度

  • GitHub 32,600+ stars,npm 周下载量 4000 万+
  • 作为众多 Headless UI 库的底层依赖,具有极高的生态影响力
  • 维护频率稳定,每月有 bugfix 和功能更新

未来展望

  • 预计将继续作为浮层定位领域的事实标准
  • 可能推出更多框架绑定(Solid、Svelte 官方支持)
  • 中间件生态将进一步丰富
  • 与 Headless UI 库的协作关系将持续深化