Notification preference
import { useState } from "react";
import { Radio } from "@cloudflare/kumo";

/** Shows a basic controlled radio group */
export function RadioBasicDemo() {
  const [value, setValue] = useState("email");
  return (
    <Radio.Group
      legend="Notification preference"
      value={value}
      onValueChange={setValue}
    >
      <Radio.Item label="Email" value="email" />
      <Radio.Item label="SMS" value="sms" />
      <Radio.Item label="Push notification" value="push" />
    </Radio.Group>
  );
}

安装

桶式导出

import { Radio } from "@cloudflare/kumo";

细粒度导入

import { Radio } from "@cloudflare/kumo/components/radio";

用法

import { Radio } from "@cloudflare/kumo";

export default function Example() {
  return (
    <Radio.Group legend="Choose an option" defaultValue="a">
      <Radio.Item label="Option A" value="a" />
      <Radio.Item label="Option B" value="b" />
    </Radio.Group>
  );
}

示例

默认布局(垂直)

单选组默认垂直排列。每个单选按钮的标签 显示在右侧。

Account type
import { useState } from "react";
import { Radio } from "@cloudflare/kumo";

/** Shows the default vertical radio group layout */
export function RadioDefaultDemo() {
  const [value, setValue] = useState("personal");
  return (
    <Radio.Group legend="Account type" value={value} onValueChange={setValue}>
      <Radio.Item label="Personal" value="personal" />
      <Radio.Item label="Business" value="business" />
      <Radio.Item label="Enterprise" value="enterprise" />
    </Radio.Group>
  );
}

水平布局

使用 orientation="horizontal" 实现内联布局。空间不足时 项目会自动换行。

Size
import { useState } from "react";
import { Radio } from "@cloudflare/kumo";

/** Shows a horizontal radio group layout */
export function RadioHorizontalDemo() {
  const [value, setValue] = useState("md");
  return (
    <Radio.Group
      legend="Size"
      orientation="horizontal"
      value={value}
      onValueChange={setValue}
    >
      <Radio.Item label="Small" value="sm" />
      <Radio.Item label="Medium" value="md" />
      <Radio.Item label="Large" value="lg" />
    </Radio.Group>
  );
}

带描述

使用 description 属性在单选项目下方添加辅助说明文字。

Shipping method

Choose how you'd like to receive your order

import { useState } from "react";
import { Radio } from "@cloudflare/kumo";

/** Shows a radio group with helper description text */
export function RadioDescriptionDemo() {
  const [value, setValue] = useState("standard");
  return (
    <Radio.Group
      legend="Shipping method"
      description="Choose how you'd like to receive your order"
      value={value}
      onValueChange={setValue}
    >
      <Radio.Item label="Standard (5-7 days)" value="standard" />
      <Radio.Item label="Express (2-3 days)" value="express" />
      <Radio.Item label="Overnight" value="overnight" />
    </Radio.Group>
  );
}

控件位置

使用 controlPosition="end" 将标签放在单选按钮左侧。

Preferences
import { Radio } from "@cloudflare/kumo";

/** Shows radio group with labels positioned before the radio control */
export function RadioControlPositionDemo() {
  return (
    <Radio.Group legend="Preferences" controlPosition="end" defaultValue="a">
      <Radio.Item label="Label before radio" value="a" />
      <Radio.Item label="Another option" value="b" />
    </Radio.Group>
  );
}

卡片式单选

在组上使用 appearance="card",可将每个选项显示为可选卡片。 再配合每个项目的 description 属性,可获得更丰富的内容。

Choose a plan
import { useState } from "react";
import { Radio } from "@cloudflare/kumo";

/** Shows radio card appearance with Cloudflare plan options */
export function RadioCardDemo() {
  const [value, setValue] = useState("free");
  return (
    <Radio.Group
      legend="Choose a plan"
      appearance="card"
      value={value}
      onValueChange={setValue}
    >
      <Radio.Item
        label="Free"
        description="For personal or hobby projects that aren't business-critical."
        value="free"
      />
      <Radio.Item
        label="Pro"
        description="For professional websites that aren't business-critical."
        value="pro"
      />
      <Radio.Item
        label="Business"
        description="For small businesses operating online."
        value="business"
      />
      <Radio.Item
        label="Contract"
        description="For mission-critical applications that are core to your business."
        value="contract"
      />
    </Radio.Group>
  );
}

卡片式单选(控件在左侧)

在卡片式单选组上使用 controlPosition="start",可将单选控件放在标签 和描述的左侧。

Choose a plan
import { useState } from "react";
import { Radio } from "@cloudflare/kumo";

/** Shows radio card appearance with the control positioned on the left via controlPosition="start" */
export function RadioCardControlStartDemo() {
  const [value, setValue] = useState("free");
  return (
    <Radio.Group
      legend="Choose a plan"
      appearance="card"
      controlPosition="start"
      value={value}
      onValueChange={setValue}
    >
      <Radio.Item
        label="Free"
        description="For personal or hobby projects that aren't business-critical."
        value="free"
      />
      <Radio.Item
        label="Pro"
        description="For professional websites that aren't business-critical."
        value="pro"
      />
    </Radio.Group>
  );
}

富标签内容

Radio.Itemlabel 属性接受 React 节点,因此可以在文字旁边嵌入图标、 徽标或其他标记。

Choose a plan
import { useState } from "react";
import { Badge, Radio } from "@cloudflare/kumo";

/** Shows Radio.Item labels with rich ReactNode content (icons, badges, or additional markup) */
export function RadioRichLabelDemo() {
  const [value, setValue] = useState("pro");
  return (
    <Radio.Group
      legend="Choose a plan"
      appearance="card"
      value={value}
      onValueChange={setValue}
    >
      <Radio.Item
        label={
          <span className="flex items-center gap-2">
            Free
            <Badge variant="neutral">$0</Badge>
          </span>
        }
        description="For personal or hobby projects."
        value="free"
      />
      <Radio.Item
        label={
          <span className="flex items-center gap-2">
            Pro
            <Badge variant="primary">Popular</Badge>
          </span>
        }
        description="For professional websites."
        value="pro"
      />
    </Radio.Group>
  );
}

卡片式单选(水平)

appearance="card"orientation="horizontal" 结合, 可实现并排的卡片布局。

Choose a plan
import { useState } from "react";
import { Radio } from "@cloudflare/kumo";

/** Shows radio card appearance in horizontal layout */
export function RadioCardHorizontalDemo() {
  const [value, setValue] = useState("free");
  return (
    <Radio.Group
      legend="Choose a plan"
      appearance="card"
      orientation="horizontal"
      value={value}
      onValueChange={setValue}
    >
      <Radio.Item
        label="Free"
        description="For personal or hobby projects that aren't business-critical."
        value="free"
      />
      <Radio.Item
        label="Pro"
        description="For professional websites that aren't business-critical."
        value="pro"
      />
      <Radio.Item
        label="Business"
        description="For small businesses operating online."
        value="business"
      />
      <Radio.Item
        label="Contract"
        description="For mission-critical applications that are core to your business."
        value="contract"
      />
    </Radio.Group>
  );
}

错误状态

使用 error 属性在组级别显示校验错误。

Payment method

Please select a payment method to continue

Payment method

Please select a payment method to continue

import { Radio } from "@cloudflare/kumo";

/** Shows error state for both default and card radio groups */
export function RadioErrorDemo() {
  return (
    <div className="grid grid-cols-2 gap-6">
      <Radio.Group
        legend="Payment method"
        error="Please select a payment method to continue"
      >
        <Radio.Item label="Credit Card" value="card" variant="error" />
        <Radio.Item label="PayPal" value="paypal" variant="error" />
      </Radio.Group>
      <Radio.Group
        legend="Payment method"
        appearance="card"
        error="Please select a payment method to continue"
      >
        <Radio.Item
          label="Credit Card"
          description="Pay with Visa, Mastercard, American Express, or Elo."
          value="card"
          variant="error"
        />
        <Radio.Item
          label="PayPal"
          description="Pay with your PayPal account."
          value="paypal"
          variant="error"
        />
      </Radio.Group>
    </div>
  );
}

禁用状态

可以禁用整个组或禁用一个或多个项目。

Disabled group
Individual disabled
Disabled card group
Individual disabled card
import { Radio } from "@cloudflare/kumo";

/** Shows disabled state for both default and card radio groups */
export function RadioDisabledDemo() {
  return (
    <div className="grid grid-cols-2 gap-6">
      <Radio.Group legend="Disabled group" disabled defaultValue="a">
        <Radio.Item label="Option A" value="a" />
        <Radio.Item label="Option B" value="b" />
      </Radio.Group>
      <Radio.Group legend="Individual disabled" defaultValue="available">
        <Radio.Item label="Available" value="available" />
        <Radio.Item label="Unavailable" value="unavailable" disabled />
      </Radio.Group>
      <Radio.Group
        legend="Disabled card group"
        appearance="card"
        disabled
        defaultValue="a"
      >
        <Radio.Item
          label="Option A"
          description="This option is disabled."
          value="a"
        />
        <Radio.Item
          label="Option B"
          description="This option is disabled."
          value="b"
        />
      </Radio.Group>
      <Radio.Group
        legend="Individual disabled card"
        appearance="card"
        defaultValue="available"
      >
        <Radio.Item
          label="Available"
          description="This option can be selected."
          value="available"
        />
        <Radio.Item
          label="Unavailable"
          description="This option is not available."
          value="unavailable"
          disabled
        />
      </Radio.Group>
    </div>
  );
}

视觉隐藏的图例

使用带 className="sr-only"Radio.Legend,可以在视觉上隐藏图例的同时 保持其为屏幕阅读器可读。当单选组已由父级 Field 或标题提供了标签, 再显示图例会形成冗余标签时, 这一用法很有用。

Paths
import { useState } from "react";
import { Radio } from "@cloudflare/kumo";

/** Shows Radio.Legend with sr-only to visually hide the legend while keeping it accessible, useful when a parent Field already provides a visible label */
export function RadioLegendSrOnlyDemo() {
  const [value, setValue] = useState("all");
  return (
    <Radio.Group defaultValue="all" value={value} onValueChange={setValue}>
      <Radio.Legend className="sr-only">Paths</Radio.Legend>
      <Radio.Item label="Allow all paths" value="all" />
      <Radio.Item label="Restrict to specific paths" value="specific" />
    </Radio.Group>
  );
}

自定义图例样式

Radio.Legend 接受 className,可完全控制图例的呈现方式。当你需要 自定义排版、颜色或布局时,请用它替代 legend 字符串属性。

Notification preference
import { useState } from "react";
import { Radio } from "@cloudflare/kumo";

/** Shows Radio.Legend with custom styling for full control over legend presentation */
export function RadioLegendCustomDemo() {
  const [value, setValue] = useState("email");
  return (
    <Radio.Group value={value} onValueChange={setValue}>
      <Radio.Legend className="text-sm font-normal text-kumo-subtle">
        Notification preference
      </Radio.Legend>
      <Radio.Item label="Email" value="email" />
      <Radio.Item label="SMS" value="sms" />
      <Radio.Item label="Push notification" value="push" />
    </Radio.Group>
  );
}

类型化值

Radio.GroupRadio.Item 支持泛型类型。传入类型参数(例如 <number> 或枚举) 可以约束两个子组件上 value 的类型,以及 Radio.GroupdefaultValueonValueChange 的类型。省略时默认为 string

Items per page
Theme
enum ThemeType {
  dark = "dark",
  light = "light",
  system = "system",
}

export function RadioTypedValueDemo() {
  const [pageSize, setPageSize] = useState<number>(10);
  const [theme, setTheme] = useState<ThemeType>(ThemeType.system);
  return (
    <div className="grid grid-cols-2 gap-6">
      <Radio.Group<number>
        legend="Items per page"
        value={pageSize}
        onValueChange={setPageSize}
      >
        <Radio.Item<number> label="10" value={10} />
        <Radio.Item<number> label="25" value={25} />
        <Radio.Item<number> label="50" value={50} />
      </Radio.Group>
      <Radio.Group<ThemeType>
        legend="Theme"
        value={theme}
        onValueChange={setTheme}
      >
        <Radio.Item<ThemeType> label="Light" value={ThemeType.light} />
        <Radio.Item<ThemeType> label="Dark" value={ThemeType.dark} />
        <Radio.Item<ThemeType> label="System" value={ThemeType.system} />
      </Radio.Group>
    </div>
  );
}

API 参考

Radio.Group

单选按钮的容器,支持图例、描述和错误提示。

PropTypeDefaultDescription
legendstring-Legend text for the group (required for accessibility). For more control over legend styling, omit this prop and use `<Radio.Legend>` as a child instead.
children*ReactNode-Child Radio.Item components (and optionally a Radio.Legend)
orientation"vertical" | "horizontal"-Layout direction of the radio items
appearanceKumoRadioAppearance-Visual appearance applied to all Radio.Item children. - `"default"` — Standard inline radio items - `"card"` — Choice card with border, padding, and highlighted selection state Individual items can override this with their own `appearance` prop.
errorstring-Error message for the group
descriptionReactNode-Helper text for the group
valueT-The controlled value of the selected radio item.
disabledboolean-Whether all radios in the group are disabled
controlPositionRadioControlPosition-Position of radio control relative to label: "start" puts radio before label, "end" puts label before radio. Defaults to "start" for default appearance and "end" for card appearance.
namestring-Form submission name for the radio group
classNamestring-Additional CSS classes
defaultValueT-The uncontrolled initial value of the selected radio item.
onValueChange(value: T, eventDetails: RadioGroupChangeEventDetails) => void-Callback fired when the selected value changes. The second argument carries native event details about the interaction.

Radio.Legend

可组合的 Radio.Group 图例子组件。接受 className 以完全控制样式 (例如 className="sr-only" 实现视觉隐藏)。当你需要自定义图例样式时, 用它替代 legend 字符串属性。

PropTypeDefaultDescription
children*ReactNode-Legend content
classNamestring-Additional CSS classes (e.g. "sr-only" to visually hide the legend)

Radio.Item

Radio.Group 中的单个单选按钮。

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

无障碍

语义化 HTML

Radio.Group 使用语义化的 <fieldset><legend> 元素,实现正确的分组和屏幕阅读器播报。

键盘导航

使用 Arrow Up/DownArrow Left/Right 在选项间移动。Space 选中当前聚焦的选项。Tab 将焦点移入或移出单选组。

屏幕阅读器

每个单选按钮都会播报其标签和选中状态。组的图例为所有选项提供上下文。