块编辑器开发文档

💡 云策文档标注

概述

@wordpress/ui 是一个实验性的 React UI 组件包,基于 WordPress 设计系统构建,提供主题化设计令牌支持。它强调组件间的用户和开发者一致性,与 @wordpress/components 相比,它更注重设计系统实现,且仅通过 npm 分发。

关键要点

  • 实验性包:早期实现,可能发生重大变更
  • 设计系统实现:提供主题化 React 组件,确保组件间一致性
  • 与 @wordpress/components 区别:不通过 window.wp 全局提供,仅作为 npm 包分发
  • 依赖 @wordpress/theme:提供设计令牌和主题系统
  • 安装:通过 npm install @wordpress/ui 安装
  • 设置:在标准 WordPress 编辑器中无需额外设置;其他场景需加载设计令牌样式表并添加隔离样式
  • 核心设计原则:支持自定义渲染、Ref 转发、自动 className 合并、受控和非受控模式
  • 受控与非受控模式:使用 defaultX、x 和 onXChange 命名约定
  • 组件作者指南:设计新组件时应遵循命名和文档规范
  • 贡献:作为 Gutenberg 项目的一部分,遵循 monorepo 结构

代码示例

import { Stack } from '@wordpress/ui';

function MyComponent() {
    return (
        <Stack gap="sm">
            <div>Item 1</div>
            <div>Item 2</div>
        </Stack>
    );
}

注意事项

  • 实验性包:使用前需注意可能的不兼容变更
  • 设置依赖:在非 WordPress 编辑器环境中,需安装 @wordpress/theme 并加载设计令牌样式表
  • 样式隔离:为正确显示弹出层,需在根元素添加 isolation: isolate 样式
  • 全局样式:为支持覆盖元素,需在 body 添加 position: relative 样式
  • 受控模式优先级:当同时提供 x 和 defaultX 时,x 优先,组件进入受控模式
  • 事件处理:推荐使用 onXChange 而非原生 onChange,以简化跨组件使用

📄 原文内容
This package is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes.

A package that provides React UI components for the WordPress Design System, built on themeable design tokens.

While similar in scope to @wordpress/components, there are a few key differences:

  • @wordpress/components grew organically as a collection of unrelated UI elements for WordPress screens. In contrast, this package is an implementation of a design system that guarantees user- and developer-facing cohesion between components.
  • Unlike @wordpress/components, this package is not bundled as a WordPress script available on the window.wp global and is instead distributed as an npm package that follows semantic versioning for release changes.

This is a companion to the @wordpress/theme package that provides:

  • Design Tokens: A comprehensive system of design tokens for colors, spacing, typography, and more
  • Theme System: A flexible theming provider for consistent theming across applications

Installation

Install using NPM:

npm install @wordpress/ui

Setup

As an implementation of the design system and companion to the @wordpress/theme package, these components depend on CSS custom properties defined by the theme package. What you need to set up depends on whether you’re building for a WordPress context, and how much of the theming features you want to use.

Within standard WordPress editor screens

In standard WordPress editor screens (such as the post editor or the site editor), stylesheets, isolation styles, and layout styles are managed centrally by Gutenberg. You don’t need to add any setup yourself — and you should avoid doing so in this shared context to prevent conflicts.

Elsewhere

The components ship with built-in fallback values for all CSS custom properties, so they work out of the box without any theme setup. For full theming capabilities, it’s recommended that you install and load the design tokens stylesheet:

npm install @wordpress/theme
import '@wordpress/theme/design-tokens.css';

This stylesheet is universal and does not have a separate RTL version.

Also, to ensure that portaled popovers appear correctly, add these isolation styles to your application’s layout root element:

.root {
    isolation: isolate;
}

Finally, in order to support overlay elements such as backdrops to correctly cover the whole browser viewport even when scrolled, add the following style to your global styles:

body {
    position: relative;
}

Usage

Basic Component Usage

import { Stack } from '@wordpress/ui';

function MyComponent() {
    return (
        <Stack gap="sm">
            <div>Item 1</div>
            <div>Item 2</div>
        </Stack>
    );
}

Core Design Principles

All components in the design system follow consistent patterns for maximum flexibility and developer experience:

Custom Rendering

Every component supports the render prop for complete control over the underlying HTML element:

import { Stack } from '@wordpress/ui';

function MyComponent() {
    // Render Stack as a <section /> instead of the default <div />
    return <Stack render={ <section /> }>{ /* ... */ }</Stack>;
}

Ref Forwarding

All components forward refs to their underlying DOM elements:

import { useRef } from '@wordpress/element';
import { Stack } from '@wordpress/ui';

function MyComponent() {
    const stackRef = useRef< HTMLDivElement >( null );

    return <Stack ref={ stackRef }>{ /* ... */ }</Stack>;
}

Automatic className Merging

Components merge provided className props with their internal styles:

import { Stack } from '@wordpress/ui';

function MyComponent() {
    // Your custom CSS className is merged with component styles
    return <Stack className="my-custom-class">{ /* ... */ }</Stack>;
}

Controlled and Uncontrolled Modes

Interactive components that manage internal state (such as open/closed, selected value, etc.) follow a consistent prop naming convention that supports both controlled and uncontrolled usage.

Prop naming pattern

For a given state x, the convention is:

Prop Purpose
defaultX Sets the initial value in uncontrolled mode. The component manages subsequent state changes internally.
x Sets the current value in controlled mode. The consumer is responsible for updating the value in response to changes.
onXChange Callback invoked when the state changes. Receives the new value as its first argument. Works in both controlled and uncontrolled modes.

For example, a component with an open/closed state would expose:

  • defaultOpen — initial open state (uncontrolled)
  • open — current open state (controlled)
  • onOpenChange — called when the open state changes

And a component with a selectable value would expose:

  • defaultValue — initial value (uncontrolled)
  • value — current value (controlled)
  • onValueChange — called when the value changes

Uncontrolled usage

In uncontrolled mode, the component manages its own state. Use defaultX to set the initial value, and optionally onXChange to react to changes:

import { Tabs } from '@wordpress/ui';

function MyTabs() {
    return (
        <Tabs.Root
            defaultValue="tab1"
            onValueChange={ ( value ) => console.log( value ) }
        >
            <Tabs.List>
                <Tabs.Tab value="tab1">Tab 1</Tabs.Tab>
                <Tabs.Tab value="tab2">Tab 2</Tabs.Tab>
            </Tabs.List>
            <Tabs.Panel value="tab1">Content 1</Tabs.Panel>
            <Tabs.Panel value="tab2">Content 2</Tabs.Panel>
        </Tabs.Root>
    );
}

Controlled usage

In controlled mode, the consumer owns the state and passes it via x. State changes are handled through onXChange:

import { useState } from '@wordpress/element';
import { CollapsibleCard } from '@wordpress/ui';

function MyCard() {
    const [ isOpen, setIsOpen ] = useState( false );

    return (
        <CollapsibleCard.Root open={ isOpen } onOpenChange={ setIsOpen }>
            <CollapsibleCard.Header>Details</CollapsibleCard.Header>
            <CollapsibleCard.Content>
                Collapsible content here.
            </CollapsibleCard.Content>
        </CollapsibleCard.Root>
    );
}

When both x and defaultX are provided, x takes precedence and the component behaves in controlled mode. When neither is provided, the component uses its own internal default (typically documented via a @default JSDoc tag on the defaultX prop).

Difference from native onChange

The onXChange callback is distinct from the native DOM onChange event handler. Native onChange fires a React.ChangeEvent tied to a specific DOM element, while onXChange provides the new value directly — making it simpler to use and consistent across all components, including compound and non-form components.

Components that wrap native form elements may still support native event handlers (like onChange, onInput) for interoperability, but onXChange is the recommended approach within this package.

Guidelines for component authors

When designing props for a new component:

  • Always offer both controlled and uncontrolled modes when the component has user-facing state.
  • Name the uncontrolled prop defaultX, the controlled prop x, and the callback onXChange.
  • In JSDoc comments, indicate which mode each prop is for and cross-reference the alternative:
    “`ts
    /**

    • Whether the panel is currently open (controlled).
    • To render an uncontrolled component, use the defaultOpen prop instead.
      */
      open?: boolean;
      /**
    • Whether the panel is initially open (uncontrolled).
    • @default false
      */
      defaultOpen?: boolean;
      /**
    • Event handler called when the open state changes.
      */
      onOpenChange?: ( open: boolean ) => void;
      “`
  • Provide a @default JSDoc tag for the uncontrolled prop when there is a sensible default.

Contributing to this package

This is an individual package that’s part of the Gutenberg project. The project is organized as a monorepo. It’s made up of multiple self-contained software packages, each with a specific purpose. The packages in this monorepo are published to npm and used by WordPress as well as other software projects.

To find out more about contributing to this package or Gutenberg as a whole, please read the project’s main contributor guide.