fluentui-react-v9-custom-components

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Building Custom Components with FluentUI v9 Base Hooks

基于FluentUI v9基础Hooks构建自定义组件

Base state hooks + render functions let you reuse Fluent's ARIA behavior, keyboard handling, and slot structure with completely custom styling. Imports come from individual component packages (e.g.
@fluentui/react-button
), not from
@fluentui/react-components
.
Your responsibility: custom styling, visible focus indicators, sufficient color contrast, and all visual accessibility. Base hooks provide ARIA structure but not visual a11y.
基础状态hooks + 渲染函数允许你复用Fluent的ARIA行为、键盘处理能力和插槽结构,同时实现完全自定义的样式。导入路径来自单独的组件包(例如
@fluentui/react-button
),而非
@fluentui/react-components
你需要自行负责: 自定义样式、可见的焦点指示器、足够的色彩对比度,以及所有视觉层面的可访问性。基础Hooks仅提供ARIA结构,不处理视觉可访问性。

The Pattern

开发模式

tsx
import * as React from 'react';
import { useButtonBase_unstable, renderButton_unstable } from '@fluentui/react-button';
import type { ButtonBaseProps, ButtonState } from '@fluentui/react-button';

type CustomButtonProps = ButtonBaseProps & {
  variant?: 'primary' | 'secondary' | 'tertiary';
  tone?: 'neutral' | 'success' | 'warning' | 'danger';
};

export const CustomButton = React.forwardRef<HTMLButtonElement, CustomButtonProps>(
  ({ variant = 'primary', tone = 'neutral', ...props }, ref) => {
    // 1. Get state: ARIA, keyboard handling, slot structure
    const state = useButtonBase_unstable(props, ref);

    // 2. Mutate state: apply your classes/styles
    state.root.className = ['btn', `btn--${variant}`, `btn--${tone}`].filter(Boolean).join(' ');
    if (state.icon) {
      state.icon.className = 'btn__icon';
    }

    // 3. Render using Fluent's render function
    return renderButton_unstable(state as ButtonState);
  },
);
tsx
import * as React from 'react';
import { useButtonBase_unstable, renderButton_unstable } from '@fluentui/react-button';
import type { ButtonBaseProps, ButtonState } from '@fluentui/react-button';

type CustomButtonProps = ButtonBaseProps & {
  variant?: 'primary' | 'secondary' | 'tertiary';
  tone?: 'neutral' | 'success' | 'warning' | 'danger';
};

export const CustomButton = React.forwardRef<HTMLButtonElement, CustomButtonProps>(
  ({ variant = 'primary', tone = 'neutral', ...props }, ref) => {
    // 1. Get state: ARIA, keyboard handling, slot structure
    const state = useButtonBase_unstable(props, ref);

    // 2. Mutate state: apply your classes/styles
    state.root.className = ['btn', `btn--${variant}`, `btn--${tone}`].filter(Boolean).join(' ');
    if (state.icon) {
      state.icon.className = 'btn__icon';
    }

    // 3. Render using Fluent's render function
    return renderButton_unstable(state as ButtonState);
  },
);

Available Hooks

可用Hooks

See
references/available-hooks.md
for the full inventory with import paths, prop/state types, and notes.
Quick reference:
ComponentHookRender functionPackage
Button
useButtonBase_unstable
renderButton_unstable
@fluentui/react-button
ToggleButton
useToggleButtonBase_unstable
renderToggleButton_unstable
@fluentui/react-button
TabList
useTabListBase_unstable
renderTabList_unstable
@fluentui/react-tabs
Tab
useTabBase_unstable
renderTab_unstable
@fluentui/react-tabs
Divider
useDividerBase_unstable
renderDivider_unstable
@fluentui/react-divider
Accordion
useAccordionBase_unstable
renderAccordion_unstable
@fluentui/react-accordion
AccordionPanel
useAccordionPanelBase_unstable
renderAccordionPanel_unstable
@fluentui/react-accordion
Toolbar
useToolbarBase_unstable
renderToolbar_unstable
@fluentui/react-toolbar
ToolbarButton
useToolbarButtonBase_unstable
renderToolbarButton_unstable
@fluentui/react-toolbar
Popover
usePopoverBase_unstable
renderPopover_unstable
@fluentui/react-popover
Persona
usePersonaBase_unstable
renderPersona_unstable
@fluentui/react-persona
Card
useCardBase_unstable
renderCard_unstable
@fluentui/react-card
完整的清单(含导入路径、属性/状态类型和说明)请查看
references/available-hooks.md
快速参考:
ComponentHookRender functionPackage
Button
useButtonBase_unstable
renderButton_unstable
@fluentui/react-button
ToggleButton
useToggleButtonBase_unstable
renderToggleButton_unstable
@fluentui/react-button
TabList
useTabListBase_unstable
renderTabList_unstable
@fluentui/react-tabs
Tab
useTabBase_unstable
renderTab_unstable
@fluentui/react-tabs
Divider
useDividerBase_unstable
renderDivider_unstable
@fluentui/react-divider
Accordion
useAccordionBase_unstable
renderAccordion_unstable
@fluentui/react-accordion
AccordionPanel
useAccordionPanelBase_unstable
renderAccordionPanel_unstable
@fluentui/react-accordion
Toolbar
useToolbarBase_unstable
renderToolbar_unstable
@fluentui/react-toolbar
ToolbarButton
useToolbarButtonBase_unstable
renderToolbarButton_unstable
@fluentui/react-toolbar
Popover
usePopoverBase_unstable
renderPopover_unstable
@fluentui/react-popover
Persona
usePersonaBase_unstable
renderPersona_unstable
@fluentui/react-persona
Card
useCardBase_unstable
renderCard_unstable
@fluentui/react-card

Compound Components (TabList, Accordion)

复合组件(TabList、Accordion)

Compound components require context values passed to the render function. Use
use{Component}ContextValues_unstable
and inject any design props needed by child components before calling it:
tsx
import {
  useTabListBase_unstable,
  useTabListContextValues_unstable,
  renderTabList_unstable,
  useTabListA11yBehavior_unstable,
} from '@fluentui/react-tabs';
import type { TabListBaseProps, TabListState } from '@fluentui/react-tabs';

type CustomTabListProps = TabListBaseProps & { appearance?: 'filled' | 'outline' };

export const CustomTabList = React.forwardRef<HTMLDivElement, CustomTabListProps>(
  ({ appearance = 'filled', ...props }, ref) => {
    const state = useTabListBase_unstable(props, ref);

    // Inject design props consumed by child Tab components via context
    Object.assign(state, { appearance, size: 'medium', reserveSelectedTabSpace: true });
    const contextValues = useTabListContextValues_unstable(state as TabListState);

    // Apply custom classes
    state.root.className = `tab-list tab-list--${appearance}`;

    // Keyboard navigation: choose one approach
    // Option A — Tabster (recommended for accessibility)
    const focusProps = useTabListA11yBehavior_unstable({ vertical: state.vertical });
    state.root = { ...state.root, ...focusProps };
    // Option B — focusgroup proposal
    // (state.root as any).focusgroup = `tablist ${state.vertical ? 'block' : 'inline'} no-memory wrap`;

    return renderTabList_unstable(state as TabListState, contextValues);
  },
);
复合组件需要向渲染函数传递上下文值。调用渲染函数前,使用
use{Component}ContextValues_unstable
并注入子组件所需的任何设计属性:
tsx
import {
  useTabListBase_unstable,
  useTabListContextValues_unstable,
  renderTabList_unstable,
  useTabListA11yBehavior_unstable,
} from '@fluentui/react-tabs';
import type { TabListBaseProps, TabListState } from '@fluentui/react-tabs';

type CustomTabListProps = TabListBaseProps & { appearance?: 'filled' | 'outline' };

export const CustomTabList = React.forwardRef<HTMLDivElement, CustomTabListProps>(
  ({ appearance = 'filled', ...props }, ref) => {
    const state = useTabListBase_unstable(props, ref);

    // Inject design props consumed by child Tab components via context
    Object.assign(state, { appearance, size: 'medium', reserveSelectedTabSpace: true });
    const contextValues = useTabListContextValues_unstable(state as TabListState);

    // Apply custom classes
    state.root.className = `tab-list tab-list--${appearance}`;

    // Keyboard navigation: choose one approach
    // Option A — Tabster (recommended for accessibility)
    const focusProps = useTabListA11yBehavior_unstable({ vertical: state.vertical });
    state.root = { ...state.root, ...focusProps };
    // Option B — focusgroup proposal
    // (state.root as any).focusgroup = `tablist ${state.vertical ? 'block' : 'inline'} no-memory wrap`;

    return renderTabList_unstable(state as TabListState, contextValues);
  },
);

TypeScript tips

TypeScript提示

  • Use
    {Component}BaseProps
    for the component's props type
  • Cast state to
    {Component}State
    when passing to render:
    renderButton_unstable(state as ButtonState)
  • Child components (e.g.
    CustomTab
    ) read design values from context via
    useTabListContext_unstable
  • 使用
    {Component}BaseProps
    作为组件的属性类型
  • 传递给渲染函数时将state强转为
    {Component}State
    类型:
    renderButton_unstable(state as ButtonState)
  • 子组件(例如
    CustomTab
    )可通过
    useTabListContext_unstable
    从上下文中读取设计值