@codastic/react-positioning-portal

中文文档
0.8.0发布于 2025/11/18MITTypeScriptESM + CJS零依赖近 30 天下载 2.5万

The positioning portal is a low level React component to build all kinds of absolutely positioned flyouts which are anchored to another element in the viewport. This can be used to create dropdowns, tooltips, context menus, etc.

原始使用文档
文档目录10 节
  1. Installation
  2. Usage
  3. Features
  4. <PositioningPortal />
  5. <PositioningPortalWithState />
  6. API Reference
  7. <PositioningPortal />
  8. <PositioningPortalWithState />
  9. PositioningStrategy
  10. Examples

React Positioning Portal

Build Status NPM

The positioning portal is a low level React component to build all kinds of absolutely positioned flyouts which are anchored to another element in the viewport. This can be used to create dropdowns, tooltips, context menus, etc.

The positioning portal is build as unopinionated as possible which means, it creates only mininmal DOM and does not have any dependencies to specific styling libraries. It only uses some inline styles on the portal for positioning.

How the portal is positioned in relation to its anchor in the DOM is defined by a positioning strategy. The default positioning strategy should be enough for a lot of use cases. It either positions the portal above (left or right aligned) to the anchor or below depending on the available space in the current viewport. This default strategy is perfectly suited to build e.g. custom dropdowns.

It is also possible to overwrite the positioning strategy. An example for a tooltip is shown in storybook.

See storybook for more examples.

Installation

$ npm install @codastic/react-positioning-portal react react-dom --save

Usage

Basic usage of <PositioningPortal /> where state is handled outside.

import { useState } from 'react';
import { PositioningPortal } from '@codastic/react-positioning-portal';

// ...

const [isPortalOpen, setIsPortalOpen] = useState(false);

<PositioningPortal
  isOpen={isPortalOpen}
  onOpen={() => setIsPortalOpen(true)}
  onShouldClose={() => setIsPortalOpen(false)}
  portalContent={
    <div>
      Portal content goes here.
      <button type="button" onClick={() => setIsPortalOpen(false)}>
        Close
      </button>
    </div>
  }
>
  <button type="button" onClick={() => setIsPortalOpen(true)}>
    Open portal
  </button>
</PositioningPortal>

Basic usage of <PositioningPortalWithState /> which handles state inside.

import { PositioningPortalWithState } from '@codastic/react-positioning-portal';

// ...

<PositioningPortalWithState
  portalContent={
    <div>
      Portal content goes here.
    </div>
  }
>
  {({ open}) => (
    <button type="button" onClick={open}>
      Open portal
    </button>
  )}
</PositioningPortalWithState>

Features

<PositioningPortal />

  • Supports custom positioning strategies.
  • State handled outside the PositioningPortal.
  • Handles outside click to close the portal.
  • Handles key down to close the portal.
  • Portal transitions when opening and closing.
  • Define a custom root node for the portal.

<PositioningPortalWithState />

This component is almost feature equivalent to <PositioningPortal /> with the exception that the isOpen state is already handled by the component itself. So if you don't have to control this state outside it can be easier to use <PositioningPortalWithState /> and let it handle the state itself.

API Reference

<PositioningPortal />

  • children: React.ReactNode:

    The PositioningPortal component takes the portal's anchor element as children. Its width is passed to the portalContent render prop argument relatedWidth.

  • portalElement?: React.ReactElement: (default: <div />)

    The portalElement wraps the portalContent. When rendering the portalContent the PositioningPortal appends the styles position, width, left, top and visibility to the portalElement. By defining your own styles on the portalElement you can overwrite those styles. E.g. if you prefer a fixed position for the portal you can do the following:

      <PositioningPortal
        portalElement={<div style={{ position: 'fixed' }} />}
      >
      // ...
    
  • portalContent: React.ReactNode | ((params: PortalContentRenderProps<Strategy>) => React.ReactNode);:

    Actual content rendered when the portal is open. portalContent can be any react node or a function returning a react node. The function receives the following props:

    interface PortalContentRenderProps<Strategy> {
      close: () => void; // Basically calls the handler passed to onShouldClose
      isOpen: boolean;
      isPositioned: boolean; // Becomes true after the portalContent is positioned and visible
      strategy: Strategy; // Whatever the positionStrategy returns
      relatedWidth: number; // The width of the PositioningPortal children
      transitionStarted: () => void; // Signals that there is a transition
      transitionEnded: () => void; // Should be called when the portal can safely be removed from the DOM
    }
    
  • onOpen: () => void:

    Will be called after the portal content is positioned and visible.

  • onClose?: () => void

    Callback when portal closes.

  • onShouldClose?: () => void:

    This gets called if PortalContentRenderProps.close gets called, by clicking outside the portal content (in case closeOnOutsideClick is true) or if closeOnKeyDown returns true.

  • closeOnOutsideClick?: boolean: (default: true)

    If set to true, onShouldClose gets called by clicking outside the portal content.

  • closeOnKeyDown?: (event: KeyboardEvent) => boolean: (default: event => event.keyCode === 27, close on ESC)

    A function, which will be called on keydown. If it returns true, the handler passed to onShouldClose will be called.

  • isOpen?: boolean:

    Sets the PositioningPortal to be open or closed.

  • positionStrategy?: PositioningStrategy<Strategy>:

    Sets a custom PositioningStrategy. For details see the PositioningStrategy section.

  • rootNode?: HTMLElement:

    Pass a custom root node for the portal to be added to.

<PositioningPortalWithState />

Extends properties of <PositioningPortal /> and adds/changes the folling properties:

  • children: React.ReactNode | ((params: RenderProps) => React.ReactNode):

    Since state is handled inside of the component, it is possible to render the children via a render function and to receive the following render props:

    interface RenderProps {
      close: () => void;
      open: () => void;
      isOpen: boolean;
    }
    

PositioningStrategy

The positioning strategy is a function which receives the rectangle of the parent anchor element parentRect relative to the viewport (measured with getBoundingClientRect()) and the rectangle for the portal. During the first render phase the portal is rendered invisible until the positioning strategy has been called.

The strategy function should return the position for the portal (top and left). I can optionally return other arbitrary data (strategy) which can be used to customize the rendering of the portal. E.g. to define which orientation the arrow of a tooltip should have.

const positionStrategy (
  parentRect,
  portalRect
  props
) => {
  // Compute where the portal should be positioned...

  return {
    top,
    left,
    strategy
  };
};

This is the type definition of a positioning strategy:

export type PositioningStrategy<Strategy> = (
  parentRect: ClientRect,
  portalRect: ClientRect,
  props: Props<Strategy>
) => {
  top: number;
  left: number;
  strategy: Strategy;
};

Examples

版本与发布记录

@codastic/react-positioning-portal 共发布 10 个版本,首次发布于 2020/04/10;过去 12 个月发布了 1 个版本。

版本发布日期距今
0.8.0latest2025/11/1811 个月前
0.7.02023/07/053 年前
0.6.02022/12/063 年前
0.5.02022/05/304 年前
0.4.02021/09/015 年前
0.3.22020/05/116 年前

关于 @codastic/react-positioning-portal 的常见问题

@codastic/react-positioning-portal 是什么?

The positioning portal is a low level React component to build all kinds of absolutely positioned flyouts which are anchored to another element in the viewport. This can be used to create dropdowns, tooltips, context menus, etc.

如何安装 @codastic/react-positioning-portal?

在项目目录运行 npm install @codastic/react-positioning-portal,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^16.12.0 || ^17.0.0 || ^18.0.0)、react-dom(^16.12.0 || ^17.0.0 || ^18.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@codastic/react-positioning-portal 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@codastic/react-positioning-portal 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@codastic/react-positioning-portal 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=16.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@codastic/react-positioning-portal 可以商用吗?

@codastic/react-positioning-portal 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@codastic/react-positioning-portal 的下载量有多少?

根据 npm 官方下载统计,@codastic/react-positioning-portal 在 2026/10/10 – 2026/11/08 这 30 天内被下载 25,204 次,日均约 840 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@codastic/react-positioning-portal 还在维护吗?

最新版本 0.8.0 发布于 2025/11/18(11 个月前);至今共发布 10 个版本,其中过去 12 个月发布了 1 个;首次发布于 2020/04/10。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。