)
│ │ └── Table.Cell ()
│ └── Table.LoadMore (optional, infinite scroll)
│ └── Table.LoadMoreContent
└── Table.Footer (optional, pagination, etc.)
```
## Item Identity
**v2:** React's `key` was used for both list reconciliation and selection state.
**v3:** Use `id` on `Table.Row` and `Table.Column` for selection/sort state; keep React's `key` for lists.
## Summary
1. **Imports**: Separate named imports → single `Table` import with dot notation
2. **New Wrappers**: `Table.ScrollContainer` and `Table.Content` wrap the table structure
3. **Props Moved**: `aria-label`, `selectionMode`, `sortDescriptor`, etc. moved from `Table` to `Table.Content`
4. **Bottom Content**: `bottomContent` prop → `Table.Footer` compound component
5. **Top Content**: `topContent` prop → place content inside `Table` before `Table.ScrollContainer`
6. **Selection Checkboxes**: Auto-rendered → explicit `Checkbox` with `slot="selection"`
7. **Empty State**: `emptyContent` prop → `renderEmptyState` on `Table.Body`
8. **Loading**: `loadingState`/`loadingContent` → `Table.LoadMore` for infinite scroll
9. **Column Resizing**: New `Table.ResizableContainer` and `Table.ColumnResizer`
10. **Item Identity**: `key` → `id` on rows and columns
11. **Styling Props Removed**: `color`, `radius`, `shadow`, `isStriped`, `isCompact` → use Tailwind CSS
12. **ClassNames Removed**: Use `className` on individual compound components
# Tabs
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/migration/tabs
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/migration/(components)/tabs.mdx
> Migration guide for Tabs from HeroUI v2 to v3
Refer to the [v3 Tabs documentation](/docs/react/components/tabs) for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.
## Structure Changes
In v2, Tabs used `Tab` component with `title` prop and children as panel content:
```tsx
import { Tabs, Tab } from "@heroui/react";
export default function App() {
return (
Content here
);
}
```
In v3, Tabs requires compound components with separate tab and panel:
```tsx
import { Tabs } from "@heroui/react";
export default function App() {
return (
Photos
Content here
);
}
```
## Key Changes
### 1. Component Structure
**v2:** `Tabs` with `Tab` children (title prop + children as panel)\
**v3:** Compound components (`Tabs.ListContainer`, `Tabs.List`, `Tabs.Tab`, `Tabs.Indicator`, `Tabs.Separator`, `Tabs.Panel`)
### 2. Prop Changes
| v2 Prop | v3 Location | Notes |
| ------------------------ | ----------------------- | ---------------------------------------------- |
| `key` (on Tab) | `id` (on Tab and Panel) | Changed prop name |
| `title` (on Tab) | — | Content goes directly in `Tabs.Tab` |
| `isVertical` | `orientation` | Changed to `"horizontal"` \| `"vertical"` |
| `placement` | — | Use `orientation` and layout |
| `variant` | `variant` | Simplified to `primary` \| `secondary` only |
| `color` | — | Removed (use Tailwind CSS) |
| `size` | — | Removed (use Tailwind CSS) |
| `radius` | — | Removed (use Tailwind CSS) |
| `classNames` | — | Use `className` props on individual components |
| `disableCursorAnimation` | — | Use `Tabs.Indicator` component |
| `disableAnimation` | — | Removed (animations handled differently) |
| `fullWidth` | — | Removed (use Tailwind CSS) |
### 3. Component Changes
* **Tab identification**: `key` → `id` (must match between `Tabs.Tab` and `Tabs.Panel`)
* **Tab content**: `title` prop → direct children in `Tabs.Tab`
* **Panel content**: Tab children → separate `Tabs.Panel` component
* **Indicator**: Automatic cursor → explicit `Tabs.Indicator` component
* **Separator**: New `Tabs.Separator` component to display separator lines between tabs
## Migration Examples
### Controlled Tabs
```tsx
import { useState } from "react";
const [selected, setSelected] = useState("photos");
Content
Content
```
```tsx
import { useState } from "react";
const [selected, setSelected] = useState("photos");
Photos
Music
Content
Content
```
### With Icons
```tsx
Photos>}>
Content
```
```tsx
Photos
Content
```
### With Separator
In v3, you can add `Tabs.Separator` inside each `Tabs.Tab` (except the first) to display separator lines between tabs. This is a new feature with no v2 equivalent.
```tsx
Photos
Music
Videos
Photos content
Music content
Videos content
```
## Component Anatomy
The v3 Tabs follows this structure:
```
Tabs (Root)
├── Tabs.ListContainer
│ └── Tabs.List
│ └── Tabs.Tab
│ ├── Tabs.Separator (optional, omit on first tab)
│ └── Tabs.Indicator (optional)
└── Tabs.Panel (one per tab, matching id)
```
## Summary
1. **Component Structure**: Must use compound components (`Tabs.ListContainer`, `Tabs.List`, `Tabs.Tab`, `Tabs.Indicator`, `Tabs.Separator`, `Tabs.Panel`)
2. **Tab Identification**: `key` → `id` (must match between tab and panel)
3. **Tab Content**: `title` prop removed - content goes directly in `Tabs.Tab`
4. **Panel Separation**: Panel content moved to separate `Tabs.Panel` component
5. **Indicator**: Must explicitly include `Tabs.Indicator` in each tab
6. **Orientation**: `isVertical` → `orientation` prop
7. **Styling**: `variant` simplified to `primary` | `secondary`; `color`, `size`, `radius`, `placement` removed - use Tailwind for more
8. **ClassNames Removed**: Use `className` props on individual components
9. **Separator (New)**: Use `Tabs.Separator` inside `Tabs.Tab` to display separator lines between tabs (no v2 equivalent)
# TimeInput
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/migration/timeinput
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/migration/(components)/timeinput.mdx
> Migration guide for TimeInput to TimeField from HeroUI v2 to v3
Refer to the [v3 TimeField documentation](/docs/react/components/time-field) for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.
## Structure Changes
In v2, TimeInput was a single component with props:
```tsx
import { TimeInput } from "@heroui/react";
export default function App() {
return ;
}
```
In v3, TimeField requires compound components with DateInputGroup and a render prop for segments:
```tsx
import { TimeField, DateInputGroup, Label } from "@heroui/react";
export default function App() {
return (
Time
{(segment) => }
);
}
```
## Key Changes
### 1. Component Naming
**v2:** `TimeInput`\
**v3:** `TimeField`
### 2. Component Structure
**v2:** Single component with props\
**v3:** Compound components: `TimeField` (root) + `DateInputGroup` with `DateInputGroup.Input` (render prop) and `DateInputGroup.Segment`; optionally `DateInputGroup.Prefix` and `DateInputGroup.Suffix`
### 3. Prop Changes
| v2 Prop | v3 Location | Notes |
| ------------------------------------------------------------- | ------------------------------- | --------------------------------------------------------- |
| `label` | — | Use `Label` component |
| `description` | — | Use `Description` component |
| `errorMessage` | — | Use `FieldError` component |
| `value`, `defaultValue`, `onChange` | `TimeField` | Same (React Aria) |
| `minValue`, `maxValue`, `granularity`, `placeholderValue` | `TimeField` | Same |
| `isRequired`, `isDisabled`, `isReadOnly`, `isInvalid`, `name` | `TimeField` | Same |
| `validationBehavior`, `shouldForceLeadingZeros` | `TimeField` | Same |
| `variant` | `DateInputGroup` | Simplified to `primary` \| `secondary` only |
| `fullWidth` | `TimeField` or `DateInputGroup` | On root or group |
| `color` | — | Removed (use Tailwind CSS) |
| `size` | — | Removed (use Tailwind CSS) |
| `radius` | — | Removed (use Tailwind CSS) |
| `labelPlacement` | — | Handle with layout |
| `startContent` | `DateInputGroup.Prefix` | Use Prefix child |
| `endContent` | `DateInputGroup.Suffix` | Use Suffix child |
| `classNames` | — | Use `className` on `TimeField` and `DateInputGroup` parts |
| `groupProps` | — | Use `className` or DOM props on `DateInputGroup` |
| `labelProps` | — | Use `className` on `Label` |
| `fieldProps` | — | Use `className` on `DateInputGroup` |
| `innerWrapperProps` | — | Use `className` on group/input parts |
| `descriptionProps` | — | Use `className` on `Description` |
| `errorMessageProps` | — | Use `className` on `FieldError` |
| `inputRef` | — | Ref handled by `TimeField` / React Aria |
## Migration Examples
### With Description and Error
```tsx
```
```tsx
import { Description, FieldError, Label } from "@heroui/react";
Start time
{(segment) => }
Select start time
Time
{(segment) => }
Please enter a valid time
```
### Controlled
```tsx
import { parseTime } from "@internationalized/date";
import { useState } from "react";
const [value, setValue] = useState(null);
```
```tsx
import type { TimeValue } from "@internationalized/date";
import { useState } from "react";
const [value, setValue] = useState(null);
Time
{(segment) => }
```
### Min/Max and Granularity
```tsx
import { parseTime } from "@internationalized/date";
```
```tsx
import { parseTime } from "@internationalized/date";
Time
{(segment) => }
```
### Start/End Content
```tsx
}
label="Time"
name="time"
startContent={ }
/>
```
```tsx
Time
{(segment) => }
```
## Component Anatomy
The v3 TimeField follows this structure:
```
TimeField (Root)
├── Label (optional)
├── DateInputGroup
│ ├── DateInputGroup.Prefix (optional)
│ ├── DateInputGroup.Input → (segment) => DateInputGroup.Segment
│ └── DateInputGroup.Suffix (optional)
├── Description (optional)
└── FieldError (optional)
```
## Summary
1. **Component Renamed**: `TimeInput` → `TimeField`
2. **Component Structure**: Must use compound components: `TimeField` (root) and `DateInputGroup` with `DateInputGroup.Input` (render prop) and `DateInputGroup.Segment`
3. **Label/Description/Error**: Use separate components (`Label`, `Description`, `FieldError`)
4. **Time Props Unchanged**: `value`, `defaultValue`, `onChange`, `minValue`, `maxValue`, `granularity`, `placeholderValue`, `isRequired`, `isDisabled`, `isInvalid`, `name`, `validationBehavior`, `shouldForceLeadingZeros` stay on `TimeField`
5. **Variant on DateInputGroup**: v3 supports only `variant="primary"` and `variant="secondary"` on `DateInputGroup`; `color`, `size`, `radius` removed — use Tailwind CSS
6. **Start/End Content**: `startContent`/`endContent` → `DateInputGroup.Prefix` and `DateInputGroup.Suffix`
7. **Label Placement Removed**: `labelPlacement` removed — handle with layout
8. **DOM/Class Props**: `groupProps`, `labelProps`, `fieldProps`, `classNames` removed — use `className` (and standard DOM props) on the relevant parts
# Toast
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/migration/toast
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/migration/(components)/toast.mdx
> Migration guide for Toast from HeroUI v2 to v3
Refer to the [v3 Toast documentation](/docs/react/components/toast) for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.
## Structure Changes
In v2, Toast used a provider and hook pattern:
```tsx
import { ToastProvider, useToast } from "@heroui/react";
function App() {
return (
);
}
function MyComponent() {
const { toast } = useToast();
return (
toast.show("Hello!")}>
Show Toast
);
}
```
In v3, Toast uses a provider component and a global `toast()` function:
```tsx
import { Toast } from "@heroui/react";
function App() {
return (
<>
>
);
}
function MyComponent() {
return (
toast("Hello!")}>
Show Toast
);
}
```
## Key Changes
### 1. Provider Pattern
**v2:** Required `ToastProvider` wrapper\
**v3:** Uses `Toast.Provider` component (can be placed anywhere)
### 2. Hook → Function
**v2:** Used `useToast()` hook\
**v3:** Uses `toast()` function directly
### 3. API Changes
**v2:** Used `toast.show()` method\
**v3:** `toast()` is a function with helper methods (`toast.success()`, `toast.danger()`, etc.)
### 4. Variant Names
**v2:** Variants like `success`, `error`, `warning`, `info`\
**v3:** Variants: `default`, `accent`, `success`, `warning`, `danger`
### 5. Compound Component Structure
**v3:** Toast uses compound components for custom rendering:
* `Toast` - Main toast container
* `Toast.Content` - Content wrapper
* `Toast.Title` - Title text
* `Toast.Description` - Description text
* `Toast.Indicator` - Icon/indicator
* `Toast.CloseButton` - Close button
* `Toast.ActionButton` - Action button
### 6. Promise Support
**v3:** Built-in promise support with `toast.promise()` for handling async operations
## Migration Examples
### Toast with Title and Description
```tsx
const { toast } = useToast();
toast.show({
title: "Success",
description: "Your changes have been saved",
variant: "success"
});
```
```tsx
import { toast } from "@heroui/react";
toast.success("Success", {
description: "Your changes have been saved"
});
```
### Helper Methods for Variants
```tsx
const { toast } = useToast();
toast.show({ variant: "success", title: "Success" });
toast.show({ variant: "error", title: "Error" });
toast.show({ variant: "warning", title: "Warning" });
toast.show({ variant: "info", title: "Info" });
```
```tsx
import { toast } from "@heroui/react";
toast.success("Success");
toast.danger("Error");
toast.warning("Warning");
toast.info("Info");
```
### Promise Support
```tsx
const { toast } = useToast();
const handleAsync = async () => {
try {
await someAsyncOperation();
toast.show({ title: "Success", variant: "success" });
} catch {
toast.show({ title: "Error", variant: "error" });
}
};
```
```tsx
import { toast } from "@heroui/react";
const handleAsync = async () => {
toast.promise(someAsyncOperation(), {
loading: "Processing...",
success: "Operation completed!",
error: "Operation failed"
});
};
```
### Custom Toast Rendering
```tsx
const { toast } = useToast();
toast.show({
title: "Custom",
render: (toast) => (
Custom content
)
});
```
```tsx
import { Toast, ToastContent, ToastTitle } from "@heroui/react";
{({ toast: toastItem }) => (
Custom content
)}
```
## Summary
* Replace `ToastProvider` with `Toast.Provider`
* Replace `useToast()` hook with `toast()` function
* Update variant names (`error` → `danger`, `info` → `accent`)
* Use helper methods: `toast.success()`, `toast.danger()`, etc.
* Use `toast.promise()` for async operations
* Compound component structure for custom rendering
* Better TypeScript support and queue management
# Tooltip
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/migration/tooltip
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/migration/(components)/tooltip.mdx
> Migration guide for Tooltip from HeroUI v2 to v3
Refer to the [v3 Tooltip documentation](/docs/react/components/tooltip) for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.
## Structure Changes
In v2, Tooltip used a `content` prop:
```tsx
import { Tooltip, Button } from "@heroui/react";
export default function App() {
return (
Hover me
);
}
```
In v3, Tooltip requires compound components:
```tsx
import { Tooltip, Button } from "@heroui/react";
export default function App() {
return (
Hover me
I am a tooltip
);
}
```
## Key Changes
### 1. Component Structure
**v2:** Simple Tooltip with `content` prop and children as trigger\
**v3:** Compound components (`Tooltip.Trigger`, `Tooltip.Content`, `Tooltip.Arrow`)
### 2. Prop Changes
| v2 Prop | v3 Location | Notes |
| --------------------------------- | ------------------------ | -------------------------------------------------------- |
| `content` | — | Use `Tooltip.Content` children |
| `showArrow` | `showArrow` (on Content) | Moved to `Tooltip.Content` |
| `placement` | `placement` (on Content) | Moved to `Tooltip.Content` |
| `offset` | `offset` (on Content) | Moved to `Tooltip.Content` |
| `color` | — | Removed (use Tailwind CSS) |
| `size` | — | Removed (use Tailwind CSS) |
| `radius` | — | Removed (use Tailwind CSS) |
| `shadow` | — | Removed (use Tailwind CSS) |
| `classNames` | — | Use `className` props on individual components |
| `motionProps` | — | Removed (animations handled differently) |
| `trigger` | `trigger` (on root) | Still exists: `"hover"` \| `"focus"` (default `"hover"`) |
| `isDisabled` | `isDisabled` (on root) | New in v3: disables the tooltip entirely |
| `delay` | `delay` (on root) | Still exists (default changed from `0` to `700`) |
| `closeDelay` | `closeDelay` (on root) | Still exists (default `0`) |
| `portalContainer` | — | Not exposed |
| `updatePositionDeps` | — | Not exposed |
| `containerPadding`, `crossOffset` | — | Not exposed |
| `shouldFlip` | — | Handled automatically |
| `triggerScaleOnOpen` | — | Not available |
| `isKeyboardDismissDisabled` | — | Not available |
| `isDismissable` | — | Not available |
| `shouldCloseOnBlur` | — | Not available |
| `shouldCloseOnInteractOutside` | — | Not available |
| `onClose` | — | Use `onOpenChange` instead |
### 3. Props Moved to Tooltip.Content
* `showArrow` - Now on `Tooltip.Content`
* `placement` - Now on `Tooltip.Content`
* `offset` - Now on `Tooltip.Content`
## Migration Examples
### Content Configuration
```tsx
{/* With arrow */}
Hover me
{/* With placement */}
Hover me
{/* With offset */}
Hover me
```
```tsx
{/* With arrow */}
Hover me
I am a tooltip
{/* With placement */}
Hover me
Tooltip
{/* With offset */}
Hover me
Tooltip
```
### Controlled Tooltip
```tsx
import { useState } from "react";
const [isOpen, setIsOpen] = useState(false);
Hover me
```
```tsx
import { useState } from "react";
const [isOpen, setIsOpen] = useState(false);
Hover me
I am a tooltip
```
### With Delay
```tsx
Hover me
```
```tsx
Hover me
Tooltip
```
### Custom Content
```tsx
Title
Description
}
>
Hover me
```
```tsx
Hover me
```
### With Custom Trigger
```tsx
Custom trigger
```
```tsx
Custom trigger
Tooltip
```
## Component Anatomy
The v3 Tooltip follows this structure:
```
Tooltip (Root)
├── Tooltip.Trigger
│ └── [Trigger element]
└── Tooltip.Content
├── Tooltip.Arrow (optional)
└── [Tooltip content]
```
## New Props in v3
### isDisabled
The `isDisabled` prop allows you to completely disable the tooltip. When disabled, the tooltip will not appear on hover or focus:
```tsx
No tooltip
This will not show
```
### trigger
The `trigger` prop controls how the tooltip is activated. It accepts `"hover"` (default) or `"focus"`:
```tsx
{/* Show tooltip only on focus */}
Focus me
Shown on focus only
```
### Custom Render Function
`Tooltip.Content` and `Tooltip.Arrow` both support a `render` prop that allows you to override the default DOM element with a custom render function for advanced use cases.
## Important Notes
### Content Prop
* **v2:** Used `content` prop for tooltip text/content
* **v3:** Content goes as children of `Tooltip.Content` component
### Arrow
* **v2:** Controlled by `showArrow` prop on root
* **v3:** Use `showArrow` prop on `Tooltip.Content` and include `Tooltip.Arrow` component
### Placement and Offset
* **v2:** `placement` and `offset` props on root
* **v3:** `placement` and `offset` props moved to `Tooltip.Content`
### Trigger Element
* **v2:** Children were automatically used as trigger
* **v3:** Must wrap trigger element in `Tooltip.Trigger` component
### Default Delay
* **v2:** `delay` default was `0`
* **v3:** `delay` default is `700` (note: examples use `delay={0}` to match v2 behavior)
## Summary
1. **Component Structure**: Must use compound components (`Tooltip.Trigger`, `Tooltip.Content`, `Tooltip.Arrow`)
2. **Content Prop Removed**: `content` prop removed - use `Tooltip.Content` children
3. **Props Moved**: `showArrow`, `placement`, `offset` moved to `Tooltip.Content`
4. **Styling Props Removed**: `color`, `size`, `radius`, `shadow` - use Tailwind CSS
5. **ClassNames Removed**: Use `className` props on individual components
6. **Motion Props Removed**: `motionProps` removed - animations handled differently
7. **Advanced Props Removed**: Many positioning and behavior props removed
8. **Default Delay Changed**: Default delay changed from `0` to `700`
9. **isDisabled Prop**: New `isDisabled` prop to completely disable the tooltip
10. **trigger Prop**: Accepts `"hover"` (default) or `"focus"` to control activation method
11. **Render Props**: `Tooltip.Content` and `Tooltip.Arrow` support a `render` prop for custom DOM rendering
# User
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/migration/user
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/migration/(components)/user.mdx
> Migration guide for User from HeroUI v2 to v3
The User component has been **removed** in HeroUI v3. Compose user displays manually using Avatar and text elements with Tailwind CSS classes.
## Key Changes
### 1. Component Removal
**v2:** `` component from `@heroui/react`\
**v3:** Manual composition using `Avatar` + text elements
### 2. Features Mapping
The v2 User component had the following features that need to be replaced:
| v2 Feature | v3 Equivalent | Notes |
| ------------------ | --------------------- | ----------------------------------------- |
| `name` prop | Text element | Render name as text or heading |
| `description` prop | Text element | Render description as text |
| `avatarProps` prop | `Avatar` component | Use v3 Avatar component directly |
| `isFocusable` prop | Manual focus handling | Add `tabIndex` and focus styles if needed |
| `classNames` prop | Tailwind classes | Apply classes directly to elements |
## Structure Changes
### v2: User Component
In v2, `User` was a convenience component combining Avatar with name:
```tsx
import { User } from "@heroui/react";
export default function App() {
return (
);
}
```
### v3: Manual Composition
In v3, compose user displays manually using Avatar and text elements:
```tsx
import { Avatar } from "@heroui/react";
export default function App() {
return (
);
}
```
## Migration Examples
### With Description
```tsx
import { User } from "@heroui/react";
```
```tsx
import { Avatar } from "@heroui/react";
JG
Junior Garcia
Software Engineer
```
### With Default Avatar (Initials)
```tsx
import { User } from "@heroui/react";
name
.split(" ")
.map((n) => n[0])
.join(""),
}}
/>
```
```tsx
import { Avatar } from "@heroui/react";
function getInitials(name: string) {
return name
.split(" ")
.map((n) => n[0])
.join("");
}
{getInitials("Junior Garcia")}
Junior Garcia
```
### With Link Description
```tsx
import { User, Link } from "@heroui/react";
@jrgarciadev
}
avatarProps={{
src: "https://example.com/avatar.jpg",
}}
/>
```
```tsx
import { Avatar, Link } from "@heroui/react";
JG
Junior Garcia
@jrgarciadev
```
### Clickable User
```tsx
import { User } from "@heroui/react";
{/* Focusable */}
{/* As button */}
```
```tsx
import { Avatar } from "@heroui/react";
{/* Focusable */}
JG
Junior Garcia
{/* As button */}
JG
Junior Garcia
```
## Creating a Reusable User Component (Recommended)
Since User displays are commonly needed, here's a reusable component:
```tsx
import { User } from "@heroui/react";
```
```tsx
import { Avatar, Link } from "@heroui/react";
import { ReactNode } from "react";
import { cn } from "@/lib/utils"; // or your cn utility
interface UserProps {
name: string | ReactNode;
description?: string | ReactNode;
avatarSrc?: string;
avatarAlt?: string;
avatarFallback?: string;
className?: string;
isFocusable?: boolean;
as?: "div" | "button" | "a";
href?: string;
onClick?: () => void;
}
function getInitials(name: string): string {
return name
.split(" ")
.map((n) => n[0])
.join("")
.toUpperCase()
.slice(0, 2);
}
export function User({
name,
description,
avatarSrc,
avatarAlt,
avatarFallback,
className,
isFocusable = false,
as = "div",
href,
onClick,
}: UserProps) {
const Component = as === "a" ? "a" : as === "button" ? "button" : "div";
const fallback = avatarFallback || (typeof name === "string" ? getInitials(name) : "?");
const content = (
<>
{avatarSrc && (
)}
{fallback}
{name}
{description && (
{description}
)}
>
);
const baseClasses = cn(
"inline-flex items-center gap-2 rounded-sm outline-none",
isFocusable && "focus-visible:ring-2 focus-visible:ring-focus",
className
);
if (Component === "button") {
return (
{content}
);
}
if (Component === "a") {
return (
{content}
);
}
return (
{content}
);
}
// Usage
```
## Styling Reference
The v2 User component used these base styles that you should replicate:
* **Base container**: `inline-flex items-center gap-2 rounded-sm`
* **Wrapper (for name/description)**: `inline-flex flex-col items-start`
* **Name**: `text-sm` (text-small)
* **Description**: `text-xs text-muted` (text-tiny text-foreground-400)
## Summary
1. **Component Removed**: `User` component no longer exists in v3
2. **Import Change**: Remove `import { User } from "@heroui/react"`
3. **Manual Composition**: Compose using Avatar + text elements
4. **Avatar Changes**: Use v3 Avatar compound component pattern
5. **Styling**: Apply Tailwind CSS classes directly
6. **Focus Handling**: Implement focus styles manually if needed
## Migration Steps
1. **Remove Import**: Remove `User` from `@heroui/react` imports
2. **Replace Component**: Replace all `` instances with manual composition
3. **Use Avatar**: Use v3 Avatar component with compound pattern
4. **Add Text Elements**: Add name and description as text elements
5. **Apply Styling**: Use Tailwind CSS classes for layout and styling
6. **Handle Focus**: Add focus styles if `isFocusable` was used
7. **Optional**: Create reusable User component for your application
## Common Patterns
### User List
```tsx
{users.map((user) => (
{getInitials(user.name)}
{user.name}
{user.role && (
{user.role}
)}
))}
```
### Clickable User
```tsx
handleUserClick(user)}
>
{getInitials(user.name)}
{user.name}
{user.email}
```
# Agent Migration Guide - Full Migration
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/migration/agent-guide-full
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/migration/(workflows)/agent-guide-full.mdx
> Full migration guide for AI assistants helping migrate HeroUI v2 to v3
## Overview
This guide is designed for AI assistants (agents) helping users migrate from HeroUI v2 to v3. It implements a **full migration approach** that analyzes the project first and migrates components in manageable batches rather than all at once.
**Critical Constraint**: HeroUI v2 and v3 **should not coexist** in the same project. The project will be broken during migration. Always work in a feature branch and migrate all component code before switching dependencies.
## Key Principles
1. **Full Migration**: Never migrate all components at once. Large projects require full migration.
2. **Project Analysis First**: Always analyze the codebase to understand component usage before creating a migration plan.
3. **Broken State Management**: **Critical**: The project will be broken during migration. Plan for this by:
* Migrating in a feature branch
* Preparing all code changes before switching dependencies
* Having a rollback plan
* Testing in isolated environment first
4. **All-or-Nothing Dependency Switch**: Once dependencies are updated to v3, ALL components must be migrated. Plan component migrations before switching dependencies.
## Major Changes in v3
## Major Changes
* **Dependencies**: Update React to v19+, HeroUI packages to v3, Tailwind CSS to v4, remove Framer Motion
* **No Provider Required**: v3 removes the need for `HeroUIProvider`
* **Component API Updates**: Many components use React Aria Components patterns
* **Compound Components**: New compound component patterns for better customization. See individual component guides for details.
* **Hooks Removed**: v2 component hooks like `useSwitch` or `useInput` are removed - use compound components instead. `useDisclosure` is replaced with `useOverlayState`. See the [Hooks Migration Guide](/docs/react/migration/hooks) for details.
* **Configuration**: Remove `heroui()` plugin from Tailwind config, update CSS imports, remove `hero.ts` file
* **Item identity**: Collection items (Dropdown, Listbox, Select, Accordion, etc.) now use `id` and `textValue` in v3; keep React's `key` for lists.
## Detailed Migration Steps
For detailed step-by-step instructions, see the [Full Migration Guide](/docs/react/migration/full-migration). The guide covers:
* Dependency updates
* Theming configuration changes
* Removing HeroUIProvider
* Component imports and migration
* Hooks migration
* Styling migration
* Testing
**Agent Note**: Dependency updates (React 19, Tailwind v4) can be done before switching HeroUI (won't break project). However, the HeroUI package switch should only happen AFTER all component code is migrated.
## Hooks Migration
HeroUI v2 provided component hooks (like `useSwitch`, `useInput`, `useCheckbox`, etc.) and utility hooks like `useDisclosure`. HeroUI v3 removes most component hooks in favor of compound components, and replaces `useDisclosure` with `useOverlayState`.
**When to migrate hooks:**
* **During component migration**: Replace component hooks (`useSwitch`, `useInput`, etc.) as you migrate each component to use compound components
* **After component migration**: Migrate `useDisclosure` → `useOverlayState` for overlay state management before styling migration
**Migration Strategy:**
1. **Identify hook usage**: Search codebase for imports from `@heroui/react` that include hook names (`useSwitch`, `useInput`, `useCheckbox`, `useRadio`, `useDisclosure`, etc.)
2. **Replace component hooks**: Use compound components instead of hooks with prop getters (done during component migration)
3. **Replace useDisclosure**: Migrate to `useOverlayState` for overlay state management (use `get_hooks_migration_guide` MCP tool)
4. **Reference guides**: Use `get_hooks_migration_guide` MCP tool for hooks migration, `get_component_migration_guides` for component-specific guides
## Component Import Changes
See the [Full Migration Guide](/docs/react/migration/full-migration#step-5-update-component-imports) for detailed component import changes.
## Component Migration Reference
Use the table below to quickly find migration guidance for each component. Use the link in the "Migration Guide" column to jump to detailed migration instructions.
**Component Development Status**: Components marked with 🔄 In Progress or 📋 Planned are still being developed. Check the [Roadmap](https://herouiv3.featurebase.app/roadmap) for the task status. Guides for these components will be available once development is finished.
| v2 Component | v3 Component | Status | Migration Guide |
| ---------------- | ---------------------------- | ----------- | ------------------------------------------------------------------------- |
| Accordion | Accordion | ✅ Available | [View guide →](/docs/react/migration/accordion) |
| Alert | Alert | ✅ Available | [View guide →](/docs/react/migration/alert) |
| Autocomplete | ComboBox | ✅ Renamed | [View guide →](/docs/react/migration/autocomplete) |
| Avatar | Avatar | ✅ Available | [View guide →](/docs/react/migration/avatar) |
| Badge | Badge | ✅ Available | [View guide →](/docs/react/migration/badge) |
| Breadcrumbs | Breadcrumbs | ✅ Available | [View guide →](/docs/react/migration/breadcrumbs) |
| Button | Button | ✅ Available | [View guide →](/docs/react/migration/button) |
| ButtonGroup | ButtonGroup | ✅ Available | [View guide →](/docs/react/migration/button-group) |
| Calendar | Calendar | ✅ Available | [View guide →](/docs/react/migration/calendar) |
| Card | Card | ✅ Available | [View guide →](/docs/react/migration/card) |
| Checkbox | Checkbox | ✅ Available | [View guide →](/docs/react/migration/checkbox) |
| CheckboxGroup | CheckboxGroup | ✅ Available | [View guide →](/docs/react/migration/checkbox-group) |
| Chip | Chip | ✅ Available | [View guide →](/docs/react/migration/chip) |
| Code | ❌ | ❌ Removed | [View guide →](/docs/react/migration/code) |
| DateInput | DateField | ✅ Renamed | [View guide →](/docs/react/migration/dateinput) |
| DatePicker | DatePicker | ✅ Available | [View guide →](/docs/react/migration/date-picker) |
| DateRangePicker | DateRangePicker | ✅ Available | [View guide →](/docs/react/migration/date-range-picker) |
| TimeInput | TimeField | ✅ Renamed | [View guide →](/docs/react/migration/timeinput) |
| Divider | Separator | ✅ Renamed | [View guide →](/docs/react/migration/divider) |
| Drawer | Drawer | ✅ Available | [View guide →](/docs/react/migration/drawer) |
| Dropdown | Dropdown | ✅ Available | [View guide →](/docs/react/migration/dropdown) |
| Form | Form | ✅ Available | [View guide →](/docs/react/migration/form) |
| Image | ❌ | ❌ Removed | [View guide →](/docs/react/migration/image) |
| Input | TextField, Input, InputGroup | ✅ Available | [View guide →](/docs/react/migration/input) |
| InputOTP | InputOTP | ✅ Available | [View guide →](/docs/react/migration/input-otp) |
| Kbd | Kbd | ✅ Available | [View guide →](/docs/react/migration/kbd) |
| Link | Link | ✅ Available | [View guide →](/docs/react/migration/link) |
| Listbox | ListBox | ✅ Available | [View guide →](/docs/react/migration/listbox) |
| Modal | Modal | ✅ Available | [View guide →](/docs/react/migration/modal) |
| Navbar | ❌ | ❌ Removed | [View guide →](/docs/react/migration/navbar) |
| NumberInput | NumberField | ✅ Renamed | [View guide →](/docs/react/migration/numberinput) |
| Pagination | Pagination | ✅ Available | [View guide →](/docs/react/migration/pagination) |
| Popover | Popover | ✅ Available | [View guide →](/docs/react/migration/popover) |
| Progress | ProgressBar | ✅ Renamed | [View guide →](/docs/react/migration/progress) |
| CircularProgress | ProgressCircle | ✅ Renamed | [View guide →](/docs/react/migration/circular-progress) |
| Radio | Radio | ✅ Available | [View guide →](/docs/react/migration/radio) |
| RadioGroup | RadioGroup | ✅ Available | [View guide →](/docs/react/migration/radio-group) |
| RangeCalendar | RangeCalendar | ✅ Available | [View guide →](/docs/react/migration/range-calendar) |
| Ripple | ❌ | ❌ Removed | [See Button ripple →](/docs/react/components/button#adding-ripple-effect) |
| ScrollShadow | ScrollShadow | ✅ Available | [View guide →](/docs/react/migration/scroll-shadow) |
| Select | Select | ✅ Available | [View guide →](/docs/react/migration/select) |
| Skeleton | Skeleton | ✅ Available | [View guide →](/docs/react/migration/skeleton) |
| Slider | Slider | ✅ Available | [View guide →](/docs/react/migration/slider) |
| Snippet | ❌ | ❌ Removed | [View guide →](/docs/react/migration/snippet) |
| Spacer | ❌ | ❌ Removed | [View guide →](/docs/react/migration/spacer) |
| Spinner | Spinner | ✅ Available | [View guide →](/docs/react/migration/spinner) |
| Switch | Switch | ✅ Available | [View guide →](/docs/react/migration/switch) |
| Table | Table | ✅ Available | [View guide →](/docs/react/migration/table) |
| Tabs | Tabs | ✅ Available | [View guide →](/docs/react/migration/tabs) |
| Toast | Toast | ✅ Available | [View guide →](/docs/react/migration/toast) |
| Tooltip | Tooltip | ✅ Available | [View guide →](/docs/react/migration/tooltip) |
| User | ❌ | ❌ Removed | [View guide →](/docs/react/migration/user) |
**Removed/In-Progress/Planned Components**: For components marked as ❌ Removed, 🔄 In Progress, or 📋 Planned, replace them with standard HTML elements during migration. You can migrate back to HeroUI components once they become available in v3.
Use the `get_component_migration_guides` MCP tool to fetch detailed guides for each component.
## New Components in v3
## New Components in v3
v3 introduces a number of new components not available in v2:
| Component | Purpose | Documentation |
| --------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------ |
| TextField | Enhanced text input with label and description support | [View docs →](/docs/react/components/text-field) |
| TextArea | Multi-line text input component | [View docs →](/docs/react/components/text-area) |
| AlertDialog | Modal dialog for confirmations and alerts | [View docs →](/docs/react/components/alert-dialog) |
| Label | Accessible form label component | [View docs →](/docs/react/components/label) |
| Description | Helper text for form fields | [View docs →](/docs/react/components/description) |
| FieldError | Form field error message display | [View docs →](/docs/react/components/field-error) |
| Fieldset | Group related form fields | [View docs →](/docs/react/components/fieldset) |
| InputGroup | Compose multiple inputs together | [View docs →](/docs/react/components/input-group) |
| Surface | Container component with elevation styles | [View docs →](/docs/react/components/surface) |
| Disclosure | Expandable/collapsible content sections | [View docs →](/docs/react/components/disclosure) |
| DisclosureGroup | Compound component for multiple disclosure sections | [View docs →](/docs/react/components/disclosure-group) |
| SearchField | Search input with clear button and optional loading state | [View docs →](/docs/react/components/search-field) |
| DateField | Date input with calendar picker | [View docs →](/docs/react/components/date-field) |
| TimeField | Time input component | [View docs →](/docs/react/components/time-field) |
| Tag, TagGroup | Tags and tag group for selection or display | [View docs →](/docs/react/components/tag-group) |
| ColorPicker | Color selection (ColorArea, ColorField, ColorSlider, ColorSwatch, ColorSwatchPicker) | [View docs →](/docs/react/components/color-picker) |
| CloseButton | Dismiss or close trigger button | [View docs →](/docs/react/components/close-button) |
| ErrorMessage | Form field error display (React Aria integration) | [View docs →](/docs/react/components/error-message) |
## Custom Theme Overrides
See the [Full Migration Guide](/docs/react/migration/full-migration#step-7-update-custom-theme-overrides) for custom theme override migration.
## Styling Migration
See the [Full Migration Guide](/docs/react/migration/full-migration#step-9-styling-migration) for detailed styling migration instructions.
**Agent Note**: Use the `get_styling_migration_guide` MCP tool for comprehensive styling migration details.
**Important**: Styling migration happens AFTER component migration and dependency switch.
## Full Migration Workflow
**IMPORTANT**: Since v2 and v3 cannot coexist, the migration happens in two main stages:
1. **Preparation Stage**: Migrate all component code while still on v2 dependencies (code will be broken)
2. **Switch Stage**: Update dependencies to v3 and fix any remaining issues
**⚠️ CRITICAL: Don't build to check for errors during migration**
* Use **typecheck** (e.g., `tsc --noEmit`) if available to check TypeScript errors
* Use **lint** (e.g., `eslint`, `biome check`) if available to check code quality
* **DO NOT** run build commands (e.g., `npm run build`, `next build`, `vite build`)
* **DO NOT** attempt to start/run the project during migration
### Phase 0: Setup and Analysis
1. **Create migration branch**
* Create a feature branch for migration work
* Example: `git checkout -b migrate/heroui-v3`
2. **Verify Migration MCP is configured**
* Check that Migration MCP server is connected
* Ensure `heroui-react` MCP is NOT connected (to avoid confusion)
* Verify MCP tools are available
3. **Analyze the project and create a migration plan**
* Use `get_migration_workflow` to fetch this guide
* Scan for HeroUI v2 imports, identify all components and usage
* Map component dependencies
* Create a phased migration plan (e.g., 3-5 components per phase, by-dependency strategy)
### Phase 1: Dependency Preparation (Before Code Changes)
These steps can be done before switching HeroUI dependencies and won't break the project:
1. **Update React to v19** (if not already)
* This can be done before switching HeroUI
2. **Update Tailwind CSS to v4** (if not already)
* This can be done before switching HeroUI
3. **🛑 CHECKPOINT: Stop and wait for user approval**
* **DO NOT proceed to next phase automatically**
* Explain configuration changes
* Wait for explicit user approval before continuing
### Phase 2-N: Code Migration (v2 Dependencies Still Active)
**Critical**: During this phase, code will reference v3 APIs but v2 dependencies are still installed. The project will be broken. This is expected and normal.
For each component group in the migration plan:
1. **Fetch component-specific guides**
* Use `get_component_migration_guides` MCP tool for each component
* Review API changes, prop migrations, structure changes
2. **Apply code migrations**
* Migrate component code to v3 API patterns
* Update imports, props, component structure
* **Note**: Code will be broken until dependencies are switched
3. **Handle dependencies**
* If components have dependencies, migrate dependencies first
* Check if dependencies are already migrated
* Migrate shared code as needed
4. **🛑 CHECKPOINT: Stop and wait for user approval**
* **DO NOT proceed to next phase automatically**
* Summarize what was migrated in this phase
* Wait for explicit user approval before continuing
5. **Document migration status**
* Track which components have been migrated
* Note any issues or concerns
### Phase Final: Dependency Switch and Fixes
**Critical**: Only proceed when ALL components have been migrated to v3 API patterns.
1. **Update dependencies**
* Remove `@heroui/react` and `@heroui/theme` (v2)
* Install `@heroui/react` and `@heroui/styles` (v3)
* Remove `framer-motion` if present
* Update CSS imports (add `@import "@heroui/styles";`)
* Remove HeroUIProvider from app root
* Update Tailwind config (remove `heroui()` plugin)
2. **Fix remaining issues**
* Run typecheck/lint if available (do NOT build)
* Fix any TypeScript errors reported by typecheck
* Fix any linting errors
* Note: Do not attempt to build or run the project during migration
3. **🛑 CHECKPOINT: Stop and wait for user approval**
* **DO NOT proceed to styling automatically**
* Verify components work correctly
* Wait for explicit user approval before styling migration
4. **Continue with styling migration**
* Use `get_styling_migration_guide` MCP tool to fetch styling guide
* Apply styling updates systematically
5. **Apply styling updates**
* Use `get_styling_migration_guide` MCP tool
* Update utility classes, color tokens, CSS variables
* Test visual appearance
6. **Final verification**
* Run typecheck/lint one final time (do NOT build)
* Verify all styling updated correctly
* Note: Full testing (visual, functionality, accessibility) should be done after migration is complete, not during migration
## Migration Strategies
### Strategy 1: By Dependency (Recommended)
* Migrate foundational components first (Button, Input, Card, etc.)
* Then migrate components that depend on them
* Best for projects with complex component hierarchies
* Ensures dependencies are ready before dependents
**Example order**:
1. Button, Input, Link (foundational)
2. Card, Modal (use Button)
3. Form, Dropdown (use Input, Button)
4. Complex components (use multiple dependencies)
### Strategy 2: By Feature
* Migrate all components in a feature/module together
* Good for feature-based code organization
* Allows feature-by-feature testing
* May require migrating dependencies first
**Example**:
* Feature: User Authentication
* Migrate: Input, Button, Form, Modal (all auth-related)
* Feature: Dashboard
* Migrate: Card, Tabs, Select (all dashboard-related)
### Strategy 3: By Frequency
* Migrate most-used components first
* Provides quick wins and early validation
* Good for large codebases with clear usage patterns
* Still need to handle dependencies
**Example**:
1. Button (used 150 times)
2. Input (used 120 times)
3. Card (used 80 times)
4. ... (continue by usage count)
## Best Practices for Agents
1. **Warn about broken state**
* Always inform user that project will be broken during migration
* Recommend using a feature branch
* Set expectations about when project will work again
2. **Migrate all code before switching dependencies**
* Complete all component code migrations first
* Only switch dependencies when ALL components are migrated
* This minimizes the broken state duration
3. **Use MCP tools for each phase**
* Use `get_migration_workflow`, `get_component_migration_guides`, `get_styling_migration_guide` as needed
* **Critical**: Always stop at checkpoints and wait for user approval between phases
4. **Work in a feature branch**
* Always recommend creating a migration branch
* Allow user to continue working on main branch
* Enable easy rollback if needed
5. **Document migration status**
* Track which components have been migrated
* Note any issues or concerns
* Keep a checklist visible
* Update status after each phase
6. **Handle errors gracefully**
* If a component migration fails, document why
* Continue with other components
* Return to failed components after dependency switch
* Some issues may resolve once dependencies are updated
7. **Verify after dependency switch**
* Only after dependencies are updated to v3
* Run typecheck/lint (do NOT build)
* Fix type/lint errors as they arise
* Don't proceed to styling until typecheck/lint passes
## Common Scenarios
### Large Project (100+ components)
* Use smaller batch sizes (3-5 components)
* Prioritize by dependency or frequency
* Allow for multiple sessions
* Create checkpoints between phases
* Use "by-dependency" strategy
* Document progress clearly
### Small Project (\<20 components)
* Can use larger batches (5-10 components)
* May complete in fewer phases
* Still verify incrementally (full migration approach)
* Can use any strategy
* Still need to migrate all code before switching deps
### Mixed v2/v3 Usage (Full Migration)
* **Not possible in full migration**: v2 and v3 cannot coexist in full migration approach
* Must migrate all components before switching dependencies
* Use feature branch to maintain working main branch
* Complete migration in one go
## Next Steps
After completing migration:
1. Remove v2 dependencies (already done in dependency switch)
2. Switch from Migration MCP to `heroui-react` MCP for v3 development
3. Update documentation references
4. Run final verification
5. Merge migration branch to main
This agent migration guide is designed to work with the Migration MCP server. Ensure the MCP server is properly configured and tools are available before starting migration.
# Agent Migration Guide - Incremental Migration with Coexistence
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/migration/agent-guide-incremental
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/migration/(workflows)/agent-guide-incremental.mdx
> Incremental coexistence migration guide for AI assistants helping migrate HeroUI v2 to v3
## Overview
This guide is designed for AI assistants (agents) helping users migrate from HeroUI v2 to v3 using **incremental coexistence migration**. This approach allows v2 and v3 components to work side-by-side, enabling component-by-component migration while keeping the project functional.
**Key Difference**: Unlike full migration, incremental coexistence migration allows the project to remain functional during migration. Both v2 and v3 components can coexist temporarily.
## Key Principles
1. **Incremental Component Migration**: Migrate one component at a time, testing each before proceeding
2. **Project Remains Functional**: Unlike full migration, the project should remain working throughout
3. **Strategy Identification**: Determine which coexistence strategy the project uses (A: pnpm aliases or B: component packages)
4. **Component-by-Component Testing**: Test each migrated component before moving to the next
5. **CSS Conflict Management**: Monitor and resolve styling conflicts between v2 and v3 during coexistence
6. **Removed Components**: v2 components with no v3 counterpart (Code, Image, Navbar, Ripple, Snippet, Spacer, User) can be left in place during coexistence. Do not migrate them unless the user explicitly asks to replace them.
## Major Changes in v3
## Major Changes
* **Dependencies**: Update React to v19+, HeroUI packages to v3, Tailwind CSS to v4, remove Framer Motion
* **No Provider Required**: v3 removes the need for `HeroUIProvider`
* **Component API Updates**: Many components use React Aria Components patterns
* **Compound Components**: New compound component patterns for better customization. See individual component guides for details.
* **Hooks Removed**: v2 component hooks like `useSwitch` or `useInput` are removed - use compound components instead. `useDisclosure` is replaced with `useOverlayState`. See the [Hooks Migration Guide](/docs/react/migration/hooks) for details.
* **Configuration**: Remove `heroui()` plugin from Tailwind config, update CSS imports, remove `hero.ts` file
* **Item identity**: Collection items (Dropdown, Listbox, Select, Accordion, etc.) now use `id` and `textValue` in v3; keep React's `key` for lists.
## Incremental Migration Setup
For detailed setup and migration instructions, see the [Incremental Migration Guide](/docs/react/migration/incremental-migration). The guide covers:
* Strategy selection (A: pnpm aliases or B: component packages)
* Detailed setup for each strategy
* CSS configuration for coexistence
* Component-by-component migration process
* CSS conflict handling
* Completing the migration
## Strategy Identification
For projects using incremental coexistence strategies, agents should:
1. **Identify the strategy**: Check if project uses pnpm aliases (Strategy A) or component packages (Strategy B)
* **Strategy A**: Look for aliases like `"@heroui-v3/react": "npm:@heroui/react@latest"` in package.json
* **Strategy B**: Look for component-specific packages like `@heroui/button`, `@heroui/card` alongside `@heroui/react`
2. **Verify setup**: Ensure the coexistence setup is correct:
* Strategy A: Both `@heroui/react` (v2) and `@heroui-v3/react` (v3 alias) are installed
* Strategy B: `@heroui/react` (v3) and component packages like `@heroui/button` (v2) are installed
* CSS is configured for both versions (see CSS Configuration section)
## Component-by-Component Migration Guidance
### For Strategy A (pnpm aliases):
1. **Identify component to migrate**
* Review the component migration reference table
* Use `get_component_migration_guides` MCP tool to fetch component-specific guide
2. **Update imports**
* Change imports from `@heroui/react` to `@heroui-v3/react`
* Example: `import {Button} from "@heroui/react"` → `import {Button} from "@heroui-v3/react"`
3. **Update component code**
* Follow component migration guide from `get_component_migration_guides` tool
* Update props, component structure, and API calls
* Replace hooks with compound components if needed
4. **Test the migrated component**
* Verify component renders correctly
* Test functionality and interactions
* Check for styling conflicts
5. **Document migration**
* Track which components have been migrated
* Note any issues or concerns
### For Strategy B (component packages):
1. **Identify component to migrate**
* Review the component migration reference table
* Use `get_component_migration_guides` MCP tool to fetch component-specific guide
2. **Remove component package**
* Remove the v2 component package from dependencies (e.g., `@heroui/button`)
* Update package.json
3. **Update imports**
* Change imports from component package to `@heroui/react` (v3)
* Example: `import {Card} from "@heroui/card"` → `import {Card} from "@heroui/react"`
4. **Update component code**
* Follow component migration guide from `get_component_migration_guides` tool
* Update props, component structure, and API calls
* Replace hooks with compound components if needed
5. **Test the migrated component**
* Verify component renders correctly
* Test functionality and interactions
* Check for styling conflicts
6. **Document migration**
* Track which components have been migrated
* Note any issues or concerns
### Handling Removed Components (No v3 Counterpart)
When encountering v2 components with no v3 counterpart (Code, Image, Navbar, Ripple, Snippet, Spacer, User):
* **Leave them in place** — do not attempt to migrate them unless the user explicitly requests removal
* **If the user wants to remove them**: Use `get_component_migration_guides` (where available) or the [Component Migration Reference](/docs/react/migration#component-migration-reference) to fetch guides and help replace with native HTML or manual implementations
## CSS Conflict Handling
During coexistence, both v2 and v3 CSS systems will be loaded. Agents should:
1. **Monitor for conflicts**
* Watch for styling inconsistencies
* Check if v2 and v3 styles are conflicting
* Verify both CSS imports are present and in correct order
2. **Guide conflict resolution**
* Ensure CSS import order: `tailwindcss` first, then `@heroui/styles`
* Check Tailwind config has v2 plugin configured
* Verify v3 CSS is imported correctly
3. **Test styling after each migration**
* Verify migrated components look correct
* Check for unexpected style overrides
* Ensure v2 components still styled correctly
## Component Migration Reference
Use the table below to quickly find migration guidance for each component. Use the link in the "Migration Guide" column to jump to detailed migration instructions.
**Component Development Status**: Components marked with 🔄 In Progress or 📋 Planned are still being developed. Check the [Roadmap](https://herouiv3.featurebase.app/roadmap) for the task status. Guides for these components will be available once development is finished.
| v2 Component | v3 Component | Status | Migration Guide |
| ---------------- | ---------------------------- | ----------- | ------------------------------------------------------------------------- |
| Accordion | Accordion | ✅ Available | [View guide →](/docs/react/migration/accordion) |
| Alert | Alert | ✅ Available | [View guide →](/docs/react/migration/alert) |
| Autocomplete | ComboBox | ✅ Renamed | [View guide →](/docs/react/migration/autocomplete) |
| Avatar | Avatar | ✅ Available | [View guide →](/docs/react/migration/avatar) |
| Badge | Badge | ✅ Available | [View guide →](/docs/react/migration/badge) |
| Breadcrumbs | Breadcrumbs | ✅ Available | [View guide →](/docs/react/migration/breadcrumbs) |
| Button | Button | ✅ Available | [View guide →](/docs/react/migration/button) |
| ButtonGroup | ButtonGroup | ✅ Available | [View guide →](/docs/react/migration/button-group) |
| Calendar | Calendar | ✅ Available | [View guide →](/docs/react/migration/calendar) |
| Card | Card | ✅ Available | [View guide →](/docs/react/migration/card) |
| Checkbox | Checkbox | ✅ Available | [View guide →](/docs/react/migration/checkbox) |
| CheckboxGroup | CheckboxGroup | ✅ Available | [View guide →](/docs/react/migration/checkbox-group) |
| Chip | Chip | ✅ Available | [View guide →](/docs/react/migration/chip) |
| Code | ❌ | ❌ Removed | [View guide →](/docs/react/migration/code) |
| DateInput | DateField | ✅ Renamed | [View guide →](/docs/react/migration/dateinput) |
| DatePicker | DatePicker | ✅ Available | [View guide →](/docs/react/migration/date-picker) |
| DateRangePicker | DateRangePicker | ✅ Available | [View guide →](/docs/react/migration/date-range-picker) |
| TimeInput | TimeField | ✅ Renamed | [View guide →](/docs/react/migration/timeinput) |
| Divider | Separator | ✅ Renamed | [View guide →](/docs/react/migration/divider) |
| Drawer | Drawer | ✅ Available | [View guide →](/docs/react/migration/drawer) |
| Dropdown | Dropdown | ✅ Available | [View guide →](/docs/react/migration/dropdown) |
| Form | Form | ✅ Available | [View guide →](/docs/react/migration/form) |
| Image | ❌ | ❌ Removed | [View guide →](/docs/react/migration/image) |
| Input | TextField, Input, InputGroup | ✅ Available | [View guide →](/docs/react/migration/input) |
| InputOTP | InputOTP | ✅ Available | [View guide →](/docs/react/migration/input-otp) |
| Kbd | Kbd | ✅ Available | [View guide →](/docs/react/migration/kbd) |
| Link | Link | ✅ Available | [View guide →](/docs/react/migration/link) |
| Listbox | ListBox | ✅ Available | [View guide →](/docs/react/migration/listbox) |
| Modal | Modal | ✅ Available | [View guide →](/docs/react/migration/modal) |
| Navbar | ❌ | ❌ Removed | [View guide →](/docs/react/migration/navbar) |
| NumberInput | NumberField | ✅ Renamed | [View guide →](/docs/react/migration/numberinput) |
| Pagination | Pagination | ✅ Available | [View guide →](/docs/react/migration/pagination) |
| Popover | Popover | ✅ Available | [View guide →](/docs/react/migration/popover) |
| Progress | ProgressBar | ✅ Renamed | [View guide →](/docs/react/migration/progress) |
| CircularProgress | ProgressCircle | ✅ Renamed | [View guide →](/docs/react/migration/circular-progress) |
| Radio | Radio | ✅ Available | [View guide →](/docs/react/migration/radio) |
| RadioGroup | RadioGroup | ✅ Available | [View guide →](/docs/react/migration/radio-group) |
| RangeCalendar | RangeCalendar | ✅ Available | [View guide →](/docs/react/migration/range-calendar) |
| Ripple | ❌ | ❌ Removed | [See Button ripple →](/docs/react/components/button#adding-ripple-effect) |
| ScrollShadow | ScrollShadow | ✅ Available | [View guide →](/docs/react/migration/scroll-shadow) |
| Select | Select | ✅ Available | [View guide →](/docs/react/migration/select) |
| Skeleton | Skeleton | ✅ Available | [View guide →](/docs/react/migration/skeleton) |
| Slider | Slider | ✅ Available | [View guide →](/docs/react/migration/slider) |
| Snippet | ❌ | ❌ Removed | [View guide →](/docs/react/migration/snippet) |
| Spacer | ❌ | ❌ Removed | [View guide →](/docs/react/migration/spacer) |
| Spinner | Spinner | ✅ Available | [View guide →](/docs/react/migration/spinner) |
| Switch | Switch | ✅ Available | [View guide →](/docs/react/migration/switch) |
| Table | Table | ✅ Available | [View guide →](/docs/react/migration/table) |
| Tabs | Tabs | ✅ Available | [View guide →](/docs/react/migration/tabs) |
| Toast | Toast | ✅ Available | [View guide →](/docs/react/migration/toast) |
| Tooltip | Tooltip | ✅ Available | [View guide →](/docs/react/migration/tooltip) |
| User | ❌ | ❌ Removed | [View guide →](/docs/react/migration/user) |
Use the `get_component_migration_guides` MCP tool to fetch detailed guides for each component.
## New Components in v3
## New Components in v3
v3 introduces a number of new components not available in v2:
| Component | Purpose | Documentation |
| --------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------ |
| TextField | Enhanced text input with label and description support | [View docs →](/docs/react/components/text-field) |
| TextArea | Multi-line text input component | [View docs →](/docs/react/components/text-area) |
| AlertDialog | Modal dialog for confirmations and alerts | [View docs →](/docs/react/components/alert-dialog) |
| Label | Accessible form label component | [View docs →](/docs/react/components/label) |
| Description | Helper text for form fields | [View docs →](/docs/react/components/description) |
| FieldError | Form field error message display | [View docs →](/docs/react/components/field-error) |
| Fieldset | Group related form fields | [View docs →](/docs/react/components/fieldset) |
| InputGroup | Compose multiple inputs together | [View docs →](/docs/react/components/input-group) |
| Surface | Container component with elevation styles | [View docs →](/docs/react/components/surface) |
| Disclosure | Expandable/collapsible content sections | [View docs →](/docs/react/components/disclosure) |
| DisclosureGroup | Compound component for multiple disclosure sections | [View docs →](/docs/react/components/disclosure-group) |
| SearchField | Search input with clear button and optional loading state | [View docs →](/docs/react/components/search-field) |
| DateField | Date input with calendar picker | [View docs →](/docs/react/components/date-field) |
| TimeField | Time input component | [View docs →](/docs/react/components/time-field) |
| Tag, TagGroup | Tags and tag group for selection or display | [View docs →](/docs/react/components/tag-group) |
| ColorPicker | Color selection (ColorArea, ColorField, ColorSlider, ColorSwatch, ColorSwatchPicker) | [View docs →](/docs/react/components/color-picker) |
| CloseButton | Dismiss or close trigger button | [View docs →](/docs/react/components/close-button) |
| ErrorMessage | Form field error display (React Aria integration) | [View docs →](/docs/react/components/error-message) |
## Completion Steps
Once all components are migrated:
1. **Remove v2 dependencies**
* Strategy A: Remove `@heroui/react`, `@heroui/theme`, and aliases
* Strategy B: Remove all remaining `@heroui/*` component packages
* If the project contains v2 removed components (Code, Image, Navbar, Ripple, Snippet, Spacer, User), inform the user they may ask to remove them later using the provided guides.
2. **Update all imports**
* Strategy A: Change `@heroui-v3/react` → `@heroui/react`
* Strategy B: All imports should already be `@heroui/react` (v3)
3. **Update CSS configuration**
* Remove v2 Tailwind plugin from config
* Keep only `@import "@heroui/styles";`
* Remove v2 CSS imports
4. **Complete styling migration**
* Follow the styling migration guide
* Use `get_styling_migration_guide` MCP tool
* Update utility classes, color tokens, etc.
## Differences from Full Migration
**Key Differences:**
* **Project State**: Project remains functional during migration (no broken state)
* **Migration Pace**: Can migrate over extended period, component by component
* **Testing**: Can test v3 components alongside v2 before full migration
* **Branch Strategy**: Feature branch less critical (though still recommended)
* **Dependency Management**: Both versions coexist temporarily
* **CSS Handling**: Both CSS systems loaded during coexistence period
**When to Use Incremental Coexistence:**
* Large codebases that need gradual migration
* Projects that must remain functional during migration
* Teams that want to test v3 components incrementally
* Projects already using component-specific packages (Strategy B)
**When to Use Full Migration:**
* Smaller projects that can migrate quickly
* Projects where temporary broken state is acceptable
* Teams that prefer all-at-once migration
* Projects using unified `@heroui/react` package (Strategy A can work but full migration may be simpler)
## Best Practices for Agents
1. **Verify project remains functional**
* After each component migration, ensure project still works
* Test both migrated and non-migrated components
* Report any issues immediately
2. **Guide component-by-component migration**
* Help migrate one component at a time
* Use `get_component_migration_guides` MCP tool for each component
* Test thoroughly before proceeding
3. **Monitor CSS conflicts**
* Watch for styling issues between v2 and v3
* Guide resolution of conflicts
* Ensure CSS configuration is correct
4. **Track migration progress**
* Keep a checklist of migrated components
* Document any issues or concerns
* Note which strategy is being used
5. **Guide completion steps**
* Once all components migrated, guide removal of v2 dependencies
* Help update all imports to v3-only
* Guide styling migration completion
## Common Scenarios
### Large Project (100+ components)
* Migrate components incrementally over time
* Test each component thoroughly
* Monitor for CSS conflicts
* Can take weeks or months to complete
### Small Project (\<20 components)
* Can migrate more quickly
* Still test each component
* Less CSS conflict risk
* May complete in days
### Mixed Strategy Projects
* Some projects may use Strategy A for some components, Strategy B for others
* Guide appropriate import updates for each component
* Ensure consistent approach where possible
### Project Uses v2 Navbar (or Other Removed Components)
* Leave in place — v2 Navbar and other removed components (Code, Image, Ripple, Snippet, Spacer, User) will continue to work during coexistence
* Inform the user they can optionally remove them later with agent assistance and the [Component Migration Reference](/docs/react/migration#component-migration-reference) guides
## Next Steps
After completing migration:
1. Remove v2 dependencies (already done in completion steps)
2. Switch from Migration MCP to `heroui-react` MCP for v3 development
3. Update documentation references
4. Run final verification
5. Complete styling migration
This agent migration guide is designed to work with the Migration MCP server. Ensure the MCP server is properly configured and tools are available before starting migration.
# Full Migration
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/migration/full-migration
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/migration/(workflows)/full-migration.mdx
> Step-by-step guide for migrating HeroUI v2 to v3 using full migration approach
## Overview
Full migration is a structured approach to migrating from HeroUI v2 to v3. This method involves migrating all component code first, then switching dependencies, ensuring a clean transition.
**Important:** Full migration means the project will be broken during migration (v2 and v3 cannot coexist). Work in a feature branch to maintain a working main branch.
### AI Assistant Resources
AI assistants can help with migration. Use the [Migration MCP Server](/docs/react/migration/mcp-server) for tools and prompts, [Migration Agent Skills](/docs/react/migration/agent-skills) for skill-based knowledge, or [AGENTS.md for Migration](/docs/react/migration/agents-md) to download migration docs into your project.
## Migration Workflow
**IMPORTANT**: Since v2 and v3 cannot coexist, the migration happens in two main stages:
1. **Preparation Stage**: Migrate all component code while still on v2 dependencies (code will be broken)
2. **Switch Stage**: Update dependencies to v3 and fix any remaining issues
**⚠️ CRITICAL: Don't build to check for errors during migration**
* Use **typecheck** (e.g., `tsc --noEmit`) if available to check TypeScript errors
* Use **lint** (e.g., `eslint`, `biome check`) if available to check code quality
* **DO NOT** run build commands (e.g., `npm run build`, `next build`, `vite build`)
* **DO NOT** attempt to start/run the project during migration
## Step-by-Step Migration Guide
### Step 1: Update Dependencies
#### Update React
v3 requires React 19+. Update your React version:
```bash
npm install react@^19.0.0 react-dom@^19.0.0
```
```bash
pnpm add react@^19.0.0 react-dom@^19.0.0
```
```bash
yarn add react@^19.0.0 react-dom@^19.0.0
```
```bash
bun add react@^19.0.0 react-dom@^19.0.0
```
**Note**: These dependency updates can be done before switching HeroUI (won't break project). However, the HeroUI package switch should only happen AFTER all component code is migrated.
#### Update HeroUI Packages
**Important**: Do this AFTER migrating all component code. Remove v2 packages and install v3:
```bash
npm uninstall @heroui/react @heroui/theme
npm install @heroui/styles @heroui/react
```
```bash
pnpm remove @heroui/react @heroui/theme
pnpm add @heroui/styles @heroui/react
```
```bash
yarn remove @heroui/react @heroui/theme
yarn add @heroui/styles @heroui/react
```
```bash
bun remove @heroui/react @heroui/theme
bun add @heroui/styles @heroui/react
```
#### Remove Framer Motion
v3 no longer requires Framer Motion:
```bash
npm uninstall framer-motion
```
```bash
pnpm remove framer-motion
```
```bash
yarn remove framer-motion
```
```bash
bun remove framer-motion
```
#### Update Tailwind CSS
Ensure you're using Tailwind CSS v4:
```bash
npm install tailwindcss@^4.0.0
```
```bash
pnpm add tailwindcss@^4.0.0
```
```bash
yarn add tailwindcss@^4.0.0
```
```bash
bun add tailwindcss@^4.0.0
```
### Step 2: Update Theming Configuration
#### Remove Tailwind Plugin Configuration
**v2 Configuration:**
```js
// tailwind.config.js
const {heroui} = require("@heroui/react");
module.exports = {
content: [
"./src/**/*.{js,ts,jsx,tsx}",
"./node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}",
],
plugins: [heroui()],
// ... other config
};
```
**v3 Configuration:**
Remove the `heroui()` plugin from your Tailwind config. If you only used Tailwind for HeroUI and have no other customizations, you can remove `tailwind.config.js` entirely. Otherwise, keep the file but remove the HeroUI plugin configuration.
#### Update CSS Imports
**v2 CSS:**
```css
/* globals.css or main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
```
**v3 CSS:**
```css
/* globals.css or main.css */
@import "tailwindcss";
@import "@heroui/styles"; /* [!code highlight]*/
```
Import order matters! Always import `tailwindcss` before `@heroui/styles`.
#### Remove Theme Plugin File
If you created a `hero.ts` file for v2, you can remove it:
```bash
rm hero.ts
```
### Step 3: Remove HeroUIProvider
v3 does not require a Provider component. Remove it from your application root.
**v2 Code:**
```tsx
// app.tsx or App.tsx
import {HeroUIProvider} from "@heroui/react";
function App() {
return (
);
}
```
**v3 Code:**
```tsx
// app.tsx or App.tsx
function App() {
return ;
}
```
**If you were using Provider props:**
If you were using Provider props like `navigate`, `useHref`, `locale`, `disableAnimation`, etc., you'll need to handle these differently:
* **Router integration**: Use React Router or your routing library directly
* **Locale**: Use React Aria's `I18nProvider` directly if needed
* **Animation**: See the Animation Changes section below
#### Animation Changes
v3 removes the Framer Motion dependency and handles animations differently:
* **CSS-based animations**: v3 uses native CSS animations and transitions instead of JavaScript-based animations
* **Better performance**: CSS animations provide better performance and smoother animations
* **No global disable**: Unlike v2's Provider `disableAnimation` prop, there's no global animation toggle in v3
* **Per-component control**: Control animations through CSS or component-specific props where available
* **Custom animations**: Use standard CSS `@keyframes` and transition properties for custom animations
### Step 4: Replace Removed Hooks
HeroUI v2 provided component hooks (like `useSwitch`, `useInput`, `useCheckbox`, etc.) and utility hooks like `useDisclosure`. HeroUI v3 removes most component hooks in favor of compound components, and replaces `useDisclosure` with `useOverlayState`.
See the comprehensive [Hooks Migration Guide](/docs/react/migration/hooks) for:
* Component hooks removal and migration to compound components
* `useDisclosure` → `useOverlayState` migration
* Migration strategies and examples
### Step 5: Update Component Imports
All components are now imported from a single package:
**v2 Imports:**
```tsx
// Individual packages (if used)
import {Button} from "@heroui/button";
import {Card} from "@heroui/card";
// Or from main package
import {Button, Card} from "@heroui/react";
```
**v3 Imports:**
```tsx
// All components from single package
import {Button, Card} from "@heroui/react";
```
#### TypeScript Considerations
If you're using TypeScript, be aware of type changes in v3:
```tsx
// Import types alongside components
import {Button, type ButtonProps} from "@heroui/react";
// Compound component types are properly exported
import {Checkbox, type CheckboxProps} from "@heroui/react";
// Type names may have changed - check component documentation
type MyButtonProps = ButtonProps & {
customProp?: string;
};
```
**Common type changes:**
* Component prop interfaces may have different names or properties
* Compound component parts have their own type exports
* Ref types updated to match React 19 patterns
### Step 6: Component Migration
Use the [Component Migration Reference](/docs/react/migration#component-migration-reference) table to find migration guides for each component. Migrate components according to their specific guides.
**Key points:**
* Review individual component migration guides as needed
* Migrate component APIs, props, and structure
* Update compound component patterns (Checkbox, Radio, Switch, Card, Modal, etc.)
* Update group components (ButtonGroup, CheckboxGroup, RadioGroup)
* Handle removed components (Code, Image, Navbar, Ripple, Snippet, Spacer, User) - replace with HTML elements
* Handle in-progress/planned components - replace with HTML elements until v3 components are available
### Step 7: Update Custom Theme Overrides
If you had custom theme overrides in v2, you'll need to update them for v3's CSS-based theming system.
**v2 Theme Customization:**
```js
// tailwind.config.js
const {heroui} = require("@heroui/react");
module.exports = {
plugins: [
heroui({
themes: {
light: {
colors: {
primary: {
// custom colors
},
},
},
},
}),
],
};
```
**v3 Theme Customization:**
v3 uses CSS variables. Override them in your CSS:
```css
/* globals.css */
@import "tailwindcss";
@import "@heroui/styles";
:root {
--color-primary: /* your color */;
/* other CSS variables */
}
```
Check the [Theming documentation](/docs/react/getting-started/handbook/theming) for available CSS variables.
This is a good point to pause and verify component functionality before proceeding to styling migration.
### Step 8: Hooks Migration
After component migration is complete, ensure all hooks have been migrated:
1. **Replace component hooks**: Replace hooks like `useSwitch`, `useInput`, `useCheckbox`, etc. with compound components
2. **Migrate useDisclosure**: Replace `useDisclosure` with `useOverlayState` for overlay state management
3. **Reference hooks guide**: See the [Hooks Migration Guide](/docs/react/migration/hooks) for detailed migration instructions and examples
### Step 9: Styling Migration
After hooks migration is complete, proceed with styling changes. This is a separate step to ensure component functionality is verified before addressing visual changes.
**Styling Migration Guide:**
See the comprehensive [Styling Migration Guide](/docs/react/migration/styling) for:
* Utility class changes (`text-tiny` → `text-xs`, `rounded-small` → `rounded-sm`, etc.)
* Color token updates (`bg-primary` → `bg-accent`, `bg-content1` → `bg-surface`, etc.)
* Component styling differences (sizes, spacing, border radius)
* CSS variable changes
* Visual differences and alignment changes
**Key Styling Changes:**
* **Utility Classes**: Custom utilities replaced with standard Tailwind classes
* **Color Tokens**: `primary` → `accent`, `secondary` removed, `content1-4` → `surface`/`overlay`
* **Numbered Scales**: Color scales like `primary-50`, `primary-100` removed
* **Border Radius**: Default values changed (smaller in v3)
* **Component Styles**: Updated default sizes, padding, and spacing
**Migration Checklist:**
* Review [Styling Migration Guide](/docs/react/migration/styling)
* Update utility classes (`text-tiny` → `text-xs`, etc.)
* Update color tokens (`bg-primary` → `bg-accent`, etc.)
* Update content colors (`bg-content1` → `bg-surface` or `bg-overlay`)
* Update numbered color scales (`bg-primary-50` → `bg-accent-soft`)
* Review component styling changes (sizes, spacing, border radius)
* Test visual appearance and adjust as needed
### Step 10: Testing
After migration, thoroughly test your application:
1. **Visual Testing**: Check all components render correctly
2. **Functionality**: Test all interactions and behaviors
3. **Accessibility**: Verify keyboard navigation and screen reader support
4. **Responsive Design**: Test on different screen sizes
5. **Performance**: Check bundle size and runtime performance
## Migration Checklist
Use this checklist to track your full migration progress:
### Dependencies
* Update React to v19+
* Update HeroUI packages to v3 (after component migration)
* Remove Framer Motion
* Update Tailwind CSS to v4
### Configuration
* Remove `heroui()` plugin from Tailwind config
* Update CSS imports
* Remove `hero.ts` file (if exists)
### Application Code - Component Migration
* Remove `HeroUIProvider` wrapper
* Handle Provider props migration (router, locale, animations)
* Update all component imports to `@heroui/react`
* Migrate renamed components (Divider → Separator, Autocomplete → Combobox, NumberInput → NumberField)
* Update compound component patterns (Checkbox, Radio, Switch, Card, Modal, etc.)
* Update group components (ButtonGroup, CheckboxGroup, RadioGroup)
* Update TypeScript type references (if using component types)
* Handle removed components (Code, Image, Navbar, Ripple, Snippet, Spacer, User) - replace with HTML elements
* Handle in-progress/planned components - replace with HTML elements until v3 components are available
* Consider using `asChild` prop for flexible composition
* **Verify component functionality before proceeding to hooks migration**
### Application Code - Hooks Migration
* Review [Hooks Migration Guide](/docs/react/migration/hooks)
* Replace component hooks (`useSwitch`, `useInput`, `useCheckbox`, etc.) with compound components
* Replace `useDisclosure` with `useOverlayState` for overlay state management
* Update all hook usages according to migration guide
* **Verify hooks migration before proceeding to styling migration**
### Application Code - Styling Migration
* Review [Styling Migration Guide](/docs/react/migration/styling)
* Update utility classes (`text-tiny` → `text-xs`, `rounded-small` → `rounded-sm`, etc.)
* Update color tokens (`bg-primary` → `bg-accent`, `bg-secondary` → `bg-default`, etc.)
* Update content colors (`bg-content1` → `bg-surface` or `bg-overlay`)
* Update numbered color scales (`bg-primary-50` → `bg-accent-soft`, etc.)
* Update transition utilities (`.transition-background` → `transition-colors`, etc.)
* Review component styling changes (sizes, spacing, border radius)
* Update custom theme overrides to CSS variables
* Test visual appearance and adjust as needed
### Testing
* Visual regression testing
* Functionality testing
* Accessibility testing
* Performance testing
## Next Steps
After completing the full migration:
1. Review the [v3 component documentation](/docs/react/components)
2. Explore new components available in v3
3. Check out the [styling guide](/docs/react/getting-started/handbook/styling)
4. Learn about [composition patterns](/docs/react/getting-started/handbook/composition)
# Incremental Migration
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/migration/incremental-migration
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/migration/(workflows)/incremental-migration.mdx
> Step-by-step guide for migrating HeroUI v2 to v3 incrementally while keeping both versions working side-by-side
## Overview
Incremental migration with coexistence allows you to migrate from HeroUI v2 to v3 component-by-component while keeping your project functional throughout the migration process. This approach uses special setup strategies to allow both v2 and v3 components to work side-by-side.
### AI Assistant Resources
AI assistants can help with migration. Use the [Migration MCP Server](/docs/react/migration/mcp-server) for tools and prompts, [Migration Agent Skills](/docs/react/migration/agent-skills) for skill-based knowledge, or [AGENTS.md for Migration](/docs/react/migration/agents-md) to download migration docs into your project.
### Limitations and Considerations
Before choosing this approach, be aware of:
* **Bundle Size**: Both versions will be included during migration, increasing bundle size
* **Styling Conflicts**: v2 and v3 styles may conflict; test thoroughly
* **Type Conflicts**: TypeScript may show conflicts if both versions are imported in the same file
* **Provider**: v2 requires `HeroUIProvider`, v3 doesn't - you may need conditional provider wrapping
* **React Version**: v3 requires React 19+, v2 supports React 18+ - ensure React 19 is installed
* **Setup Complexity**: Requires more complex initial setup compared to full migration
### Components Without v3 Counterparts
Since v2 and v3 can coexist, v2 components that have no v3 counterpart can remain in your project. These components will continue to work during migration:
* **Code**, **Image**, **Navbar**, **Ripple**, **Snippet**, **Spacer**, **User**
You do not need to replace them to complete the migration. If you prefer to remove them and use native HTML or manual implementations instead, you may ask your agent to help using the [Component Migration Reference](/docs/react/migration#component-migration-reference) guides.
## Strategy Selection
Choose your strategy based on how you currently import v2 components:
* **Using `@heroui/react`**: Use Strategy A (pnpm aliases)
* **Using component packages** (`@heroui/button`, `@heroui/card`, etc.): Use Strategy B (component packages)
## Strategy A: Using pnpm Aliases
This strategy uses pnpm package aliases to install v3 packages under different names, allowing both versions to coexist.
### Setup
1. Install v3 packages with aliases:
```json
{
"dependencies": {
"@heroui/react": "2.8.6",
"@heroui/theme": "2.4.24",
"@heroui-v3/react": "npm:@heroui/react@latest",
"@heroui-v3/styles": "npm:@heroui/styles@latest"
}
}
```
2. Import v2 components from `@heroui/react`:
```tsx
import {Button} from "@heroui/react"; // v2
```
3. Import v3 components from `@heroui-v3/react`:
```tsx
import {Button} from "@heroui-v3/react"; // v3
```
### Migration Process
1. **Migrate one component at a time:**
* Update imports to use `@heroui-v3/react`
* Update component code to v3 API
* Test the migrated component
* Verify styling looks correct
* For v2 components with no v3 counterpart (Code, Image, Navbar, Ripple, Snippet, Spacer, User), leave them in place. They will continue to work during coexistence.
2. **Continue until all components are migrated**
* Track which components have been migrated
* Test each migrated component thoroughly
* If you want to remove v2 removed components later, use the [Component Migration Reference](/docs/react/migration#component-migration-reference) guides and ask your agent to help replace them with native HTML or manual implementations.
3. **Once all components are migrated, switch to v3-only:**
* Remove v2 dependencies (`@heroui/react`, `@heroui/theme`)
* Remove aliases
* Update all imports to `@heroui/react` (remove `-v3` suffix)
* Complete styling migration
### Considerations
* Both CSS systems will be loaded (v2 via Tailwind plugin, v3 via CSS import)
* You'll need both Tailwind configs temporarily
* Bundle size will be larger during migration
* Some styling conflicts may occur
## Strategy B: Using Component Packages
This strategy uses v2's component-specific packages alongside v3's unified package.
### Setup
1. Install v3 main package and v2 component packages:
```json
{
"dependencies": {
"@heroui/react": "latest", // v3
"@heroui/styles": "latest", // v3
"@heroui/button": "2.8.6", // v2
"@heroui/card": "2.8.6", // v2
// ... other v2 component packages as needed
}
}
```
2. Import v3 components from `@heroui/react`:
```tsx
import {Button} from "@heroui/react"; // v3
```
3. Import v2 components from component packages:
```tsx
import {Card} from "@heroui/card"; // v2
```
### Migration Process
1. **Install v3 packages** (`@heroui/react`, `@heroui/styles`)
2. **Install v2 component packages** for components not yet migrated
3. **Migrate one component at a time:**
* Remove the v2 component package from dependencies
* Update imports to use `@heroui/react` (v3)
* Update component code to v3 API
* Test the migrated component
* For v2 components with no v3 counterpart (Code, Image, Navbar, Ripple, Snippet, Spacer, User), leave them in place. They will continue to work during coexistence.
4. **Continue until all components are migrated**
* If you want to remove v2 removed components later, use the [Component Migration Reference](/docs/react/migration#component-migration-reference) guides and ask your agent to help replace them with native HTML or manual implementations.
5. **Remove remaining v2 component packages**
6. **Complete styling migration**
### Considerations
* Only works if your project uses component-specific packages
* Requires managing multiple package dependencies
* v3 doesn't have component packages, so this is a one-way migration path
## CSS Configuration for Coexistence
During coexistence, you'll need both CSS systems:
```css
/* globals.css */
@import "tailwindcss";
/* v2 styles via Tailwind plugin */
/* (configured in tailwind.config.js) */
/* v3 styles */
@import "@heroui/styles";
```
**Important:** Import order matters. Import `tailwindcss` first, then `@heroui/styles`.
## Tailwind Configuration
You'll need both Tailwind configs temporarily:
**v2 Config (tailwind.config.js):**
```js
const {heroui} = require("@heroui/react");
module.exports = {
plugins: [heroui()],
// ... other config
};
```
**v3 Config:** No plugin needed, but ensure Tailwind v4 is installed.
## Component Migration
For each component you migrate:
1. **Review the component migration guide**
* Use the [Component Migration Reference](/docs/react/migration#component-migration-reference) table
* Check component-specific migration guides
2. **Update imports**
* Strategy A: Change `@heroui/react` → `@heroui-v3/react`
* Strategy B: Change component package → `@heroui/react`
3. **Update component code**
* Follow component migration guide
* Update props, component structure, and API calls
* Replace hooks with compound components if needed
4. **Test the migrated component**
* Verify component renders correctly
* Test functionality and interactions
* Check for styling conflicts
* Ensure v2 components still styled correctly
5. **Document migration**
* Track which components have been migrated
* Note any issues or concerns
## CSS Conflict Handling
During coexistence, both v2 and v3 CSS systems will be loaded. Monitor for conflicts:
1. **Watch for styling inconsistencies**
* Check if v2 and v3 styles are conflicting
* Verify both CSS imports are present and in correct order
2. **Guide conflict resolution**
* Ensure CSS import order: `tailwindcss` first, then `@heroui/styles`
* Check Tailwind config has v2 plugin configured
* Verify v3 CSS is imported correctly
3. **Test styling after each migration**
* Verify migrated components look correct
* Check for unexpected style overrides
* Ensure v2 components still styled correctly
## Completing the Migration
Once all components are migrated:
1. **Remove v2 dependencies:**
* Remove `@heroui/react` and `@heroui/theme` (Strategy A)
* Remove all `@heroui/*` component packages (Strategy B)
* Remove aliases (Strategy A)
* If you left v2 removed components in place (Code, Image, Navbar, Ripple, Snippet, Spacer, User), you have two options: (1) Replace them per the migration guides before removing v2 dependencies, or (2) Keep v2 dependencies until you are ready to replace them. You may ask an agent to help remove them using the provided guides.
2. **Update all imports:**
* Change `@heroui-v3/react` → `@heroui/react` (Strategy A)
* Change component package imports → `@heroui/react` (Strategy B)
3. **Update CSS:**
* Remove v2 Tailwind plugin from config
* Keep only `@import "@heroui/styles";`
4. **Complete styling migration:**
* Follow the [Styling Migration Guide](/docs/react/migration/styling)
* Update utility classes, color tokens, etc.
## Migration Checklist for Coexistence
Use this checklist to track your incremental migration progress:
### Initial Setup
* Choose strategy (A: aliases or B: component packages)
* Install v3 packages (with aliases or directly)
* Configure CSS for both versions
* Ensure React 19+ is installed
* Set up both Tailwind configs temporarily
### Component Migration (Repeat for each component)
* Review component migration guide
* Update imports (Strategy A or B)
* Update component code to v3 API
* Test the migrated component
* Check for styling conflicts
* Document migration progress
* Identify v2 removed components (Code, Image, Navbar, Ripple, Snippet, Spacer, User) — leave in place or replace per guides
### Completion
* Migrate all components
* Optional: Replace removed components using migration guides before removing v2 deps, or remove v2 deps only after all removed components are replaced
* Remove v2 dependencies
* Remove aliases (Strategy A)
* Update all imports to v3-only
* Remove v2 Tailwind plugin
* Update CSS to v3-only
* Complete styling migration
* Test entire application
## Next Steps
After completing the incremental migration:
1. Review the [v3 component documentation](/docs/react/components)
2. Explore new components available in v3
3. Check out the [styling guide](/docs/react/getting-started/handbook/styling)
4. Learn about [composition patterns](/docs/react/getting-started/handbook/composition)
# 所有组件
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/index.mdx
> 浏览库中所有可用组件的完整列表,更多组件正在路上。
## 按钮
## 集合
## 颜色
## 控件
## 数据展示
## 日期与时间
## 反馈
## 表单
## 布局
## 媒体
## 导航
## 浮层
## 选择器
## 排版
## 工具
# 介绍
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/index.mdx
> 一个开源 UI 组件库,用于构建美观且易于访问的用户界面。
HeroUI 是一个基于 [Tailwind CSS v4](https://tailwindcss.com/) 和 [React Aria Components](https://react-spectrum.adobe.com/react-aria/index.html) 构建的 React 组件库。每个组件都带有流畅的动画、精致的细节和内置的无障碍支持——开箱即用,完全可定制。
## 为什么选择 HeroUI?
**默认美观** — 开箱即用,外观专业,无需额外样式配置。
**无障碍** — 基于 [React Aria Components](https://react-spectrum.adobe.com/react-aria/components.html) 构建,内置焦点管理、键盘导航和屏幕阅读器支持。
**灵活** — 每个组件都由可定制的部件组成。按需修改,其余保持不动。
**开发者友好** — 完整的类型化 API、可预期的模式以及出色的自动补全。
**持续维护** — 我们负责处理更新、错误修复和新功能。你只需更新依赖包。
**轻量级** — 支持 Tree Shaking,只把你实际使用的部分打包到应用中。
**面向未来** — 为 [React 19](https://react.dev/blog/2024/12/05/react-19) 和 [Tailwind v4](https://tailwindcss.com/blog/tailwindcss-v4) 而构建,专为 AI 辅助开发而设计。
## 一个精心打造的组件库,而非复制粘贴
复制粘贴的代码能用,直到它出问题为止。你将不得不维护那些不再演进的过时依赖。
HeroUI 则不同,它是与你共同演进的组件库:
* 自动更新和修复
* 无需额外工作即可获得新功能
* 组件与 React、Tailwind 和浏览器保持同步
* 深度定制,而非浅层主题调整
* 面向代码生成的 AI 友好 API
## HeroUI 生态系统
* **🌐 HeroUI v3**(Web) — 你正在浏览的就是这里!基于 Tailwind CSS v4 的 React 组件
* **📱 [HeroUI Native](https://link.heroui.com/native)**(移动端) — 为 React Native 提供精美组件
* **🤖 [HeroUI Chat](https://heroui.chat?ref=heroui-v3)**(文本生成应用) — 用自然语言创建应用
* **🧠 面向 LLM 的 UI** — 全新平台与 MCP 即将推出
**为什么选择 React Aria?** 我们选择 React Aria 是为了大规模实现无障碍能力。从 HeroUI v2 起我们就在使用它,v3 也保留了熟悉的 API 约定,例如 `isDisabled` 和 `onPress`。感谢 [Devon Govett](https://x.com/devongovett) 以及 Adobe 团队。
## 常见问题
**HeroUI 免费吗?**
是的,基于 Apache License 2.0 完全免费且开源。
**可以用于生产环境吗?**
可以。HeroUI v3 已经稳定,可放心用于生产环境。
**我可以定制组件吗?**
当然可以!你可以使用 Tailwind 工具类、CSS 变量、[BEM](https://getbem.com/) 修饰符,或以不同的方式组合组件。每一个插槽都可以定制。
**它支持 TypeScript 吗?**
完全类型化,提供出色的 IDE 支持和自动补全。
**无障碍能力如何?**
基于 React Aria Components 构建,符合 WCAG 标准。内置键盘导航、焦点管理和屏幕阅读器支持。
**可以在不使用 React 的情况下使用样式吗?**
可以,CSS 可以应用于纯 HTML。请查看我们的 [Tailwind Play 示例](https://play.tailwindcss.com/vMYXzKPyUx)。
**有 Figma 文件吗?**
有!欢迎访问我们的设计系统:[HeroUI Figma Kit V3](https://www.figma.com/community/file/1546526812159103429)。
## 参与其中
加入社区、分享反馈或参与贡献:
* [GitHub Discussions](https://github.com/heroui-inc/heroui/discussions)
* [Discord](https://discord.gg/9b6yyZKmH4)
* [X/Twitter](https://x.com/hero_ui)
* [贡献指南](https://github.com/heroui-inc/heroui/blob/main/CONTRIBUTING.md)
HeroUI 基于 [Apache License 2.0](https://github.com/heroui-inc/heroui/blob/main/LICENSE) 协议发布。
# 迁移(面向 AI 助手)
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/agent-index
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/agent-index.mdx
> 供 AI 助手协助将 HeroUI v2 迁移到 v3 时使用的入口
面向 AI 助手:在协助将 HeroUI v2 → v3 进行迁移时,请将本文档作为入口。
## 入口
请选择一种迁移策略:
* **完整迁移**(迁移过程中项目将无法正常运行)→ 阅读 `(workflows)/agent-guide-full.mdx`。
* **渐进式迁移**(v2 与 v3 并存)→ 阅读 `(workflows)/agent-guide-incremental.mdx`。
## 本套文档中的参考资料
* **通用指南:** `hooks.mdx`、`styling.mdx`。
* 上述工作流程指南已经内嵌了「主要变更」、「关键 API 变更」、组件参考表以及「新增组件」等内容。
* **各组件指南:** `(components)/.mdx`(例如 `(components)/button.mdx`、`(components)/select.mdx`)。请通过工作流程指南中的组件参考表来定位对应的文件。
# Hooks
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/hooks
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/hooks.mdx
> HeroUI Hooks 从 v2 到 v3 的迁移指南
完整的 API 参考请参阅 [v3 组件文档](/docs/components-list)。本指南重点介绍如何从 HeroUI v2 迁移 Hooks。
## 概述
HeroUI v3 移除了 v2 中存在的大多数组件 Hooks,转而使用复合组件,并新增了一个用于管理浮层状态的 Hook。本指南涵盖:
* 组件 Hooks 的移除(`useSwitch`、`useInput`、`useCheckbox` 等)
* `useDisclosure` → `useOverlayState` 的迁移
* 迁移策略与示例
## 组件 Hooks 的移除
HeroUI v2 提供了一系列组件 Hooks(例如 `useSwitch`、`useInput`、`useCheckbox` 等),它们返回一组 prop getter(`getBaseProps`、`getWrapperProps`、`getThumbProps` 等),让用户在无法直接修改内部子组件的情况下也能自定义组件结构。HeroUI v3 通过复合组件解决了这个问题,从而无需再依赖这些 Hooks。
### 为什么 v2 中存在这些 Hooks
在 v2 中,组件具有固定的内部结构。为了自定义这些结构,用户需要使用提供 prop getter 的 Hooks。例如,`useSwitch` 返回 `getBaseProps()`、`getWrapperProps()`、`getThumbProps()` 等,用户可以将其展开到自定义元素上,从而构建自己的 Switch 结构。
### v3 的解决方案:复合组件
v3 采用了复合组件模式,让你可以直接访问组件的各个部分。你不再需要使用带有 prop getter 的 Hooks,而是可以直接通过 `Switch.Control`、`Switch.Thumb`、`Checkbox.Control`、`Checkbox.Indicator` 等子组件进行组合。
### 迁移策略
1. **识别 Hook 用法**:在你的代码库中搜索来自 `@heroui/react` 的导入,找出包含 Hook 名称(`useSwitch`、`useInput`、`useCheckbox`、`useRadio` 等)的引用。
2. **替换为复合组件**:使用复合组件模式,替代带有 prop getter 的 Hooks。
3. **保留原有结构**:迁移时,尽量保持与原 Hook 实现一致的组件结构。例如:
* 如果你之前用 `useSwitch` 创建了一个 **不带** thumb 的 Switch,那么在 v3 中也不要添加 `Switch.Thumb`
* 如果你之前用 `useCheckbox` 创建了一个 **不带** indicator 的 Checkbox,那么在 v3 中也不要添加 `Checkbox.Indicator`
* 只引入原本基于 Hook 的实现中实际用到的子组件
4. **参考各组件指南**:查看各个组件的迁移指南,获取具体示例。
### 主要差异
* **v2**:Hooks 提供 prop getter,用于自定义固定的组件结构
* **v3**:复合组件允许直接组合组件的各个部分
### 保留结构示例
**v2:不带 thumb 的 Switch**
```tsx
import { useSwitch } from "@heroui/react";
function CustomSwitch() {
const { getBaseProps } = useSwitch();
return (
{/* No thumb element */}
);
}
```
**v3:等效结构**
```tsx
import { Switch } from "@heroui/react";
function CustomSwitch() {
return (
{/* No Switch.Thumb - preserving the original structure */}
);
}
```
有关具体组件的详细迁移示例,请参阅各个组件的迁移指南。
## useDisclosure → useOverlayState
v2 中的 `useDisclosure` 钩子在 v3 中已替换为 `useOverlayState`。该钩子用于管理 Modal、Popover 等浮层组件的打开/关闭状态。
### v2:useDisclosure
**API:**
```tsx
const {isOpen, onOpen, onClose, onOpenChange, isControlled, getButtonProps, getDisclosureProps} = useDisclosure({
isOpen?: boolean;
defaultOpen?: boolean;
onClose?(): void;
onOpen?(): void;
onChange?(isOpen: boolean | undefined): void;
id?: string;
});
```
**示例:**
```tsx
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button, useDisclosure } from "@heroui/react";
export default function App() {
const {isOpen, onOpen, onOpenChange} = useDisclosure();
return (
<>
Open Modal
Title
Content
Close
>
);
}
```
### v3:useOverlayState
**API:**
```tsx
const state = useOverlayState({
isOpen?: boolean;
defaultOpen?: boolean;
onOpenChange?: (isOpen: boolean) => void;
});
// Returns:
// {
// isOpen: boolean;
// open(): void;
// close(): void;
// toggle(): void;
// setOpen(isOpen: boolean): void;
// }
```
**示例:**
```tsx
import { Modal, Button, useOverlayState } from "@heroui/react";
export default function App() {
const state = useOverlayState();
return (
Open Modal
{({close}) => (
<>
Title
Content
Close
>
)}
);
}
```
### 迁移指南
#### 基本迁移
**v2:**
```tsx
const {isOpen, onOpen, onClose, onOpenChange} = useDisclosure();
```
**v3:**
```tsx
const state = useOverlayState();
// Use state.open(), state.close(), state.toggle(), state.setOpen(boolean)
```
#### 受控状态
**v2:**
```tsx
const {isOpen, onOpenChange} = useDisclosure({
isOpen: controlledIsOpen,
onChange: (isOpen) => setControlledIsOpen(isOpen)
});
```
**v3:**
```tsx
const state = useOverlayState({
isOpen: controlledIsOpen,
onOpenChange: setControlledIsOpen
});
```
#### 非受控状态
**v2:**
```tsx
const {isOpen, onOpen, onClose} = useDisclosure({
defaultOpen: false
});
```
**v3:**
```tsx
const state = useOverlayState({
defaultOpen: false
});
// Use state.open(), state.close(), state.toggle()
```
### API 差异
| v2(useDisclosure) | v3(useOverlayState) | 说明 |
| ---------------------- | ------------------- | ----------- |
| `isOpen` | `isOpen` | 相同 |
| `onOpen()` | `open()` | 方法重命名 |
| `onClose()` | `close()` | 方法重命名 |
| `onOpenChange()` | `toggle()` | 新增的切换方法 |
| `onOpenChange`(prop) | `setOpen(boolean)` | API 不同 |
| `isControlled` | - | 已移除(由内部处理) |
| `getButtonProps()` | - | 已移除(改用复合组件) |
| `getDisclosureProps()` | - | 已移除(改用复合组件) |
### useOverlayState 的优势
* **更简洁的 API**:使用专用方法(`open()`、`close()`、`toggle()`),而非回调
* **更简单的状态管理**:在受控与非受控两种模式下都能无缝工作
* **更完善的 TypeScript 支持**:改进了类型推断和自动补全
* **与 React Aria 保持一致**:与 React Aria Components 的模式相符
### 替代方案:useState
对于简单的场景,你也可以直接使用 React 的 `useState`:
```tsx
import { useState } from "react";
import { Modal, Button } from "@heroui/react";
export default function App() {
const [isOpen, setIsOpen] = useState(false);
return (
setIsOpen(true)}>Open
{/* content */}
);
}
```
不过,`useOverlayState` 为常见操作提供了更简洁的 API 和专用方法。
## 已移除的 Hooks
v2 中的以下 Hooks 在 v3 中已被移除:
* **useDraggable**:已移除
* **useClipboard**:已移除
* **usePagination**:已移除
* **useToast**:已移除
## 总结
* **组件 Hooks**(`useSwitch`、`useInput` 等)→ 改用 **复合组件**
* **useDisclosure** → 改用 **useOverlayState** 来管理浮层状态
* **useOverlayState** 提供了更简洁的 API,包含 `open()`、`close()`、`toggle()` 与 `setOpen()` 方法
* **已移除的 Hooks**:`useDraggable`、`useClipboard`、`usePagination`、`useToast` 不再可用
* 对于简单的场景可以直接使用 `useState`,但 `useOverlayState` 在使用体验上更佳
有关具体组件的 Hooks 迁移示例,请参阅各个组件的迁移指南。
# 迁移
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/index.mdx
> 将 HeroUI v2 应用迁移到 v3 的完整指南
## 面向 AI 助手
以下是 AI 助手获取迁移文档的三种方式。***我们推荐使用 HeroUI Migration MCP 服务器***,并充分利用其 prompt 与工具,不过其它方式也都能为 agent 提供完整的文档。
| 对比项 | MCP Server | Agent Skills | AGENTS.md |
| -------- | ------------------------------------------------ | ---------------------------------------------------- | ---------------------------------------------- |
| **数据来源** | 远程端点 | 远程端点 | 本地文件 |
| **访问方式** | MCP 工具 | 脚本文件 | 读取本地文件 |
| **配置方式** | MCP 配置 | 安装命令 | `heroui-cli` 命令 |
| **更新** | 实时 | 实时 | 手动 |
| **离线** | ❌ | ❌ | ✅ |
| **工具** | MCP 工具 + prompt | 脚本 | ❌ |
| **指南** | [MCP Server →](/docs/react/migration/mcp-server) | [Agent Skills →](/docs/react/migration/agent-skills) | [AGENTS.md →](/docs/react/migration/agents-md) |
## 主要变化
* **依赖项**:将 React 升级到 v19+、HeroUI 包升级到 v3、Tailwind CSS 升级到 v4,并移除 Framer Motion
* **无需 Provider**:v3 不再需要 `HeroUIProvider`
* **组件 API 更新**:许多组件改用 React Aria Components 模式
* **复合组件**:全新的复合组件模式带来了更好的定制能力。详情请参阅各组件的迁移指南。
* **已移除的 Hooks**:v2 中的组件 Hooks(如 `useSwitch`、`useInput`)已被移除——请改用复合组件。`useDisclosure` 已被替换为 `useOverlayState`。详情请参阅 [Hooks 迁移指南](/docs/react/migration/hooks)。
* **配置变更**:从 Tailwind 配置中移除 `heroui()` 插件,更新 CSS 导入,并删除 `hero.ts` 文件
* **条目标识**:集合类组件(Dropdown、ListBox、Select、Accordion 等)在 v3 中改用 `id` 和 `textValue`;列表本身仍需保留 React 的 `key`。
### 条目标识与无障碍(key、id、textValue)
在 v2 中,集合类组件(Dropdown、ListBox、Select、Accordion 等)使用 **React 的 `key`** 作为条目标识。同一个值既驱动 React 的列表协调,又承担组件的选中/展开状态。在使用 React Aria Components 的 v3 中,这两种职责被拆分开:
* **`id`** — v3 在每个条目上使用显式的 **`id`** 属性来表示选中状态、焦点以及回调(例如 `selectedKeys`、`expandedKeys`、`onSelectionChange`)。请使用与你在 v2 中给 `key` 设置的相同(或等效)的值,这样状态和回调仍能正确指向对应的条目。
* **`textValue`** — 当条目的可见内容不是纯文本时(例如使用了 `Label`、图标或 `Description`),v3 需要在条目上提供 **`textValue`**。它用于屏幕阅读器播报和键入快速定位(type-ahead)。
* **`key`** — **继续在列表项上使用 React 的 `key`**。它仍然是 React 列表协调所必需的,且与 `id` 相互独立。
迁移时:为 v3 的 API 添加 `id`(必要时再加上 `textValue`),同时保留供 React 使用的 `key`。
## 迁移策略
如果不做特殊设置,HeroUI v2 与 v3 不能在同一个项目中共存。你可以选择以下两种迁移方式:
### 一次性完整迁移
**适用场景:** 能够集中投入时间、一次性完成迁移的项目。
**工作方式:**
* 先迁移所有组件代码(此阶段项目将无法正常运行)
* 将依赖项切换到 v3
* 完成样式迁移
**优点:**
* 配置更简单——无需复杂的共存配置
* 切换更干净——同一时间只有一个版本处于激活状态
* 由 Migration MCP 的 prompt 提供支持
**缺点:**
* 项目在迁移期间无法正常运行
* 必须在切换依赖之前完成所有组件的迁移
**开始迁移:** [完整迁移指南](/docs/react/migration/full-migration)
### 渐进式迁移
**适用场景:** 需要在迁移过程中保持可用的项目、希望分阶段逐步迁移的团队,以及按功能逐个迁移的大型代码库。
**工作方式:**
* 通过 pnpm 别名或组件包来设置共存
* 在保持项目可用的前提下,逐个迁移组件
* 完成迁移后再移除 v2 依赖项
**优点:**
* 项目在迁移期间始终可用
* 可以分阶段、按需逐步推进
* 可以将 v3 组件与 v2 并存测试
**缺点:**
* 初始配置更为复杂
* 可能出现样式冲突
* 需要同时管理两个版本
**开始迁移:** [渐进式迁移指南](/docs/react/migration/incremental-migration)
## 组件迁移参考
可以通过下表快速查找每个组件的迁移指南。点击「迁移指南」列中的链接,即可跳转到对应的详细迁移说明。
**组件开发状态**:标有 🔄 进行中或 📋 计划中的组件仍在开发中。可以查看[路线图](https://herouiv3.featurebase.app/roadmap)了解任务状态。这些组件的迁移指南将在开发完成后提供。
| v2 组件 | v3 组件 | 状态 | 迁移指南 |
| ---------------- | -------------------------- | ------ | ------------------------------------------------------------------------ |
| Accordion | Accordion | ✅ 可用 | [查看指南 →](/docs/react/migration/accordion) |
| Alert | Alert | ✅ 可用 | [查看指南 →](/docs/react/migration/alert) |
| Autocomplete | ComboBox | ✅ 已重命名 | [查看指南 →](/docs/react/migration/autocomplete) |
| Avatar | Avatar | ✅ 可用 | [查看指南 →](/docs/react/migration/avatar) |
| Badge | Badge | ✅ 可用 | [查看指南 →](/docs/react/migration/badge) |
| Breadcrumbs | Breadcrumbs | ✅ 可用 | [查看指南 →](/docs/react/migration/breadcrumbs) |
| Button | Button | ✅ 可用 | [查看指南 →](/docs/react/migration/button) |
| ButtonGroup | ButtonGroup | ✅ 可用 | [查看指南 →](/docs/react/migration/button-group) |
| Calendar | Calendar | ✅ 可用 | [查看指南 →](/docs/react/migration/calendar) |
| Card | Card | ✅ 可用 | [查看指南 →](/docs/react/migration/card) |
| Checkbox | Checkbox | ✅ 可用 | [查看指南 →](/docs/react/migration/checkbox) |
| CheckboxGroup | CheckboxGroup | ✅ 可用 | [查看指南 →](/docs/react/migration/checkbox-group) |
| Chip | Chip | ✅ 可用 | [查看指南 →](/docs/react/migration/chip) |
| Code | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/code) |
| DateInput | DateField | ✅ 已重命名 | [查看指南 →](/docs/react/migration/dateinput) |
| DatePicker | DatePicker | ✅ 可用 | [查看指南 →](/docs/react/migration/date-picker) |
| DateRangePicker | DateRangePicker | ✅ 可用 | [查看指南 →](/docs/react/migration/date-range-picker) |
| TimeInput | TimeField | ✅ 已重命名 | [查看指南 →](/docs/react/migration/timeinput) |
| Divider | Separator | ✅ 已重命名 | [查看指南 →](/docs/react/migration/divider) |
| Drawer | Drawer | ✅ 可用 | [查看指南 →](/docs/react/migration/drawer) |
| Dropdown | Dropdown | ✅ 可用 | [查看指南 →](/docs/react/migration/dropdown) |
| Form | Form | ✅ 可用 | [查看指南 →](/docs/react/migration/form) |
| Image | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/image) |
| Input | TextField、Input、InputGroup | ✅ 可用 | [查看指南 →](/docs/react/migration/input) |
| InputOTP | InputOTP | ✅ 可用 | [查看指南 →](/docs/react/migration/input-otp) |
| Kbd | Kbd | ✅ 可用 | [查看指南 →](/docs/react/migration/kbd) |
| Link | Link | ✅ 可用 | [查看指南 →](/docs/react/migration/link) |
| Listbox | ListBox | ✅ 可用 | [查看指南 →](/docs/react/migration/listbox) |
| Modal | Modal | ✅ 可用 | [查看指南 →](/docs/react/migration/modal) |
| Navbar | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/navbar) |
| NumberInput | NumberField | ✅ 已重命名 | [查看指南 →](/docs/react/migration/numberinput) |
| Pagination | Pagination | ✅ 可用 | [查看指南 →](/docs/react/migration/pagination) |
| Popover | Popover | ✅ 可用 | [查看指南 →](/docs/react/migration/popover) |
| Progress | ProgressBar | ✅ 已重命名 | [查看指南 →](/docs/react/migration/progress) |
| CircularProgress | ProgressCircle | ✅ 已重命名 | [查看指南 →](/docs/react/migration/circular-progress) |
| Radio | Radio | ✅ 可用 | [查看指南 →](/docs/react/migration/radio) |
| RadioGroup | RadioGroup | ✅ 可用 | [查看指南 →](/docs/react/migration/radio-group) |
| RangeCalendar | RangeCalendar | ✅ 可用 | [查看指南 →](/docs/react/migration/range-calendar) |
| Ripple | ❌ | ❌ 已移除 | [参见 Button 的水波纹效果 →](/docs/react/components/button#adding-ripple-effect) |
| ScrollShadow | ScrollShadow | ✅ 可用 | [查看指南 →](/docs/react/migration/scroll-shadow) |
| Select | Select | ✅ 可用 | [查看指南 →](/docs/react/migration/select) |
| Skeleton | Skeleton | ✅ 可用 | [查看指南 →](/docs/react/migration/skeleton) |
| Slider | Slider | ✅ 可用 | [查看指南 →](/docs/react/migration/slider) |
| Snippet | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/snippet) |
| Spacer | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/spacer) |
| Spinner | Spinner | ✅ 可用 | [查看指南 →](/docs/react/migration/spinner) |
| Switch | Switch | ✅ 可用 | [查看指南 →](/docs/react/migration/switch) |
| Table | Table | ✅ 可用 | [查看指南 →](/docs/react/migration/table) |
| Tabs | Tabs | ✅ 可用 | [查看指南 →](/docs/react/migration/tabs) |
| Toast | Toast | ✅ 可用 | [查看指南 →](/docs/react/migration/toast) |
| Tooltip | Tooltip | ✅ 可用 | [查看指南 →](/docs/react/migration/tooltip) |
| User | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/user) |
## v3 中新增的组件
v3 引入了一系列 v2 中尚未提供的全新组件:
| 组件 | 用途 | 文档 |
| --------------- | -------------------------------------------------------------------- | ------------------------------------------------- |
| TextField | 增强型文本输入框,支持 label 与 description | [查看文档 →](/docs/react/components/text-field) |
| TextArea | 多行文本输入组件 | [查看文档 →](/docs/react/components/text-area) |
| AlertDialog | 用于确认与提醒的模态对话框 | [查看文档 →](/docs/react/components/alert-dialog) |
| Label | 无障碍的表单标签组件 | [查看文档 →](/docs/react/components/label) |
| Description | 表单字段的辅助说明文本 | [查看文档 →](/docs/react/components/description) |
| FieldError | 表单字段的错误信息显示 | [查看文档 →](/docs/react/components/field-error) |
| Fieldset | 对相关表单字段进行分组 | [查看文档 →](/docs/react/components/fieldset) |
| InputGroup | 将多个输入框组合在一起 | [查看文档 →](/docs/react/components/input-group) |
| Surface | 带有层级样式的容器组件 | [查看文档 →](/docs/react/components/surface) |
| Disclosure | 可展开 / 可折叠的内容区域 | [查看文档 →](/docs/react/components/disclosure) |
| DisclosureGroup | 用于管理多个 Disclosure 区域的复合组件 | [查看文档 →](/docs/react/components/disclosure-group) |
| SearchField | 带清除按钮与可选加载状态的搜索输入框 | [查看文档 →](/docs/react/components/search-field) |
| DateField | 配合日历选择器的日期输入框 | [查看文档 →](/docs/react/components/date-field) |
| TimeField | 时间输入组件 | [查看文档 →](/docs/react/components/time-field) |
| Tag、TagGroup | 用于选择或展示的 Tag 与 TagGroup | [查看文档 →](/docs/react/components/tag-group) |
| ColorPicker | 颜色选择(ColorArea、ColorField、ColorSlider、ColorSwatch、ColorSwatchPicker) | [查看文档 →](/docs/react/components/color-picker) |
| CloseButton | 用于关闭或解除浮层的触发按钮 | [查看文档 →](/docs/react/components/close-button) |
| ErrorMessage | 表单字段错误信息展示(基于 React Aria 集成) | [查看文档 →](/docs/react/components/error-message) |
## 其他迁移指南
* **[Hooks 迁移指南](/docs/react/migration/hooks)**:将 Hooks 从 v2 迁移到 v3 的详细说明
* **[样式迁移指南](/docs/react/migration/styling)**:更新工具类、颜色 token 与组件样式的综合指南
## 获取帮助
如果你在迁移过程中遇到问题:
1. 查看 [v3 文档](/docs/react)
2. 查阅具体组件的迁移指南
3. 在 [GitHub Discussions](https://github.com/heroui-inc/heroui/discussions) 中查找或提问
4. 加入 [Discord 社区](https://discord.gg/9b6yyZKmH4)
# 样式与主题
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/styling
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/styling.mdx
> 从 HeroUI v2 到 v3 的样式变更与主题系统迁移完整指南
本指南涵盖了 HeroUI v2 与 v3 之间所有与样式相关的变更,包括工具类、组件样式、主题系统架构以及视觉差异。具体组件的 API 变更请参阅各个组件的迁移指南。
**注:** v3 已将 `classNames` 这一对象类型的 prop 替换为标准的 React `className` prop。所有组件现在都使用标准的 React `className` prop,而不再使用 v2 中的 `classNames` 对象属性。
## 概述
HeroUI v3 对样式系统进行了重大变更:
* **CSS 优先架构**:以纯 CSS 文件取代 Tailwind 插件
* **标准 Tailwind 工具类**:以标准 Tailwind 类取代自定义工具类
* **CSS 变量**:全新的 CSS 变量命名与结构
* **组件样式**:调整了默认尺寸、间距与视觉表现
* **无需插件**:移除了对 Tailwind 插件配置的依赖
* **颜色系统重构**:语义颜色被重新组织(`primary` → `accent`,移除 `secondary`,移除数字色阶)
* **移除 Content 颜色**:`content1-4` 由 `surface` 与 `overlay` 体系替代
## 快速参考
### 工具类对照
| v2 工具类 | v3 对应类 | 说明 |
| ---------------------------- | --------------------------- | ----------------------------------- |
| `text-tiny` | `text-xs` | 字体大小:0.75rem → 0.75rem(相同) |
| `text-small` | `text-sm` | 字体大小:0.875rem → 0.875rem(相同) |
| `text-medium` | `text-base` | 字体大小:1rem → 1rem(相同) |
| `text-large` | `text-lg` | 字体大小:1.125rem → 1.125rem(相同) |
| `rounded-small` | `rounded-sm` | 圆角:8px → 4px(不同) |
| `rounded-medium` | `rounded-md` | 圆角:12px → 6px(不同) |
| `rounded-large` | `rounded-lg` | 圆角:14px → 8px(不同) |
| `border-small` | `border` | 边框宽度:1px → 1px(使用标准 Tailwind) |
| `border-medium` | `border-2` | 边框宽度:2px → 2px(使用标准 Tailwind) |
| `border-large` | `border-[3px]` | 边框宽度:3px → 3px(使用任意值) |
| `bg-content1` | `bg-surface` 或 `bg-overlay` | Content 颜色已移除,请改用 surface / overlay |
| `bg-content2` | `bg-surface-secondary` | Content 颜色已移除,请改用 surface 层级 |
| `bg-primary` | `bg-accent` | `primary` 已重命名为 `accent` |
| `bg-secondary` | `bg-default` | `secondary` 颜色已移除,请改用 `default` |
| `bg-primary-50` | `bg-accent-soft` | 数字色阶已移除 |
| `bg-primary-100` | `bg-accent-soft` | 数字色阶已移除 |
| `text-primary-600` | `text-accent` | 数字色阶已移除 |
| `.transition-background` | 标准 CSS 过渡 | 已移除的工具类 |
| `.transition-colors-opacity` | 标准 CSS 过渡 | 已移除的工具类 |
## 工具类迁移
### 文本工具类
HeroUI v2 提供了一组自定义的文本尺寸工具类,并映射到 CSS 变量。v3 改用标准的 Tailwind 文本尺寸类。
**v2 文本工具类:**
```tsx
// v2 - Custom utilities with CSS variables
Tiny text
Small text
Medium text
Large text
```
**v3 文本工具类:**
```tsx
// v3 - Standard Tailwind classes
Tiny text
Small text
Medium text
Large text
```
**对照详情:**
| v2 类 | 字体大小 | 行高 | v3 类 | 字体大小 | 行高 |
| ------------- | -------------- | ------------- | ----------- | -------------- | ------------- |
| `text-tiny` | 0.75rem(12px) | 1rem(16px) | `text-xs` | 0.75rem(12px) | 1rem(16px) |
| `text-small` | 0.875rem(14px) | 1.25rem(20px) | `text-sm` | 0.875rem(14px) | 1.25rem(20px) |
| `text-medium` | 1rem(16px) | 1.5rem(24px) | `text-base` | 1rem(16px) | 1.5rem(24px) |
| `text-large` | 1.125rem(18px) | 1.75rem(28px) | `text-lg` | 1.125rem(18px) | 1.75rem(28px) |
### 圆角工具类
v2 使用了自定义的圆角工具类(`rounded-small`、`rounded-medium`、`rounded-large`),并映射到 CSS 变量。v3 改用标准的 Tailwind 圆角类,但实际取值有所不同。
**v2 圆角:**
```tsx
// v2 - Custom utilities
Small radius
Medium radius
Large radius
```
**v3 圆角:**
```tsx
// v3 - Standard Tailwind classes
Small radius
Medium radius
Large radius
```
**取值对比:**
| v2 类 | v2 取值 | v3 类 | v3 取值 | 差异 |
| ---------------- | -------------- | ------------ | ------------- | -- |
| `rounded-small` | 8px(0.5rem) | `rounded-sm` | 4px(0.25rem) | 更小 |
| `rounded-medium` | 12px(0.75rem) | `rounded-md` | 6px(0.375rem) | 更小 |
| `rounded-large` | 14px(0.875rem) | `rounded-lg` | 8px(0.5rem) | 更小 |
**注意:** v3 默认的圆角取值更小。如果你需要精确还原 v2 的取值,请使用任意值:
```tsx
// Match v2 rounded-small (8px)
Custom radius
// Match v2 rounded-medium (12px)
Custom radius
// Match v2 rounded-large (14px)
Custom radius
```
### 边框宽度工具类
v2 提供了自定义的边框宽度工具类(`border-small`、`border-medium`、`border-large`)。v3 改用标准的 Tailwind 边框宽度类。
**v2 边框宽度:**
```tsx
// v2 - Custom utilities
1px border
2px border
3px border
```
**v3 边框宽度:**
```tsx
// v3 - Standard Tailwind classes
1px border
2px border
3px border
```
**对照:**
| v2 类 | 宽度 | v3 类 | 宽度 |
| --------------- | --- | -------------- | -------- |
| `border-small` | 1px | `border` | 1px |
| `border-medium` | 2px | `border-2` | 2px |
| `border-large` | 3px | `border-[3px]` | 3px(任意值) |
### 过渡工具类
v2 为常见的动画模式提供了一组自定义的过渡工具类,默认持续时间为 250ms。v3 移除了这些工具类,转而推荐使用标准的 Tailwind `transition-*` 工具类,由你显式指定要应用过渡的属性。
**v2 过渡工具类:**
v2 提供了默认持续时间为 250ms、缓动函数为 `ease` 的自定义过渡工具类。下表展示了每个工具类对应的 CSS 过渡属性:
| v2 工具类 | 过渡属性 |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `.transition-background` | `background` |
| `.transition-colors-opacity` | `color, background-color, border-color, text-decoration-color, fill, stroke, opacity` |
| `.transition-width` | `width` |
| `.transition-height` | `height` |
| `.transition-size` | `width, height` |
| `.transition-left` | `left` |
| `.transition-transform-opacity` | `transform, scale, opacity rotate` |
| `.transition-transform-background` | `transform, scale, background` |
| `.transition-transform-colors` | `transform, scale, color, background, background-color, border-color, text-decoration-color, fill, stroke` |
| `.transition-transform-colors-opacity` | `transform, scale, color, background, background-color, border-color, text-decoration-color, fill, stroke, opacity` |
**注意:** 这些工具类在 v3 中不再可用。请使用 Tailwind 标准的 `transition-*` 工具类,并显式指定要应用过渡的属性。
### 其他工具类
**滚动条工具类:**
v2 提供了 `.scrollbar-hide` 和 `.scrollbar-default` 工具类。v3 现在通过 `@heroui/styles` 暴露基于标准属性的滚动条工具类:`scrollbar`、`scrollbar-thin`、`scrollbar-default` 和 `scrollbar-none`。如需按子树控制,可在祖先元素上使用 `data-scrollbar="thin"`、`data-scrollbar="default"` 或 `data-scrollbar="none"`。
**动画工具类:**
v2 提供了 spinner 相关的动画工具类(如 `.spinner-bar-animation`、`.spinner-dot-animation` 等)。在 v3 中,这些动画由组件内部处理,不再作为公开的工具类暴露。
**其他自定义工具类:**
v2 中还包含一些自定义工具类,例如:
* `.leading-inherit` → 改用 `leading-[inherit]`
* `.tap-highlight-transparent` → 改用 `[-webkit-tap-highlight-color:transparent]`
* `.input-search-cancel-button-none` → 如有需要,请使用自定义 CSS
## 主题系统架构
### v2:基于插件的体系
v2 采用了 Tailwind CSS 插件方式:
1. **生成工具类**:通过 JavaScript 创建自定义工具类
2. **CSS 变量**:通过插件注入 CSS 变量
3. **主题配置**:需要在 `tailwind.config.js` 中进行配置
4. **构建时生成**:工具类在构建时生成
**v2 配置:**
```js
// tailwind.config.js
const {heroui} = require("@heroui/react");
module.exports = {
plugins: [
heroui({
layout: {
fontSize: {
tiny: "0.75rem",
small: "0.875rem",
medium: "1rem",
large: "1.125rem",
},
radius: {
small: "8px",
medium: "12px",
large: "14px",
},
},
themes: {
light: {
colors: {
primary: {
// color definitions
},
},
},
},
}),
],
};
```
### v3:CSS 优先体系
v3 采用纯 CSS 的方式:
1. **CSS 文件**:样式直接定义在 CSS 文件中(位于 `packages/styles/`)
2. **CSS 变量**:变量在 CSS 中定义,而非由插件生成
3. **无需插件**:不再需要 Tailwind 插件
4. **基于导入**:通过 CSS `@import` 引入样式
**v3 配置:**
```css
/* globals.css */
@import "tailwindcss";
@import "@heroui/styles";
```
**无需 Tailwind 配置:**
如果你只使用 HeroUI,可以完全删除 `tailwind.config.js`。如果你已有自定义的 Tailwind 配置,请保留它,但移除其中的 HeroUI 插件。
### 架构对比
| 对比项 | v2 | v3 |
| ----------- | ----------------------- | --------- |
| **样式方案** | Tailwind 插件(JavaScript) | CSS 文件 |
| **工具类生成方式** | 由插件在构建时生成 | 预定义的 CSS |
| **CSS 变量** | 由插件生成 | 在 CSS 中定义 |
| **配置方式** | `tailwind.config.js` | CSS 导入 |
| **定制方式** | 插件配置 | 覆盖 CSS 变量 |
| **构建依赖** | 需要插件 | 无需插件 |
## CSS 变量与设计 token
### 变量命名变更
v2 采用 `--heroui-{property}-{scale}` 的命名模式,而 v3 改用 `--{property}` 或 `--color-{property}`。
**v2 CSS 变量:**
```css
--heroui-font-size-tiny: 0.75rem;
--heroui-font-size-small: 0.875rem;
--heroui-radius-small: 8px;
--heroui-radius-medium: 12px;
--heroui-border-width-medium: 2px;
--heroui-disabled-opacity: 0.5;
```
**v3 CSS 变量:**
```css
/* Typography - handled by Tailwind */
/* No custom font-size variables */
/* Radius */
--radius-xs: calc(var(--radius) * 0.25);
--radius-sm: calc(var(--radius) * 0.5);
--radius-md: calc(var(--radius) * 0.75);
--radius-lg: calc(var(--radius) * 1);
--radius-xl: calc(var(--radius) * 1.5);
/* Colors */
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-accent: var(--accent);
--color-muted: var(--muted);
/* Opacity */
--disabled-opacity: 0.5;
```
### 颜色系统变更
**v2 颜色结构:**
```css
--heroui-primary: 210 100% 50%;
--heroui-primary-50: 210 100% 95%;
--heroui-primary-100: 210 100% 90%;
/* ... more shades ... */
```
**v3 颜色结构:**
```css
--accent: oklch(0.6204 0.195 253.83);
--accent-foreground: var(--snow);
--accent-hover: color-mix(in oklab, var(--accent) 90%, var(--accent-foreground) 10%);
```
**主要差异:**
1. **颜色格式**:v2 使用 HSL,v3 使用 OKLCH
2. **命名**:v2 使用数字色阶(50-900),v3 使用语义命名
3. **计算颜色**:v3 通过 `color-mix()` 计算悬停等状态色
4. **前景色**:v3 显式定义了前景色
5. **primary → accent**:`primary` 颜色已重命名为 `accent`
6. **移除 secondary 颜色**:v2 中的 `secondary` 语义颜色(紫色)已被移除
7. **移除数字色阶**:`primary-50`、`primary-100` 等数字色阶不再存在
### primary → accent 重命名
v2 使用 `primary` 作为主品牌色。v3 将其重命名为 `accent`,使语义更加清晰。
**v2 中的 primary 颜色:**
```tsx
// v2 - Primary color with numbered scales
Primary Button
Primary background
Light primary
Lighter primary
Primary text
```
**v3 中的 accent 颜色:**
```tsx
// v3 - Accent color (no numbered scales)
Primary Button
Accent background
Soft accent
Accent text
```
**迁移:**
| v2 类 | v3 对应类 | 说明 |
| ------------------ | ---------------- | ------------ |
| `bg-primary` | `bg-accent` | 基础 accent 色 |
| `text-primary` | `text-accent` | accent 文本颜色 |
| `bg-primary-50` | `bg-accent-soft` | 浅色 accent 变体 |
| `bg-primary-100` | `bg-accent-soft` | 浅色 accent 变体 |
| `bg-primary-500` | `bg-accent` | 基础 accent 色 |
| `text-primary-600` | `text-accent` | accent 文本颜色 |
| `border-primary` | `border-accent` | accent 边框 |
**注意:** v3 不再提供数字色阶(`-50`、`-100`、`-200` 等)。请使用语义化的变体(如 `-soft`、`-hover`),或自定义的 Tailwind 类。
### secondary 颜色已移除
v2 提供了 `secondary` 语义颜色(紫色),该颜色已在 v3 中移除。名为「secondary」的组件变体现在改用其他颜色。
**v2 中的 secondary 颜色:**
```tsx
// v2 - Secondary as a semantic color (purple)
Secondary Button
Secondary background
Light secondary
Secondary text
```
**v3 中的 secondary 变体:**
```tsx
// v3 - Secondary is a variant, not a color
Secondary Button
Default background (used by secondary variant)
Accent text
```
**迁移:**
| v2 类 | v3 对应类 | 说明 |
| ------------------ | --------------- | ------------------------- |
| `bg-secondary` | `bg-default` | secondary 变体改用 default 颜色 |
| `text-secondary` | `text-accent` | 使用 accent 进行强调 |
| `bg-secondary-50` | `bg-default` | 改用 default 颜色 |
| `border-secondary` | `border-accent` | 使用 accent 边框 |
**注意:** 在 v3 中,「secondary」指的是组件变体样式(例如 `button--secondary`),而不是颜色 token。secondary 变体通常使用 `bg-default` 和 `text-accent`。
### 数字色阶已移除
v2 为所有语义颜色都提供了 50–900 的数字色阶。v3 移除了这些数字色阶,改用语义化的变体与计算得出的颜色。
**v2 数字色阶:**
```tsx
// v2 - Numbered color scales
Lightest
Lighter
Light
Base
Dark
Darkest
```
**v3 语义化变体:**
```tsx
// v3 - Semantic variants and calculated colors
Soft variant
Base color
Hover state
```
**迁移:**
* **浅色调**(`-50`、`-100`、`-200`):改用 `-soft` 变体或自定义的 Tailwind 透明度类
* **基础色**(`-500`):直接使用基础色名(`bg-accent`、`bg-danger` 等)
* **深色调**(`-600`、`-700`、`-800`、`-900`):改用 hover 变体或自定义的 Tailwind 类
### Content 颜色已移除
v2 提供了 `content1`、`content2`、`content3` 和 `content4` 颜色,用于分层背景。这些颜色已在 v3 中移除,并由语义化的 surface 颜色替代。
**v2 Content 颜色:**
```tsx
// v2 - Content colors for layered backgrounds
Base content
Secondary content
Tertiary content
Quaternary content
```
**v3 Surface 颜色:**
```tsx
// v3 - Surface colors for non-overlay components
Base surface
Secondary surface
Tertiary surface
Quaternary surface
// v3 - Overlay colors for floating components
Overlay (tooltips, popovers, modals)
```
**迁移对照:**
| v2 类 | v3 对应类 | 用法 |
| ------------- | ----------------------- | ----------------------------- |
| `bg-content1` | `bg-surface` | 非浮层组件(Card、Accordion 等) |
| `bg-content1` | `bg-overlay` | 浮层组件(Tooltip、Popover、Modal 等) |
| `bg-content2` | `bg-surface-secondary` | 二级 surface 层级 |
| `bg-content3` | `bg-surface-tertiary` | 三级 surface 层级 |
| `bg-content4` | `bg-surface-quaternary` | 四级 surface 层级 |
**主要变化:**
1. **语义命名**:`content1-4` 已替换为 `surface` 与 `overlay`,使语义更清晰
2. **针对不同组件**:页面级组件使用 `bg-surface`,浮层组件使用 `bg-overlay`
3. **自动计算**:surface 的各级(`secondary`、`tertiary`、`quaternary`)通过 `color-mix()` 从基础 `surface` 颜色自动计算得出
### 间距与布局 token
**v2 布局 token:**
```css
--heroui-divider-weight: 1px;
--heroui-disabled-opacity: 0.5;
--heroui-hover-opacity: 0.8;
```
**v3 布局 token:**
```css
--border-width: 0px;
--field-border-width: var(--border-width);
--disabled-opacity: 0.5;
--cursor-interactive: pointer;
--cursor-disabled: not-allowed;
--radius: 0.5rem;
--field-radius: calc(var(--radius) * 1.5);
```
### 阴影 token
**v2 阴影:**
```css
--heroui-box-shadow-small: 0px 0px 5px 0px rgb(0 0 0 / 0.02), ...;
--heroui-box-shadow-medium: 0px 0px 15px 0px rgb(0 0 0 / 0.03), ...;
--heroui-box-shadow-large: 0px 0px 30px 0px rgb(0 0 0 / 0.04), ...;
```
**v3 阴影:**
```css
--surface-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.04), ...;
--overlay-shadow: 0 4px 16px 0 rgba(24, 24, 27, 0.08), ...;
--field-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.04), ...;
```
**主要变化:**
1. **语义命名**:v3 使用语义名称(`surface-shadow`、`overlay-shadow`),而非按尺寸命名
2. **针对不同组件**:阴影与组件类型(surface、overlay、field)相绑定
3. **深色模式**:在 v3 中,深色模式下的阴影为透明
## 视觉差异
### 对齐变化
**Button 对齐:**
* v2:图标与文本通过 `items-center justify-center` 对齐
* v3:对齐方式相同,但加入了响应式高度调整
**Input 对齐:**
* v2:文本通过 `text-left` 对齐
* v3:对齐方式相同,但内边距的调整可能影响视觉平衡
### 间距变化
**组件内边距:**
v3 中大多数组件的内边距都有所增加:
* **Card**:12px → 16px
* **Button**:内边距相近,但高度改为响应式
* **Input**:新增垂直内边距(`py-2`)
**间隙(gap):**
v3 使用更一致的间隙:
* **Card**:页眉、内容、页脚之间使用 `gap-3`
* **Button**:图标与文字之间使用 `gap-2`
* **Chip**:元素之间使用 `gap-1.5`
### 尺寸变化
**Button 高度:**
* **Small**:32px → 36px(移动端)/ 32px(桌面端)
* **Medium**:40px → 40px(移动端)/ 36px(桌面端)
* **Large**:48px → 44px(移动端)/ 40px(桌面端)
**Input 高度:**
* **Medium**:40px → 36px(默认值,且为唯一可用尺寸)
### 圆角变化
**默认圆角:**
* v2:组件默认使用 `rounded-medium`(12px)
* v3:组件使用更大的圆角值:
* Button:`rounded-3xl`(24px)
* Card:`rounded-3xl`(24px)
* Chip:`rounded-2xl`(16px)
* Input:`rounded-field`(通常为 12–16px)
### 颜色表现变化
**颜色系统:**
* v2:HSL 颜色格式
* v3:OKLCH 颜色格式(在感知上更均匀)
**默认颜色:**
* v2:`primary`、`secondary`、`success`、`warning`、`danger`
* v3:`accent`(替代 `primary`)、`success`、`warning`、`danger`
**Muted 颜色:**
* v2:使用 `foreground-400`、`foreground-500` 表示弱化文本
* v3:使用 `muted` 颜色 token 表示弱化文本
## 迁移示例
### 工具类迁移
**示例:文本工具类**
```tsx
```
```tsx
```
### 圆角迁移
**示例:还原 v2 的圆角取值**
```tsx
Content
```
```tsx
{/* Option 1: Use standard Tailwind (smaller radius) */}
Content
{/* Option 2: Match exact v2 value (12px) */}
Content
```
### 主题定制迁移
**示例:自定义颜色**
```js
// tailwind.config.js
const {heroui} = require("@heroui/react");
module.exports = {
plugins: [
heroui({
themes: {
light: {
colors: {
primary: {
DEFAULT: "#006FEE",
50: "#E6F1FE",
// ... more shades
},
},
},
},
}),
],
};
```
```css
/* globals.css */
@import "tailwindcss";
@import "@heroui/styles";
:root {
--accent: oklch(0.6204 0.195 253.83);
--accent-foreground: oklch(0.9911 0 0);
}
```
## 最佳实践
1. **优先使用标准 Tailwind**:相比自定义工具类,优先使用标准的 Tailwind 工具类
2. **还原 v2 取值**:如果需要精确还原 v2 的视觉效果,请使用任意值
3. **响应式测试**:v3 支持响应式尺寸——请在多种屏幕尺寸下进行测试
4. **更新 CSS 变量**:自定义时请通过覆盖 CSS 变量来实现,而非修改 Tailwind 配置
5. **查阅组件文档**:API 变更请参阅各个组件的迁移指南
## 相关指南
* [迁移总指南](/docs/react/migration)
* [主题文档](/docs/react/getting-started/handbook/theming)
* [样式指南](/docs/react/getting-started/handbook/styling)
# 所有版本
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/index.mdx
> HeroUI v3 的所有更新与变更,包含新功能、修复以及破坏性变更。
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 最新版本
### v3.2.0
**2026 年 6 月 6 日**
基于 React Aria 1.18 的 Calendar 周/日视图、重做的年份选择器与范围日历演示,新增 Tooltip 延迟主题变量,并纳入随之发布的补丁修复。破坏性变更:`Radio`、`Checkbox`、`Switch` 改为显式的 `*.Content` 组合(控件嵌套进 `*.Content`,标签为纯文本、不嵌套 ``,帮助文本变为兄弟节点)。
[阅读完整发布说明 →](/docs/react/releases/v3-2-0)
### v3.1.0
**2026 年 5 月 25 日**
小版本发布:新增中文 React 文档与本地化示例,加入更可访问的 soft foreground 令牌和 vibrant palette 选项,统一滚动条系统,修复 `useTheme`、Toast、Fieldset、Link、浮层问题,并改进 Table、Picker 与 MenuItem 的 RTL 支持。
[阅读完整发布说明 →](/docs/react/releases/v3-1-0)
### v3.0.5
**2026 年 5 月 15 日**
补丁版本:`Text` 重命名为 `Typography` 以解决 `tailwind-merge` 冲突(⚠️ **破坏性变更**),派生颜色令牌重构为无前缀源变量,Checkbox 与 Radio 的 field-border 边框样式对齐,Calendar 悬停改用 `accent-soft-foreground` 以提升可读性,并新增 CLI 文档页。
[阅读完整发布说明 →](/docs/react/releases/v3-0-5)
### v3.0.4
**2026 年 5 月**
补丁版本:从 HeroUI Pro 移植的全新 `Text` 复合组件、文档主题选择器、在 45+ 个组件 CSS 文件中采用 `min()` 上限约束的圆角令牌、重做的 Table 聚焦环,以及对 Checkbox、Autocomplete、Tooltip 与表单字段内边距的修复。
[阅读完整发布说明 →](/docs/react/releases/v3-0-4)
### v3.0.3
**2026 年 4 月 17 日**
补丁版本:升级到 React Aria Components 1.17(合并依赖、安装更快、Table 支持可展开行),为 Vite 与 CRA 提供 `useTheme` Hook,为轻量级组件提供 DOM 多态的 render prop,并修复了 NumberField 的重置问题以及嵌套 Tabs 的样式问题。
[阅读完整发布说明 →](/docs/react/releases/v3-0-3)
### v3.0.2
**2026 年 4 月 3 日**
修复了多个 bug,Drawer 过渡更加平滑,新增了 `--backdrop` 主题变量,并优化了 trigger、arrow 与 Tag 的样式。修复了 Autocomplete 弹出层的宽度跟踪问题,浮层触发器现在使用 `inline-block`,Tag 在间距与无障碍方面也有所改进。
[阅读完整发布说明 →](/docs/react/releases/v3-0-2)
### v3.0.0
**2026 年 3 月**
面向 React 与 React Native 的彻底重写。包含 75+ Web 组件、37 个原生组件、Tailwind CSS v4、React Aria、复合组件架构、基于 OKLCH token 的 CSS 优先主题、通过 `data-reduce-motion` 控制动画,并面向利用 MCP Server、Agent Skills 与 LLMs.txt 的 AI 辅助开发而打造。
[阅读完整发布说明 →](/docs/react/releases/v3-0-0)
### HeroUI Pro
面向 React 与 React Native 的高级组件、模板与 AI 工具。预售价格现已上线。
[访问 heroui.pro 查看套餐与定价 →](https://heroui.pro)
### v3.0.0-rc.1
**2026 年 3 月 14 日**
新增七个组件([Drawer](/docs/components/drawer)、[ToggleButton](/docs/components/toggle-button)、[ToggleButtonGroup](/docs/components/toggle-button-group)、[Meter](/docs/components/meter)、[ProgressBar](/docs/components/progress-bar)、[ProgressCircle](/docs/components/progress-circle)、[Toolbar](/docs/components/toolbar)),为 [Table](/docs/components/table) 和 [ListBox](/docs/components/list-box) 引入了 **虚拟化**,[ButtonGroup](/docs/components/button-group) 新增 `ButtonGroup.Separator` 与垂直方向支持,React Aria Components 升级到 v1.16.0。
[阅读完整发布说明 →](/docs/react/releases/v3-0-0-rc-1)
### v3.0.0-beta.8
**2026 年 3 月 2 日**
此版本新增三个组件([Badge](/docs/components/badge)、[Pagination](/docs/components/pagination)、[Table](/docs/components/table)),并为 [DateField](/docs/components/date-field) 与 [TimeField](/docs/components/time-field) 提供了新的 `InputContainer` 组合 API。⚠️ **破坏性变更**:TextField 的 CSS 类已从 `.text-field` 重命名为 `.textfield`。
[阅读完整发布说明 →](/docs/react/releases/v3-0-0-beta-8)
### v3.0.0-beta.7
**2026 年 2 月 18 日**
此版本引入了完整的**日期与时间**系统,包含四个新组件([Calendar](/docs/components/calendar)、[RangeCalendar](/docs/components/range-calendar)、[DatePicker](/docs/components/date-picker)、[DateRangePicker](/docs/components/date-range-picker)),新增了 [Switch.Content](/docs/components/switch) 子组件、显式的 [Tabs.Separator](/docs/components/tabs) 用于按需启用分隔线,以及 ⚠️ **破坏性变更**:从 Tabs 中移除 `hideSeparator`,并将 `DateInputGroup` / `ColorInputGroup` 合并到各自的 Field 组件之下。
[阅读完整发布说明 →](/docs/react/releases/v3-0-0-beta-7)
### v3.0.0-beta.6
**2026 年 2 月 6 日**
此版本引入了完整的**颜色系统**,包含六个新组件([ColorPicker](/docs/components/color-picker)、[ColorArea](/docs/components/color-area)、[ColorSlider](/docs/components/color-slider)、[ColorField](/docs/components/color-field)、[ColorSwatch](/docs/components/color-swatch)、[ColorSwatchPicker](/docs/components/color-swatch-picker)),对 [Toast](/docs/components/toast) 进行了重大改进,加入了加载状态与 Promise 支持,[Separator](/docs/components/separator) 新增变体,以及 ⚠️ **破坏性变更**:将 `Toast.Container` 重命名为 `Toast.Provider`,并将 CSS 类名统一为连字符格式。
[阅读完整发布说明 →](/docs/react/releases/v3-0-0-beta-6)
### v3.0.0-beta.5
* 修复构建问题
### v3.0.0-beta.4
**2026 年 1 月 20 日**
**已修复关键构建问题**:此版本(beta.4)存在一个关键构建问题,已在 **beta.5** 中修复。请升级到 `@heroui/styles@3.0.0-beta.5` 与 `@heroui/react@3.0.0-beta.5`,以确保 TypeScript 声明文件能正确生成、导出能正确解析。
此版本引入了用于可视化主题定制的全新[主题构建器](/themes)、三个新组件([Autocomplete](/docs/components/autocomplete)、[Breadcrumbs](/docs/components/breadcrumbs)、[Toast](/docs/components/toast)),为 [Tabs](/docs/components/tabs) 添加了 secondary 变体,为 [Input](/docs/components/input) 与 [InputGroup](/docs/components/input-group) 添加了 primary / secondary 变体,以及 ⚠️ **破坏性变更**:移除了 Link 的下划线变体,并从表单组件中移除了 `isInSurface` prop。
[阅读完整发布说明 →](/docs/react/releases/v3-0-0-beta-4)
### v3.0.0-beta.3
**2025 年 12 月 19 日**
此版本引入了七个新组件([ButtonGroup](/docs/components/button-group)、[DateField](/docs/components/date-field)、[ErrorMessage](/docs/components/error-message)、[ScrollShadow](/docs/components/scroll-shadow)、[SearchField](/docs/components/search-field)、[TagGroup](/docs/components/tag-group)、[TimeField](/docs/components/time-field)),为表单与输入组件添加了 `fullWidth` 支持,为 [Tabs](/docs/components/tabs)、[ButtonGroup](/docs/components/button-group) 和 [Accordion](/docs/components/accordion) 引入 `hideSeparator` 以获得更简洁的布局,包含若干样式修复,以及 ⚠️ **破坏性变更**:移除 `asChild` prop,并更新了 [AlertDialog](/docs/components/alert-dialog) 与 [Modal](/docs/components/modal) 的 backdrop 变体。
[阅读完整发布说明 →](/docs/react/releases/v3-0-0-beta-3)
### v3.0.0-beta.2
**2025 年 11 月 20 日**
此版本引入了六个重要的新组件([AlertDialog](/docs/components/alert-dialog)、[ComboBox](/docs/components/combo-box)、[Dropdown](/docs/components/dropdown)、[InputGroup](/docs/components/input-group)、[Modal](/docs/components/modal)、[NumberField](/docs/components/number-field)),增强了主题兼容性与动效偏好支持,改进了 [Select](/docs/components/select) 组件的 API(包含 ⚠️ **破坏性变更**),并附带多项优化和 bug 修复。
[阅读完整发布说明 →](/docs/react/releases/v3-0-0-beta-2)
### v3.0.0-beta.1
**2025 年 11 月 6 日**
此版本对 HeroUI v3 进行了全面重新设计,将 v2 的美观与动效与 v3 的简洁性融为一体。所有组件均经过重新设计,新增 8 个组件([Alert](/docs/components/alert)、[Checkbox](/docs/components/checkbox)、[InputOTP](/docs/components/input-otp)、[ListBox](/docs/components/list-box)、[Select](/docs/components/select)、[Slider](/docs/components/slider)、[Surface](/docs/components/surface)),并对设计系统进行了彻底重构,包括更完善的颜色 token、阴影体系与整体架构。本版本还包含对设计系统变量、组件 API 以及灵活组件模式的破坏性变更。
[阅读完整发布说明 →](/docs/react/releases/v3-0-0-beta-1)
## 早期版本
### v3.0.0-alpha.35
**2025 年 10 月 21 日**
#### React Server Components 支持
* 修复了复合组件在 React Server Components(RSC)中无法正常工作的关键问题
* 将复合组件模式的逻辑从组件移至 index 文件,解决 `"use client"` 冲突
* **(⚠️ 破坏性变更)**:主组件现在需要 `.Root` 后缀(例如 `` → ``)
* 命名导出保持不变,并完全继续支持
#### React 19 相关改进
* 移除了 `forwardRef`([React 19](https://react.dev/blog/2024/12/05/react-19#ref-as-a-prop) 现已原生支持)
* 简化了 Context 的使用方式(`Context.Provider` → [React 19](https://react.dev/blog/2024/12/05/react-19#context-as-a-provider))
#### Switch 组件重构
* **(⚠️ 破坏性变更)**:将 Switch 与 SwitchGroup 拆分为独立组件
* 更简洁的 API:`` 取代 `` 和 ``
* 与 Radio / RadioGroup 模式保持一致
* 各自拥有独立的样式、类型和实现
#### 受影响的组件
以下复合组件均需要使用 `.Root` 后缀:`Accordion`、`Avatar`、`Card`、`Disclosure`、`Fieldset`、`Kbd`、`Link`、`Popover`、`Radio`、`Switch`、`Tabs`、`Tooltip`
[阅读完整发布说明 →](/docs/react/releases/v3-0-0-alpha-35)
### v3.0.0-alpha.34
**2025 年 10 月 15 日**
* 新增基于表单的组件:[Description](/docs/components/description)、[FieldError](/docs/components/field-error)、[Fieldset](/docs/components/fieldset)、[Form](/docs/components/form)、[Input](/docs/components/input)、[Label](/docs/components/label)、[RadioGroup](/docs/components/radio-group)、[TextField](/docs/components/text-field) 以及 [TextArea](/docs/components/textarea)
* 引入表单字段相关的 token `--field-*`
* 按类别重新组织 Storybook
* **(破坏性变更)**:在 [Skeleton](/docs/components/skeleton) 中将 `--skeleton-default-animation-type` 重命名为 `--skeleton-animation`
* 统一了各组件中 `data-slot` 标记的命名
* 改进了文档
[阅读完整发布说明 →](/docs/react/releases/v3-0-0-alpha-34)
### v3.0.0-alpha.33
**2025 年 10 月 5 日**
* 将 RAC 升级至 [2025 年 10 月 2 日发布的版本](https://react-spectrum.adobe.com/releases/2025-10-02.html)
* 调整了 [Tabs](/docs/components/tabs) 中 Indicator 的顺序(**破坏性变更**)
* 将 [Tabs](/docs/components/tabs) 组件改为使用 React Aria 的 `SelectionIndicator`,现已支持 SSR
* 更新了 [Disclosure](/docs/components/disclosure) 和 [Disclosure Group](/docs/components/disclosure-group) 组件,使其在展开/折叠动画中使用 RAC 的 CSS 变量
* 更新了 [Switch](/docs/components/switch) 组件的样式与动画
* 为 [Switch](/docs/components/switch#sizes) 新增 `size` 变体并补充了对应演示
* 在 [Button](/docs/components/button)、[Tabs](/docs/components/tabs)、[Disclosure](/docs/components/disclosure)、[Disclosure Group](/docs/components/disclosure-group) 中添加了相关示例
* 改进了文档
[阅读完整发布说明 →](/docs/react/releases/v3-0-0-alpha-33)
### v3.0.0-alpha.32
**2025 年 10 月 1 日**
重新设计了 Card 组件,引入了[新变体](/docs/components/card),新增了 [CloseButton](/docs/components/close-button) 组件,发布了面向 AI 编码助手的 [MCP 服务器](/docs/ui-for-agents/mcp-server),并改进了文档。
[阅读完整发布说明 →](/docs/react/releases/v3-0-0-alpha-32)
### v3.0.0-alpha.31
**2025 年 9 月 22 日**
* 🎨 **展示页面** - 使用 HeroUI 构建的站点案例集
* 🌀 **DisclosureGroup 组件** - 将多个 Disclosure 组合在一起
* 📇 **Card 组件**(预览) - Card 组件的首个版本
* 🔀 **Switch 组件**(预览) - 用于设置项的切换开关
## 发布周期
HeroUI v3 现已稳定。后续版本将遵循固定的发布周期:
* **补丁版本**:按需修复 bug 与小幅优化
* **次要版本**:新增组件与功能,通常每月一次
* **主版本**:包含架构层面的变更,并提供迁移指南
## 参与贡献
发现问题或希望参与贡献?欢迎查看我们的 [GitHub 仓库](https://github.com/heroui-inc/heroui)。
# v3.0.0-alpha.32
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-0-alpha-32
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-0-alpha-32.mdx
> Card 组件重新设计、CloseButton 组件,以及面向 AI 助手的 MCP 服务器。
2025 年 10 月 1 日
此版本新增了用于 AI 开发的工具,并更新了 [Card 组件](/docs/components/card) 的 API,以提升开发者体验。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@alpha @heroui/react@alpha
```
```bash
pnpm add @heroui/styles@alpha @heroui/react@alpha
```
```bash
yarn add @heroui/styles@alpha @heroui/react@alpha
```
```bash
bun add @heroui/styles@alpha @heroui/react@alpha
```
## 新增功能
### MCP 服务器
HeroUI 现已包含一个 [MCP 服务器](/docs/ui-for-agents/mcp-server),可让 Cursor、Claude Code、VS Code Copilot 等 AI 助手直接访问 HeroUI v3 的文档与组件信息。
**快速配置:**
### Cursor
或在 **Cursor Settings** → **Tools** → **MCP Servers** 中手动添加:
```json
{
"mcpServers": {
"heroui-react": {
"command": "npx",
"args": ["-y", "@heroui/react-mcp@latest"]
}
}
}
```
### Claude Code
在终端中运行以下命令:
```bash
claude mcp add heroui-react -- npx -y @heroui/react-mcp@latest
```
[了解更多](/docs/ui-for-agents/mcp-server)
### Card 组件 API 重新设计
[Card 组件](/docs/components/card) 已升级到全新的变体系统,使用更加灵活。
**破坏性变更:**
* 将 `surface` prop 替换为新的 `variant` 系统
* 移除了 `Card.Image`、`Card.Details` 与 `Card.CloseButton`(请改用组合方式实现)
* 新增变体:`flat`、`outlined`、`elevated`、`filled`
**之前:**
```tsx
Old Card
```
**之后:**
```tsx
New Card
```
**新功能:**
* 支持水平布局
* 与 Avatar 集成
* 支持背景图片
* 通过语义化 HTML 提升无障碍体验
[查看 Card 组件文档](/docs/components/card)
### CloseButton 组件
新增 [CloseButton 组件](/docs/components/close-button),用于关闭对话框、模态框以及其他可关闭的元素。
```tsx
import {CloseButton} from "@heroui/react";
// Basic usage
console.log("Closed")} />
// With custom icon
```
## 文档改进
### 面向 AI 的 UI
* **[MCP 服务器文档](/docs/ui-for-agents/mcp-server)** —— 介绍如何借助 AI 助手进行开发
* **[llms.txt](/docs/ui-for-agents/llms-txt)** —— 对 LLM 更友好的文档文件
* 主流 AI 编码工具的配置指南
### 组件文档
* **[Card](/docs/components/card)**:重写了文档,包含 anatomy、变体与更多示例
* **[Switch](/docs/components/switch)**:新增 anatomy 示意图与更完善的示例
* **[CloseButton](/docs/components/close-button)**:全新文档,附带使用示例
## 迁移指南
### Card 组件迁移
1. **更新 variant prop:**
* `surface="1"` → `variant="flat"`
* `surface="2"` → `variant="outlined"`
* `surface="3"` → `variant="elevated"`
* `surface="4"` → `variant="filled"`
* 自定义 surface → 改用新的变体系统
2. **更新组件结构:**
* 将 `Card.Image` 替换为放在 `Card.Header` 中的 ` `
* 将 `Card.Details` 替换为 `Card.Body`
* 将 `Card.CloseButton` 迁移为使用新的 `CloseButton` 组件
3. **更新导入:**
```tsx
// Add CloseButton if needed
import {Card, CloseButton} from "@heroui/react";
```
## 链接
* [GitHub PR #5747](https://github.com/heroui-inc/heroui/pull/5747)
* [MCP 服务器文档](/docs/ui-for-agents/mcp-server)
* [Card 组件指南](/docs/components/card)
* [CloseButton 组件](/docs/components/close-button)
## 贡献者
感谢每一位为本次发布做出贡献的开发者!
# v3.0.0-alpha.33
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-0-alpha-33
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-0-alpha-33.mdx
> 升级 RAC、重新设计 Tabs 指示器、新增 Switch 尺寸变体,以及相关示例展示。
2025 年 10 月 5 日
此版本升级了 React Aria Components,重新设计了 Tabs 指示器,为 Switch 新增尺寸支持,并补充了一系列组件示例。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@alpha @heroui/react@alpha
```
```bash
pnpm add @heroui/styles@alpha @heroui/react@alpha
```
```bash
yarn add @heroui/styles@alpha @heroui/react@alpha
```
```bash
bun add @heroui/styles@alpha @heroui/react@alpha
```
## 新增功能
### RAC 升级
将 React Aria Components 升级到 [2025 年 10 月 2 日发布版本](https://react-spectrum.adobe.com/releases/2025-10-02.html)。
本次升级包括:
* 用于动画的 CSS 变量
* 更好的 SSR 支持
* 选择指示器的性能改进
### Disclosure 与 DisclosureGroup 更新
[Disclosure](/docs/components/disclosure) 与 [DisclosureGroup](/docs/components/disclosure-group) 现在使用 React Aria 的 CSS 变量来驱动动画。组件会通过 `--disclosure-panel-width` 与 `--disclosure-panel-height` 变量在展开 / 折叠期间跟踪面板的实际尺寸。
### Tabs 指示器重新设计
[Tabs](/docs/components/tabs) 现在使用 React Aria 的 `SelectionIndicator` 并支持 SSR,这修复了初次渲染时的布局抖动问题。
**🚧 破坏性变更:**
* 将 `Tabs.Indicator` 移至每一个 `Tabs.Tab` 内部
**之前:**
```diff tsx
+
-
```
### Switch 更新
[Switch](/docs/components/switch) 的样式与动画都得到了更新。新增 `size` prop,可选值为 `sm`、`md`、`lg`。
```tsx
import {Switch} from "@heroui/react";
export function Sizes() {
return (
小
中
大
);
}
```
### 相关示例
我们在 [Button](/docs/components/button)、[Disclosure](/docs/components/disclosure)、[DisclosureGroup](/docs/components/disclosure-group) 与 [Tabs](/docs/components/tabs) 中新增了「相关示例」展示。
## 文档改进
### 组件文档
* **[Tabs](/docs/components/tabs)**:更新了 anatomy,根据新的指示器设计重写了示例,并新增了相关示例展示
* **[Switch](/docs/components/switch)**:新增尺寸示例,并重写了 with-icon 示例
* **[Button](/docs/components/button)**、**[Disclosure](/docs/components/disclosure)**、**[DisclosureGroup](/docs/components/disclosure-group)**:新增相关示例展示
## 迁移指南
### Tabs 组件迁移
1. **更新组件结构:**
* 将 ` ` 移至每一个 ` ` 内部
## 链接
* [GitHub PR #5777](https://github.com/heroui-inc/heroui/pull/5777)
* [Tabs 组件](/docs/components/tabs)
* [Switch 组件](/docs/components/switch)
* [Button 组件](/docs/components/button)
* [Disclosure 组件](/docs/components/disclosure)
* [DisclosureGroup 组件](/docs/components/disclosure-group)
## 贡献者
感谢每一位为本次发布做出贡献的开发者!
# v3.0.0-alpha.34
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-0-alpha-34
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-0-alpha-34.mdx
> 用 Form、TextField、RadioGroup、Label、Input、Fieldset 等简洁 API 构建表单的核心组件。
2025 年 10 月 15 日
此版本引入了一系列基于表单的组件、表单字段 token,重新组织了 Storybook,并对各组件之间的 data-slot 标识做了统一对齐。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@alpha @heroui/react@alpha
```
```bash
pnpm add @heroui/styles@alpha @heroui/react@alpha
```
```bash
yarn add @heroui/styles@alpha @heroui/react@alpha
```
```bash
bun add @heroui/styles@alpha @heroui/react@alpha
```
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增功能
### 基于表单的组件
我们引入了一整套基于 React Aria Components 构建的表单类组件,为构建表单提供了无障碍且可组合的基础构件。这些组件包括 [Description](/docs/components/description)、[FieldError](/docs/components/field-error)、[Fieldset](/docs/components/fieldset)、[Form](/docs/components/form)、[Input](/docs/components/input)、[Label](/docs/components/label)、[RadioGroup](/docs/components/radio-group)、[TextField](/docs/components/text-field) 与 [TextArea](/docs/components/textarea)。
#### Description
```tsx
import {Description, Input, Label} from "@heroui/react";
export function Basic() {
return (
邮箱
我们不会将你的邮箱分享给任何人。
);
}
```
#### FieldError
```tsx
"use client";
import {FieldError, Input, Label, TextField} from "@heroui/react";
import {useState} from "react";
export function Basic() {
const [value, setValue] = useState("jr");
const isInvalid = value.length > 0 && value.length < 3;
return (
用户名
setValue(e.target.value)}
/>
用户名至少需要 3 个字符
);
}
```
#### Fieldset
```tsx
"use client";
import {FloppyDisk} from "@gravity-ui/icons";
import {
Button,
Description,
FieldError,
FieldGroup,
Fieldset,
Form,
Input,
Label,
TextArea,
TextField,
} from "@heroui/react";
export function Basic() {
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: Record = {};
// Convert FormData to plain object
formData.forEach((value, key) => {
data[key] = value.toString();
});
alert("表单提交成功!");
};
return (
);
}
```
#### Form
```tsx
"use client";
import {Check} from "@gravity-ui/icons";
import {Button, Description, FieldError, Form, Input, Label, TextField} from "@heroui/react";
export function Basic() {
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: Record = {};
// Convert FormData to plain object
formData.forEach((value, key) => {
data[key] = value.toString();
});
alert(`表单提交数据:${JSON.stringify(data, null, 2)}`);
};
return (
{
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)) {
return "请输入有效的邮箱地址";
}
return null;
}}
>
邮箱
{
if (value.length < 8) {
return "密码至少需要 8 个字符";
}
if (!/[A-Z]/.test(value)) {
return "密码至少需要包含一个大写字母";
}
if (!/[0-9]/.test(value)) {
return "密码至少需要包含一个数字";
}
return null;
}}
>
密码
至少 8 个字符,且包含 1 个大写字母和 1 个数字
提交
重置
);
}
```
#### Input
```tsx
import {Input} from "@heroui/react";
export function Basic() {
return ;
}
```
#### Label
```tsx
import {Input, Label} from "@heroui/react";
export function Basic() {
return (
姓名
);
}
```
#### RadioGroup
```tsx
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Basic() {
return (
选择套餐
选择最适合你的套餐
基础版
每月包含 100 条消息
高级版
每月包含 200 条消息
商业版
无限消息
);
}
```
#### TextField
#### TextArea
```tsx
import {TextArea} from "@heroui/react";
export function Basic() {
return (
);
}
```
### 表单字段 token
引入 `--field-*` 表单字段 token,确保各表单组件之间样式保持一致。`--field-*` 变量的具体说明请参阅 [主题](/docs/handbook/theming#calculated-variables-tailwind)。
### Storybook 重新组织
按类别重新组织了 Storybook,方便导航与组件查找。
### Skeleton 动画 token
**🚧 破坏性变更:** 为了与其他组件 token 保持一致,[Skeleton](/docs/components/skeleton) 中的 `--skeleton-default-animation-type` 已重命名为 `--skeleton-animation`。
### data-slot 对齐
我们统一了各组件的 data-slot 标识,使样式与定制更加一致。这项标准化让通过 CSS 选择器定位特定组件部件变得更容易,整体上也优化了自定义组件样式时的开发体验。
组件现在使用一致的 `data-slot` 属性,例如:
* `data-slot="base"` —— 用于根元素
* `data-slot="label"` —— 用于标签文本
* `data-slot="description"` —— 用于描述文本
* `data-slot="error"` —— 用于错误信息
这样在所有表单组件中都能用可预期的方式进行 CSS 定位:
```css
.radio {
[data-slot="label"] {
/* Styles apply to radio labels */
}
}
```
## 文档改进
### 组件文档
* **[Link](/docs/components/link)**:新增 anatomy 与带图标的示例,更新了 Link 与 Link.Icon 的 prop 章节。
* **[Description](/docs/components/description)**、**[FieldError](/docs/components/field-error)**、**[Fieldset](/docs/components/fieldset)**、**[Form](/docs/components/form)**、**[Input](/docs/components/input)**、**[Label](/docs/components/label)**、**[RadioGroup](/docs/components/radio-group)**、**[TextField](/docs/components/text-field)**,以及 **[TextArea](/docs/components/textarea)**:附带使用示例的全新文档
## 迁移指南
### Skeleton 组件迁移
1. **更新动画 token:**
* 将 `--skeleton-default-animation-type` 替换为 `--skeleton-animation`
## 链接
* [GitHub PR #5780](https://github.com/heroui-inc/heroui/pull/5780)
* [Description 组件](/docs/components/description)
* [FieldError 组件](/docs/components/field-error)
* [Fieldset 组件](/docs/components/fieldset)
* [Form 组件](/docs/components/form)
* [Input 组件](/docs/components/input)
* [Label 组件](/docs/components/label)
* [RadioGroup 组件](/docs/components/radio-group)
* [TextField 组件](/docs/components/text-field)
* [TextArea 组件](/docs/components/textarea)
* [Skeleton 组件](/docs/components/skeleton)
## 贡献者
感谢每一位为本次发布做出贡献的开发者!
# v3.0.0-alpha.35
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-0-alpha-35
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-0-alpha-35.mdx
> 复合组件支持 React Server Components、面向 React 19 的改进,以及关键 bug 修复。
2025 年 10 月 21 日
此版本修复了一个关键问题:**复合组件在 React Server Components(RSC)中无法正常工作**。同时,本版本采用了 React 19 的最佳实践,移除了 `forwardRef`,并简化了 Context 的使用方式。Switch 组件已经过重构,与 Radio / RadioGroup 模式保持一致,提供更清晰、更统一的 API。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@alpha @heroui/react@alpha
```
```bash
pnpm add @heroui/styles@alpha @heroui/react@alpha
```
```bash
yarn add @heroui/styles@alpha @heroui/react@alpha
```
```bash
bun add @heroui/styles@alpha @heroui/react@alpha
```
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增功能
### React Server Components 支持
复合组件现在可以在 React Server Components 中正常工作。此前的实现把复合模式逻辑放在了组件内部,与 `"use client"` 指令存在冲突。通过将模式逻辑迁移到组件的索引文件中,这一问题已被修复。
### 面向 React 19 的改进
本版本采用了 React 19 的最佳实践:
1. **移除 `forwardRef`**:在 React 19 中已不再需要,`ref` 现在可以作为普通的 prop 使用(参见 [React 19 文档](https://react.dev/blog/2024/12/05/react-19#ref-as-a-prop))
2. **简化 Context**:将 `Context.Provider` 替换为直接使用 `Context`(参见 [React 19 文档](https://react.dev/blog/2024/12/05/react-19#context-as-a-provider))
### Switch 组件架构改进
Switch 组件已经过重构,遵循与 Radio / RadioGroup 相同的清晰拆分模式:
* **拆分组件**:Switch 与 SwitchGroup 现在是独立的组件(此前合并在一起)
* **更清晰的 API**:用 `` 取代了嵌套的 `` 与 `` 模式
* **更合理的组织**:每个组件都有各自独立的样式、类型与实现
* **一致的模式**:与 Radio / RadioGroup 架构保持一致,API 更具可预测性
**之前:**
```tsx
...
```
**之后:**
```tsx
...
...
```
## ⚠️ 破坏性变更
### 主组件需要使用 `.Root` 后缀
为支持 React Server Components,复合组件模式已经过重构。在使用复合写法时,主组件现在需要带上 `.Root` 后缀。
**之前:**
```tsx
import { Avatar } from "@heroui/react"
JR
```
**之后:**
```tsx
import { Avatar } from "@heroui/react"
JR
```
**说明:** 命名导出(例如 ``、``、``)保持不变,依然完全支持。
### Switch 组件 API 变更
Switch 组件的分组 API 已经过重构,与 Radio / RadioGroup 模式保持一致:
**之前:**
```tsx
import { Switch } from "@heroui/react"
Notifications
Marketing
```
**之后:**
```tsx
import { Switch, SwitchGroup } from "@heroui/react"
Notifications
Marketing
```
这次变更带来了:
* **拆分组件**:Switch 与 SwitchGroup 现在是独立的组件(此前合并在一起)
* **更清晰的 API**:用 `` 取代了嵌套的 `` 与 `` 模式
* **更合理的组织**:每个组件都有各自独立的样式、类型与实现
* **一致的模式**:与 Radio / RadioGroup 架构保持一致,API 更具可预测性
**迁移步骤:**
1. 单独导入 `SwitchGroup`:`import { Switch, SwitchGroup } from "@heroui/react"`
2. 将 `` 替换为 ``
3. 移除嵌套的 `` 包装
4. 单个的 `Switch.Root` 组件保持不变
#### 受影响的组件
所有复合组件都受到影响:
* `Accordion` → `Accordion.Root`
* `Avatar` → `Avatar.Root`
* `Card` → `Card.Root`
* `Disclosure` → `Disclosure.Root`
* `Fieldset` → `Fieldset.Root`
* `Kbd` → `Kbd.Root`
* `Link` → `Link.Root`
* `Popover` → `Popover.Root`
* `RadioGroup` → `RadioGroup.Root`
* `Switch` → `Switch.Root`
* `Tabs` → `Tabs.Root`
* `Tooltip` → `Tooltip.Root`
## 迁移指南
使用 HeroUI 的复合组件有两种选择:
### 选项 1:改为使用 `.Root`(复合写法)
如果你使用的是复合写法(点号语法),请将主组件改为使用 `.Root`:
**Card 示例:**
```tsx
import { Card } from "@heroui/react"
Card Title
Card description
Card content
Card footer
```
**Tabs 示例:**
```tsx
import { Tabs } from "@heroui/react"
Tab 1
Tab 2
Panel 1
Panel 2
```
[更多示例请参阅文档](/docs/components/card)
**Avatar 示例:**
```tsx
import { Avatar } from "@heroui/react"
JD
```
[更多示例请参阅文档](/docs/components/avatar)
### 选项 2:使用命名导出
我们已经为所有复合组件添加了命名导出支持,你可以这样使用:
**Card 示例:**
```tsx
import {
CardRoot,
CardHeader,
CardTitle,
CardDescription,
CardContent,
CardFooter,
} from "@heroui/react"
Card Title
Card description
Card content
Card footer
```
**Tabs 示例:**
```tsx
import { TabsRoot, TabListContainer, TabList, Tab, TabIndicator, TabPanel } from "@heroui/react"
Tab 1
Tab 2
Panel 1
Panel 2
```
**Avatar 示例:**
```tsx
import { Avatar, AvatarImage, AvatarFallback } from "@heroui/react"
JD
```
### 迁移步骤
如果你使用的是复合写法,只需将主组件改为使用 `.Root`:
1. **查找复合组件的所有使用位置**(例如内部包含 `` 等的 ``)
2. **为主组件添加 `.Root`**:
```tsx
// Before
// After
```
3. **就这样!** 所有子组件(如 `Avatar.Image`、`Avatar.Fallback`)保持不变。
### 完整的迁移参考
| 组件 | 命名导出写法 | 复合写法(带 `.Root`) | 额外变更 |
| -------------- | ------------------------------ | ------------------------------------ | ---------------------------------------- |
| **Accordion** | `` | `` | - |
| **Avatar** | `` | `` | - |
| **Card** | `` | `` | - |
| **Disclosure** | `` | `` | - |
| **Fieldset** | `` | `` | - |
| **Kbd** | `` | `` | - |
| **Link** | ` ` | `` | - |
| **Popover** | `` | `` | - |
| **Radio** | `` | `` | - |
| **Switch** | ``、`` | ``、`` | `` → ``(独立组件) |
| **Tabs** | ``、`` | ``、`` | - |
| **Tooltip** | ``、`` | ``、`` | - |
### 自动化迁移
对于使用复合写法的大型代码库,可以借助查找 / 替换:
```bash
# Example for Avatar component
# Update the main component to use .Root
sed -i 's///g' **/*.tsx
sed -i 's/<\/Avatar>/<\/Avatar.Root>/g' **/*.tsx
# Switch component requires additional steps
# First, ensure SwitchGroup is imported
# Then replace Switch.Group with SwitchGroup
sed -i 's//<\/SwitchGroup>/g' **/*.tsx
# Remove Switch.GroupItems wrapper
sed -i 's///g' **/*.tsx
sed -i 's/<\/Switch\.GroupItems>//g' **/*.tsx
# Repeat for other compound components (Card, Tabs, etc.)
# Note: This only affects files using the compound pattern
```
**重要事项:**
* 使用自动替换时务必小心,确保只替换复合写法的用法,而不要影响命名导出。
* Switch 的迁移完成后,请确认 `SwitchGroup` 已被导入:`import { Switch, SwitchGroup } from "@heroui/react"`
* 在执行完自动迁移后请测试代码,确认所有变更均符合预期。
## 为什么需要这次变更?
这次变更是修复 React Server Components 兼容性所必需的。此前的实现存在一些架构上的限制:
1. **RSC 兼容性**:复合模式逻辑与 `"use client"` 指令存在冲突
2. **拥抱 React 19**:移除了 `forwardRef` 与 `Context.Provider` 等已被弃用的写法
3. **更清晰的架构**:模式逻辑现在位于索引文件中,而不是组件文件中
4. **更彻底的拆分**:服务端组件与客户端组件现在可以无缝协作
## 文档更新
组件文档将同步更新,以反映新的写法:
* 示例将展示带 `.Root` 的复合写法
* 命名导出形式的示例依然有效且仍受支持
* 迁移指南将帮助你顺利完成升级
* 两种写法都获得完整支持,行为完全一致
## 需要帮助?
如果你在迁移过程中遇到任何问题:
1. **复合写法用户**:将主组件改为使用 `.Root`(例如 `` → ``)
2. **命名导出用户**:无需做任何修改,你的代码仍可正常工作
3. 查阅组件文档中的示例
4. 反馈问题:[GitHub Issues](https://github.com/heroui-inc/heroui/issues)
## 链接
* [组件文档](/docs/react/components)
* [React Server Components](https://react.dev/reference/rsc/server-components)
* [React 19 发布](https://react.dev/blog/2024/12/05/react-19)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
## 贡献者
感谢每一位为本次发布做出贡献的开发者,是你们让 React Server Components 支持与 React 19 兼容性得到了改进!
# v3.0.0-beta.1
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-0-beta-1
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-0-beta-1.mdx
> 重大重新设计,带来全新的设计系统、8 个新组件以及更佳的开发者体验。
2025 年 11 月 6 日
此版本对 HeroUI v3 进行了全面重新设计,将 v2 的美观与动效与 v3 的简洁性融为一体。所有组件均经过重新设计,新增 8 个组件,并对设计系统进行了改进,包括更完善的颜色 token、阴影体系与整体架构。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@beta @heroui/react@beta
```
```bash
pnpm add @heroui/styles@beta @heroui/react@beta
```
```bash
yarn add @heroui/styles@beta @heroui/react@beta
```
```bash
bun add @heroui/styles@beta @heroui/react@beta
```
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增功能
### 全新的设计系统
我们花了数周时间打造一套全新的设计系统,将 HeroUI v2 的灵魂与 v3 的简洁性融合在一起。每一个组件都经过重新设计,注重细节、流畅的动效以及更佳的开发者体验。新的设计系统已发布在我们的 [Figma Kit v3](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)。
本次重新设计带来了:
* 让 v3 的视觉愿景落地、并具备独特辨识度的全新色彩系统
* 更精致的阴影系统,呈现更好的层次感
* 全新的变量与 token,提供更强的定制能力
* 基于表单的组件自动具备 `isOnSurface` 支持
* 增强的边框与间距 token
* 更好的对比度与无障碍体验
* Web 与 Native 之间一致的组件模式
### 新组件
本次发布共引入 **8 个** 新的基础组件:
* **[Alert](#alert)**:带状态指示器,用于展示重要的消息与通知。
* **[Checkbox 与 CheckboxGroup](#checkbox-checkboxgroup)**:用于在列表中选择多个条目。
* **[InputOTP](#inputotp)**:用于身份验证流程的一次性密码输入框。
* **[ListBox](#listbox)**:展示一组可单选或多选的选项。
* **[Select](#select)**:基于 ListBox 构建的下拉选择组件。
* **[Slider](#slider)**:从一个范围中选择数值,支持自定义刻度与标签。
* **[Surface](#surface)**:用于构建带高度的容器的基础 surface 组件。
### Alert
```tsx
import {Alert, Button, CloseButton, Spinner} from "@heroui/react";
import React from "react";
export function Basic() {
return (
{/* 默认 — 一般信息 */}
新功能已上线
查看我们的最新更新,包括深色模式支持与改进的无障碍体验。
{/* 强调 — 重要信息含操作 */}
有可用更新
应用有新版本可用。请刷新页面以获取最新功能与问题修复。
刷新
刷新
{/* 危险 — 错误与排查步骤 */}
无法连接到服务器
当前遇到连接问题,请尝试以下操作:
重试
重试
{/* 无描述 */}
个人资料已更新
{/* 自定义指示器 — 加载中 */}
正在处理你的请求
正在同步你的数据,请稍候,这可能需要一点时间。
{/* 无关闭按钮 */}
计划维护
我们将于 UTC 时间 3 月 15 日(周日)凌晨 2:00 至上午 6:00
进行计划维护,期间服务将暂时不可用。
);
}
```
### Checkbox 与 CheckboxGroup
```tsx
import {Checkbox} from "@heroui/react";
export function Basic() {
return (
接受条款与条件
);
}
```
```tsx
import {Checkbox, CheckboxGroup, Description, Label} from "@heroui/react";
export function Basic() {
return (
选择你的兴趣
可多选
编程
热爱构建软件
设计
喜欢打造精美界面
写作
热衷于内容创作
);
}
```
### InputOTP
```tsx
import {InputOTP, Label, Link} from "@heroui/react";
export function Basic() {
return (
验证账户
我们已向 a****@gmail.com 发送验证码
);
}
```
### ListBox
```tsx
import {Avatar, Description, Label, ListBox} from "@heroui/react";
export function Default() {
return (
B
Bob
bob@heroui.com
F
Fred
fred@heroui.com
M
Martha
martha@heroui.com
);
}
```
### Select
```tsx
import {Label, ListBox, Select} from "@heroui/react";
export function Default() {
return (
州
佛罗里达
特拉华
加利福尼亚
德克萨斯
纽约
华盛顿
);
}
```
### Slider
```tsx
import {Label, Slider} from "@heroui/react";
export function Default() {
return (
音量
);
}
```
### Surface
```tsx
import {Surface} from "@heroui/react";
export function Variants() {
return (
默认
表面内容
这是默认表面变体,使用 bg-surface 样式。
次要
表面内容
这是次要表面变体,使用 bg-surface-secondary 样式。
第三
表面内容
这是第三表面变体,使用 bg-surface-tertiary 样式。
透明
表面内容
这是透明表面变体,无背景,适用于遮罩层和自定义背景的卡片。
);
}
```
### 组件 API 改进
多个组件的 API 都得到了改进:
* **Link**:新增 `underline` 与 `underlineOffset` prop,支持更细粒度的定制
```tsx
import {Link} from "@heroui/react";
export function LinkBasic() {
return (
立即行动
);
}
```
* **Card**:变体与样式系统得到改进
```tsx
import {CircleDollar} from "@gravity-ui/icons";
import {Avatar, Button, Card, CloseButton, Link} from "@heroui/react";
export function WithImages() {
return (
{/* 第 1 行:大图商品卡 */}
成为 ACME 创作者!
这是一段占位说明文字,用于展示横向卡片布局、配图与右上角关闭按钮的排版效果。
仅剩 10 个名额
报名截止:10 月 10 日
立即申请
{/* 第 2 行 */}
{/* 左栏 */}
{/* 上方卡片 */}
支付
现已支持加密货币提现
在设置中添加钱包即可提现
前往设置
{/* 下方小卡 */}
{/* 左卡 */}
JK
Indie Hackers
148 位成员
JK
创建者:约翰
{/* 右卡 */}
AB
AI Builders
362 位成员
M
创建者:玛莎
{/* 右栏 */}
{/* 背景图 */}
{/* 标题区 */}
NEO
家用机器人
{/* 底部 */}
通知我
{/* 第 3 行 */}
{/* 左:大图卡 */}
立即购买
{/* 右:堆叠小卡 */}
{/* 1 */}
连接未来
今天 18:30
{/* 2 */}
牛油果黑客松
周三 16:30
{/* 3 */}
Sound Electro|超越艺术
周五 20:00
);
}
```
* **Chip**:新增尺寸变体并改进了颜色系统
```tsx
import {Chip} from "@heroui/react";
export function ChipBasic() {
return (
默认
强调
成功
警告
危险
);
}
```
* **Switch**:从底层重新设计,视觉与动画都得到优化
```tsx
import {Switch} from "@heroui/react";
export function Basic() {
return (
启用通知
);
}
```
* **RadioGroup**:从底层重新设计,API 与样式更佳
```tsx
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Basic() {
return (
选择套餐
选择最适合你的套餐
基础版
每月包含 100 条消息
高级版
每月包含 200 条消息
商业版
无限消息
);
}
```
### 灵活的组件模式
HeroUI 现在支持更灵活的组件写法。复合组件可以带 `.Root` 也可以不带 `.Root`,也可以使用命名导出——三种写法表现完全一致。
**可用模式:**
```tsx
import { Avatar } from "@heroui/react"
// 1. Compound pattern (no .Root needed) - recommended
JD
// 2. Compound pattern with .Root - still supported
JD
// 3. Named exports
import { AvatarRoot, AvatarImage, AvatarFallback } from "@heroui/react"
JD
```
**简单组件**(如 Button)的写法也完全一致:
```tsx
import { Button } from "@heroui/react"
// No .Root needed
Label
// Or with .Root
Label
// Or named export
import { ButtonRoot } from "@heroui/react"
Label
```
**你也可以在同一个组件中混用复合写法与命名导出:**
```tsx
import { Avatar, AvatarFallback } from "@heroui/react"
JD
```
由此带来的好处:
* **更简洁的 API**:主组件不再强制要求 `.Root` 后缀
* **灵活性**:可以在「复合写法」、「带 `.Root` 的复合写法」与「命名导出」之间自由选择
* **向后兼容**:`.Root` 写法依然可用
* **命名一致性**:统一了命名约定(例如使用「Container」而非「Wrapper」)
### 全局动画控制
HeroUI 现在通过 `data-reduce-motion` 属性提供了便捷的全局动画控制方式。只需在 `` 或 `` 标签上加上 `data-reduce-motion="true"`,即可禁用整个应用中的所有动画。
```html
```
HeroUI 会自动通过 `prefers-reduced-motion` 媒体查询尊重用户的动画偏好,并扩展了 Tailwind 的 `motion-reduce:` 变体,使其同时支持系统偏好与基于 data 属性的手动控制。这样既能灵活控制动画,也能符合无障碍最佳实践。
了解更多关于动画与动效偏好的内容,请参阅 [动画文档](/docs/handbook/animation)。
## ⚠️ 破坏性变更
### 设计系统变量
#### Panel → Surface 与 Overlay
`--panel` 变量已被替换为 `--surface` 与 `--overlay`,以更好地区分非浮层组件(Card、Accordion)与浮层组件(Tooltip、Popover、Modal)。
**之前:**
```css
--panel: var(--white);
--panel-foreground: var(--foreground);
--shadow-panel: 0 0 1px 0 rgba(0, 0, 0, 0.3) inset, 0 2px 8px 0 rgba(0, 0, 0, 0.08);
```
**之后:**
```css
--surface: var(--white);
--surface-foreground: var(--foreground);
--overlay: var(--white);
--overlay-foreground: var(--foreground);
--shadow-surface: 0 2px 4px 0 rgba(0, 0, 0, 0.04), 0 1px 2px 0 rgba(0, 0, 0, 0.06), 0 0 1px 0 rgba(0, 0, 0, 0.06);
--shadow-overlay: 0 4px 16px 0 rgba(24, 24, 27, 0.08), 0 8px 24px 0 rgba(24, 24, 27, 0.09);
```
**迁移:**
* 非浮层组件请将 `bg-panel` 替换为 `bg-surface`
* 浮层组件请将 `bg-panel` 替换为 `bg-overlay`
* 将 `shadow-panel` 替换为 `shadow-surface` 或 `shadow-overlay`
* 将 `--color-panel` 替换为 `--color-surface` 或 `--color-overlay`
#### Surface 层级简化
`--surface-1`、`--surface-2` 与 `--surface-3` 变量已被移除。Surface 各层级现在通过 `color-mix` 自动从 `--surface` 计算得出,因此你只需声明基础的 surface 颜色。
**之前(手动声明):**
```css
--surface-1: var(--background);
--surface-2: var(--color-neutral-100);
--surface-3: var(--color-neutral-200);
```
**之后(自动计算):**
```css
/* You only declare the base surface */
--surface: var(--white);
--surface-foreground: var(--foreground);
/* HeroUI automatically calculates these using color-mix */
--color-surface-secondary: color-mix(in oklab, var(--surface) 94%, var(--surface-foreground) 6%);
--color-surface-tertiary: color-mix(in oklab, var(--surface) 92%, var(--surface-foreground) 8%);
--color-surface-quaternary: color-mix(in oklab, var(--surface) 86%, var(--surface-foreground) 14%);
```
**自定义:**
可以通过 Tailwind 的 `@theme` 指令覆盖默认的计算结果:
```css
@theme inline {
--color-surface-secondary: color-mix(in oklab, var(--surface) 96%, var(--surface-foreground) 4%);
--color-surface-tertiary: color-mix(in oklab, var(--surface) 94%, var(--surface-foreground) 6%);
--color-surface-quaternary: color-mix(in oklab, var(--surface) 90%, var(--surface-foreground) 10%);
}
```
**迁移:**
* 将 `bg-surface-1` 替换为 `bg-surface`(基础 surface)
* 将 `bg-surface-2` 替换为 `bg-surface-secondary`(自动计算)
* 将 `bg-surface-3` 替换为 `bg-surface-tertiary`(自动计算)
同样的自动计算模式也适用于:
* **背景色阶**:从 `--background` 计算 → `background-secondary`、`background-tertiary`、`background-quaternary`
* **柔和色**:从状态色计算 → `accent-soft`、`danger-soft`、`warning-soft`、`success-soft`
#### 边框宽度默认值变更
默认边框宽度已从 `1px` 改为 `0px`。边框现在改为按需启用,而不是默认存在。
**之前:**
```css
--border-width: 1px;
```
**之后:**
```css
--border-width: 0px; /* no border by default */
```
**迁移:**
* 如果你的样式依赖默认边框,请在自定义样式中显式设置 `border-width`
* 表单字段现在默认使用 `transparent` 边框
#### 边框颜色默认值变更
默认边框颜色的不透明度已从 `15%` 改为 `0%`(透明)。
**之前:**
```css
--border: oklch(0 0 0 / 15%);
```
**之后:**
```css
--border: oklch(0 0 0 / 0%);
```
**字段边框默认值:**
```css
--field-border: transparent; /* no border by default on form fields */
```
#### 阴影系统更新
阴影系统已被完全重新设计,为 surface 与 overlay 各自提供独立的阴影。
**之前:**
```css
--panel-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.3) inset, 0 2px 8px 0 rgba(0, 0, 0, 0.08);
--field-shadow: 0 0 0 0 rgba(255, 255, 255, 0.1) inset, 0 1px 2px 0 rgba(0, 0, 0, 0.05);
```
**之后(浅色模式):**
```css
--surface-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.04), 0 1px 2px 0 rgba(0, 0, 0, 0.06), 0 0 1px 0 rgba(0, 0, 0, 0.06);
--overlay-shadow: 0 4px 16px 0 rgba(24, 24, 27, 0.08), 0 8px 24px 0 rgba(24, 24, 27, 0.09);
--field-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.04), 0 1px 2px 0 rgba(0, 0, 0, 0.06), 0 0 1px 0 rgba(0, 0, 0, 0.06);
```
**之后(深色模式):**
```css
--surface-shadow: 0 0 0 0 transparent inset; /* No shadow on dark mode */
--overlay-shadow: 0 0 0 0 transparent inset; /* No shadow on dark mode */
--field-shadow: 0 0 0 0 transparent inset; /* Transparent shadow to allow ring utilities to work */
```
#### 强调色更新
强调色经过更新,对比度与视觉吸引力都有所提升。
**之前:**
```css
--accent: var(--color-neutral-950);
--accent-foreground: var(--snow);
```
**之后:**
```css
--accent: oklch(0.6204 0.195 253.83);
--accent-foreground: var(--snow);
```
#### 状态颜色优化
success、warning 与 danger 颜色经过优化,一致性与对比度都更佳。
**Success:**
* **之前:** `oklch(0.5503 0.1244 153.56)`
* **之后:** `oklch(0.7329 0.1935 150.81)`
* 浅色模式下,前景色从 `var(--snow)` 改为 `var(--eclipse)`
**Warning:**
* **之前:** `oklch(0.7186 0.1521 64.85)`
* **之后:** `oklch(0.7819 0.1585 72.33)`(浅色),`oklch(0.8203 0.1388 76.34)`(深色)
**Danger:**
* **之前:** `oklch(0.6259 0.1908 29.19)`
* **之后:** `oklch(0.6532 0.2328 25.74)`(浅色),`oklch(0.594 0.1967 24.63)`(深色)
### 组件 API 变更
#### Chip 组件
Chip 组件的 `type` prop 已重命名为 `color`,同时新增 `size` prop,并引入了新的 `soft` 变体。
**之前:**
```tsx
import { Chip } from "@heroui/react";
Label
```
**之后:**
```tsx
import { Chip } from "@heroui/react";
Label
```
**迁移:**
* 将 `type` prop 替换为 `color` prop
* 使用 `size` prop(`sm`、`md`、`lg`)控制 Chip 尺寸
* `soft` 变体提供低调的外观,适用于不那么突出的 Chip
#### Link 组件
Link 组件现在支持 `underline` 与 `underlineOffset` prop,并加入了对 `asChild` 的支持。
**之前:**
```tsx
import { Link } from "@heroui/react";
Link text
```
**之后:**
```tsx
import { Link } from "@heroui/react";
Link text
```
**新增 prop:**
* `underline`:`"none" | "hover" | "always"` —— 控制下划线的可见性
* `underlineOffset`:`number` —— 控制下划线相对文本的偏移
#### 类型引用语法
由于采用了双模式实现,通过命名空间语法引用类型的方式不再支持。请改用对象样式语法或具名类型导入。
**之前(不再可用):**
```tsx
type AvatarProps = Avatar.RootProps
```
**之后(方式 1 —— 对象样式语法):**
```tsx
type AvatarProps = Avatar["RootProps"]
```
**之后(方式 2 —— 具名类型导入,推荐):**
```tsx
import type { AvatarRootProps } from "@heroui/react"
type AvatarProps = AvatarRootProps
```
此变更会影响访问 prop 类型的所有复合组件。
#### Tabs 组件重命名
为保持一致性,Tabs 组件的包装元素已重命名:
* **复合属性**:`Tabs.ListWrapper` → `Tabs.ListContainer`
* **命名导出**:`TabListWrapper` → `TabListContainer`
* **CSS 类**:`.tabs__list-wrapper` → `.tabs__list-container`
* **data 属性**:`data-slot="tabs-list-wrapper"` → `data-slot="tabs-list-container"`
**迁移:**
请查找并替换所有 `TabListWrapper`,将其改为 `TabListContainer`:
```bash
# Component usage
TabListWrapper → TabListContainer
Tabs.ListWrapper → Tabs.ListContainer
# CSS selectors (if using custom styles)
.tabs__list-wrapper → .tabs__list-container
[data-slot="tabs-list-wrapper"] → [data-slot="tabs-list-container"]
```
#### 已移除的变量
以下变量已被移除:
* `--panel` → 改用 `--surface` 或 `--overlay`
* `--panel-foreground` → 改用 `--surface-foreground` 或 `--overlay-foreground`
* `--surface-1`、`--surface-2`、`--surface-3` → 改用背景色阶或 surface 层级
* `--accent-soft` → 改用 `--color-accent-soft`(现已自动计算)
* `--radius-panel` 与 `--radius-panel-inner` → 改用标准的 radius 取值
## 设计系统更新
### 全新的色彩系统
#### Surface 与 Overlay 概念
设计系统现在区分两类带高度的组件:
* **Surface**:用于直接放置在页面上的非浮层组件,如 Card、Accordion、Disclosure Group
* **Overlay**:用于浮在页面之上的浮层组件,如 Tooltip、Popover、Modal、Menu
这种区分带来:
* 更好的视觉层级
* 更合适的阴影深度
* 更优的深色模式对比度
* 更清晰的组件语义
#### 自动计算的色彩系统
HeroUI 现在使用 CSS `color-mix` 自动计算各种色阶以及柔和色变体。你只需声明基础颜色,剩下的交给 HeroUI 处理。
**背景色阶**
背景色阶会自动从 `--background` 计算:
```css
/* You only declare the base */
--background: oklch(0.9702 0 0);
--foreground: var(--eclipse);
/* HeroUI automatically calculates these */
--color-background-secondary: color-mix(in oklab, var(--color-background) 96%, var(--color-foreground) 4%);
--color-background-tertiary: color-mix(in oklab, var(--color-background) 92%, var(--color-foreground) 8%);
--color-background-quaternary: color-mix(in oklab, var(--color-background) 86%, var(--color-foreground) 14%);
```
**Surface 层级**
Surface 各层级会自动从 `--surface` 计算:
```css
/* You only declare the base */
--surface: var(--white);
--surface-foreground: var(--foreground);
/* HeroUI automatically calculates these */
--color-surface-secondary: color-mix(in oklab, var(--surface) 94%, var(--surface-foreground) 6%);
--color-surface-tertiary: color-mix(in oklab, var(--surface) 92%, var(--surface-foreground) 8%);
--color-surface-quaternary: color-mix(in oklab, var(--surface) 86%, var(--surface-foreground) 14%);
```
**柔和色变体**
柔和色变体会自动从状态色计算:
```css
/* You declare the base status colors */
--accent: oklch(0.6204 0.195 253.83);
--danger: oklch(0.6532 0.2328 25.74);
--warning: oklch(0.7819 0.1585 72.33);
--success: oklch(0.7329 0.1935 150.81);
/* HeroUI automatically calculates these at 15% opacity */
--color-accent-soft: color-mix(in oklab, var(--color-accent) 15%, transparent);
--color-danger-soft: color-mix(in oklab, var(--color-danger) 15%, transparent);
--color-warning-soft: color-mix(in oklab, var(--color-warning) 15%, transparent);
--color-success-soft: color-mix(in oklab, var(--color-success) 15%, transparent);
```
每个柔和色变体都包含悬停态(20% 不透明度)以及对应的前景色,以保证合适的对比度。
**自定义:**
可以通过 Tailwind 的 `@theme` 指令覆盖任意自动计算结果:
```css
@theme inline {
/* Adjust surface levels */
--color-surface-secondary: color-mix(in oklab, var(--surface) 96%, var(--surface-foreground) 4%);
/* Adjust soft colors */
--color-accent-soft: color-mix(in oklab, var(--color-accent) 20%, transparent);
}
```
这套自动计算系统减少了你需要管理的变量数量,同时在需要时仍能完全自定义。
### 阴影系统
阴影系统经过重新设计,提供:
* 为 surface 与 overlay 提供各自独立的阴影
* 更好的层次感
* 深色模式支持(透明阴影)
* 一致的字段阴影
阴影会自动适配浅色与深色模式,为每种主题提供合适的层次提示。
### 焦点系统
焦点颜色现在使用强调色,以保持一致性:
```css
--focus: var(--accent);
```
这样既能让焦点指示器与你的品牌色一致,也能保留无障碍能力。
### 排版 token
部分与排版相关的变量已被移除,转而推荐直接使用 Tailwind 的排版工具类。设计系统现在专注于颜色与间距 token,将排版交给 Tailwind 处理。
## 迁移指南
### 第 1 步:更新设计系统变量
将旧的 panel 变量替换为 surface / overlay:
```css
/* Before */
.my-card {
background: var(--panel);
box-shadow: var(--shadow-panel);
}
/* After */
.my-card {
background: var(--surface);
box-shadow: var(--shadow-surface);
}
.my-tooltip {
background: var(--overlay);
box-shadow: var(--shadow-overlay);
}
```
### 第 2 步:更新 surface 层级
Surface 层级现在会自动从 `--surface` 计算得出,因此无需手动声明。直接使用新的工具类即可:
```css
/* Before */
.bg-surface-1 → .bg-surface (base surface)
.bg-surface-2 → .bg-surface-secondary (auto-calculated)
.bg-surface-3 → .bg-surface-tertiary (auto-calculated)
/* You can also use background shades */
.bg-surface-2 → .bg-background-secondary (auto-calculated from --background)
.bg-surface-3 → .bg-background-tertiary (auto-calculated from --background)
```
**说明:** Surface 层级(`surface-secondary`、`surface-tertiary` 等)会基于你的 `--surface` 颜色自动计算。除非你想自定义计算方式,否则不需要手动声明任何 CSS 变量。
### 第 3 步:更新组件 props
更新 Chip 与 Link 组件:
```tsx
// Chip: type → color, add size if needed
→
// Link: Add underline props if customizing underlines
Text // Still works, underline props are optional
```
### 第 4 步:简化组件写法(可选)
如果你在 v3.0.0-alpha.35 中已经采用了 `.Root` 后缀,现在可以将其移除以简化代码:
**之前(v3.0.0-alpha.35):**
```tsx
JD
```
**之后(更简洁):**
```tsx
JD
```
**说明:** 如果你更喜欢 `.Root` 写法,它仍然可用。
### 第 5 步:更新类型引用
如果你之前用命名空间语法来引用类型,请改用对象样式语法或具名导入:
**之前:**
```tsx
type ButtonProps = Button.RootProps
```
**之后(方式 1 —— 对象样式):**
```tsx
type ButtonProps = Button["RootProps"]
```
**之后(方式 2 —— 具名导入,推荐):**
```tsx
import type { ButtonRootProps } from "@heroui/react"
type ButtonProps = ButtonRootProps
```
### 第 6 步:更新 Tabs 组件
将 `TabListWrapper` 替换为 `TabListContainer`:
**之前:**
```tsx
import { Tabs } from "@heroui/react"
Home
Content
```
**之后:**
```tsx
import { Tabs } from "@heroui/react"
Home
Content
```
### 第 7 步:处理边框相关变更
如果你的自定义样式依赖默认边框:
```css
/* Add explicit borders where needed */
.my-component {
border-width: 1px;
border-color: var(--color-border);
}
```
### 第 8 步:更新状态颜色
如果你曾经定制过状态颜色,请查阅新的取值并按需调整你的自定义主题:
```css
/* Check if your custom status colors need updates */
--success: oklch(0.7329 0.1935 150.81); /* New value */
--warning: oklch(0.7819 0.1585 72.33); /* New value */
--danger: oklch(0.6532 0.2328 25.74); /* New value */
```
### 自动化迁移
对于较大的代码库,可以借助查找 / 替换:
```bash
# Panel → Surface
--panel → --surface
bg-panel → bg-surface
shadow-panel → shadow-surface
# Panel → Overlay (for floating components)
--panel → --overlay (where appropriate)
bg-panel → bg-overlay (for tooltips, popovers, etc.)
shadow-panel → shadow-overlay (for floating components)
# Chip type prop
type=" → color="
# Surface levels
bg-surface-1 → bg-surface
bg-surface-2 → bg-surface-secondary
bg-surface-3 → bg-surface-tertiary
# Tabs component
TabListWrapper → TabListContainer
Tabs.ListWrapper → Tabs.ListContainer
# Type references
Component.RootProps → Component["RootProps"] or use named imports
```
## 组件更新
### Card 组件
Card 组件经过优化,变体更丰富、语义结构更合理。该组件现已使用新的 surface 系统,样式更加一致。
### Accordion 组件
Accordion 现在也使用 surface 系统,与其他组件在视觉上更加一致。
### 表单组件
表单组件(Input、TextField、TextArea)已更新为使用新的字段边框系统(默认透明),在保留无障碍能力的前提下呈现更简洁的外观。
### 组件模式更新
所有组件现在都支持灵活的写法。支持双模式的组件包括:
* **简单组件**:Button、Link、Spinner、Chip、Kbd
* **复合组件**:Accordion、Avatar、Card、Disclosure、Fieldset、Popover、RadioGroup、Switch、Tabs、Tooltip
以上所有组件都可以使用三种写法中的任意一种:不带 `.Root` 的复合写法、带 `.Root` 的复合写法,或具名导出。
## HeroUI Pro
HeroUI Pro 正基于全新的设计系统从零进行重塑。新版 Pro 将带来:
* 基于 HeroUI v3 构建的新组件
* Tailwind CSS v4 原生支持
* 基于 CSS 的原生动画
* 更强的可定制能力
我们将很快分享更多更新。
## 路线图
我们正以发布稳定版本为目标,计划在 2025 年的 **第四季度** 完成。本次 beta 让我们距离这个目标更进一步:
* 更完整的组件集合
* 更精炼的设计系统
* 更佳的开发者体验
* 更好的性能
## 社区
Native 端的反响非常热烈。感谢你在我们打造 HeroUI v3 的过程中给予的支持!你的反馈让我们每一天都在变得更好。
来看看社区的声音:[HeroUI Native 用户反响](https://x.com/hero_ui/status/1985721976220966926)
## 链接
* [组件文档](/docs/react/components)
* [设计系统 - Figma Kit v3](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
* [HeroUI Native](https://link.heroui.com/native)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [GitHub PR #5872](https://github.com/heroui-inc/heroui/pull/5872)
## 贡献者
感谢每一位为本次发布做出贡献的开发者,是你们让我们打造出了一套既美观又实用的设计系统!
# v3.0.0-beta.2
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-0-beta-2
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-0-beta-2.mdx
> 六个新组件(AlertDialog、ComboBox、Dropdown、InputGroup、Modal、NumberField)、Select API 改进以及多项组件优化。
2025 年 11 月 20 日
此版本引入了六个重要的新组件,改进了 Select 组件的 API,并包含多项优化与 bug 修复。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@beta @heroui/react@beta
```
```bash
pnpm add @heroui/styles@beta @heroui/react@beta
```
```bash
yarn add @heroui/styles@beta @heroui/react@beta
```
```bash
bun add @heroui/styles@beta @heroui/react@beta
```
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增功能
### 新组件
本次发布引入了 **6 个** 新的基础组件:
* **[AlertDialog](#alert-dialog)**:用于需要用户确认的重要决策的模态对话框。([文档](/docs/components/alert-dialog))
* **[ComboBox](#combo-box)**:将文本输入与列表框结合,让用户可以在选项列表中过滤。([文档](/docs/components/combo-box))
* **[Dropdown](#dropdown)**:展示一组可供用户选择的操作或选项。([文档](/docs/components/dropdown))
* **[InputGroup](#inputgroup)**:通过 prefix 与 suffix 元素将相关输入控件组合在一起,强化表单字段。([文档](/docs/components/input-group))
* **[Modal](#modal)**:用于聚焦用户交互与重要内容的对话框浮层。([文档](/docs/components/modal))
* **[NumberField](#numberfield)**:数字输入框,带有递增 / 递减按钮、表单校验以及国际化格式化。([文档](/docs/components/number-field))
### AlertDialog
```tsx
"use client";
import {AlertDialog, Button} from "@heroui/react";
export function Default() {
return (
删除项目
要永久删除项目吗?
此操作将永久删除 我的精彩项目 及其全部数据,且无法撤销。
取消
删除项目
);
}
```
### ComboBox
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function Default() {
return (
最喜欢的动物
土豚
猫
狗
袋鼠
熊猫
蛇
);
}
```
### Dropdown
```tsx
"use client";
import {Button, Dropdown, Label} from "@heroui/react";
export function Default() {
return (
操作
console.log(`Selected: ${key}`)}>
新建文件
复制链接
编辑文件
删除文件
);
}
```
### Modal
```tsx
"use client";
import {Rocket} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
export function Default() {
return (
打开模态框
欢迎使用 HeroUI
一套美观、快速、现代的 React UI 库,可轻松构建无障碍且高度可定制的 Web 应用。
继续
);
}
```
### InputGroup
```tsx
"use client";
import {Globe} from "@gravity-ui/icons";
import {InputGroup, Label, TextField} from "@heroui/react";
export function WithIconPrefixAndTextSuffix() {
return (
网站
.com
);
}
```
### NumberField
```tsx
import {Label, NumberField} from "@heroui/react";
export function Basic() {
return (
宽度
);
}
```
### 样式改进
#### 自定义变体与主题兼容性
增强了 CSS 变体与主题系统,提供更好的可定制性:
**动效偏好:**
* 新增 `motion-safe` 变体,可与 `data-reduce-motion="true"` 属性配合使用
* 增强后的 `motion-reduce` 现在支持祖先元素与伪元素
**深色模式:**
* 类与 `data-theme="dark"` 属性选择器现在优先于 `prefers-color-scheme`
* 在深色模式下完整支持伪元素
**主题变量:**
* 扩展了浅色主题的覆盖范围,以支持嵌套主题(`:root`、`.light`、`.default`、`[data-theme="light"]`、`[data-theme="default"]`)
### 组件改进
#### Select 组件 API 更新
Select 组件的 API 已经过改进,与其他组件保持一致。`Content` 子组件已重命名为 `Popover`。
**之前:**
```tsx
{/* items */}
```
**之后:**
```tsx
{/* items */}
```
#### Chip 组件改进
Chip 组件的尺寸已更新,以提升一致性:
* **小(`sm`)**:`px-1 py-0 text-xs`
* **中(`md`)**:`text-xs`(现在显式设置)
* **大(`lg`)**:`px-3 py-1 text-sm font-medium`
#### Separator 组件增强
Separator 组件现在能自动检测是否被放置在 surface 组件中(使用 `bg-surface`),并应用合适的分隔线颜色以获得更好的可见性。同时新增了 `isOnSurface` prop,用于手动控制。
**新增的计算变量:**
* `--color-separator-on-surface`:通过 `color-mix` 自动生成的计算变量,确保分隔线在 surface 背景上仍然清晰可见。与其他计算变量一样,可在你的主题中覆盖它。
**用法:**
```tsx
```
当 Separator 检测到外层存在 `SurfaceContext` Provider(由 Card、Alert、Popover、Modal 等组件提供)时,`isOnSurface` prop 会自动启用。
你也可以直接在 Tailwind 类中使用这个计算变量:
```tsx
```
#### 动画改进
* 更新了加载状态 spinner 的颜色,提升可见性
* 调整了 Select 与 Slider 组件的样式,改进动画效果
* 改进了 Checkbox 动画(过渡更快)
* 在伪元素中更好地支持 `prefers-reduced-motion`
## ⚠️ 破坏性变更
### Select 组件
为与 ComboBox、Dropdown 等组件保持一致,`Select.Content` 子组件已重命名为 `Select.Popover`。
**迁移:**
将所有 `Select.Content` 替换为 `Select.Popover`:
```tsx
// Before
...
// After
...
```
**类型导入:**
```tsx
// Before
import type { SelectContentProps } from "@heroui/react"
// After
import type { SelectPopoverProps } from "@heroui/react"
```
**命名导出:**
```tsx
// Before
import { SelectContent } from "@heroui/react"
// After
import { SelectPopover } from "@heroui/react"
```
### CSS 变量与工具类:divider → separator
为与 Separator 组件名保持一致,所有与 `divider` 相关的 CSS 变量与工具类均已重命名为 `separator`。
**CSS 变量:**
```css
/* Before */
border-bottom: 1px solid var(--divider);
/* After */
border-bottom: 1px solid var(--separator);
```
**Tailwind 工具类:**
```tsx
// Before
// After
```
**主题覆盖:**
如果你的自定义主题中覆盖了 separator 相关变量,请同步更新:
```css
/* Before */
:root {
--divider: oklch(92% 0.004 286.32);
}
.dark {
--divider: oklch(22% 0.006 286.033);
}
/* After */
:root {
--separator: oklch(92% 0.004 286.32);
}
.dark {
--separator: oklch(22% 0.006 286.033);
}
```
## Bug 修复
* 修复了加载状态 spinner 的颜色,提升可见性
* 修复了 bordered 状态下焦点样式优先于 hover 状态的表现
* 修复了文档中的动画卡顿问题
* 改进了模态表单的样式
* 增强了 motion-reduce 在伪元素上的支持
* 修复了移动端触摸交互后悬停状态保留的问题——将 hover 样式包裹在 `@media (hover: hover)` 媒体查询中。同时通过移除不必要的 `="true"` data 属性值选择器,简化了相关代码。
## 链接
* [组件文档](/docs/react/components)
* [设计系统 - Figma Kit v3(已更新)](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [GitHub PR #5885](https://github.com/heroui-inc/heroui/pull/5885)
## 贡献者
感谢每一位为本次发布做出贡献的开发者!
# v3.0.0-beta.3
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-0-beta-3
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-0-beta-3.mdx
> 七个新组件、fullWidth 与 hideSeparator 支持、样式修复,以及 AlertDialog / Modal backdrop 变体调整与移除 asChild prop 等破坏性变更。
2025 年 12 月 19 日
此版本引入了七个新组件([ButtonGroup](/docs/components/button-group)、[DateField](/docs/components/date-field)、[ErrorMessage](/docs/components/error-message)、[ScrollShadow](/docs/components/scroll-shadow)、[SearchField](/docs/components/search-field)、[TagGroup](/docs/components/tag-group)、[TimeField](/docs/components/time-field)),为表单组件添加 `fullWidth` 支持,为 [Tabs](/docs/components/tabs)、[ButtonGroup](/docs/components/button-group) 与 [Accordion](/docs/components/accordion) 引入 `hideSeparator`,包含若干样式修复,以及 ⚠️ **破坏性变更**:移除 `asChild` prop,并更新了 [AlertDialog](/docs/components/alert-dialog) 与 [Modal](/docs/components/modal) 的 backdrop 变体。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@beta @heroui/react@beta
```
```bash
pnpm add @heroui/styles@beta @heroui/react@beta
```
```bash
yarn add @heroui/styles@beta @heroui/react@beta
```
```bash
bun add @heroui/styles@beta @heroui/react@beta
```
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增功能
### 新组件
本次发布引入了 **7 个** 新的基础组件:
* **[ButtonGroup](#button-group)**:以一致的样式与间距将相关按钮分组。([文档](/docs/components/button-group))
* **[DateField](#date-field)**:日期输入字段,支持 label、description 与表单校验,基于 React Aria DateField 构建。([文档](/docs/components/date-field))
* **[ErrorMessage](#error-message)**:底层的错误信息组件,用于在非表单组件中展示错误。([文档](/docs/components/error-message))
* **[ScrollShadow](#scroll-shadow)**:通过视觉阴影提示可滚动内容溢出,并可自动检测滚动位置。([文档](/docs/components/scroll-shadow))
* **[SearchField](#search-field)**:带有内置搜索图标与清除按钮的搜索输入字段。([文档](/docs/components/search-field))
* **[TagGroup](#tag-group)**:一组可聚焦的标签,支持键盘导航、选择与删除。([文档](/docs/components/tag-group))
* **[TimeField](#time-field)**:时间输入字段,支持 label、description 与表单校验,基于 React Aria TimeField 构建。([文档](/docs/components/time-field))
### ButtonGroup
```tsx
import {
ChevronDown,
ChevronLeft,
ChevronRight,
CodeFork,
Ellipsis,
Picture,
Pin,
QrCode,
Star,
TextAlignCenter,
TextAlignJustify,
TextAlignLeft,
TextAlignRight,
ThumbsDown,
ThumbsUp,
Video,
} from "@gravity-ui/icons";
import {Button, ButtonGroup, Chip, Description, Dropdown, Label} from "@heroui/react";
export function Basic() {
return (
{/* 单个按钮与下拉菜单 */}
合并拉取请求
创建合并提交
此分支上的所有提交都将加入基础分支
压缩并合并
此分支上的 14 个提交将合并为一次提交并加入基础分支
变基并合并
此分支上的 14 个提交将变基后加入基础分支
{/* 独立按钮 */}
复刻
24
扫码支付
2.4K
星标
104
已置顶
{/* 上一页 / 下一页 */}
上一页
下一页
{/* 内容类型选择 */}
{/* 文本对齐 */}
左对齐
居中
右对齐
{/* 仅图标:对齐 */}
);
}
```
### DateField
```tsx
"use client";
import {DateField, Label} from "@heroui/react";
export function Basic() {
return (
日期
{(segment) => }
);
}
```
### ErrorMessage
```tsx
"use client";
import type {Key} from "@heroui/react";
import {Description, ErrorMessage, Label, Tag, TagGroup} from "@heroui/react";
import {useMemo, useState} from "react";
export function ErrorMessageBasic() {
const [selected, setSelected] = useState>(new Set());
const isInvalid = useMemo(() => Array.from(selected).length === 0, [selected]);
return (
setSelected(keys)}
>
必选分类
新闻
旅游
游戏
购物
请至少选择一个分类
{!!isInvalid && <>请至少选择一个分类>}
);
}
```
### SearchField
```tsx
import {Label, SearchField} from "@heroui/react";
export function Basic() {
return (
搜索
);
}
```
### ScrollShadow
```tsx
import {Card, ScrollShadow} from "@heroui/react";
const images = [
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/robot1.jpeg",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/avocado.jpeg",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/oranges.jpeg",
];
export default function Orientation() {
const getRandomImage = (idx: number) => {
return images[idx % images.length];
};
return (
垂直
{Array.from({length: 10}).map((_, idx) => (
段落 {idx + 1}:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
pulvinar risus non risus hendrerit venenatis. Pellentesque sit amet hendrerit
risus, sed porttitor quam. Morbi accumsan cursus enim, sed ultricies sapien.
))}
水平
{Array.from({length: 10}).map((_, idx) => (
连接未来
今天 18:30
))}
);
}
```
### TagGroup
```tsx
"use client";
import {PlanetEarth, Rocket, ShoppingBag, SquareArticle} from "@gravity-ui/icons";
import {Tag, TagGroup} from "@heroui/react";
export function TagGroupBasic() {
return (
资讯
旅行
游戏
购物
);
}
```
### TimeField
```tsx
"use client";
import {Label, TimeField} from "@heroui/react";
export function Basic() {
return (
时间
{(segment) => }
);
}
```
### 全宽支持
为表单与输入组件新增 `fullWidth` 支持,可以让它们撑满容器的整个宽度。这在构建一致的表单布局与响应式设计时尤其有用。
**支持的组件:**
* [ButtonGroup](/docs/components/button-group)
* [Button](/docs/components/button)
* [ComboBox](/docs/components/combo-box)
* [DateField](/docs/components/date-field)
* [DateInputGroup](/docs/components/date-input-group)
* [InputGroup](/docs/components/input-group)
* [Input](/docs/components/input)
* [NumberField](/docs/components/number-field)
* [SearchField](/docs/components/search-field)
* [Select](/docs/components/select)
* [TextField](/docs/components/text-field)
* [TextArea](/docs/components/textarea)
* [TimeField](/docs/components/time-field)
## 组件改进
### 分隔线控制增强
为 [Tabs](/docs/components/tabs)、[ButtonGroup](/docs/components/button-group) 与 [Accordion](/docs/components/accordion) 组件新增 `hideSeparator` 支持,可隐藏条目之间的分隔线,呈现更简洁、更纯粹的外观。
**Tabs:**
```tsx
Overview
Analytics
```
**ButtonGroup:**
```tsx
First
Second
Third
```
**Accordion:**
```tsx
Item 1
Content
```
### 文档图标集成
将 [@gravity-ui/icons](https://github.com/gravity-ui/icons) 集成到文档组件中,统一图标渲染,同时改进了 SSR 支持并提升了性能。
## 依赖更新
### React Aria Components v1.14.0
将 [React Aria Components](https://react-aria.adobe.com/releases/v1-14-0) 升级到 v1.14.0。本次升级包含:
**增强:**
* SearchField:新增 `isReadOnly` 与 `isRequired` 渲染属性
* Tooltip:新增 `shouldCloseOnPress` 属性
* Tabs:支持在 tab 面板之间进行动画过渡
* 其他:`useControlledState` 现已在 `setState` 回调中提供支持
**修复:**
* ComboBox:修复 VoiceOver 不读取 ListBox 项 `aria-label` 的问题
* 日期与时间:增强了对 absolute 日期与日期时间字符串的错误处理
* NumberField:在移动端滚动时不再误触发递增 / 递减
* Overlay:修复了设置 boundary container 时 overlay 定位与 flip 的问题
* Table:修复了在键盘导航期间进行拖放时的崩溃问题
* 其他多项 bug 修复与改进
完整变更请参阅 [React Aria Components v1.14.0 发布说明](https://react-aria.adobe.com/releases/v1-14-0)。
### 其他依赖升级
* `@internationalized/date`:3.10.0 → 3.10.1
* `@radix-ui/react-avatar`:1.1.10 → 1.1.11
* `tailwind-merge`:3.3.1 → 3.4.0
* `tailwind-variants`:3.1.1 → 3.2.2
## 样式修复
### 表单组件的禁用状态
修复了 [Input](/docs/components/input) 与 [TextArea](/docs/components/textarea) 组件的禁用状态样式。
### 样式优化
* **提高选择器精确度**:增强 CSS 选择器特异性,让样式隔离更好、性能更优
* **动画增强**:改进了多个组件的动画性能与流畅度
* **新增 no-highlight 工具类**:新增 `no-highlight` 工具类,用于防止交互元素中的文字被选中,从而提升体验
* **优化 will-change 属性**:在多个组件中调整 `will-change` CSS 属性,以获得更好的动画性能
* **移除全局滚动条样式**:移除了全局滚动条样式,避免与自定义滚动条实现冲突,并修复了 modal / overlay 的交互问题
## ⚠️ 破坏性变更
### AlertDialog 与 Modal 的 backdrop 变体
`backdropVariant` / `variant` prop 的取值已从 `"solid"` 重命名为 `"opaque"`,以提升语义清晰度——「opaque」(不透明)更准确地描述了遮罩的视觉外观。
**迁移:**
将 AlertDialog 中所有 `backdropVariant="solid"` 替换为 `backdropVariant="opaque"`,将 Modal 中所有 `variant="solid"` 替换为 `variant="opaque"`:
```tsx
// Before
{/* content */}
{/* content */}
// After
{/* content */}
{/* content */}
```
**可用的 backdrop 变体:**
* `"opaque"` —— 深色不透明遮罩,完全遮挡背景(即此前的 `"solid"`)
* `"blur"` —— 模糊遮罩,柔和地遮挡背景
* `"transparent"` —— 透明遮罩,保持背景可见
### 移除 `asChild` prop
为提供更清晰的 API、更强的类型安全性以及更简单的使用方式,组件中的 `asChild` 模式已被移除。
关于组件组合模式的更多细节,请参阅 [组合指南](/docs/handbook/composition)。
## Bug 修复
* 修复了 `isInvalid` 样式在 surface 背景上使用相关组件时的表现
* 修复了 AlertDialog 与 Modal 关闭后重新渲染的问题
* 修复了浮层关闭时未能正确清理的问题
* 修复了文档中 Storybook 链接与导航的问题
## 链接
* [组件文档](/docs/react/components)
* [设计系统 - Figma Kit v3(已更新)](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [GitHub PR #5923](https://github.com/heroui-inc/heroui/pull/5923)
## 贡献者
感谢每一位为本次发布做出贡献的开发者!
# v3.0.0-beta.4
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-0-beta-4
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-0-beta-4.mdx
> 全新的主题构建器、三个新组件(Autocomplete、Breadcrumbs、Toast)、Tabs 的 secondary 变体、Input / InputGroup 变体,以及多项改进。
2026 年 1 月 20 日
**已修复关键构建问题**:此版本(beta.4)存在一个关键构建问题,已在 **beta.5** 中修复。请升级到 `@heroui/styles@3.0.0-beta.5` 与 `@heroui/react@3.0.0-beta.5`,以确保 TypeScript 声明文件能正确生成、导出能正确解析。
此版本引入了用于可视化主题定制的全新 [主题构建器](/themes),三个新组件([Autocomplete](/docs/components/autocomplete)、[Breadcrumbs](/docs/components/breadcrumbs)、[Toast](/docs/components/toast)),为 [Tabs](/docs/components/tabs) 添加 secondary 变体,为 [Input](/docs/components/input) 与 [InputGroup](/docs/components/input-group) 添加 primary / secondary 变体,InputGroup 新增对 TextArea 的支持,以及 ⚠️ **破坏性变更**:移除 Link 的下划线变体,并从表单组件中移除 `isInSurface` prop。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@beta @heroui/react@beta
```
```bash
pnpm add @heroui/styles@beta @heroui/react@beta
```
```bash
yarn add @heroui/styles@beta @heroui/react@beta
```
```bash
bun add @heroui/styles@beta @heroui/react@beta
```
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增功能
### 主题构建器
我们很高兴推出 **[主题构建器](/themes)** —— 用于创建与定制 HeroUI 主题的强大可视化工具。可在实时预览中构建你专属的主题,并导出可直接使用的 CSS。
**主要特性:**
* **可视化颜色编辑**:通过 OKLCH 颜色选择器以及直观的亮度、色度、色相滑块来调整颜色
* **实时预览**:在实时组件预览中立即查看你的修改
* **自定义强调色**:定义你的品牌色,并观察它如何贯穿到所有组件
* **预设主题**:从精选预设(如 Default、Airbnb、Coinbase、Discord)入手
* **导出即可用**:生成 CSS 变量,直接复制到你的项目即可
* **浅色与深色模式**:可联动也可独立地同时定制两套主题
* **键盘快捷键**:支持撤销 / 重做以及快速操作,提升工作流效率
立即在 [v3.heroui.com/themes](/themes) 上试用。
### 新组件
本次发布共引入 **3 个** 新的基础组件:
* **[Autocomplete](#autocomplete)**:将 Select 与过滤功能结合,让用户可以在选项列表中搜索并选择。([文档](/docs/components/autocomplete))
* **[Breadcrumbs](#breadcrumbs)**:导航面包屑,用于展示当前页面在层级结构中的位置。([文档](/docs/components/breadcrumbs))
* **[Toast](#toast)**:用于展示临时通知与消息,支持自动关闭以及自定义放置位置。([文档](/docs/components/toast))
### Autocomplete
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
EmptyState,
Label,
ListBox,
SearchField,
Tag,
TagGroup,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export default function Default() {
const {contains} = useFilter({sensitivity: "base"});
const [selectedKeys, setSelectedKeys] = useState([]);
const items = [
{id: "florida", name: "Florida"},
{id: "delaware", name: "Delaware"},
{id: "california", name: "California"},
{id: "texas", name: "Texas"},
{id: "new-york", name: "New York"},
{id: "washington", name: "Washington"},
];
const onRemoveTags = (keys: Set) => {
setSelectedKeys((prev) => prev.filter((key) => !keys.has(key)));
};
return (
setSelectedKeys(keys as Key[])}
>
计划前往的州
{({defaultChildren, isPlaceholder, state}: any) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItemsKeys = state.selectedItems.map((item: any) => item.key);
return (
{selectedItemsKeys.map((selectedItemKey: Key) => {
const item = items.find((s) => s.id === selectedItemKey);
if (!item) return null;
return (
{item.name}
);
})}
);
}}
未找到结果 }>
{items.map((item) => (
{item.name}
))}
);
}
```
### Breadcrumbs
```tsx
"use client";
import {Breadcrumbs} from "@heroui/react";
export default function BreadcrumbsBasic() {
return (
首页
产品
电子产品
笔记本电脑
);
}
```
### Toast
该组件目前处于预览阶段,部分功能可能尚未按预期工作。
```tsx
"use client";
import {HardDrive, Persons} from "@gravity-ui/icons";
import {Button, toast} from "@heroui/react";
const noop = () => {};
export function Variants() {
return (
{
toast("您已被邀请加入团队", {
actionProps: {
children: "忽略",
onPress: () => toast.clear(),
variant: "tertiary",
},
description: "Bob 邀请您加入 HeroUI 团队",
indicator: ,
variant: "default",
});
}}
>
默认 Toast
toast.info("您还剩 2 个积分", {
actionProps: {children: "升级", onPress: noop},
description: "升级付费方案以获取更多积分",
})
}
>
强调 Toast
toast.success("您已升级方案", {
actionProps: {
children: "账单",
className: "bg-success text-success-foreground",
onPress: noop,
},
description: "您可以继续使用 HeroUI Chat",
})
}
>
成功 Toast
toast.warning("您的积分已用完", {
actionProps: {
children: "升级",
className: "bg-warning text-warning-foreground",
onPress: noop,
},
description: "升级付费方案以继续使用",
})
}
>
警告 Toast
toast.danger("存储空间已满", {
actionProps: {children: "删除", onPress: noop, variant: "danger"},
description: "删除文件以释放空间。此处增加更多文字以演示较长内容的显示效果",
indicator: ,
})
}
>
危险 Toast
);
}
```
## 组件改进
### Tabs 的 secondary 变体
为 [Tabs](/docs/components/tabs) 新增 `secondary` 变体,使用下划线指示器样式。该变体同时支持水平与垂直方向。
```tsx
import {Tabs} from "@heroui/react";
export function Secondary() {
return (
概览
分析
报告
查看项目概览与近期活动。
跟踪指标并分析性能数据。
生成并下载详细报告。
);
}
```
**用法:**
```tsx
Overview
Analytics
Content
Content
```
### Input 变体
为 [Input](/docs/components/input) 组件新增 `primary` 与 `secondary` 变体:
* **`primary`**(默认):带阴影的标准样式,适用于大多数场景
* **`secondary`**:不带阴影的低调变体,适合在 Surface 组件内部使用
```tsx
import {Input} from "@heroui/react";
export function Variants() {
return (
);
}
```
### InputGroup 增强
[InputGroup](/docs/components/input-group) 组件获得多项改进:
**TextArea 支持**:可使用 `InputGroup.TextArea` 来构建带有 prefix 与 suffix 的多行文本输入。
```tsx
"use client";
import {ArrowUp, At, Microphone, PlugConnection, Plus} from "@gravity-ui/icons";
import {Button, InputGroup, Kbd, Spinner, TextField, Tooltip} from "@heroui/react";
import {useState} from "react";
export function WithTextArea() {
const [value, setValue] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = () => {
if (!value.trim()) return;
setIsSubmitting(true);
setTimeout(() => {
setIsSubmitting(false);
setValue("");
}, 1000);
};
return (
添加上下文
setValue(event.target.value)}
/>
添加文件等
连接应用
语音输入
{({isPending}) => (isPending ? : )}
发送
);
}
```
**变体**:新增与 Input 组件相匹配的 `primary` 与 `secondary` 变体。
```tsx
import {Envelope} from "@gravity-ui/icons";
import {InputGroup, Label, TextField} from "@heroui/react";
export function Variants() {
return (
主要变体
次要变体
);
}
```
### Button 与 ButtonGroup 的 outline 变体
为 [Button](/docs/components/button) 与 [ButtonGroup](/docs/components/button-group) 同时新增 `outline` 变体,用于呈现描边样式。
```tsx
import {Button, ButtonGroup} from "@heroui/react";
export function OutlineVariant() {
return (
);
}
```
### AlertDialog 尺寸支持
为 [AlertDialog](/docs/components/alert-dialog) 组件新增尺寸支持,让你可以控制对话框的大小。
```tsx
"use client";
import {Rocket} from "@gravity-ui/icons";
import {AlertDialog, Button} from "@heroui/react";
const SIZE_LABELS = {
cover: "通栏",
lg: "大",
md: "中",
sm: "小",
xs: "超小",
} as const;
export function Sizes() {
const sizes = ["xs", "sm", "md", "lg", "cover"] as const;
return (
{sizes.map((size) => (
{SIZE_LABELS[size]}
尺寸:{SIZE_LABELS[size]}
{size === "cover" ? (
<>
此警告框使用 cover 尺寸:在移动端与桌面端保留边距(移动端约
16px、桌面端约
40px)铺满可视区域,仍保持圆角与标准内边距,适合需要最大宽度又保留对话框气质的关键确认。
>
) : (
<>
此警告框使用 {size}{" "}
尺寸。在移动端各尺寸都会接近全宽以便阅读;在桌面端则对应不同的最大宽度,以适配不同信息量。
>
)}
取消
确认
))}
);
}
```
### Checkbox 动画改进
为 [Checkbox](/docs/components/checkbox) 提供更快的动画与更粗的描边宽度,反馈更明显。
```tsx
import {Checkbox} from "@heroui/react";
export function Basic() {
return (
接受条款与条件
);
}
```
### Link 的文本装饰
[Link](/docs/components/link) 组件现在使用 Tailwind CSS 类来设置文本装饰,而不再依赖内置变体。这样既更灵活,也更贴合 Tailwind 的使用习惯。
**可用的 Tailwind 工具类:**
* `underline` —— 始终显示下划线
* `no-underline` —— 移除下划线
* `hover:underline` —— 仅在悬停时显示下划线
* `decoration-primary`、`decoration-secondary` 等 —— 设置下划线颜色
* `decoration-1`、`decoration-2`、`decoration-4` —— 控制下划线粗细
* `underline-offset-1`、`underline-offset-2` 等 —— 调整下划线偏移
```tsx
import {Link} from "@heroui/react";
export function LinkUnderlineAndOffset() {
return (
调整下划线偏移
偏移 1(1px 间距)
偏移 2(2px 间距)
偏移 3(3px 间距)
偏移 4(4px 间距)
);
}
```
## ⚠️ 破坏性变更
### Link 组件 —— 移除下划线相关变体
Link 组件内置的 `underline` 与 `underlineOffset` prop 已被移除。请改用 Tailwind CSS 类来控制文本装饰。
**之前:**
```tsx
Link text
```
**之后:**
```tsx
Link text
```
**可用的 Tailwind 类:**
* `underline`、`no-underline`、`hover:underline` —— 装饰线
* `decoration-primary`、`decoration-muted` 等 —— 装饰线颜色
* `decoration-solid`、`decoration-dashed`、`decoration-dotted` —— 装饰线样式
* `decoration-1`、`decoration-2`、`decoration-4` —— 装饰线粗细
* `underline-offset-1`、`underline-offset-2`、`underline-offset-4` —— 下划线偏移
详见 [Link 文档](/docs/components/link)。
### 表单组件 —— 移除 `isInSurface` prop
`isInSurface` prop 以及自动 surface 检测已从基于表单的组件中移除。当你将表单组件放置在 Surface、Card 或其他基于 Surface 的容器中时,请改用 `variant="secondary"`。
**之前:**
```tsx
{/* Input automatically detected surface context */}
```
**之后:**
```tsx
{/* Use variant="secondary" for surface backgrounds */}
```
**受影响的组件:**
* Input
* InputGroup
* TextField
* TextArea
* SearchField
* NumberField
* DateField
* TimeField
* Select
* ComboBox
* Autocomplete
`secondary` 变体提供不带阴影的低调样式,更适合在 surface 背景上使用。
## 样式修复
* **Button**:更新 secondary 按钮颜色,提升视觉一致性
* **Checkbox**:优化动画速度并加粗描边,反馈更明显(详见 [Checkbox 动画改进](#checkbox-animation-improvements))
* **Link**:更新装饰线样式与过渡时长
* **Focus Visible**:在 focus-visible 选择器中加入 `:not(:focus)`,避免与 hover 状态冲突
* **Separator**:将固定样式仅应用到水平方向的分隔线
## Bug 修复
* 修复使用按钮变体样式的 Link
* 修复 Safari 中 BEM 样式下 Fieldset 的 Flexbox 兼容性问题
* 修复 SearchField 在空状态时未正确禁用清除按钮的问题
* 修复 ButtonGroup 的 context 仅对直接子元素生效的问题
* 修复 ButtonGroup 中 `BUTTON_GROUP_CHILD` 重新导出的类型声明
## 依赖更新
### 直接从 React Aria Components 重新导出
HeroUI 现在直接从 `react-aria-components` 重新导出了一系列基元与工具,方便你访问。这些导出对于 [React Aria 框架配置](https://react-aria.adobe.com/frameworks) 尤其有用。
**Provider:**
* `RouterProvider` —— 配置 React Aria 的 Link 使用客户端路由器
* `I18nProvider` —— 设置 React Aria Components 使用的 locale
**Hook 与工具:**
* `isRTL` —— 检查某个 locale 是否为从右到左
* `useLocale` —— 访问当前 locale 与方向
* `useFilter` —— 对集合进行过滤与排序
**组件:**
* `Collection` —— 用于管理列表的集合组件
* `ListBoxLoadMoreItem` —— 用于加载更多条目的 ListBox 项
**国际化工具:**
* `getLocalizationScript` —— 获取用于服务端渲染的本地化脚本(来自 `react-aria-components/i18n`)
以上这些都可以直接从 `@heroui/react` 引入:
```tsx
import {
RouterProvider,
I18nProvider,
isRTL,
useLocale,
useFilter,
getLocalizationScript
} from "@heroui/react";
```
## 链接
* [主题构建器](/themes)
* [组件文档](/docs/react/components)
* [设计系统 - Figma Kit v3(已更新)](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [GitHub PR #6121](https://github.com/heroui-inc/heroui/pull/6121)
## 贡献者
感谢每一位为本次发布做出贡献的开发者!
# v3.0.0-beta.6
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-0-beta-6
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-0-beta-6.mdx
> 新增 6 个颜色组件(ColorPicker、ColorArea、ColorSlider、ColorField、ColorSwatch、ColorSwatchPicker)、Toast 改进,以及多项样式修复。
2026 年 2 月 6 日
本次发布引入了完整的**颜色系统**,新增六个用于颜色选择与处理的组件:[ColorPicker](/docs/components/color-picker)、[ColorArea](/docs/components/color-area)、[ColorSlider](/docs/components/color-slider)、[ColorField](/docs/components/color-field)、[ColorSwatch](/docs/components/color-swatch) 与 [ColorSwatchPicker](/docs/components/color-swatch-picker)。同时还包含 [Separator](/docs/components/separator) 的新变体以及多项样式改进。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@beta @heroui/react@beta
```
```bash
pnpm add @heroui/styles@beta @heroui/react@beta
```
```bash
yarn add @heroui/styles@beta @heroui/react@beta
```
```bash
bun add @heroui/styles@beta @heroui/react@beta
```
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增功能
### 颜色系统
我们很高兴推出完整的**颜色系统**——一整套用于颜色选择、处理与展示的组件。这些组件基于 React Aria 的颜色基元构建,可以无缝协同工作。
**主要特性:**
* **完整的色彩空间支持**:支持 HSL、HSB 与 RGB 色彩空间
* **基于通道的编辑**:可单独操作每一个颜色通道(hue、saturation、lightness、brightness、red、green、blue、alpha)
* **默认无障碍**:完整支持键盘导航与屏幕阅读器
* **可组合的设计**:自由搭配组件,构建你自己的颜色选择器
### 新组件
本次发布共引入 **6 个** 新的颜色组件:
* **[ColorPicker](#colorpicker)**:完整的颜色选择器,包含 trigger、popover 以及可组合的内部部件。([文档](/docs/components/color-picker))
* **[ColorArea](#colorarea)**:二维渐变区域,可同时选择两个颜色通道。([文档](/docs/components/color-area))
* **[ColorSlider](#colorslider)**:单通道滑块,用于精细调整颜色。([文档](/docs/components/color-slider))
* **[ColorField](#colorfield)**:用于输入与编辑颜色值的文本框。([文档](/docs/components/color-field))
* **[ColorSwatch](#colorswatch)**:可视化的颜色预览,支持透明度。([文档](/docs/components/color-swatch))
* **[ColorSwatchPicker](#colorswatchpicker)**:可选的颜色块网格,便于快速选择颜色。([文档](/docs/components/color-swatch-picker))
### ColorPicker
ColorPicker 是一个复合组件,将所有颜色组件组合在一起,提供完整的颜色选择体验。
```tsx
import {ColorArea, ColorPicker, ColorSlider, ColorSwatch, Label} from "@heroui/react";
export function Basic() {
return (
选择颜色
色相
);
}
```
### ColorArea
二维渐变区域,可同时选择两个颜色通道,通常用于 saturation 与 brightness。
```tsx
import {ColorArea} from "@heroui/react";
export function ColorAreaBasic() {
return (
);
}
```
### ColorSlider
用于调整单个颜色通道(如 hue、saturation、lightness 或 alpha)的滑块。
```tsx
import {ColorSlider, Label} from "@heroui/react";
export function Basic() {
return (
色相
);
}
```
**不同的通道:**
```tsx
"use client";
import {ColorSlider, ColorSwatch, Label} from "@heroui/react";
import {useState} from "react";
import {parseColor} from "react-aria-components";
export function Channels() {
const [color, setColor] = useState(parseColor("hsl(0, 100%, 50%)"));
return (
色相
饱和度
明度
当前颜色:{color.toString("hsl")}
);
}
```
### ColorField
用于直接输入颜色值的文本输入框,支持多种颜色格式。
```tsx
"use client";
import type {Color} from "@heroui/react";
import {ColorField, ColorSwatch, Label, parseColor} from "@heroui/react";
import {useState} from "react";
export function Basic() {
const [color, setColor] = useState(parseColor("#0485F7"));
return (
颜色
);
}
```
### ColorSwatch
颜色值的可视化展示,支持透明度图案。
```tsx
import {ColorSwatch} from "@heroui/react";
export function ColorSwatchBasic() {
return (
);
}
```
### ColorSwatchPicker
色块网格,可从预定义的调色板中快速选择颜色。
```tsx
import {ColorSwatchPicker} from "@heroui/react";
const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"];
export function Basic() {
return (
{colors.map((color) => (
))}
);
}
```
## 组件改进
### Toast 增强
[Toast](/docs/components/toast) 组件经过了重大改进,新增了多项功能并提升了稳定性(#6151):
**新功能:**
* **加载状态**:新增 `isLoading` prop,会显示一个 spinner 替代默认指示器
* **默认超时**:Toast 现在默认 4 秒后自动关闭(可通过 `timeout` prop 配置)
* **宽度控制**:在 `Toast.Provider` 上新增 `width` prop,可自定义 Toast 的宽度
* **自适应高度**:Toast 会根据内容自适应高度
* **更好的堆叠效果**:通过绝对定位与高度同步,修复了 Toast 堆叠时出现的布局抖动
* **更稳健的关闭处理**:将 `onClose` 回调延迟执行,避免 Toast 过渡死锁
* **仅最前 Toast 显示关闭按钮**:关闭按钮仅出现在最前一个 Toast 上,UI 更加干净
* **Promise 支持增强**:改进了 `toast.promise()`,加载状态与错误处理更加完善
**新增演示:**
* Promise 与加载状态
* 回调与超时处理
```tsx
"use client";
import {HardDrive, Persons} from "@gravity-ui/icons";
import {Button, toast} from "@heroui/react";
const noop = () => {};
export function Variants() {
return (
{
toast("您已被邀请加入团队", {
actionProps: {
children: "忽略",
onPress: () => toast.clear(),
variant: "tertiary",
},
description: "Bob 邀请您加入 HeroUI 团队",
indicator: ,
variant: "default",
});
}}
>
默认 Toast
toast.info("您还剩 2 个积分", {
actionProps: {children: "升级", onPress: noop},
description: "升级付费方案以获取更多积分",
})
}
>
强调 Toast
toast.success("您已升级方案", {
actionProps: {
children: "账单",
className: "bg-success text-success-foreground",
onPress: noop,
},
description: "您可以继续使用 HeroUI Chat",
})
}
>
成功 Toast
toast.warning("您的积分已用完", {
actionProps: {
children: "升级",
className: "bg-warning text-warning-foreground",
onPress: noop,
},
description: "升级付费方案以继续使用",
})
}
>
警告 Toast
toast.danger("存储空间已满", {
actionProps: {children: "删除", onPress: noop, variant: "danger"},
description: "删除文件以释放空间。此处增加更多文字以演示较长内容的显示效果",
indicator: ,
})
}
>
危险 Toast
);
}
```
### Separator 变体
为 [Separator](/docs/components/separator) 组件新增了变体,提供不同的视觉风格。
### Chip 组件 —— Label slot
[Chip](/docs/components/chip) 组件现在支持 `Chip.Label` 子组件,以获得更好的视觉对齐。当移除起始或末尾的内容(如图标)时,标签文字会过于贴近 Chip 的边缘。为了向后兼容,纯文本的 children 会自动被包裹在 `` 中。
**用法:**
```tsx
import { Chip } from '@heroui/react';
// Automatic wrapping (backward compatible)
Label text
// Explicit label with custom styling
Custom Label
// Mixing icons and labels
With Icon
```
## 样式修复
* **浮层内容**:修复了浮层内容上的模糊效果(#6136)
* **Invalid 字段**:在字段处于 invalid 状态时,将 ring 改为 outline(#6184)
* **Link 与按钮**:修复了使用按钮变体的 Link 组件的样式(#6138)
* **Toast 内容**:修复了 Toast 内容的垂直对齐问题(#6147)
* **Safari SVG**:修复了 SVG 在 Safari 中位置偏移的问题(#6149)
* **Placeholder 颜色**:将 placeholder 的颜色与输入文本对齐(#6139)
* **Tooltip**:从 tooltip 触发组件中移除了 cursor 样式
* **CSS 变量**:让计算变量仅依赖根变量(#6154)
## Bug 修复
* 修复了视图过渡期间页面交互不可用的问题(#6128)
* 修复了 Markdown URL 的格式化问题(#6162)
* 修复了指向 ComboBox 页面的链接错误(#6164)
* 修复了 `index.css` 中 Autocomplete 样式的引入顺序
* 修复了 CSS 类名的连字符格式(#6191)
## ⚠️ 破坏性变更
### Toast 组件 —— Container 重命名为 Provider
为提升语义清晰度,`Toast.Container` 已重命名为 `Toast.Provider`(#6151)。
**之前:**
```tsx
```
**之后:**
```tsx
```
**其他变更:**
* 默认的 `gap` prop 从 `14` 改为 `12` 像素
* 默认 `timeout` 现在为 `4000`(4 秒),无需再显式设置
* 为保持一致性,`Toast.Action` 已重命名为 `Toast.ActionButton`
### CSS 类名命名约定
为了一致性,CSS 类名已统一改为连字符格式(#6141)。这一调整更贴合 BEM 规范,也提升了与 Tailwind CSS 的兼容性。
**重要说明**:`textarea` 类名最初被改为 `text-area`,但由于与 Tailwind 原生的 `textarea` 类名冲突,已在 PR #6191 中回滚为 `textarea`。TextArea 组件相关的类名无需修改。
#### 组件类名变更
以下 CSS 类名已更新。如果你的自定义 CSS 直接使用了这些类名,请同步更新选择器:
| 组件 | 旧类名 | 新类名 | 说明 |
| ------------------ | -------------------------- | --------------------------- | ------------------------------ |
| **ComboBox** | `.combobox` | `.combo-box` | 全部相关类名同步更新 |
| | `.combobox__input-group` | `.combo-box__input-group` | |
| | `.combobox__trigger` | `.combo-box__trigger` | |
| | `.combobox__popover` | `.combo-box__popover` | |
| | `.combobox--full-width` | `.combo-box--full-width` | |
| **ListBox** | `.listbox` | `.list-box` | 全部相关类名同步更新 |
| **ListBoxItem** | `.listbox-item` | `.list-box-item` | 全部相关类名同步更新 |
| | `.listbox-item__indicator` | `.list-box-item__indicator` | |
| | `.listbox-item--default` | `.list-box-item--default` | |
| | `.listbox-item--danger` | `.list-box-item--danger` | |
| **ListBoxSection** | `.listbox-section` | `.list-box-section` | 全部相关类名同步更新 |
| **TextArea** | `.textarea` | `.textarea` | **未变更** —— 为避免与 Tailwind 冲突已回滚 |
#### 迁移指南
**之前:**
```css
/* Custom styles targeting old class names */
.combobox {
/* styles */
}
.listbox-item {
/* styles */
}
```
**之后:**
```css
/* Update to new hyphenated class names */
.combo-box {
/* styles */
}
.list-box-item {
/* styles */
}
```
**JavaScript / TypeScript 更新:**
如果你在 JavaScript 或 TypeScript 代码中使用了这些类名:
```tsx
// Before
// After
```
**说明**:组件 props 与 TypeScript 类型保持不变,仅 CSS 类名做了更新。
### 移除的 CSS 变量
作为 surface 颜色重构的一部分,部分 CSS 变量已被移除(#6204)。这些变量要么改为直接引用其他变量,要么被完全移除。
#### Surface 颜色变量
以下经过计算的 surface 颜色变量已被移除,并改为直接引用对应的变量:
**已移除:**
* `--color-surface-secondary`(之前通过 `color-mix` 计算得到)
* `--color-surface-tertiary`(之前通过 `color-mix` 计算得到)
**替代方案:**
这些变量现在直接引用 `variables.css` 中定义的基础变量:
* `--color-surface-secondary` → 直接使用 `var(--surface-secondary)`
* `--color-surface-tertiary` → 直接使用 `var(--surface-tertiary)`
基础变量 `--surface-secondary` 与 `--surface-tertiary` 现在直接定义在 `variables.css` 中,而不再在 `theme.css` 中通过计算得出。
#### On Surface 颜色变量
所有 `--color-on-surface-*` 变量都已被完全移除:
**已移除:**
* `--color-on-surface`
* `--color-on-surface-foreground`
* `--color-on-surface-hover`
* `--color-on-surface-focus`
* `--color-on-surface-secondary`
* `--color-on-surface-secondary-foreground`
* `--color-on-surface-secondary-hover`
* `--color-on-surface-secondary-focus`
* `--color-on-surface-tertiary`
* `--color-on-surface-tertiary-foreground`
* `--color-on-surface-tertiary-hover`
* `--color-on-surface-tertiary-focus`
**迁移方式:**
如果你之前用到了这些变量,请改用对应的 surface 变量:
```css
/* Before */
.element {
background: var(--color-on-surface);
color: var(--color-on-surface-foreground);
}
.element:hover {
background: var(--color-on-surface-hover);
}
/* After */
.element {
background: var(--surface-secondary);
color: var(--surface-secondary-foreground);
}
.element:hover {
background: color-mix(in oklab, var(--surface-secondary) 92%, var(--surface-secondary-foreground) 8%);
}
```
或者使用 Tailwind 工具类:
```tsx
// Before
// After
```
**相关 PR:** [#6204](https://github.com/heroui-inc/heroui/pull/6204)
## 链接
* [组件文档](/docs/react/components)
* [设计系统 - Figma Kit v3](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [GitHub PR #6201](https://github.com/heroui-inc/heroui/pull/6201)
## 贡献者
感谢每一位为本次发布做出贡献的开发者!
# v3.0.0-beta.7
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-0-beta-7
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-0-beta-7.mdx
> 新增 4 个组件(Calendar、RangeCalendar、DatePicker、DateRangePicker)以及多项 API 改进。
2026 年 2 月 19 日
本次发布新增 4 个组件:[Calendar](/docs/components/calendar)、[RangeCalendar](/docs/components/range-calendar)、[DatePicker](/docs/components/date-picker) 与 [DateRangePicker](/docs/components/date-range-picker)。同时还引入了 [Switch.Content](#switchcontent),用于将 label 与 description 组合到 Switch 控件旁边;以及 [Tabs.Separator](#tabsseparator),用于在 Tab 之间按需添加分隔线。
⚠️ **破坏性变更**:从 Tabs 中移除了 `hideSeparator`;`DateInputGroup` 与 `ColorInputGroup` 已分别合并到 `DateField.Group`、`TimeField.Group` 与 `ColorField.Group` 之下。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@beta @heroui/react@beta
```
```bash
pnpm add @heroui/styles@beta @heroui/react@beta
```
```bash
yarn add @heroui/styles@beta @heroui/react@beta
```
```bash
bun add @heroui/styles@beta @heroui/react@beta
```
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增功能
### 日期与时间体系
**日期与时间** —— Calendar、DatePicker、RangeCalendar 与 DateRangePicker 均基于 React Aria 的日期基元构建。支持国际化、时区,以及完整的键盘导航与 ARIA 无障碍能力。
**主要特性:**
* **历法系统**:公历、佛历、波斯历等
* **年份选择器**:用于快速跳转年份的浮层
* **单元格指示器**:在单元格上展示事件、可用状态或状态点
* **范围选择**:日期范围带有视觉高亮
* **无障碍**:键盘导航、屏幕阅读器、ARIA 全部支持
所有日期值都使用 [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) 提供的类型(`CalendarDate`、`CalendarDateTime`、`ZonedDateTime`)。可以用 [`I18nProvider`](https://react-aria.adobe.com/I18nProvider) 覆盖区域设置,并通过 [`useLocale`](https://react-aria.adobe.com/useLocale) 读取它。
### 新组件
* **[Calendar](#calendar)**:单日期选择,支持年份选择器、指示器与多月份显示。([文档](/docs/components/calendar))
* **[RangeCalendar](#rangecalendar)**:日期范围选择,支持范围高亮与多月份显示。([文档](/docs/components/range-calendar))
* **[DatePicker](#datepicker)**:日期输入框 + popover 日历。([文档](/docs/components/date-picker))
* **[DateRangePicker](#daterangepicker)**:两个日期输入框 + popover 范围日历。([文档](/docs/components/date-range-picker))
### Calendar
支持单日期选择的日历,包含年份选择器、单元格指示器、多月份视图以及国际化历法。
```tsx
"use client";
import {Calendar} from "@heroui/react";
export function Basic() {
return (
{(day) => {day} }
{(date) => }
);
}
```
**年份选择器:**
```tsx
"use client";
import {Calendar} from "@heroui/react";
export function YearPicker() {
return (
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
**国际化历法:**
```tsx
"use client";
import {Calendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
import {I18nProvider} from "react-aria-components";
export function InternationalCalendar() {
return (
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### RangeCalendar
日期范围选择,支持范围高亮与多月份视图。
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
export function Basic() {
return (
{(day) => {day} }
{(date) => }
);
}
```
**多月份显示:**
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
export function MultipleMonths() {
return (
{(day) => {day} }
{(date) => }
{(day) => {day} }
{(date) => }
);
}
```
### DatePicker
日期输入框 + popover 日历。支持格式选项、国际化、自定义指示器与表单校验。
```tsx
"use client";
import {Calendar, DateField, DatePicker, Label} from "@heroui/react";
export function Basic() {
return (
日期
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
**日期与时间(搭配 TimeField):**
```tsx
"use client";
import type {TimeValue} from "@heroui/react";
import type {DateValue} from "@internationalized/date";
import {
Calendar,
DateField,
DatePicker,
Label,
ListBox,
Select,
Switch,
TimeField,
} from "@heroui/react";
import {getLocalTimeZone, parseDate, parseZonedDateTime} from "@internationalized/date";
import {useMemo, useState} from "react";
type Granularity = "day" | "hour" | "minute" | "second";
type HourCycle = 12 | 24;
const granularityOptions: {label: string; value: Granularity}[] = [
{label: "日", value: "day"},
{label: "时", value: "hour"},
{label: "分", value: "minute"},
{label: "秒", value: "second"},
];
const hourCycleOptions: {label: string; value: HourCycle}[] = [
{label: "12 小时制", value: 12},
{label: "24 小时制", value: 24},
];
export function FormatOptions() {
const [granularity, setGranularity] = useState("minute");
const [hourCycle, setHourCycle] = useState(12);
const [hideTimeZone, setHideTimeZone] = useState(false);
const [shouldForceLeadingZeros, setShouldForceLeadingZeros] = useState(false);
const timeGranularity = granularity !== "day" ? granularity : undefined;
const showTimeField = !!timeGranularity;
const defaultValue = useMemo(() => {
const localTimeZone = getLocalTimeZone();
if (granularity === "day") {
return parseDate("2026-02-03");
}
return parseZonedDateTime(`2026-02-03T08:45:00[${localTimeZone}]`);
}, [granularity]);
return (
{({state}) => (
<>
日期和时间
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
{!!showTimeField && (
时间
state.setTimeValue(v as TimeValue)}
>
{(segment) => }
)}
>
)}
setGranularity(value as Granularity)}
>
粒度
{granularityOptions.map((option) => (
{option.label}
))}
setHourCycle(Number(value) as HourCycle)}
>
小时制
{hourCycleOptions.map((option) => (
{option.label}
))}
隐藏时区
强制前导零
);
}
```
### DateRangePicker
两个日期输入框 + popover 范围日历。
```tsx
"use client";
import {DateField, DateRangePicker, Label, RangeCalendar} from "@heroui/react";
export function Basic() {
return (
出行日期
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
## API 改进
### Switch.Content
`Switch.Content` 用于将 label 与 description 组合到 Switch 控件旁边([#6240](https://github.com/heroui-inc/heroui/pull/6240))。
**之前:**
```tsx
import { Switch, Label, Description } from '@heroui/react';
Email notifications
Get notified when someone mentions you
```
### Tabs.Separator
[Tabs](/docs/components/tabs) 组件现在新增了一个显式的 `Tabs.Separator` 子组件,用于在 Tab 之间添加视觉分隔线。它取代了之前自动生成的 CSS 伪元素分隔线以及 `hideSeparator` prop([#6243](https://github.com/heroui-inc/heroui/pull/6243))。
分隔线现在改为 **按需启用** —— 在希望出现分隔线的 `` 内部添加 ` ` 即可。
### Field 子组件的合并
`DateField`、`TimeField` 与 `ColorField` 现在直接暴露各自的输入组子组件,不再需要单独引入 `DateInputGroup` 或 `ColorInputGroup`。具体的迁移方式请参阅 [破坏性变更](#-breaking-changes)。
### Breadcrumbs 修复
传给 `Breadcrumbs.Item` 的 props 现在会正确转发到底层的 `Link`([#6233](https://github.com/heroui-inc/heroui/pull/6233))。
## 样式修复
* **ListBox Item**:将悬停背景色从 `bg-default-hover` 调整为 `bg-default`,以保持一致性
* **Date Input Group**:将段(segment)文本从 `tabular-nums` 调整为 `text-nowrap`,优化布局
* **Date Input Group**:改进 focus-within 样式,使其将日期选择器触发器排除在字段聚焦高亮之外
## 依赖更新
* **React Aria Components**:从 `1.14.0` 升级到 `1.15.0` —— 新增了 [`render` prop](https://react-aria.adobe.com/customization#dom-elements),可用于自定义任何 React Aria 组件渲染的 DOM 元素(适用于路由链接、Motion 等动画库)
* **@react-aria/utils**:从 `3.32.0` 升级到 `3.33.0`
* **@react-types/shared**:从 `3.32.1` 升级到 `3.33.0`
* **@internationalized/date**:从 `3.10.1` 升级到 `3.11.0` —— 日期字段现在改为在失焦时进行约束,而不是在输入过程中实时约束
* 新增 `@react-aria/i18n` 与 `@react-stately/utils`,用于日历的国际化
## ⚠️ 破坏性变更
### Tabs —— 移除 `hideSeparator` prop
`hideSeparator` prop 已从 Tabs 组件中移除。分隔线现在改为 **按需启用**,通过新增的 ` ` 子组件来添加,而不再通过 CSS 伪元素自动生成([#6243](https://github.com/heroui-inc/heroui/pull/6243))。
**之前:**
```tsx
{/* Separators shown by default, hidden via prop */}
Tab 1
Tab 2
```
**之后:**
```tsx
{/* No separators by default — explicitly add them where needed */}
Tab 1
Tab 2
```
**CSS 变更:**
* Tab 的分隔线样式已从伪元素(`.tabs__tab:not(:first-child):before`)迁移到独立的 `.tabs__separator` 类
* 已移除 `[data-hide-separator]` 这一 data 属性
### Field 子组件 API 变更
`DateInputGroup` 与 `ColorInputGroup` 不再从 `@heroui/react` 直接导出。它们的子组件已分别合并到对应的 Field 组件之下(`DateField`、`TimeField`、`ColorField`)。
#### DateField 变更
**之前:**
```tsx
import {DateField, Label, DateInputGroup, Description} from '@heroui/react';
Date
...
{(segment) => }
...
Pick a date
```
**之后:**
```tsx
import {DateField, Label, Description} from '@heroui/react';
Date
...
{(segment) => }
...
Pick a date
```
#### TimeField 变更
模式与 DateField 相同:
| 之前 | 之后 |
| ------------------------ | ------------------- |
| `DateInputGroup` | `TimeField.Group` |
| `DateInputGroup.Input` | `TimeField.Input` |
| `DateInputGroup.Segment` | `TimeField.Segment` |
| `DateInputGroup.Prefix` | `TimeField.Prefix` |
| `DateInputGroup.Suffix` | `TimeField.Suffix` |
#### ColorField 变更
| 之前 | 之后 |
| ------------------------ | ------------------- |
| `ColorInputGroup` | `ColorField.Group` |
| `ColorInputGroup.Input` | `ColorField.Input` |
| `ColorInputGroup.Prefix` | `ColorField.Prefix` |
| `ColorInputGroup.Suffix` | `ColorField.Suffix` |
**用法:**
```tsx
import {ColorField, Label, ColorInputGroup, ColorSwatch} from '@heroui/react';
Color
```
**之后:**
```tsx
import {ColorField, Label, ColorSwatch} from '@heroui/react';
Color
```
> **说明:** 底层的 CSS 类名(`.date-input-group`、`.color-input-group` 等)保持不变,仅 JavaScript 引入路径与组件名称发生了变化。
## 链接
* [组件文档](/docs/react/components)
* [设计系统 - Figma Kit v3](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [GitHub PR #6237](https://github.com/heroui-inc/heroui/pull/6237)
## 贡献者
感谢每一位为本次发布做出贡献的开发者!
# v3.0.0-beta.8
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-0-beta-8
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-0-beta-8.mdx
> 新增 3 个组件(Badge、Pagination、Table)、DateField 的多项改进,以及关键的 API / 样式修复。
2026 年 3 月 2 日
本次发布新增三个组件:[Badge](/docs/components/badge)、[Pagination](/docs/components/pagination) 与 [Table](/docs/components/table),并为 [DateField](/docs/components/date-field) 与 [TimeField](/docs/components/time-field) 提供了新的 `InputContainer` 组合 API。
⚠️ **破坏性变更**:TextField 的 CSS 类已从 `.text-field` 重命名为 `.textfield`。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@beta @heroui/react@beta
```
```bash
pnpm add @heroui/styles@beta @heroui/react@beta
```
```bash
yarn add @heroui/styles@beta @heroui/react@beta
```
```bash
bun add @heroui/styles@beta @heroui/react@beta
```
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增功能
### 新组件
* **[Badge](#badge)**:紧凑的状态 + 计数指示器,可配置颜色、变体、放置位置与尺寸。([文档](/docs/components/badge))
* **[Pagination](#pagination)**:分页相关的复合组件基元,提供摘要、省略号以及上一页 / 下一页等控件。([文档](/docs/components/pagination))
* **[Table](#table)**:数据表格基元,支持排序、选择、列宽调整、异步加载以及表脚组合。([文档](/docs/components/table))
### Badge
新增徽章基元,可用于计数、标签以及通过 `Badge.Anchor` 与 `Badge.Label` 锚定的浮层。
```tsx
import {Avatar, Badge} from "@heroui/react";
const GREEN_AVATAR_URL = "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg";
const ORANGE_AVATAR_URL =
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg";
const BLUE_AVATAR_URL = "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg";
export function BadgeBasic() {
return (
);
}
```
### Pagination
新增导航组件,由可组合的部件构成(`Root`、`Content`、`Item`、`Link`、`Previous`、`Next`、`Summary`、`Ellipsis`)。
```tsx
"use client";
import {Pagination} from "@heroui/react";
import {useState} from "react";
export function PaginationWithEllipsis() {
const [page, setPage] = useState(1);
const totalPages = 12;
const getPageNumbers = () => {
const pages: (number | "ellipsis")[] = [];
pages.push(1);
if (page > 3) {
pages.push("ellipsis");
}
const start = Math.max(2, page - 1);
const end = Math.min(totalPages - 1, page + 1);
for (let i = start; i <= end; i++) {
pages.push(i);
}
if (page < totalPages - 2) {
pages.push("ellipsis");
}
pages.push(totalPages);
return pages;
};
return (
setPage((p) => p - 1)}>
上一页
{getPageNumbers().map((p, i) =>
p === "ellipsis" ? (
) : (
setPage(p)}>
{p}
),
)}
setPage((p) => p + 1)}>
下一页
);
}
```
### Table
基于 React Aria 构建的复合数据表格,支持可排序的列、行选择、自定义单元格、加载更多哨兵行以及可调整宽度的列。
```tsx
import {Table} from "@heroui/react";
export function Basic() {
return (
姓名
角色
状态
邮箱
Kate Moore
首席执行官
在职
kate@acme.com
John Smith
首席技术官
在职
john@acme.com
Sara Johnson
首席营销官
休假
sara@acme.com
Michael Brown
首席财务官
在职
michael@acme.com
);
}
```
**自定义单元格:**
```tsx
"use client";
import type {Selection, SortDescriptor} from "@heroui/react";
import {Avatar, Button, Checkbox, Chip, Table} from "@heroui/react";
import {Icon} from "@iconify/react";
import {useMemo, useState} from "react";
interface User {
id: number;
name: string;
image_url: string;
role: string;
status: "在职" | "未激活" | "休假";
email: string;
}
const statusColorMap: Record = {
休假: "warning",
在职: "success",
未激活: "danger",
};
const users: User[] = [
{
email: "kate@acme.com",
id: 4586932,
image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg",
name: "Kate Moore",
role: "首席执行官",
status: "在职",
},
{
email: "john@acme.com",
id: 5273849,
image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg",
name: "John Smith",
role: "首席技术官",
status: "在职",
},
{
email: "sara@acme.com",
id: 7492836,
image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg",
name: "Sara Johnson",
role: "首席营销官",
status: "休假",
},
{
email: "michael@acme.com",
id: 8293746,
image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg",
name: "Michael Brown",
role: "首席财务官",
status: "在职",
},
{
email: "emily@acme.com",
id: 1234567,
image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg",
name: "Emily Davis",
role: "产品经理",
status: "未激活",
},
];
export function CustomCells() {
const [selectedKeys, setSelectedKeys] = useState(new Set());
const [sortDescriptor, setSortDescriptor] = useState({
column: "name",
direction: "ascending",
});
const sortedUsers = useMemo(() => {
return [...users].sort((a, b) => {
const col = sortDescriptor.column as keyof User;
const first = String(a[col]);
const second = String(b[col]);
let cmp = first.localeCompare(second);
if (sortDescriptor.direction === "descending") {
cmp *= -1;
}
return cmp;
});
}, [sortDescriptor]);
return (
{({sortDirection}) => (
员工 ID
)}
{({sortDirection}) => (
成员
)}
{({sortDirection}) => (
角色
)}
{({sortDirection}) => (
状态
)}
操作
{sortedUsers.map((user) => (
#{user.id.toString()}{" "}
{user.name
.split(" ")
.map((n) => n[0])
.join("")}
{user.name}
{user.email}
{user.role}
{user.status}
))}
);
}
```
**分页:**
```tsx
"use client";
import {Pagination, Table} from "@heroui/react";
import {useMemo, useState} from "react";
const columns = [
{id: "name", name: "姓名"},
{id: "role", name: "角色"},
{id: "status", name: "状态"},
{id: "email", name: "邮箱"},
];
const users = [
{email: "kate@acme.com", id: 1, name: "Kate Moore", role: "首席执行官", status: "在职"},
{email: "john@acme.com", id: 2, name: "John Smith", role: "首席技术官", status: "在职"},
{email: "sara@acme.com", id: 3, name: "Sara Johnson", role: "首席营销官", status: "休假"},
{email: "michael@acme.com", id: 4, name: "Michael Brown", role: "首席财务官", status: "在职"},
{
email: "emily@acme.com",
id: 5,
name: "Emily Davis",
role: "产品经理",
status: "未激活",
},
{email: "davis@acme.com", id: 6, name: "Davis Wilson", role: "首席设计师", status: "在职"},
{
email: "olivia@acme.com",
id: 7,
name: "Olivia Martinez",
role: "前端工程师",
status: "在职",
},
{
email: "james@acme.com",
id: 8,
name: "James Taylor",
role: "后端工程师",
status: "在职",
},
];
const ROWS_PER_PAGE = 4;
export function PaginationDemo() {
const [page, setPage] = useState(1);
const totalPages = Math.ceil(users.length / ROWS_PER_PAGE);
const pages = Array.from({length: totalPages}, (_, i) => i + 1);
const paginatedItems = useMemo(() => {
const start = (page - 1) * ROWS_PER_PAGE;
return users.slice(start, start + ROWS_PER_PAGE);
}, [page]);
const start = (page - 1) * ROWS_PER_PAGE + 1;
const end = Math.min(page * ROWS_PER_PAGE, users.length);
return (
{(column) => (
{column.name}
)}
{(user) => (
{(column) => {user[column.id as keyof typeof user]} }
)}
{start}–{end} / 共 {users.length} 条
setPage((p) => Math.max(1, p - 1))}
>
上一页
{pages.map((p) => (
setPage(p)}>
{p}
))}
setPage((p) => Math.min(totalPages, p + 1))}
>
下一页
);
}
```
**空状态:**
```tsx
"use client";
import {EmptyState, Table} from "@heroui/react";
import {Icon} from "@iconify/react";
export function EmptyStateDemo() {
return (
姓名
角色
状态
邮箱
(
未找到结果
)}
>
{[]}
);
}
```
## 组件 + API 改进
### DateField 与 TimeField 的增强
`DateField` 与 `TimeField` 现在暴露了 `InputContainer`,用于在前缀与后缀内容之间包裹输入段(segment)。
**之前:**
```tsx
...
{(segment) => }
...
```
**之后:**
```tsx
...
{(segment) => }
...
```
```tsx
"use client";
import type {TimeValue} from "@heroui/react";
import {DateField, DateRangePicker, Label, RangeCalendar, TimeField} from "@heroui/react";
import {getLocalTimeZone, parseZonedDateTime} from "@internationalized/date";
export function InputContainer() {
const localTimeZone = getLocalTimeZone();
const defaultValue = {
end: parseZonedDateTime(`2026-02-10T18:45:00[${localTimeZone}]`),
start: parseZonedDateTime(`2026-02-03T08:45:00[${localTimeZone}]`),
};
return (
{({state}) => (
<>
日期范围
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
开始时间
state.setTimeRange({
end: state.timeRange?.end as TimeValue,
start: v as TimeValue,
})
}
>
{(segment) => }
结束时间
state.setTimeRange({
end: v as TimeValue,
start: state.timeRange?.start as TimeValue,
})
}
>
{(segment) => }
>
)}
);
}
```
## ⚠️ 破坏性变更
### TextField 类名 + 路径重命名
为避免与 Tailwind 的 `text-*` 工具类前缀冲突,TextField 的样式命名已统一调整。
| 组件 | 旧类名 | 新类名 | 说明 |
| ------------------------ | ------------------------- | ------------------------ | ------ |
| **TextField Root** | `.text-field` | `.textfield` | 根类名重命名 |
| **TextField Full Width** | `.text-field--full-width` | `.textfield--full-width` | 修饰类重命名 |
同一变更涉及的其他重命名:
* 样式文件:`text-field.css` -> `textfield.css`
* 样式导出路径:`@heroui/styles/src/components/text-field` -> `@heroui/styles/src/components/textfield`
## 样式修复
* **RangeCalendar**:为日历单元格添加圆角,优化范围选择的视觉效果([#6270](https://github.com/heroui-inc/heroui/pull/6270))
## Bug 修复
* 通过将 `isRequired` 转化为 `data-required`,为 **DatePicker** 与 **DateRangePicker** 补齐了必填状态的红色星号行为([#6270](https://github.com/heroui-inc/heroui/pull/6270))
* 修复了 **Autocomplete** 与 **Select** 中触发器无效状态样式缺失的问题——将 invalid 样式限定在根状态范围内([#6270](https://github.com/heroui-inc/heroui/pull/6270))
* 更新了 TextField 的文档与演示引用,使其指向新的 `textfield-*` demo key 以及对应的源码 / 样式路径([#6270](https://github.com/heroui-inc/heroui/pull/6270))
## 链接
* [组件文档](/docs/components)
* [设计系统 - Figma Kit v3](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [GitHub PR #6270](https://github.com/heroui-inc/heroui/pull/6270)
## 贡献者
感谢每一位为本次发布做出贡献的开发者!
# v3.0.0-rc.1
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-0-rc-1
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-0-rc-1.mdx
> 新增 7 个组件(Drawer、ToggleButton、ToggleButtonGroup、Meter、ProgressBar、ProgressCircle、Toolbar),Table 与 ListBox 支持虚拟化,ButtonGroup 多项改进,以及若干 bug 修复。
2026 年 3 月 14 日
新增七个组件:[Drawer](/docs/components/drawer)、[ToggleButton](/docs/components/toggle-button)、[ToggleButtonGroup](/docs/components/toggle-button-group)、[Meter](/docs/components/meter)、[ProgressBar](/docs/components/progress-bar)、[ProgressCircle](/docs/components/progress-circle) 与 [Toolbar](/docs/components/toolbar)。Table 与 ListBox 支持虚拟化,ButtonGroup 新增 `Separator` 子组件并支持垂直方向,React Aria Components 升级到 v1.16.0。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@rc @heroui/react@rc
```
```bash
pnpm add @heroui/styles@rc @heroui/react@rc
```
```bash
yarn add @heroui/styles@rc @heroui/react@rc
```
```bash
bun add @heroui/styles@rc @heroui/react@rc
```
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增功能
### 新组件
* **[Drawer](#drawer)**:滑出式面板,支持拖拽关闭、4 种放置位置、多种背景遮罩变体以及可滚动的内容区([文档](/docs/components/drawer))
* **[ToggleButton](#toggle-button)**:选中 / 未选中两态的切换按钮,支持全部按钮变体以及仅图标模式([文档](/docs/components/toggle-button))
* **[ToggleButtonGroup](#toggle-button-group)**:单选或多选的切换按钮组,支持 attached / detached 布局以及方向设置([文档](/docs/components/toggle-button-group))
* **[Meter](#meter)**:在已知范围内呈现某个数值——例如磁盘占用、密码强度、配额等([文档](/docs/components/meter))
* **[ProgressBar](#progress-bar)**:线性进度条,支持确定 / 不确定态、多种颜色与自定义格式([文档](/docs/components/progress-bar))
* **[ProgressCircle](#progress-circle)**:基于 SVG 的环形进度条,可自定义轨道圆与填充圆([文档](/docs/components/progress-circle))
* **[Toolbar](#toolbar)**:将按钮、切换控件与分隔线按水平或垂直方向组合在一起的工具栏组件([文档](/docs/components/toolbar))
### Drawer
带有背景遮罩的滑出式浮层面板,支持顶部 / 底部 / 左侧 / 右侧四种放置位置、拖拽关闭手势以及多种背景遮罩变体。复合部件包括:`Trigger`、`Backdrop`、`Content`、`Dialog`、`Header`、`Heading`、`Body`、`Footer`、`Handle`、`CloseTrigger`。
```tsx
import {Button, Drawer} from "@heroui/react";
export function Basic() {
return (
打开抽屉
抽屉标题
这是一个基于 React Aria Modal 组件构建的抽屉。它会从屏幕边缘滑入,并通过流畅的 CSS
过渡呈现动画效果。
取消
确认
);
}
```
**放置位置:**
```tsx
import {Button, Drawer} from "@heroui/react";
const PLACEMENT_LABELS = {
bottom: "底部",
left: "左侧",
right: "右侧",
top: "顶部",
} as const;
export function Placements() {
const placements = ["bottom", "top", "left", "right"] as const;
return (
{placements.map((placement) => (
{PLACEMENT_LABELS[placement]}
{placement === "bottom" && }
{PLACEMENT_LABELS[placement]}抽屉
此抽屉从屏幕{PLACEMENT_LABELS[placement]} 边缘滑入。
取消
完成
{placement === "top" && }
))}
);
}
```
**配合表单使用:**
```tsx
import {Button, Drawer, Input, Label, TextField} from "@heroui/react";
export function WithForm() {
return (
编辑资料
编辑资料
姓名
邮箱
简介
取消
保存更改
);
}
```
### Toggle Button
具备状态的切换按钮,可在选中与未选中之间切换。支持全部按钮变体与尺寸、仅图标模式,以及受控 / 非受控两种使用方式。
```tsx
import {Heart} from "@gravity-ui/icons";
import {ToggleButton} from "@heroui/react";
export function Basic() {
return (
点赞
);
}
```
**变体:**
```tsx
import {Heart} from "@gravity-ui/icons";
import {ToggleButton} from "@heroui/react";
export function Variants() {
return (
默认
幽灵
);
}
```
### Toggle Button Group
单选或多选的切换按钮组。支持 attached(连接式)与 detached(分离式)两种布局、垂直方向、整宽展示,以及一个 `Separator` 子组件。
```tsx
import {Bold, Italic, Strikethrough, Underline} from "@gravity-ui/icons";
import {ToggleButton, ToggleButtonGroup} from "@heroui/react";
export function Basic() {
return (
);
}
```
**选择模式:**
```tsx
import {
Bold,
Italic,
Strikethrough,
TextAlignCenter,
TextAlignLeft,
TextAlignRight,
Underline,
} from "@gravity-ui/icons";
import {ToggleButton, ToggleButtonGroup} from "@heroui/react";
export function SelectionMode() {
return (
);
}
```
**Attached 模式:**
```tsx
import {Bold, Italic, Strikethrough, Underline} from "@gravity-ui/icons";
import {ToggleButton, ToggleButtonGroup} from "@heroui/react";
export function Attached() {
return (
);
}
```
### Meter
在已知范围内呈现某个数值——例如磁盘占用、密码强度、配额等。复合部件包括:`Root`、`Output`、`Track`、`Fill`。
```tsx
import {Label, Meter} from "@heroui/react";
export function Basic() {
return (
存储空间
);
}
```
**颜色:**
```tsx
import {Label, Meter} from "@heroui/react";
const colors = ["default", "accent", "success", "warning", "danger"] as const;
const COLOR_LABELS: Record<(typeof colors)[number], string> = {
accent: "强调",
danger: "危险",
default: "默认",
success: "成功",
warning: "警告",
};
export function Colors() {
return (
{colors.map((color) => (
{COLOR_LABELS[color]}
))}
);
}
```
### Progress Bar
线性进度指示器,支持确定 / 不确定态、颜色变体、多种尺寸以及自定义数值显示。复合部件包括:`Root`、`Output`、`Track`、`Fill`。
```tsx
import {Label, ProgressBar} from "@heroui/react";
export function Basic() {
return (
加载中
);
}
```
**不确定态:**
```tsx
import {Label, ProgressBar} from "@heroui/react";
export function Indeterminate() {
return (
加载中…
);
}
```
### Progress Circle
基于 SVG 的环形进度条,提供 `TrackCircle` 与 `FillCircle` 两个子组件,便于直接控制 SVG。同时支持确定与不确定态。
```tsx
import {ProgressCircle} from "@heroui/react";
export function Basic() {
return (
);
}
```
**自定义 SVG:**
```tsx
import {ProgressCircle} from "@heroui/react";
export function CustomSvg() {
return (
);
}
```
### Toolbar
将按钮、切换按钮与分隔线组合到一个具备无障碍语义的工具栏中。支持水平或垂直方向,可与 `ButtonGroup` 和 `ToggleButtonGroup` 组合使用。
```tsx
import {Bold, Copy, Italic, Scissors, Underline} from "@gravity-ui/icons";
import {
Button,
ButtonGroup,
Separator,
ToggleButton,
ToggleButtonGroup,
Toolbar,
} from "@heroui/react";
export function Basic() {
return (
);
}
```
**配合 Button Group 使用:**
```tsx
import {
ArrowUturnCcwLeft,
ArrowUturnCwRight,
Bold,
Italic,
TextAlignCenter,
TextAlignLeft,
TextAlignRight,
Underline,
} from "@gravity-ui/icons";
import {
Button,
ButtonGroup,
Separator,
ToggleButton,
ToggleButtonGroup,
Toolbar,
} from "@heroui/react";
export function WithButtonGroup() {
return (
撤销
重做
);
}
```
## 组件改进
### ButtonGroup 的增强
新增的 `ButtonGroup.Separator` 子组件可在按钮之间显式插入一条视觉分隔线。在水平和垂直两种方向下都能正常工作。
```tsx
import {
ChevronDown,
ChevronLeft,
ChevronRight,
CodeFork,
Ellipsis,
Picture,
Pin,
QrCode,
Star,
TextAlignCenter,
TextAlignJustify,
TextAlignLeft,
TextAlignRight,
ThumbsDown,
ThumbsUp,
Video,
} from "@gravity-ui/icons";
import {Button, ButtonGroup, Chip, Description, Dropdown, Label} from "@heroui/react";
export function Basic() {
return (
{/* 单个按钮与下拉菜单 */}
合并拉取请求
创建合并提交
此分支上的所有提交都将加入基础分支
压缩并合并
此分支上的 14 个提交将合并为一次提交并加入基础分支
变基并合并
此分支上的 14 个提交将变基后加入基础分支
{/* 独立按钮 */}
复刻
24
扫码支付
2.4K
星标
104
已置顶
{/* 上一页 / 下一页 */}
上一页
下一页
{/* 内容类型选择 */}
{/* 文本对齐 */}
左对齐
居中
右对齐
{/* 仅图标:对齐 */}
);
}
```
### Table 与 ListBox 的虚拟化
Table 与 ListBox 现在可以借助 React Aria 的 `Virtualizer` 来支持大数据集的虚拟化渲染。`Virtualizer`、`TableLayout` 和 `ListLayout` 都已从 `@heroui/react` 重新导出。
```tsx
"use client";
import {Table, TableLayout, Virtualizer} from "@heroui/react";
interface User {
id: number;
name: string;
role: string;
email: string;
}
export function Virtualization() {
const roles = [
"软件工程师",
"高级工程师",
"资深工程师",
"产品经理",
"设计师",
"数据分析师",
"测试工程师",
"DevOps 工程师",
"营销经理",
"销售代表",
];
const firstNames = [
"Emma",
"Liam",
"Olivia",
"Noah",
"Ava",
"James",
"Sophia",
"Oliver",
"Isabella",
"Lucas",
"Mia",
"Ethan",
"Charlotte",
"Mason",
"Amelia",
"Logan",
"Harper",
"Alexander",
"Ella",
"Benjamin",
];
const lastNames = [
"Smith",
"Johnson",
"Williams",
"Brown",
"Jones",
"Garcia",
"Miller",
"Davis",
"Rodriguez",
"Martinez",
"Anderson",
"Taylor",
"Thomas",
"Jackson",
"White",
"Harris",
"Clark",
"Lewis",
"Robinson",
"Walker",
];
function generateUsers(count: number): User[] {
const users: User[] = [];
for (let i = 0; i < count; i++) {
const firstName = firstNames[i % firstNames.length];
const lastName = lastNames[Math.floor(i / firstNames.length) % lastNames.length];
const name = `${firstName} ${lastName}`;
users.push({
email: `${firstName?.toLowerCase()}.${lastName?.toLowerCase()}@acme.com`,
id: i + 1,
name,
role: roles[i % roles.length] || "",
});
}
return users;
}
const virtualizedUsers = generateUsers(1000);
return (
姓名
角色
邮箱
{(user) => (
{user.name}
{user.role}
{user.email}
)}
);
}
```
### ButtonGroup 的方向
`ButtonGroup` 现在接受一个 `orientation` prop(`"horizontal"` | `"vertical"`),并在两种方向下都正确处理边框圆角与分隔线方向。根元素也已从 `` 升级为 React Aria 的 `Group`,从而具备正确的 `role="group"` 语义。
```tsx
import {TextAlignCenter, TextAlignJustify, TextAlignLeft, TextAlignRight} from "@gravity-ui/icons";
import {Button, ButtonGroup} from "@heroui/react";
export function Orientation() {
return (
);
}
```
### ButtonGroup 的焦点环
成组按钮上的焦点环现在使用 `ring-inset`,确保焦点环留在按钮内部边界,而不会与相邻按钮重叠。
### 细粒度的组件引入
`@heroui/react` 现在支持按组件的子路径入口,方便更明确地按需引入([#6301](https://github.com/heroui-inc/heroui/pull/6301)):
```tsx
// Before — root entrypoint
import { Button } from "@heroui/react";
// After — granular subpath import
import { Button } from "@heroui/react/button";
```
## 依赖更新
将 `react-aria-components` 从 v1.15.1 升级到 v1.16.0,同时升级了相关包:
| 包名 | 旧版本 | 新版本 |
| ------------------------- | ------- | ------- |
| `react-aria-components` | 1.15.1 | 1.16.0 |
| `@react-aria/i18n` | 3.12.15 | 3.12.16 |
| `@react-aria/utils` | 3.33.0 | 3.33.1 |
| `@react-types/shared` | 3.33.0 | 3.33.1 |
| `@react-types/color` | 3.1.3 | 3.1.4 |
| `@internationalized/date` | 3.11.0 | 3.12.0 |
| `@react-stately/data` | 3.15.1 | 3.15.2 |
## Bug 修复
* **InputGroup**:聚焦样式现在仅在实际的 input / textarea 获得焦点时(`:has([data-slot]:focus)`)才会触发,不再因 `:focus-within` 而被任意可聚焦的子元素触发([#6274](https://github.com/heroui-inc/heroui/pull/6274))
* **Avatar**:fallback 元素现在会从父级继承 `border-radius`,而不再硬编码为 `rounded-full`,因此 `className` 覆盖能够正确生效([#6300](https://github.com/heroui-inc/heroui/pull/6300))
* **Modal 与 AlertDialog**:背景遮罩的点击事件不会再透过 portal 传播到父级元素([#6297](https://github.com/heroui-inc/heroui/pull/6297))
* **Table**:修复了 Firefox 中表头圆角与背景色溢出的问题([#6298](https://github.com/heroui-inc/heroui/pull/6298))
## 链接
* [组件文档](/docs/react/components)
* [Figma Kit v3](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [GitHub PR #6285](https://github.com/heroui-inc/heroui/pull/6285)
## 贡献者
感谢每一位为本次发布做出贡献的开发者!
# HeroUI v3 正式发布
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-0
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-0.mdx
> 面向 React 与 React Native 的彻底重写。75+ Web 组件、37 个原生组件、Tailwind CSS v4、React Aria、复合组件架构,以及为 AI 辅助开发而打造的工具链。
2026 年 3 月
每个组件都已重写。所有动画都已迁移到 CSS。样式与实现完全解耦。全新打造的 React Native 库。以及一套把 AI 助手视为「主要开发界面」的工具链。
## 概览
### React(Web)
75+ 组件。无障碍能力由 [React Aria Components](https://react-aria.adobe.com/) 提供。基于 Tailwind CSS v4 + CSS 变量进行主题化。样式被独立成单独的包,可以与任意框架配合使用。
[查看详情](#compound-components)
### React Native
37 个组件,共享同一套设计 token、采用复合组件模式、统一的动画 API,以及自适应的呈现模式。每个平台都基于原生实现,并通过 [Uniwind](https://uniwind.dev/) 提供 Tailwind CSS v4 的支持。
[查看详情](#heroui-native)
### HeroUI Pro
面向 React 与 React Native 的高级组件、模板与 AI 工具。包含 Command Palette、Kanban、DataGrid、Dashboard 模板等。预售价格已上线 [heroui.pro](https://heroui.pro)。
[查看详情](#heroui-pro)
## 设计原则
**组合优于配置:** v2 的组件是黑盒。v3 采用复合组件模式:每一个内部部件都是真实的元素,你可以为它们设置样式、调整位置、替换或移除。
**样式与实现分离:** `@heroui/styles` 是独立的 CSS 包,`@heroui/react` 负责行为逻辑。这套样式可以配合 React、原生 HTML + Tailwind 或任意框架使用。BEM 类名让每一个 slot 都能在全局层面被定制。切换主题不仅会改变变量,还能改变组件的外观与质感。
**按需变身 Headless:** 只要不引入 `@heroui/styles`,你就拥有了一套 headless 组件库。我们负责功能与无障碍,你专注于自己的产品。
**默认就有好性能:** v2 的所有动画都依赖 Framer Motion。v3 已将其替换为原生的 CSS transition 与 keyframes。打包体积更小、可使用 GPU 加速,且无需任何 JS 动画运行时。
**从一开始就无障碍:** 已从 React Aria hooks 迁移到 [React Aria Components](https://react-aria.adobe.com/)。键盘导航、焦点管理、屏幕阅读器与 ARIA 属性均已内置。
## 复合组件
下面是复合组件模式在实际使用中的样子:
```tsx
Product
Details about this product.
Card content goes here.
Buy now
```
代码确实多了几行。但每个部件都是真实的元素,你可以自由设置样式、调整位置或替换。这一模式贯穿整个组件库,从 Accordion 到 Toast 都是如此。
```tsx
import {
ArrowsRotateLeft,
Box,
ChevronDown,
CreditCard,
PlanetEarth,
Receipt,
ShoppingBag,
} from "@gravity-ui/icons";
import {Accordion} from "@heroui/react";
const items = [
{
content: "浏览我们的商品,将商品加入购物车并前往结账。完成购买需要提供收货与支付信息。",
icon: ,
title: "如何下单?",
},
{
content: "可以,在订单发货前你可以修改或取消。订单一旦进入处理流程,将无法再更改。",
icon: ,
title: "可以修改或取消订单吗?",
},
{
content: "我们接受主流信用卡,包括 Visa、Mastercard 和 American Express。",
icon: ,
title: "支持哪些支付方式?",
},
{
content: "运费因收货地址与订单体积而异。订单满 50 美元可享受免运费。",
icon: ,
title: "运费如何计算?",
},
{
content: "是的,我们可向多数国家/地区发货。请查看运费说明与政策了解更多信息。",
icon: ,
title: "是否提供国际配送?",
},
{
content: "若对购买不满意,可在购买后 30 天内申请退款。请联系客服团队协助处理。",
icon: ,
title: "如何申请退款?",
},
];
export function Basic() {
return (
{items.map((item, index) => (
{item.icon ? (
{item.icon}
) : null}
{item.title}
{item.content}
))}
);
}
```
每个复合组件都通过 React context 共享状态。根组件会创建样式 context,子组件依次消费。你无需手动逐层传递 className:
```tsx
Profile updated
Your changes have been saved.
```
```tsx
import {Alert, Button, CloseButton, Spinner} from "@heroui/react";
import React from "react";
export function Basic() {
return (
{/* 默认 — 一般信息 */}
新功能已上线
查看我们的最新更新,包括深色模式支持与改进的无障碍体验。
{/* 强调 — 重要信息含操作 */}
有可用更新
应用有新版本可用。请刷新页面以获取最新功能与问题修复。
刷新
刷新
{/* 危险 — 错误与排查步骤 */}
无法连接到服务器
当前遇到连接问题,请尝试以下操作:
重试
重试
{/* 无描述 */}
个人资料已更新
{/* 自定义指示器 — 加载中 */}
正在处理你的请求
正在同步你的数据,请稍候,这可能需要一点时间。
{/* 无关闭按钮 */}
计划维护
我们将于 UTC 时间 3 月 15 日(周日)凌晨 2:00 至上午 6:00
进行计划维护,期间服务将暂时不可用。
);
}
```
### 渐进式呈现
组件同时支持简单写法和复合写法。先从一行代码起步,需要时再补充结构:
```tsx
// One line
Submit
// With icon
Submit
// Full control
{isLoading ? : }
{isLoading ? "Saving..." : "Submit"}
```
```tsx
import {Button} from "@heroui/react";
export function Variants() {
return (
主要
次要
第三
线框
幽灵
危险
柔和危险
);
}
```
## Tailwind CSS v4 + CSS 变量
主题系统基于 Tailwind CSS v4 原生的 CSS 变量层与 OKLCH 颜色实现。每一个设计 token 都是一个 CSS 变量:
```css
:root {
--background: oklch(0.9702 0 0);
--foreground: oklch(0.2103 0.0059 285.89);
--accent: oklch(0.6204 0.195 253.83);
--surface: oklch(100% 0 0);
--danger: oklch(0.6532 0.2328 25.74);
--radius: 0.5rem;
}
```
Tailwind 的 `@theme` 指令会将这些 token 映射为工具类。`bg-accent`、`text-foreground`、`rounded-lg` 都会解析为对应的 CSS 变量。切换浅色 / 深色模式只需替换变量值:
```css
.dark, [data-theme="dark"] {
--background: oklch(12% 0.005 285.823);
--foreground: oklch(0.9911 0 0);
--surface: oklch(0.2103 0.0059 285.89);
}
```
不需要 Provider 组件,也不需要 JavaScript 主题对象。一次 CSS 引入,两行代码:
```css
@import "tailwindcss";
@import "@heroui/styles";
```
### BEM 类名
通过标准 CSS 即可在全局覆写任意组件:
```css
@layer components {
.button {
@apply font-semibold tracking-wide;
}
.button--primary {
@apply bg-blue-600 hover:bg-blue-700;
}
}
```
无需层层传递 className,也无需在 style prop 上反复折腾。设计系统的覆写就发生在 CSS 中——它本就属于这里。
### 自定义主题
通过定义你自己的 token 集合即可创建主题,其他一切都会随之级联:
```css
@layer base {
[data-theme="ocean"] {
--accent: oklch(0.450 0.150 230);
--background: oklch(0.985 0.015 225);
--radius: 0.75rem;
--border: oklch(0.50 0.060 230 / 22%);
}
}
```
只需一个 data 属性即可应用:
```html
```
[主题构建器](/themes) 可以可视化地生成这些变量:选择颜色、调整圆角与间距,再导出 CSS。
### 按需引入
可以一次性引入完整样式库,也可以只挑选特定组件的样式:
```css
@import "tailwindcss";
@import "@heroui/styles/base" layer(base);
@import "@heroui/styles/themes/default" layer(theme);
@import "@heroui/styles/components/button.css" layer(components);
@import "@heroui/styles/components/card.css" layer(components);
```
只发布你实际用到的 CSS,避免在生产环境中携带未使用的组件样式。
## 尊重用户的动画
所有组件动画都通过 CSS transition 与 keyframes 实现,并绑定到对应的 data 属性。Popover 通过 `[data-entering]` 淡入,Button 在 `[data-pressed]` 时缩放,Accordion 通过 `[aria-hidden="false"]` 展开。
```css
.popover[data-entering] {
@apply animate-in zoom-in-90 fade-in-0 duration-200;
}
.button:active,
.button[data-pressed="true"] {
transform: scale(0.97);
}
```
### Reduce Motion
部分用户需要禁用动画。HeroUI 扩展了 Tailwind 的 `motion-reduce:` 变体,使其同时支持系统级偏好和自定义 data 属性:
```css
.button {
@apply transition-colors motion-reduce:transition-none;
}
```
它会响应原生的 `prefers-reduced-motion: reduce` 媒体查询,同时也会响应 HTML 元素上的 `data-reduce-motion="true"`,从而支持应用级别的控制:
```html
```
data 属性的优先级高于系统设置。将其设为 `data-reduce-motion="false"` 可强制开启动画,移除该属性则交由操作系统决定。所有带动画的组件都会遵循这一规则,无需额外开启。
### 自带动画库也无妨
Framer Motion、Motion One 或任何 CSS 动画库都可以与 HeroUI 内置的过渡共存:
```tsx
import { motion } from "framer-motion";
import { Button } from "@heroui/react";
const MotionButton = motion(Button);
Animated
```
## 75+ React 组件
### 日期与时间
六个组件:Calendar、RangeCalendar、DateField、DatePicker、DateRangePicker 与 TimeField。基于 React Aria 的国际化日期库构建,默认支持公历、佛历、波斯历等多种历法。键盘导航、屏幕阅读器标签以及按区域格式化全部开箱即用。
```tsx
"use client";
import {Calendar, DateField, DatePicker, Label} from "@heroui/react";
export function Basic() {
return (
日期
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
export function Basic() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### 颜色
六个颜色组件:ColorPicker、ColorArea、ColorSlider、ColorField、ColorSwatch 与 ColorSwatchPicker。可以在二维色域中取色、调整色相与透明度滑块、输入 hex 值,或从色板中直接选择。
```tsx
import {ColorArea, ColorPicker, ColorSlider, ColorSwatch, Label} from "@heroui/react";
export function Basic() {
return (
选择颜色
色相
);
}
```
### 数据
需要一个支持排序、行选择、列宽调整、异步加载与自定义单元格的表格?Table 全部都能搞定。面对大数据集时,可借助 React Aria 的 `Virtualizer` 启用虚拟化,ListBox 也共享同样的虚拟化能力。
```tsx
"use client";
import {Table, TableLayout, Virtualizer} from "@heroui/react";
interface User {
id: number;
name: string;
role: string;
email: string;
}
export function Virtualization() {
const roles = [
"软件工程师",
"高级工程师",
"资深工程师",
"产品经理",
"设计师",
"数据分析师",
"测试工程师",
"DevOps 工程师",
"营销经理",
"销售代表",
];
const firstNames = [
"Emma",
"Liam",
"Olivia",
"Noah",
"Ava",
"James",
"Sophia",
"Oliver",
"Isabella",
"Lucas",
"Mia",
"Ethan",
"Charlotte",
"Mason",
"Amelia",
"Logan",
"Harper",
"Alexander",
"Ella",
"Benjamin",
];
const lastNames = [
"Smith",
"Johnson",
"Williams",
"Brown",
"Jones",
"Garcia",
"Miller",
"Davis",
"Rodriguez",
"Martinez",
"Anderson",
"Taylor",
"Thomas",
"Jackson",
"White",
"Harris",
"Clark",
"Lewis",
"Robinson",
"Walker",
];
function generateUsers(count: number): User[] {
const users: User[] = [];
for (let i = 0; i < count; i++) {
const firstName = firstNames[i % firstNames.length];
const lastName = lastNames[Math.floor(i / firstNames.length) % lastNames.length];
const name = `${firstName} ${lastName}`;
users.push({
email: `${firstName?.toLowerCase()}.${lastName?.toLowerCase()}@acme.com`,
id: i + 1,
name,
role: roles[i % roles.length] || "",
});
}
return users;
}
const virtualizedUsers = generateUsers(1000);
return (
姓名
角色
邮箱
{(user) => (
{user.name}
{user.role}
{user.email}
)}
);
}
```
### 表单
十三个表单组件:TextField、Select、Autocomplete、ComboBox、Checkbox、CheckboxGroup、RadioGroup、Switch、InputOTP、NumberField、SearchField、Slider 与 Fieldset。全部集成了 React Aria 的表单校验:`isRequired`、`isInvalid` 以及通过 FieldError 自定义错误信息——所有组件均可使用。
```tsx
import {Label, ListBox, Select} from "@heroui/react";
export function Default() {
return (
州
佛罗里达
特拉华
加利福尼亚
德克萨斯
纽约
华盛顿
);
}
```
```tsx
import {InputOTP, Label, Link} from "@heroui/react";
export function Basic() {
return (
验证账户
我们已向 a****@gmail.com 发送验证码
);
}
```
### 浮层
七个浮层组件。Drawer 支持四种放置位置以及拖拽关闭手势。Toast 可堆叠通知,并支持自动关闭与 Promise。Menu 支持子菜单与分组组合。此外还有 Modal、AlertDialog、Popover 与 Tooltip。
```tsx
import {Button, Drawer} from "@heroui/react";
export function Basic() {
return (
打开抽屉
抽屉标题
这是一个基于 React Aria Modal 组件构建的抽屉。它会从屏幕边缘滑入,并通过流畅的 CSS
过渡呈现动画效果。
取消
确认
);
}
```
```tsx
"use client";
import {Persons} from "@gravity-ui/icons";
import {Button, toast} from "@heroui/react";
export function Default() {
return (
{
toast("您已被邀请加入团队", {
actionProps: {
children: "忽略",
onPress: () => toast.clear(),
variant: "tertiary",
},
description: "Bob 邀请您加入 HeroUI 团队",
indicator: ,
variant: "default",
});
}}
>
显示 Toast
);
}
```
### 导航
Tabs、Accordion、Breadcrumbs、Pagination 与 Link。Tabs 支持水平与垂直两种排列方向。Accordion 支持单一或多个面板同时展开。
```tsx
import {Tabs} from "@heroui/react";
export function Basic() {
return (
概览
分析
报告
查看项目概览与近期活动。
跟踪指标并分析性能数据。
生成并下载详细报告。
);
}
```
### 反馈
ProgressBar 与 ProgressCircle 同时支持确定态与不确定态。Meter 会根据数值映射到语义颜色:绿色代表安全,黄色代表警告,红色代表严重。Skeleton 与 Spinner 共同补齐这一组件家族。
```tsx
import {Label, Meter} from "@heroui/react";
const colors = ["default", "accent", "success", "warning", "danger"] as const;
const COLOR_LABELS: Record<(typeof colors)[number], string> = {
accent: "强调",
danger: "危险",
default: "默认",
success: "成功",
warning: "警告",
};
export function Colors() {
return (
{colors.map((color) => (
{COLOR_LABELS[color]}
))}
);
}
```
```tsx
import {Skeleton} from "@heroui/react";
export function Basic() {
return (
);
}
```
### 按钮与切换
Button、ButtonGroup、ToggleButton、ToggleButtonGroup、CloseButton 与 Toolbar。ButtonGroup 通过共享边框将多个按钮连接在一起,并支持垂直方向。Toolbar 会将按钮、切换控件与分隔线组合到一个具备无障碍语义的 `role="toolbar"` 容器中。
```tsx
import {Bold, Italic, Strikethrough, Underline} from "@gravity-ui/icons";
import {ToggleButton, ToggleButtonGroup} from "@heroui/react";
export function Attached() {
return (
);
}
```
### 细粒度引入
你可以从根路径引入,也可以从每个组件的子路径引入,两种方式都可用:
```tsx
// Root import
import { Button, Card, Table } from "@heroui/react";
// Subpath import
import { Button } from "@heroui/react/button";
import { Card } from "@heroui/react/card";
import { Table } from "@heroui/react/table";
```
## 面向 Agent 的 UI
如今越来越多的开发者通过 prompt 来构建产品,而不是逐字阅读 API 文档。HeroUI v3 为此做好了准备。
### MCP 服务器
HeroUI MCP 服务器将 AI 编码助手(Cursor、Claude Code、VS Code Copilot、Windsurf、Zed)连接到组件文档、props、源码、CSS 样式与主题变量。AI 可以直接读取权威信息源,而不必再依赖训练数据进行猜测。
```json
{
"mcpServers": {
"heroui-react": {
"command": "npx",
"args": ["-y", "@heroui/react-mcp@latest"]
}
}
}
```
对你的 AI 助手说一句「把 HeroUI 升级到最新版本」,它就会自动对比版本、检查更新日志中的破坏性变更,并完成必要的代码更新。
### Agent Skills
面向 Cursor 与 Claude Code 提供可安装的知识包,覆盖组件模式、变体用法、主题说明以及升级指南。提前注入上下文,让 AI 第一次就能写出正确的 HeroUI 代码。
### LLMs.txt
针对 AI 上下文窗口优化的结构化文档文件。这些文件发布在 `/llms.txt` 与 `/llms-components.txt`,为任何基于 LLM 的工具提供一份机器可读的 HeroUI API 摘要。
MCP 服务器、Agent Skills、LLMs.txt——三层组合让 AI 助手能像人类开发者从文档中获取信息一样,获得对 HeroUI 的完整访问能力。
## HeroUI Native
HeroUI Native 是与 v3 网页版同时推出的全新组件库。渲染引擎不同,但心智模型一致。在平台差异较大的地方,API 也会贴合各自平台的原生体验。
### 在你的设备上试试
用设备摄像头或 [Expo Go](https://expo.dev/go) 扫描下方二维码,即可在线体验全部 37 个组件:
**[📱 在 Expo Go 中打开演示应用](https://link.heroui.com/native-demo)**
**Android 用户:** 如果扫码后跳转到浏览器并显示 404 错误,请先打开 Expo Go,再使用应用内置的扫码功能。
### 37 个组件
涵盖表单、导航、浮层、反馈与布局。从 Button、Input、Checkbox,到 Dialog、BottomSheet、Select、Toast 与 InputOTP,所有组件都遵循复合组件模式:
```tsx
import { Dialog, Button } from "heroui-native";
Open
Confirm action
This cannot be undone.
```
### 跨平台一致的体验
只要你熟悉 Web 版的 HeroUI,大部分知识都能直接迁移过来。组件命名、点表示法与 prop 模式都尽可能保持一致。在平台差异较大的地方(布局基元、手势、导航等),API 会做出相应调整以贴近原生,但整体的心智模型保持一致:
```tsx
// React (web)
Profile updated
Your changes have been saved.
// React Native — similar API, native behavior
Profile updated
Your changes have been saved.
```
同时开发 Web 与移动端的团队可以共享同一套知识与模式。即便组件本身有所不同,跨平台的学习成本也极低。
### 共享设计 token
两个平台读取的是同一份 token 集合。`accent`、`surface`、`danger`、`success` 等颜色在 Web 与 Native 上解析结果完全一致。你不必维护两套独立的系统,品牌也能保持一致。
```tsx
import { View, Text } from "react-native";
Card Title
Consistent on web and mobile.
```
两个平台都使用 Tailwind CSS v4:Native 端通过 [Uniwind](https://uniwind.dev/),Web 端使用标准 Tailwind。
### 统一的动画 API
每一个带动画的原生组件都只暴露一个 `animation` prop。数值、时长、弹簧参数、进出场过渡都集中在这里配置。底层由 Reanimated 负责计算,但你完全不需要直接接触它:
```tsx
import { Switch } from "heroui-native";
```
可以在任意层级关闭动画——单个组件、整棵子树,或者全局:
```tsx
// Single component
// Entire subtree
...
// App-wide
```
Reduce Motion 全自动生效。当用户在系统设置中启用它时,所有动画都会停止,无需任何额外代码。
### 自适应的呈现模式
Popover、Select 与 Menu 通过一个 prop 即可在 popover、bottom-sheet 与 dialog 之间切换。同一个组件,根据上下文以不同形式呈现:
```tsx
...
...
...
```
目前还没有其他 React Native 组件库提供这一能力。
### 细粒度引入
每一个原生组件都有自己的入口路径,只引入你实际用到的部分即可:
```tsx
import { HeroUINativeProvider } from "heroui-native/provider";
import { Button } from "heroui-native/button";
import { Card } from "heroui-native/card";
```
### Native 端的 AI 工具
HeroUI Native 也配套提供了自己的 MCP 服务器、Agent Skills 与 LLMs.txt,与 Web 组件库的工具链结构完全一致:
```json
{
"mcpServers": {
"heroui-native": {
"command": "npx",
"args": ["-y", "@heroui/native-mcp@latest"]
}
}
}
```
## HeroUI Pro
与 v3 同步,[HeroUI Pro](https://heroui.pro) 的预售也已经上线。面向 React 与 React Native,提供高级组件、模板与 AI 工具。
### Pro 组件
超越核心组件库的更多组件:Command Palette、Kanban、Stats Dashboard、Filters、Agenda、DataGrid 等。无障碍、动画以及各平台的边界情况都已处理妥当,未来将同时支持 Web 与 Native。
### 模板
完整可用的响应式起步模板:Dashboard、Mail、Chat 与 Finances。布局真实、结构完整,让你从一个能直接运行的项目出发,再按需定制。
### 高级 AI 工具
Pro 许可包含高级版的 MCP 服务器与 Agent Skills,并内置 Pro 组件文档、使用模式与升级路径。
预售价格已上线。v2 Pro 用户可享受升级折扣,使用同一邮箱或联系客服即可。
[访问 heroui.pro 查看套餐与定价](https://heroui.pro)
## 快速上手
### React(Web)
```bash
npm i @heroui/styles @heroui/react
```
```bash
pnpm add @heroui/styles @heroui/react
```
```bash
yarn add @heroui/styles @heroui/react
```
```bash
bun add @heroui/styles @heroui/react
```
在你的 CSS 中加入这两行:
```css
@import "tailwindcss";
@import "@heroui/styles";
```
### React Native
```bash
npm install heroui-native
```
```bash
pnpm add heroui-native
```
```bash
yarn add heroui-native
```
```bash
bun add heroui-native
```
完整的安装指南请参阅 [React 文档](/docs/react/getting-started/quick-start) 与 [React Native 文档](/docs/native/getting-started/quick-start)(涵盖 peer dependencies、Uniwind 配置以及 Provider 设置)。
**正在从 HeroUI v2 升级?** 请按照 [迁移指南](/docs/react/migration) 一步步完成升级。
## Figma Kit v3
HeroUI v3 中的每一个组件在 Figma 中都有 1:1 的对应实现。变体、命名与结构完全一致。整套 Kit 全程采用 auto layout,使用与代码 token 直接对应的 Figma 变量(`--accent`、`--surface`、`--radius`),并借助 Figma 新推出的 [slots](https://help.figma.com/hc/en-us/articles/38231200344599-Use-slots-to-build-flexible-components-in-Figma) 来实现灵活的组件组合。设计师可以像开发者写代码一样,自由调整、替换与定制组件的各个部件。
[获取 Figma Kit](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
## 致谢
[React Aria](https://react-aria.adobe.com/) 提供了我们自己难以做到这种水准的无障碍能力层。Tailwind CSS v4 原生的 CSS 变量方案塑造了我们整个主题系统。复合组件模式则是通过研究 [Radix](https://www.radix-ui.com/)、[Ark UI](https://ark-ui.com/) 与 [Base UI](https://base-ui.com/) 在组合性问题上的解法逐步打磨而来。
感谢每一位在 alpha 与 RC 阶段提交 issue、测试预发布版本以及反馈意见的社区成员。这套组件库因为你们而变得更好。
## 链接
* [React 文档](/docs/react/getting-started/quick-start)
* [React Native 文档](/docs/native/getting-started/quick-start)
* [主题构建器](/themes)
* [MCP 服务器](/docs/ui-for-agents/mcp-server)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [Figma Kit v3](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
# v3.0.2
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-2
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-2.mdx
> 修复了多个 bug,Drawer 过渡更平滑,新增 --backdrop 主题变量,并优化了 trigger、arrow 与 Tag 的样式。
2026 年 4 月 3 日
补丁版本,包含若干 bug 修复、样式优化,以及新增的 `--backdrop` 主题变量。Drawer 过渡已重写为原生 CSS,动画更加平滑。浮层触发器现在以 `inline-block` 形式呈现,Tooltip 和 Popover 的默认箭头形状也已更新。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@latest @heroui/react@latest
```
```bash
pnpm add @heroui/styles@latest @heroui/react@latest
```
```bash
yarn add @heroui/styles@latest @heroui/react@latest
```
```bash
bun add @heroui/styles@latest @heroui/react@latest
```
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增功能
### `--backdrop` 主题变量
新增用于浮层背景遮罩的 `--backdrop` CSS 变量([#6375](https://github.com/heroui-inc/heroui/pull/6375))。浅色主题默认值为 `rgba(0, 0, 0, 0.5)`,深色主题为 `rgba(0, 0, 0, 0.6)`。[Modal](/docs/components/modal)、[AlertDialog](/docs/components/alert-dialog) 与 [Drawer](/docs/components/drawer) 现在都引用该变量,而不再使用硬编码的值。
可以在全局进行覆写:
```css
:root {
--backdrop: rgba(0, 0, 0, 0.7);
}
```
也可以直接使用对应的工具类 `bg-backdrop`。
## 样式改进
### Trigger 改用 `inline-block` 显示
Popover、Tooltip、Dropdown、Modal、AlertDialog、Drawer 与 Disclosure 中的触发元素现在都会应用 `inline-block`,避免触发器包裹行内内容时出现布局塌陷的问题([#6373](https://github.com/heroui-inc/heroui/pull/6373))。
### Tooltip 与 Popover 的箭头
默认箭头的 SVG 路径已从二次贝塞尔曲线改为三次贝塞尔曲线,形状更平滑、更自然([#6372](https://github.com/heroui-inc/heroui/pull/6372))。
### Tag 的间距
为提升可读性,Tag 在 `sm`(由 `px-1` 改为 `px-2`)和 `md`(由 `px-1.5` 改为 `px-2`)尺寸下增加了水平内边距([#6315](https://github.com/heroui-inc/heroui/pull/6315))。
## Bug 修复
* **Autocomplete**:popover 上的 `--trigger-width` CSS 变量现在通过 `useResizeObserver` 跟踪触发元素的宽度,修复了下拉宽度不对齐的问题([#6374](https://github.com/heroui-inc/heroui/pull/6374))
* **Drawer**:面板过渡已从 Tailwind 的 `animate-in` / `animate-out` 重写为原生的 CSS `translate` 过渡,使各种放置位置下的开关动画都更加平滑([#6393](https://github.com/heroui-inc/heroui/pull/6393))
* **InputGroup**:secondary 变体的聚焦背景现在仅在实际的 input 或 textarea 获得焦点时才会触发,不再因组内任意可聚焦的子元素被聚焦而触发([#6362](https://github.com/heroui-inc/heroui/pull/6362))
* **Tag**:Tag 内部的 `CloseButton` 现在带有显式的 `aria-label="Remove tag"`,可被屏幕阅读器正确识别([#6341](https://github.com/heroui-inc/heroui/pull/6341))
## 链接
* [组件文档](/docs/react/components)
* [Figma Kit v3](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [GitHub PR #6364](https://github.com/heroui-inc/heroui/pull/6364)
## 贡献者
感谢每一位为本次发布做出贡献的开发者!
# v3.0.3
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-3
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-3.mdx
> 升级到 RAC 1.17(依赖减少 90%)、Table 支持可展开行、采用 Apache 2.0 协议、新增 useTheme Hook、DOM 多态 render-prop API,以及若干 bug 修复。
2026 年 4 月 17 日
补丁版本:升级到 React Aria Components 1.17(依赖数量减少 90%)、Table 支持可展开行、采用 Apache 2.0 协议,为 Vite 与 CRA 应用提供 `useTheme` Hook、新增可在 render prop 中替换元素的 DOM 多态工具函数,以及若干 bug 修复。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@latest @heroui/react@latest
```
```bash
pnpm add @heroui/styles@latest @heroui/react@latest
```
```bash
yarn add @heroui/styles@latest @heroui/react@latest
```
```bash
bun add @heroui/styles@latest @heroui/react@latest
```
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增功能
### React Aria Components 1.17
本次发布将 React Aria Components 升级到 [v1.17.0](https://react-aria.adobe.com/releases/v1-17-0)。最大亮点是依赖整合:**RAC 的传递依赖减少了 90%**,安装与构建都更快。详情请查看完整的 [RAC 1.17 发布说明](https://react-aria.adobe.com/releases/v1-17-0)。
### Table 可展开行
Table 现在支持以可展开行的形式呈现树形数据。设置 `treeColumn` 并在对应单元格中渲染一个 chevron 图标即可展开 / 折叠子行——非常适合用于文件浏览器、嵌套分类以及层级数据。
```tsx
"use client";
import type {Selection} from "@heroui/react";
import {Button, Table, cn} from "@heroui/react";
import {Icon} from "@iconify/react";
import {useState} from "react";
export function ExpandableRows() {
type Row = {
children: Row[];
date: string;
id: string;
title: string;
type: string;
};
const data: Row[] = [
{
children: [
{
children: [
{children: [], date: "7/10/2025", id: "3", title: "周报", type: "文件"},
{children: [], date: "8/20/2025", id: "4", title: "预算", type: "文件"},
],
date: "8/2/2025",
id: "2",
title: "项目",
type: "文件夹",
},
],
date: "10/20/2025",
id: "1",
title: "文档",
type: "文件夹",
},
{
children: [
{children: [], date: "1/23/2026", id: "6", title: "图片 1", type: "文件"},
{children: [], date: "2/3/2026", id: "7", title: "图片 2", type: "文件"},
],
date: "2/3/2026",
id: "5",
title: "照片",
type: "文件夹",
},
];
const [expandedKeys, setExpandedKeys] = useState(() => new Set(["1"]));
const renderExpandableRow = (item: Row) => {
return (
{({hasChildItems, isDisabled, isExpanded, isTreeColumn}) => (
{hasChildItems && isTreeColumn ? (
) : null}
{item.title}
)}
{item.type}
{item.date}
{renderExpandableRow}
);
};
return (
姓名
类型
修改日期
{renderExpandableRow}
);
}
```
### useTheme Hook
对于使用 Vite 或 Create React App 搭建(没有 Next.js 主题 Provider)的纯 React 应用,可以从 `@heroui/react` 中引入 `useTheme`。它接受任意主题名(`"light"`、`"dark"`、`"brutalism-light"` 等),传入 `"system"` 则会跟随操作系统偏好。`useTheme` 会将主题值持久化到 `localStorage`,并在 `` 元素上同步设置 `data-theme` 与 `class`,二者均为解析后的主题名。
```tsx
"use client";
import { Button, useTheme } from "@heroui/react";
export function ThemeSwitch() {
const { theme, setTheme } = useTheme("light");
return (
setTheme("light")}>
Light
setTheme("dark")}>
Dark
setTheme("system")}>
System
setTheme("brutalism-light")}>
Brutalism Light
Current: {theme}
);
}
```
### DOM 多态工具
DOM 多态辅助工具让那些没有基于 React Aria 基元构建的轻量级组件,也能通过 render prop 切换其宿主元素。
**示例:** 将 Card 组件渲染为 ` `
```tsx
render={(props) => } />
```
### 协议变更
HeroUI 现已采用 [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0) 协议,取代此前的 MIT 协议。Apache 2.0 同样提供宽松的使用自由,并在此基础上增加了显式的专利授权,提供更进一步的法律保护。现有用户无需进行任何操作。
## Bug 修复
* **Tabs**:限定 secondary 变体样式的作用范围,嵌套的 tab 组将不再继承父级变体([#6384](https://github.com/heroui-inc/heroui/pull/6384))
## 依赖更新
* **React Aria Components**:从 `1.16.0` 升级到 [`1.17.0`](https://react-aria.adobe.com/releases/v1-17-0)
* **@react-aria/utils**:从 `3.33.1` 升级到 `3.34.0`
* **@react-types/shared**:从 `3.33.1` 升级到 `3.34.0`
* **@internationalized/date**:从 `3.12.0` 升级到 `3.12.1`
## 链接
* [组件文档](/docs/react/components)
* [Figma Kit v3](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [GitHub PR #6441](https://github.com/heroui-inc/heroui/pull/6441)
## 贡献者
感谢每一位为本次发布做出贡献的开发者!
# v3.0.4
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-4
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-4.mdx
> 全新 Text 组件、文档主题选择器、采用 min() 上限约束的圆角设计令牌、Table 聚焦环重构,以及多项 bug 修复。
2026 年 5 月
补丁版本:从 HeroUI Pro 移植的全新 `Text` 复合组件;文档站新增主题选择器,可在不同主题下预览各组件;在约 45 个组件 CSS 文件中将圆角设计令牌改为使用 `min()` 上限约束;重做 Table 的聚焦环;并修复 Checkbox、Autocomplete、Tooltip 与表单字段内边距等问题。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@latest @heroui/react@latest
```
```bash
pnpm add @heroui/styles@latest @heroui/react@latest
```
```bash
yarn add @heroui/styles@latest @heroui/react@latest
```
```bash
bun add @heroui/styles@latest @heroui/react@latest
```
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增功能
### Text 组件
用于结构化排版的新复合组件,从 HeroUI Pro 移植 ([#6479](https://github.com/heroui-inc/heroui/pull/6479))。会根据子组件或 `type` prop 自动渲染正确的 HTML 元素——`` 至 ``、` ` 或 ``。
子组件:`Text.Heading`、`Text.Paragraph`、`Text.Code`、`Text.Prose`。
各子组件支持的 prop:`align`、`color`、`weight`、`truncate`。`Text.Heading` 支持 `level`(1–6)。`Text.Paragraph` 支持 `size`(`"base"`、`"sm"`、`"xs"`)。
使用 `Text.Prose` 包裹混合内容以获得自动的文章体间距:
```tsx
快速开始
安装依赖包并引入组件。
```
### 文档主题选择器
文档站现内置主题选择器,可在浅色、深色、粗野主义等多种主题下预览每个组件。你的选择会保存到 `localStorage`,在会话之间保持 ([#6471](https://github.com/heroui-inc/heroui/pull/6471))。
### 圆角设计令牌
约 45 个组件 CSS 文件中的 `rounded-full` 与硬编码 `border-radius` 现均通过 `min()` 限制计算后的半径 ([#6465](https://github.com/heroui-inc/heroui/pull/6465))。当用户设置过大的自定义圆角主题时,组件外观不再失真——半径在即将超过元素尺寸时停止增大。
### Table 聚焦环重构
表格行的聚焦指示改为在每个单元格上使用内阴影分别绘制,而非在整行使用单一 `box-shadow`。聚焦环在所有单元格之间视觉上保持连续,并在虚拟化表格包装器中正常工作。
## Bug 修复
* **Checkbox**:移除选中/半选指示态中硬编码的 `accent-hover` 背景 ([#6487](https://github.com/heroui-inc/heroui/pull/6487))
* **Autocomplete**:`isDisabled` 现通过 context 从根节点传递到 `Trigger` 与 `ClearButton` ([#6443](https://github.com/heroui-inc/heroui/pull/6443))
* **Description**:移除表单字段中说明文字的多余水平内边距(textfield、color-field、date-field、number-field、search-field、time-field) ([#6484](https://github.com/heroui-inc/heroui/pull/6484))
* **Tooltip**:内边距由 `px-2 py-1` 调整为 `p-2`,圆角改为使用设计令牌 ([#6481](https://github.com/heroui-inc/heroui/pull/6481))
* **Theme Builder**:修复 `accent-foreground` 取值被对调的问题 ([#6401](https://github.com/heroui-inc/heroui/pull/6401))
* **柔和色对比度**:浅色强调主题(Sky、Lavender、Mint)下,`accent-soft-foreground` 现使用更深一级的色阶,修复次要按钮、Chip 与 Badge 上文字几乎不可见的问题。共享主题现从 `--accent-soft-foreground` 读取,并回退到 `--accent`
## 链接
* [组件文档](/docs/react/components)
* [Figma Kit v3](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [GitHub PR #6483](https://github.com/heroui-inc/heroui/pull/6483)
## 贡献者
感谢每一位为本次发布做出贡献的开发者!
# v3.0.5
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-0-5
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-0-5.mdx
> Text 重命名为 Typography(破坏性变更),颜色令牌重构为无前缀源变量,修复 Checkbox 与 Radio 边框对齐,并新增 CLI 文档页。
2026 年 5 月 15 日
补丁版本:`Text` 重命名为 `Typography`,以解决 `tailwind-merge` 冲突导致变体类名被静默丢弃的问题。派生颜色令牌(`hover`、`soft`、`border-secondary` 等)从 `theme.css` 中的 `@theme inline` 别名迁移到 `variables.css` 作为无前缀源变量,组件 CSS 可直接引用。此外还修复了 Checkbox 与 Radio 的边框对齐问题,优化了 Calendar 的 `accent-soft-foreground` 悬停样式,并新增了 CLI 文档页。
⚠️ **破坏性变更**:`Text` → `Typography`。BEM 块名由 `text` 改为 `typography`(例如 `text--body-sm` → `typography--body-sm`)。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@latest @heroui/react@latest
```
```bash
pnpm add @heroui/styles@latest @heroui/react@latest
```
```bash
yarn add @heroui/styles@latest @heroui/react@latest
```
```bash
bun add @heroui/styles@latest @heroui/react@latest
```
**正在使用 AI 助手?** 只需对它说一句「Hey Cursor,把 HeroUI 升级到最新版本」,AI 助手就会自动对比版本并应用必要的变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## ⚠️ 破坏性变更
### `Text` → `Typography`
[v3.0.4](/docs/react/releases/v3-0-4) 中发布的该组件使用了 `text-*` BEM 修饰符,与 Tailwind 的 `text-*` 工具类家族冲突。`tailwind-variants` 在每次变体组合时都会运行 `tailwind-merge`,并将 `text--body-sm`、`text--color-muted`、`text--weight-normal` 去重为单个类名——从而静默丢弃其他类名。
将块名重命名为 `typography` 可永久解决该冲突 ([#6505](https://github.com/heroui-inc/heroui/pull/6505),修复 [#6497](https://github.com/heroui-inc/heroui/issues/6497))。
**之前:**
```tsx
import {Text} from "@heroui/react";
Hello world
;
```
**之后:**
```tsx
import {Typography} from "@heroui/react";
Hello world
;
```
子组件同样重命名:
| 之前 | 之后 |
| ---------------- | ---------------------- |
| `Text` | `Typography` |
| `Text.Heading` | `Typography.Heading` |
| `Text.Paragraph` | `Typography.Paragraph` |
| `Text.Code` | `Typography.Code` |
| `Text.Prose` | `Typography.Prose` |
CSS BEM 类名也会相应变更:`.text` → `.typography`,`.text--body-sm` → `.typography--body-sm` 等。完整类名参考请参阅 [Typography 文档](/docs/components/typography)。
```tsx
import {Typography} from "@heroui/react";
const scale = [
{
label: "h1",
meta: "36px / 600 / 1.11 / tight",
sample: "打造更出色的界面",
type: "h1" as const,
},
{
label: "h2",
meta: "30px / 600 / 1.17 / tight",
sample: "为智能时代而生",
type: "h2" as const,
},
{
label: "h3",
meta: "24px / 600 / 1.25 / tight",
sample: "按您的条件定价",
type: "h3" as const,
},
{
label: "h4",
meta: "20px / 600 / 1.33 / tight",
sample: "申请创业计划",
type: "h4" as const,
},
{
label: "h5",
meta: "18px / 600 / 1.39 / tight",
sample: "卡片标题",
type: "h5" as const,
},
{
label: "h6",
meta: "16px / 600 / 1.50 / tight",
sample: "较小的功能标题",
type: "h6" as const,
},
{
label: "body",
meta: "16px / 400 / 1.75",
sample: "用于文档、营销文案与描述的主要正文。",
type: "body" as const,
},
{
label: "body-sm",
meta: "14px / 400 / 1.50",
sample: "次要正文、表格单元格、导航与侧边栏项。",
type: "body-sm" as const,
},
{
label: "body-xs",
meta: "12px / 400 / 1.25",
sample: "说明文字、徽章、辅助文本与细则。",
type: "body-xs" as const,
},
{
label: "code",
meta: "14px / mono",
sample: "pnpm add @heroui/react",
type: "code" as const,
},
] as const;
export const TypographyScale = () => {
return (
{scale.map((row) => (
{row.label}
{row.meta}
{row.sample}
))}
);
};
```
## 样式重构
### 无前缀源颜色令牌
所有派生颜色令牌——`*-hover`、`*-soft`、`*-soft-foreground`、`border-secondary` 等——从 `theme.css` 中的 `@theme inline` 别名迁移到 `variables.css` 作为无前缀源变量,24 个组件 CSS 文件现直接引用源令牌 ([#6499](https://github.com/heroui-inc/heroui/pull/6499))。
**1. `color-mix` 公式从 `theme.css` 移至 `variables.css`**
之前——计算逻辑位于 `@theme inline` 块内:
```css
/* packages/styles/themes/shared/theme.css */
--color-accent-hover: color-mix(in oklab, var(--accent) 90%, var(--accent-foreground) 10%);
--color-accent-soft: var(--accent-soft, color-mix(in oklab, var(--accent) 15%, transparent));
--color-accent-soft-foreground: var(--accent-soft-foreground, var(--accent));
--color-border-secondary: color-mix(in oklab, var(--surface) 78%, var(--surface-foreground) 22%);
```
之后——`theme.css` 仅保留别名;公式位于 `variables.css`:
```css
/* packages/styles/themes/default/variables.css */
--accent-hover: color-mix(in oklab, var(--accent) 90%, var(--accent-foreground) 10%);
--accent-soft: color-mix(in oklab, var(--accent) 15%, transparent);
--accent-soft-foreground: var(--accent);
--border-secondary: color-mix(in oklab, var(--surface) 78%, var(--surface-foreground) 22%);
/* packages/styles/themes/shared/theme.css */
--color-accent-hover: var(--accent-hover);
--color-accent-soft: var(--accent-soft);
--color-accent-soft-foreground: var(--accent-soft-foreground);
--color-border-secondary: var(--border-secondary);
```
**2. 组件 CSS 直接使用源令牌**
之前——各处使用 `var(--color-*)` 引用:
```css
/* packages/styles/components/button.css */
.button--primary {
--button-bg: var(--color-accent);
--button-bg-hover: var(--color-accent-hover);
--button-fg: var(--color-accent-foreground);
}
.button--secondary {
--button-bg: var(--color-default);
--button-bg-hover: var(--color-default-hover);
--button-fg: var(--color-accent-soft-foreground);
}
```
之后——无前缀源令牌:
```css
/* packages/styles/components/button.css */
.button--primary {
--button-bg: var(--accent);
--button-bg-hover: var(--accent-hover);
--button-fg: var(--accent-foreground);
}
.button--secondary {
--button-bg: var(--default);
--button-bg-hover: var(--default-hover);
--button-fg: var(--accent-soft-foreground);
}
```
`@theme inline` 块仍将每个 `--color-*` 暴露为 Tailwind 工具类别名,因此用户侧的类名用法(`bg-accent`、`text-accent` 等)不受影响。
**3. Theme builder 合并精简**
三个派生令牌辅助函数(`getAccentDerivedVariables`、`getSemanticDerivedVariables`、`getFieldDerivedVariables`)合并为单一的 `getDerivedColorVariables()`,输出完整的无前缀源令牌集合,并包含 `darkenForSoftForeground` 逻辑,以确保浅色强调主题下 soft-foreground 文字仍清晰可读。
## 样式修复
* **Calendar / Range Calendar**:日期单元格默认悬停样式现使用 `accent-soft-foreground` 而非 `accent`,使浅色强调主题下悬停单元格仍清晰可读。文档站搜索标签的选中状态也做了相同调整 ([#6500](https://github.com/heroui-inc/heroui/pull/6500))。
* **Checkbox**:`.checkbox__control` 现已应用与 `.radio__control` 相同的基础样式:`border`、`border-field-border` 以及 `[border-width:var(--border-width-field)]`,并将 `border-color` 加入过渡属性列表 ([#6521](https://github.com/heroui-inc/heroui/pull/6521))。
* **Radio**:`.radio__control` 默认边框现使用 `border-field-border`,而非 Tailwind 通用的 `border` 颜色,与 Input、Select、TextArea、NumberField 保持一致 ([#6522](https://github.com/heroui-inc/heroui/pull/6522))。
## 文档
### CLI 页面
新增 [CLI 文档页](/docs/react/getting-started/cli),涵盖安装、`init`、`install`、`upgrade`、`uninstall`、`list`、`doctor` 与 `env` 命令及示例输出 ([#6498](https://github.com/heroui-inc/heroui/pull/6498))。
## 依赖项
各软件包版本升级 ([#6529](https://github.com/heroui-inc/heroui/pull/6529)):
* **`react` / `react-dom`**:`19.2.3` → `19.2.6`
* **`@types/react`**:`19.2.7` → `19.2.14`
* **`next`**(文档站):`16.1.1` → `16.2.6`
* **CI actions**:`actions/checkout@v6`、`actions/setup-node@v6`、`actions/cache@v5`、`pnpm/action-setup@v6`
## 链接
* [组件文档](/docs/react/components)
* [Figma Kit V3](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [GitHub PR #6503](https://github.com/heroui-inc/heroui/pull/6503)
## 贡献者
感谢所有为本次发布做出贡献的朋友!
# v3.1.0
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-1-0
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-1-0.mdx
> 中文 React 文档、可访问的 soft foreground 令牌、统一滚动条、useTheme SSR 修复、Toast 清理、Link 下划线与 RTL 布局优化。
import {HandPointUp} from "@gravity-ui/icons";
2026 年 5 月 25 日
v3.1.0 是小版本发布:新增中文 React 文档与本地化示例,soft foreground 默认达到无障碍对比度,滚动条统一由 `data-scrollbar` 和主题变量控制;同时修复 `useTheme`、Toast、Link、Fieldset、浮层和 RTL 布局问题。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@latest @heroui/react@latest
```
```bash
pnpm add @heroui/styles@latest @heroui/react@latest
```
```bash
yarn add @heroui/styles@latest @heroui/react@latest
```
```bash
bun add @heroui/styles@latest @heroui/react@latest
```
**正在使用 AI 助手?** 对它说「Hey Cursor,把 HeroUI 升级到最新版本」。它会对比版本并应用必要变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增内容
### 中文 React 文档
React 文档、迁移指南、发布说明和示例现在都有中文版本 ([#6533](https://github.com/heroui-inc/heroui/pull/6533))。组件页、入门指南和迁移参考都可以按 locale 发布。
### 可访问的 Soft Foreground 令牌
Soft 状态不再直接套语义色,而是使用专门的 foreground token,让 Badge、Chip、Alert、Toast、Avatar 和 Calendar 范围状态的文字对比度更稳定 ([#6548](https://github.com/heroui-inc/heroui/pull/6548))。
可直接覆盖这些 token:
* `--default-soft`
* `--default-soft-foreground`
* `--default-soft-hover`
* `--accent-soft-foreground`
* `--danger-soft-foreground`
* `--warning-soft-foreground`
* `--success-soft-foreground`
默认 palette 现在使用符合无障碍对比度的 soft foreground。需要旧版更饱和、但对比度更低的颜色时,在根元素启用 vibrant palette:
```html
...
```
```tsx
import {Chip, Separator} from "@heroui/react";
const variants = ["primary", "secondary", "tertiary", "soft"] as const;
const colors = ["accent", "default", "success", "warning", "danger"] as const;
const VARIANT_LABELS: Record<(typeof variants)[number], string> = {
primary: "主要",
secondary: "次要",
soft: "柔和",
tertiary: "第三",
};
const COLOR_LABELS: Record<(typeof colors)[number], string> = {
accent: "强调",
danger: "危险",
default: "默认",
success: "成功",
warning: "警告",
};
function ChipMatrix({isVibrant, title}: {isVibrant?: boolean; title: string}) {
return (
{title}
{colors.map((color) => (
{COLOR_LABELS[color]}
))}
{variants.map((variant) => (
{VARIANT_LABELS[variant]}
{colors.map((color) => (
{COLOR_LABELS[color]}
))}
))}
);
}
export function ChipVibrantPalette() {
return (
);
}
```
### 统一滚动条系统
滚动容器现在共享同一套标准 CSS 滚动条工具:主题 token 负责颜色和宽度,`data-scrollbar` 负责模式切换 ([#6545](https://github.com/heroui-inc/heroui/pull/6545))。组件滚动区域和自定义 overflow 区域都读取同一组 `--scrollbar-*` 变量。
三种模式:
* HeroUI 纤细:不设置 `data-scrollbar`,或设置 `data-scrollbar="thin"`。
* 浏览器默认:设置 `data-scrollbar="default"`,使用操作系统 / 浏览器滚动条。
* 隐藏:设置 `data-scrollbar="none"`,隐藏滚动条但保留滚动。
```html
使用 HeroUI 主题滚动条
使用浏览器默认滚动条
隐藏后代 HeroUI 滚动条
```
垂直滚动
```tsx
import {ListBox, Surface} from "@heroui/react";
type ScrollbarMode = {
id: string;
label: string;
scrollbar?: "thin" | "default" | "none";
};
const modes: ScrollbarMode[] = [
{
id: "heroui",
label: "HeroUI 纤细",
scrollbar: "thin",
},
{
id: "browser",
label: "浏览器默认",
scrollbar: "default",
},
{
id: "hidden",
label: "隐藏",
scrollbar: "none",
},
];
const animals = [
{id: "aardvark", name: "土豚"},
{id: "alpaca", name: "羊驼"},
{id: "antelope", name: "羚羊"},
{id: "bear", name: "熊"},
{id: "cat", name: "猫"},
{id: "dog", name: "狗"},
{id: "fox", name: "狐狸"},
{id: "giraffe", name: "长颈鹿"},
{id: "kangaroo", name: "袋鼠"},
{id: "koala", name: "考拉"},
{id: "lemur", name: "狐猴"},
{id: "otter", name: "水獭"},
{id: "panda", name: "熊猫"},
{id: "penguin", name: "企鹅"},
{id: "rabbit", name: "兔子"},
{id: "snake", name: "蛇"},
{id: "turtle", name: "海龟"},
{id: "wombat", name: "袋熊"},
{id: "zebra", name: "斑马"},
];
function ScrollbarListBox({mode}: {mode: ScrollbarMode}) {
return (
{mode.label}
{animals.map((animal) => (
{animal.name}
))}
);
}
export function ScrollbarModes() {
return (
{modes.map((mode) => (
))}
);
}
```
Select、ComboBox、Autocomplete、Dropdown、DatePicker、DateRangePicker、ColorPicker、Table、Tabs、Modal、Drawer 和 ScrollShadow 已接入同一套工具。
自定义滚动插槽使用 `scrollbar`。它读取最近的 `data-scrollbar` 祖先,并自动应用对应的 `--scrollbar-width`、`--scrollbar-color` 和 `--scrollbar-gutter`。
```css
.alert-dialog__body {
@apply min-h-0 flex-1 scrollbar;
}
```
主题变量也从单个固定的 `--scrollbar` 颜色,改为一组更细的滚动条 token:
```css
/* before */
--scrollbar: oklch(70.5% 0.015 286.067);
/* after */
--scrollbar: var(--scrollbar-thumb);
--scrollbar-thumb: color-mix(in oklch, var(--foreground) 15%, transparent);
--scrollbar-track: transparent;
--scrollbar-gutter: auto;
--scrollbar-width: thin;
--scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
```
`--scrollbar` 保留为兼容别名;新的 thumb、track、gutter、width 和 color token 控制最终滚动条。
恢复浏览器默认滚动条:在 ``、`` 或任意嵌套容器上设置 `data-scrollbar="default"`。如果全局已设为默认,但某个局部仍要 HeroUI 样式,在该容器上加 `data-scrollbar="thin"`。
```html
```
## 组件与运行时修复
* **Fieldset**:`disabled` 会传给字段标签,并禁用后代 React Aria `RadioGroup` 与 `Slider` ([#6547](https://github.com/heroui-inc/heroui/pull/6547))。
* **Toast**:非前台 Toast 不再进入 Tab 顺序;卸载时清理已测量高度 ([#6510](https://github.com/heroui-inc/heroui/pull/6510), [#6512](https://github.com/heroui-inc/heroui/pull/6512))。
* **`useTheme`**:SSR 不再读取浏览器 API;`resolvedTheme` 改为派生值;系统主题订阅改用 `useSyncExternalStore` ([#6561](https://github.com/heroui-inc/heroui/pull/6561))。
* **Link**:默认无下划线;hover 使用 50% 装饰色;active / pressed 使用 100%;移除下划线装饰色过渡 ([#6570](https://github.com/heroui-inc/heroui/pull/6570), [#6571](https://github.com/heroui-inc/heroui/pull/6571))。
## 布局、浮层与 RTL 修复
* **浮层定位**:进入动画只过渡 `opacity` 和 `transform`,不再动画化 React Aria 写入的定位值 ([#6549](https://github.com/heroui-inc/heroui/pull/6549))。
* **Dialog 与 Modal 聚焦**:Modal 和 AlertDialog 内容改用裁剪处理,避免 focus 触发程序化滚动;body focus ring 不再被 overflow 裁掉 ([#6448](https://github.com/heroui-inc/heroui/pull/6448), [#6557](https://github.com/heroui-inc/heroui/pull/6557))。
* **RTL Table 圆角**:Table 改用逻辑方向的 `border-radius`,RTL 外侧圆角保持正确 ([#6568](https://github.com/heroui-inc/heroui/pull/6568))。
* **RTL Picker 与 Menu 指示器**:Select、ListBox.Item、Autocomplete、ComboBox 和 MenuItem 改用 logical inline start/end,覆盖 chevron、value、checkmark、trigger 和 submenu indicator ([#6573](https://github.com/heroui-inc/heroui/pull/6573))。
## 文档与依赖
* 主题文档同步当前 `theme.css` / `variables.css`:soft foreground 令牌和滚动条变量都已更新。
* 发布说明和迁移文档同步 Link 行为。
* 文档站新增 `@fumadocs/language`;`fumadocs-core` / `fumadocs-ui` 升级到 `16.9.0`。
* 文档和样式包的 Tailwind 工具升级到 `4.3.0`。
## 链接
* [组件文档](/docs/react/components)
* [主题文档](/docs/react/getting-started/theming)
* [Figma Kit V3](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [GitHub PR #6576](https://github.com/heroui-inc/heroui/pull/6576)
## 贡献者
感谢所有为本次发布做出贡献的朋友!
# v3.2.0
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/releases/v3-2-0
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/releases/v3-2-0.mdx
> 基于 React Aria 1.18 的 Calendar 周/日视图与年份选择器,以及 Radio、Checkbox、Switch 的破坏性组合方式变更。
2026 年 6 月 6 日
Calendar 新增周视图与日视图、重做的年份选择器,以及基于 React Aria 1.18 的范围日历演示。[Tooltip](/docs/components/tooltip) 新增用于全局显示与隐藏延迟的主题变量。Radio、Checkbox、Switch 迁移到 React Aria 的 `*Field` + `*Button` 组合方式。本次发布同时纳入了随 React Aria 1.18 一起发布的补丁修复(`Table.SortableColumnHeader`、Toast 与 Fieldset 修复、滚动与 RTL 样式修复)。
⚠️ **破坏性变更**:`Radio`、`Checkbox`、`Switch` 改为显式的 `*.Content` 组合 —— `*.Control` 嵌套进 `*.Content`,标签变为 `*.Content` 内的纯文本(不嵌套 ``),`Description`/`FieldError` 变为 `*.Content` 的兄弟节点。详见[破坏性变更](#-breaking-changes)。
## 安装
升级到最新版本:
```bash
npm i @heroui/styles@latest @heroui/react@latest
```
```bash
pnpm add @heroui/styles@latest @heroui/react@latest
```
```bash
yarn add @heroui/styles@latest @heroui/react@latest
```
```bash
bun add @heroui/styles@latest @heroui/react@latest
```
**正在使用 AI 助手?** 对它说「Hey Cursor,把 HeroUI 升级到最新版本」。它会对比版本并应用必要变更。了解更多请参阅 [HeroUI MCP 服务器](/docs/ui-for-agents/mcp-server)。
## 新增内容
### Calendar
`Calendar` 与 `RangeCalendar` 新增周视图与日视图,以及来自 React Aria 1.18 的新日历属性。
* **周视图 / 日视图**:通过 `visibleDuration` 渲染多周或单日布局
* **多选**:在单个 `Calendar` 中选择多个日期
* **React Aria 1.18 属性**:`weeksInMonth` 与用于范围选择的 `isDateUnavailable(date, anchorDate)`
* **内部实现**:月份标题使用 React Aria 的 `CalendarHeading`,年份选择器基于 React Aria calendar hooks 重建
**周视图:**
```tsx
"use client";
import {Calendar, Label, ListBox, Select} from "@heroui/react";
import {useState} from "react";
const weekOptions = [
{id: "1", name: "1 周"},
{id: "2", name: "2 周"},
{id: "3", name: "3 周"},
{id: "4", name: "4 周"},
{id: "5", name: "5 周"},
{id: "6", name: "6 周"},
{id: "8", name: "8 周"},
] as const;
export function WeekView() {
const [weeks, setWeeks] = useState(1);
return (
value && setWeeks(Number(value))}
>
可见周数
{weekOptions.map((option) => (
{option.name}
))}
{(day) => {day} }
{(date) => }
);
}
```
**日视图:**
```tsx
"use client";
import {Calendar, Label, ListBox, Select} from "@heroui/react";
import {useState} from "react";
const dayOptions = [
{id: "1", name: "1 天"},
{id: "5", name: "5 天"},
{id: "7", name: "7 天"},
{id: "8", name: "8 天"},
{id: "10", name: "10 天"},
{id: "14", name: "14 天"},
{id: "21", name: "21 天"},
] as const;
export function DayView() {
const [days, setDays] = useState(5);
return (
value && setDays(Number(value))}
>
可见天数
{dayOptions.map((option) => (
{option.name}
))}
{(day) => {day} }
{(date) => }
);
}
```
**多选:**
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Calendar, Description} from "@heroui/react";
import {useState} from "react";
export function MultipleSelection() {
const [value, setValue] = useState([]);
return (
{(day) => {day} }
{(date) => }
{value?.length ? `已选择 ${value.length} 个日期` : "可选择多个日期"}
);
}
```
**多选:**
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Calendar, Description} from "@heroui/react";
import {useState} from "react";
export function MultipleSelection() {
const [value, setValue] = useState([]);
return (
{(day) => {day} }
{(date) => }
{value?.length ? `已选择 ${value.length} 个日期` : "可选择多个日期"}
);
}
```
### Table.SortableColumnHeader
`Table.SortableColumnHeader` 用于渲染 sortable 列标题和可选的升降序指示器。放在 `Table.Column` render prop 中,并传入 `sortDirection` ([#6588](https://github.com/heroui-inc/heroui/pull/6588))。
```tsx
{({sortDirection}) => (
Name
)}
```
* **默认指示器**:存在排序方向时显示 chevron
* **自定义指示器**:传入 `indicator`,或用 `showIndicator={false}` 隐藏
* **样式插槽**:`.table__sortable-column-header` + `.table__sortable-column-indicator`
```tsx
"use client";
import type {SortDescriptor} from "@heroui/react";
import {Table} from "@heroui/react";
import {useMemo, useState} from "react";
interface User {
id: number;
name: string;
role: string;
status: string;
email: string;
}
const users: User[] = [
{email: "kate@acme.com", id: 1, name: "Kate Moore", role: "首席执行官", status: "在职"},
{email: "john@acme.com", id: 2, name: "John Smith", role: "首席技术官", status: "在职"},
{email: "sara@acme.com", id: 3, name: "Sara Johnson", role: "首席营销官", status: "休假"},
{email: "michael@acme.com", id: 4, name: "Michael Brown", role: "首席财务官", status: "在职"},
{
email: "emily@acme.com",
id: 5,
name: "Emily Davis",
role: "产品经理",
status: "未激活",
},
];
export function Sorting() {
const [sortDescriptor, setSortDescriptor] = useState({
column: "name",
direction: "ascending",
});
const sortedUsers = useMemo(() => {
return [...users].sort((a, b) => {
const col = sortDescriptor.column as keyof User;
const first = String(a[col]);
const second = String(b[col]);
let cmp = first.localeCompare(second);
if (sortDescriptor.direction === "descending") {
cmp *= -1;
}
return cmp;
});
}, [sortDescriptor]);
return (
{({sortDirection}) => (
姓名
)}
{({sortDirection}) => (
角色
)}
{({sortDirection}) => (
状态
)}
{({sortDirection}) => (
邮箱
)}
{sortedUsers.map((user) => (
{user.name}
{user.role}
{user.status}
{user.email}
))}
);
}
```
### Tooltip 延迟主题变量
[Tooltip](/docs/components/tooltip) 现在会从主题 CSS 变量读取默认的显示与隐藏延迟 ([#6617](https://github.com/heroui-inc/heroui/pull/6617)):
* `--tooltip-delay` — 显示 Tooltip 前的延迟(默认:`1500ms`)
* `--tooltip-close-delay` — 隐藏 Tooltip 前的延迟(默认:`500ms`)
全局覆盖示例:
```css
:root {
--tooltip-delay: 700ms;
--tooltip-close-delay: 0ms;
}
```
单个 Tooltip 上的 `delay` 和 `closeDelay` 属性仍会覆盖这些值。
**行为变更:** 使用默认 HeroUI 主题时,Tooltip 延迟现在默认为 `1500ms` / `500ms`,而不是之前的 React Aria 默认值 `700ms` / `0ms`。如需保留旧行为,请显式设置 CSS 变量或 props。
## 组件修复
* **Toast**:Toast 队列会串行化 ViewTransition 更新,避免在 `toast.promise()` 关闭 loading toast 并打开成功/失败反馈时出现被跳过的过渡和 AbortError ([#6511](https://github.com/heroui-inc/heroui/pull/6511))。
* **Fieldset**:`disabled` 会传递到 React Aria 的 Button、CheckboxGroup、Link、RadioGroup、Slider、ToggleButton 和 ToggleButtonGroup 上下文 ([#6596](https://github.com/heroui-inc/heroui/pull/6596))。
## 样式修复
* **Modal / AlertDialog**:`scroll-inside` 对话框通过 `max-h-full min-h-0` 限制高度,使内容区域滚动而非溢出 ([#6597](https://github.com/heroui-inc/heroui/pull/6597))。
* **ScrollShadow**:渐隐遮罩通过 `--scroll-shadow-scrollbar-size` 为可见的原生滚动条预留空间 ([#6598](https://github.com/heroui-inc/heroui/pull/6598))。
* **Table RTL**:列分隔线与拖拽手柄在 RTL 下使用逻辑属性 `end-0` 定位 ([#6606](https://github.com/heroui-inc/heroui/pull/6606))。
* **Link**:移除硬编码的 `text-sm`,让链接从父元素继承字号;`.link__icon` 改为相对单位 `size-[0.75em]`,随文本大小缩放,而不再使用固定的 `size-2` ([#6621](https://github.com/heroui-inc/heroui/pull/6621))。
* **DatePicker / DateRangePicker**:日历 popover 由 `max-w-(--trigger-width)` 改为 `min-w-(--trigger-width)`,确保 popover 至少与触发器一样宽,避免被水平裁剪 ([#6622](https://github.com/heroui-inc/heroui/pull/6622))。
* **Table**:当 Table 被 React Aria 的 `` 包裹时,secondary 表头的边框与圆角能够正确渲染 —— 列选择器不再把每个虚拟化列都同时视为 first 和 last child ([#6624](https://github.com/heroui-inc/heroui/pull/6624))。
## 依赖
* **React Aria Components**:`1.17.0` → `1.18.0` ([#6586](https://github.com/heroui-inc/heroui/pull/6586))。1.18 引入了 toggles 所采用的 `*Field` + `*Button` 组合方式、`CalendarHeading`,以及 `isDateUnavailable(date, anchorDate)`。
* **@internationalized/date**:`3.12.1` → `3.12.2`
* **React Aria / Stately 辅助包**:`@react-aria/*`、`@react-stately/*` 与 `@react-types/shared` 补丁更新
## ⚠️ Breaking Changes
### Radio、Checkbox 与 Switch:显式 `*.Content` 组合
这些组件现在在底层使用 React Aria 的 `*Field` + `*Button` 组合方式。`X.Content` 现在是**可点击的 label**(React Aria 的 `*Button`)。共有三点变化:
* **`X.Control` 移进 `X.Content`** —— 它们以前是兄弟节点。
* **标签是 `X.Content` 内部的纯文本** —— `X.Content` 渲染的是 `` 元素,所以不要嵌套 `` 组件(嵌套的 `` 是无效 HTML)。若要使用独立的 `Label`,请把它放在**外部**,并用 `htmlFor` + 组件 `id` 关联。
* **`Description`/`FieldError` 移到外部**,作为 `X.Content` 的兄弟节点,这样它们会通过 `aria-describedby` 朗读,而不会被并入无障碍名称。
**Checkbox**
```tsx
// v3.1
Accept terms
You agree to our terms
// v3.2
Accept terms
You agree to our terms
```
**Radio**
```tsx
// v3.1
Option A
// v3.2
Option A
```
**Switch**
```tsx
// v3.1
Enable notifications
// v3.2
Enable notifications
```
**迁移对照**
| v3.1 | v3.2 |
| --------------------------------------------- | ------------------------------------------------- |
| `X.Control` 与 `X.Content` 为兄弟节点 | `X.Control` 嵌套进 `X.Content` |
| `X.Content` 是包裹 `Label` + 帮助文本的布局 `` | `X.Content` 是包裹 `X.Control` + 标签文本的可点击 `
` |
| 通过 `X.Content` 内的 `` 提供标签 | 标签是 `X.Content` 内的**纯文本**(不嵌套 ``) |
| `Description` / `FieldError` 位于 `X.Content` 内 | `Description` / `FieldError` 作为 `X.Content` 的兄弟节点 |
**外部标签** —— 若要使用独立的 `Label`,请把它放在组件外部,并用 `htmlFor` + 组件 `id` 关联:
```tsx
Accept terms
```
**仅含控件的 Checkbox 和 Switch**(没有标签,例如表格行选择或图标开关)仍需用 `X.Content` 作为可点击包装。请把 `Checkbox.Control` / `Switch.Control` 放进 `X.Content`,省略标签,并在根组件上传入 `aria-label`。
各组件完整迁移指南:[Checkbox](/docs/react/migration/checkbox)、[Checkbox Group](/docs/react/migration/checkbox-group)、[Radio](/docs/react/migration/radio)、[Radio Group](/docs/react/migration/radio-group)、[Switch](/docs/react/migration/switch)。
## 链接
* [Calendar 文档](/docs/react/components/calendar)
* [Tooltip 文档](/docs/react/components/tooltip)
* [Checkbox 文档](/docs/react/components/checkbox)
* [Radio Group 文档](/docs/react/components/radio-group)
* [Switch 文档](/docs/react/components/switch)
* [组件文档](/docs/react/components)
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [GitHub PR #6616](https://github.com/heroui-inc/heroui/pull/6616)
## 贡献者
感谢所有为本次发布做出贡献的人!
# ButtonGroup 按钮组
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/button-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(buttons)/button-group.mdx
> 将相关按钮组合在一起,并提供一致的样式与间距。
## 引入
```tsx
import { ButtonGroup, Button } from '@heroui/react';
```
### 用法
```tsx
import {
ChevronDown,
ChevronLeft,
ChevronRight,
CodeFork,
Ellipsis,
Picture,
Pin,
QrCode,
Star,
TextAlignCenter,
TextAlignJustify,
TextAlignLeft,
TextAlignRight,
ThumbsDown,
ThumbsUp,
Video,
} from "@gravity-ui/icons";
import {Button, ButtonGroup, Chip, Description, Dropdown, Label} from "@heroui/react";
export function Basic() {
return (
{/* 单个按钮与下拉菜单 */}
合并拉取请求
创建合并提交
此分支上的所有提交都将加入基础分支
压缩并合并
此分支上的 14 个提交将合并为一次提交并加入基础分支
变基并合并
此分支上的 14 个提交将变基后加入基础分支
{/* 独立按钮 */}
复刻
24
扫码支付
2.4K
星标
104
已置顶
{/* 上一页 / 下一页 */}
上一页
下一页
{/* 内容类型选择 */}
{/* 文本对齐 */}
左对齐
居中
右对齐
{/* 仅图标:对齐 */}
);
}
```
### 组件结构
导入 ButtonGroup 组件后,可通过点语法访问所有子部分。
```tsx
import { ButtonGroup, Button } from '@heroui/react';
export default () => (
First
Second
Third
);
```
> **ButtonGroup** 将多个 Button 组件包裹在一起,应用一致的样式、间距以及自动圆角处理。它使用 React Context 将 `size`、`variant` 与 `isDisabled` props 传递给所有子按钮。
### 变体
```tsx
import {Button, ButtonGroup} from "@heroui/react";
export function Variants() {
return (
);
}
```
### 尺寸
```tsx
import {Button, ButtonGroup} from "@heroui/react";
export function Sizes() {
return (
);
}
```
### 方向
使用 `orientation` prop 将按钮按水平或垂直方向排列。
```tsx
import {TextAlignCenter, TextAlignJustify, TextAlignLeft, TextAlignRight} from "@gravity-ui/icons";
import {Button, ButtonGroup} from "@heroui/react";
export function Orientation() {
return (
);
}
```
### 带图标
```tsx
import {Globe, Plus, TrashBin} from "@gravity-ui/icons";
import {Button, ButtonGroup} from "@heroui/react";
export function WithIcons() {
return (
);
}
```
### 全宽
```tsx
import {TextAlignCenter, TextAlignLeft, TextAlignRight} from "@gravity-ui/icons";
import {Button, ButtonGroup} from "@heroui/react";
export function FullWidth() {
return (
第一项
第二项
第三项
);
}
```
### 禁用状态
```tsx
import {Button, ButtonGroup} from "@heroui/react";
export function Disabled() {
return (
组已禁用,但单个按钮可覆盖
第一项
第二项
第三项(可用)
);
}
```
### 无分隔线
直接在按钮中省略 ` ` 组件即可。
```tsx
import {Button, ButtonGroup} from "@heroui/react";
export function WithoutSeparator() {
return (
第一项
第二项
第三项
);
}
```
## Related Components
* **Button**: Allows a user to perform an action
* **Dropdown**: Context menu with actions and options
* **Chip**: Compact elements for tags and filters
## 样式
### 传入 Tailwind CSS 类
```tsx
import { ButtonGroup, Button } from '@heroui/react';
function CustomButtonGroup() {
return (
First
Second
Third
);
}
```
### 自定义组件类
要自定义 ButtonGroup 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.button-group {
@apply gap-2 rounded-lg;
}
.button-group__separator {
@apply opacity-25;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
ButtonGroup 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/button-group.css)):
#### 基础类
* `.button-group` - 按钮组容器的基础样式
* `.button-group--full-width` - 全宽修饰符
* `.button-group__separator` - 按钮之间的分隔线元素
ButtonGroup 会自动为按钮处理圆角:
* 第一个按钮获得左侧/起始侧圆角
* 最后一个按钮获得右侧/结束侧圆角
* 中间按钮不带圆角
* 仅有一个按钮时,四边都会应用完整圆角
在每个 Button(第一个除外)内部添加 ` `,即可在按钮之间显示分隔线。
## API 参考
### ButtonGroup Props
| Prop | 类型 | 默认值 | 描述 |
| ------------- | --------------------------------------------------------------- | -------------- | --------------------- |
| `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'ghost' \| 'danger'` | - | 应用于组内所有按钮的视觉变体 |
| `size` | `'sm' \| 'md' \| 'lg'` | - | 应用于组内所有按钮的尺寸 |
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | 按钮组的排列方向 |
| `fullWidth` | `boolean` | `false` | 按钮组是否占满容器宽度 |
| `isDisabled` | `boolean` | `false` | 是否禁用组内全部按钮(可在单个按钮上覆盖) |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `React.ReactNode` | - | 需要组合在一起的按钮组件 |
### ButtonGroup.Separator Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------- | --- | --------- |
| `className` | `string` | - | 额外的 CSS 类 |
### 说明
* ButtonGroup 使用 React Context 将 `size`、`variant` 与 `isDisabled` props 传递给所有子 Button 组件
* **只有直接子级按钮会接收 ButtonGroup 的 props**:即使某个按钮是 ButtonGroup 的后代,只要它嵌套在其他组件(如 Modal、Dropdown)中,就不会继承组级 props
* 单个 Button 可通过设置 `isDisabled={false}` 覆盖组级别的 `isDisabled`
* 组件会自动处理按钮之间的圆角
* 在每个 Button(第一个除外)中添加 ` ` 可显示分隔线
* 按钮组中的按钮会移除激活/按压时的缩放变换,以获得更统一的视觉效果
# Button 按钮
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/button
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(buttons)/button.mdx
> 可点击的按钮组件,支持多种变体与状态。
## 引入
```tsx
import { Button } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {Button} from "@heroui/react";
export function Basic() {
return console.log("按钮已按下")}>点我 ;
}
```
### 变体
```tsx
import {Button} from "@heroui/react";
export function Variants() {
return (
主要
次要
第三
线框
幽灵
危险
柔和危险
);
}
```
### 带图标
```tsx
import {Envelope, Globe, Plus, TrashBin} from "@gravity-ui/icons";
import {Button} from "@heroui/react";
export function WithIcons() {
return (
);
}
```
### 仅图标
```tsx
import {Ellipsis, Gear, TrashBin} from "@gravity-ui/icons";
import {Button} from "@heroui/react";
export function IconOnly() {
return (
);
}
```
### 加载中
```tsx
"use client";
import {Button, Spinner} from "@heroui/react";
import React from "react";
export function Loading() {
return (
{({isPending}) => (
<>
{isPending ? : null}
上传中…
>
)}
);
}
```
### 加载状态
```tsx
"use client";
import {Paperclip} from "@gravity-ui/icons";
import {Button, Spinner} from "@heroui/react";
import React, {useState} from "react";
export function LoadingState() {
const [isLoading, setLoading] = useState(false);
const handlePress = () => {
setLoading(true);
setTimeout(() => setLoading(false), 2000);
};
return (
{({isPending}) => (
<>
{isPending ? : }
{isPending ? "上传中…" : "上传文件"}
>
)}
);
}
```
### 尺寸
```tsx
import {Button} from "@heroui/react";
export function Sizes() {
return (
小
中
大
);
}
```
### 全宽
```tsx
import {Plus} from "@gravity-ui/icons";
import {Button} from "@heroui/react";
export function FullWidth() {
return (
);
}
```
### 禁用状态
```tsx
import {Button} from "@heroui/react";
export function Disabled() {
return (
主要
次要
第三
线框
幽灵
危险
);
}
```
### 社交按钮
```tsx
import {Button} from "@heroui/react";
import {Icon} from "@iconify/react";
export function Social() {
return (
使用 Google 登录
使用 GitHub 登录
使用 Apple 登录
);
}
```
### 自定义渲染函数
```tsx
"use client";
import {Button} from "@heroui/react";
export function CustomRenderFunction() {
return (
(
)}
>
点按
);
}
```
## Related Components
* **Popover**: Displays content in context with a trigger
* **Tooltip**: Contextual information on hover or focus
* **Form**: Form validation and submission handling
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Button } from '@heroui/react';
function CustomButton() {
return (
Purple Button
);
}
```
### 自定义组件类
若要自定义 Button 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.button {
@apply bg-purple-500 text-white hover:bg-purple-600;
}
.button--icon-only {
@apply rounded-lg bg-blue-500;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### 添加自定义变体
你可以通过封装 HeroUI 组件并添加自定义变体来扩展其能力。
```tsx
import type {ButtonProps} from "@heroui/react";
import type {VariantProps} from "tailwind-variants";
import {Button, buttonVariants} from "@heroui/react";
import {tv} from "tailwind-variants";
const myButtonVariants = tv({
base: "text-md font-semibold shadow-md text-shadow-lg data-[pending=true]:opacity-40",
defaultVariants: {
radius: "full",
variant: "primary",
},
extend: buttonVariants,
variants: {
radius: {
full: "rounded-full",
lg: "rounded-lg",
md: "rounded-md",
sm: "rounded-sm",
},
size: {
lg: "h-12 px-8",
md: "h-11 px-6",
sm: "h-10 px-4",
xl: "h-13 px-10",
},
variant: {
primary: "text-white dark:bg-white/10 dark:text-white dark:hover:bg-white/15",
},
},
});
type MyButtonVariants = VariantProps;
export type MyButtonProps = Omit &
MyButtonVariants & {className?: string};
function CustomButton({className, radius, variant, ...props}: MyButtonProps) {
return ;
}
export function CustomVariants() {
return 自定义按钮 ;
}
```
### 添加涟漪效果
Button 组件支持通过组合方式实现涟漪效果,你可以将涟漪组件作为子节点嵌套。此示例使用 [m3-ripple](https://github.com/saltyaom/m3-ripple)。
```tsx
"use client";
import {Button} from "@heroui/react";
import {Ripple} from "m3-ripple";
import "m3-ripple/ripple.css";
export function RippleEffect() {
return (
点我
);
}
```
### CSS 类
Button 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/button.css)):
#### 基础与尺寸类
* `.button` - 按钮基础样式
* `.button--sm` - 小尺寸变体
* `.button--md` - 中尺寸变体
* `.button--lg` - 大尺寸变体
#### 变体类
* `.button--primary`
* `.button--secondary`
* `.button--tertiary`
* `.button--outline`
* `.button--ghost`
* `.button--danger`
#### 修饰符类
* `.button--icon-only`
* `.button--icon-only.button--sm`
* `.button--icon-only.button--lg`
### 交互状态
该按钮同时支持 CSS 伪类与 data 属性,以提供更灵活的状态控制:
* **悬停**:`:hover` 或 `[data-hovered="true"]`
* **激活/按压**:`:active` 或 `[data-pressed="true"]`(包含缩放变换)
* **聚焦**:`:focus-visible` 或 `[data-focus-visible="true"]`(显示焦点环)
* **禁用**:`:disabled` 或 `[aria-disabled="true"]`(降低透明度,禁用指针事件)
* **等待中**:`[data-pending]`(加载期间禁用指针事件)
## API 参考
### Button Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | ---------------------------------------------------------------------------- | ----------- | --------------------- |
| `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'outline' \| 'ghost' \| 'danger'` | `'primary'` | 视觉样式变体 |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 按钮尺寸 |
| `fullWidth` | `boolean` | `false` | 按钮是否占满容器宽度 |
| `isDisabled` | `boolean` | `false` | 按钮是否禁用 |
| `isPending` | `boolean` | `false` | 按钮是否处于加载状态 |
| `isIconOnly` | `boolean` | `false` | 按钮是否仅包含图标 |
| `onPress` | `(e: PressEvent) => void` | - | 按钮被按下时的事件处理函数 |
| `children` | `React.ReactNode \| (values: ButtonRenderProps) => React.ReactNode` | - | 按钮内容或渲染 prop |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### ButtonRenderProps
使用渲染 prop 模式时,会提供以下值:
| Prop | 类型 | 描述 |
| ---------------- | --------- | ----------- |
| `isPending` | `boolean` | 按钮是否处于加载状态 |
| `isPressed` | `boolean` | 按钮当前是否被按压 |
| `isHovered` | `boolean` | 按钮是否处于悬停状态 |
| `isFocused` | `boolean` | 按钮是否处于聚焦状态 |
| `isFocusVisible` | `boolean` | 按钮是否应显示焦点指示 |
| `isDisabled` | `boolean` | 按钮是否禁用 |
# CloseButton 关闭按钮
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/close-button
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(buttons)/close-button.mdx
> 用于关闭对话框、模态框或收起内容的按钮组件。
## 引入
```tsx
import { CloseButton } from "@heroui/react";
```
### 用法
```tsx
import {CloseButton} from "@heroui/react";
export function Default() {
return ;
}
```
### 自定义图标
```tsx
import {CircleXmark, Xmark} from "@gravity-ui/icons";
import {CloseButton} from "@heroui/react";
export function WithCustomIcon() {
return (
);
}
```
### 交互
```tsx
"use client";
import {CloseButton} from "@heroui/react";
import {useState} from "react";
export function Interactive() {
const [count, setCount] = useState(0);
return (
setCount(count + 1)} />
已点击:{count} 次
);
}
```
## Related Components
* **Alert**: Display important messages and notifications
* **AlertDialog**: Critical confirmations requiring user attention
* **Chip**: Compact elements for tags and filters
## 样式
### 传入 Tailwind CSS 类
```tsx
import {CloseButton} from "@heroui/react";
function CustomCloseButton() {
return Close ;
}
```
### 自定义组件类
要自定义 CloseButton 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.close-button {
@apply bg-red-100 text-red-800 hover:bg-red-200;
}
.close-button--custom {
@apply rounded-full border-2 border-red-300;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
CloseButton 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/close-button.css)):
#### 基础类
* `.close-button` - 组件基础样式
#### 变体类
* `.close-button--default` - 默认变体
### 交互状态
该组件同时支持 CSS 伪类与 data 属性,便于灵活编写样式:
* **悬停**:`:hover` 或 `[data-hovered="true"]`
* **激活/按压**:`:active` 或 `[data-pressed="true"]`
* **聚焦**:`:focus-visible` 或 `[data-focus-visible="true"]`
* **禁用**:`:disabled` 或 `[aria-disabled="true"]`
## API 参考
### CloseButton Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | ----------------------- | --------------- | -------------- |
| `variant` | `"default"` | `"default"` | 按钮的视觉变体 |
| `children` | `ReactNode \| function` | ` ` | 显示内容(默认为关闭图标) |
| `onPress` | `() => void` | - | 按钮按下时触发的事件处理函数 |
| `isDisabled` | `boolean` | `false` | 是否禁用按钮 |
### React Aria Button Props
CloseButton 继承所有 React Aria Button props。常见 props 包括:
| Prop | 类型 | 描述 |
| ------------------ | -------- | -------------- |
| `aria-label` | `string` | 提供给屏幕阅读器的无障碍标签 |
| `aria-labelledby` | `string` | 用于标注按钮的元素 id |
| `aria-describedby` | `string` | 用于描述按钮的元素 id |
### RenderProps
使用渲染 prop 模式时,会提供以下值:
| Prop | 类型 | 描述 |
| ------------ | --------- | ---------- |
| `isHovered` | `boolean` | 按钮是否处于悬停状态 |
| `isPressed` | `boolean` | 按钮是否处于按压状态 |
| `isFocused` | `boolean` | 按钮是否处于聚焦状态 |
| `isDisabled` | `boolean` | 按钮是否禁用 |
# ToggleButtonGroup 切换按钮组
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/toggle-button-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(buttons)/toggle-button-group.mdx
> 将多个 ToggleButton 组合为统一控件,允许用户选择单个或多个选项。
## 引入
```tsx
import { ToggleButtonGroup, ToggleButton } from '@heroui/react';
```
### 用法
```tsx
import {Bold, Italic, Strikethrough, Underline} from "@gravity-ui/icons";
import {ToggleButton, ToggleButtonGroup} from "@heroui/react";
export function Basic() {
return (
);
}
```
### 组件结构
导入 ToggleButtonGroup 组件,并通过点语法访问所有子部分。
```tsx
import { ToggleButtonGroup, ToggleButton } from '@heroui/react';
export default () => (
First
Second
Third
);
```
### 尺寸
```tsx
import {Bold, Italic, Strikethrough, Underline} from "@gravity-ui/icons";
import {ToggleButton, ToggleButtonGroup} from "@heroui/react";
export function Sizes() {
return (
);
}
```
### 方向
```tsx
import {Bold, Italic, Underline} from "@gravity-ui/icons";
import {ToggleButton, ToggleButtonGroup} from "@heroui/react";
export function Orientation() {
return (
);
}
```
### 分离模式
使用 `isDetached` 让按钮之间留出间隔,而不是彼此连接。
```tsx
import {Bold, Italic, Strikethrough, Underline} from "@gravity-ui/icons";
import {ToggleButton, ToggleButtonGroup} from "@heroui/react";
export function Attached() {
return (
);
}
```
### 全宽
```tsx
import {
Bold,
Italic,
Strikethrough,
TextAlignCenter,
TextAlignLeft,
TextAlignRight,
Underline,
} from "@gravity-ui/icons";
import {ToggleButton, ToggleButtonGroup} from "@heroui/react";
export function FullWidth() {
return (
左对齐
居中
右对齐
);
}
```
### 选择模式
使用 `selectionMode="single"` 实现互斥选择,或使用 `selectionMode="multiple"` 实现独立切换。
```tsx
import {
Bold,
Italic,
Strikethrough,
TextAlignCenter,
TextAlignLeft,
TextAlignRight,
Underline,
} from "@gravity-ui/icons";
import {ToggleButton, ToggleButtonGroup} from "@heroui/react";
export function SelectionMode() {
return (
);
}
```
### 受控
```tsx
"use client";
import type {Key} from "@heroui/react";
import {Bold, Italic, Strikethrough, Underline} from "@gravity-ui/icons";
import {ToggleButton, ToggleButtonGroup} from "@heroui/react";
import {useState} from "react";
export function Controlled() {
const [selectedKeys, setSelectedKeys] = useState(new Set(["bold"]));
return (
已选:
{selectedKeys.size > 0 ? [...selectedKeys].join(", ") : "无"}
);
}
```
### 禁用
```tsx
import {Bold, Italic, Underline} from "@gravity-ui/icons";
import {ToggleButton, ToggleButtonGroup} from "@heroui/react";
export function Disabled() {
return (
);
}
```
### 无分隔线
在按钮中直接省略 ` ` 组件即可。
```tsx
import {Bold, Italic, Strikethrough, Underline} from "@gravity-ui/icons";
import {ToggleButton, ToggleButtonGroup} from "@heroui/react";
export function WithoutSeparator() {
return (
);
}
```
## Related Components
* **ToggleButton**: Interactive toggle control for on/off states
* **ButtonGroup**: Group related buttons together
* **Button**: Allows a user to perform an action
## 样式
### 传入 Tailwind CSS 类
```tsx
import { ToggleButtonGroup, ToggleButton } from '@heroui/react';
function CustomToggleButtonGroup() {
return (
Option A
Option B
);
}
```
### 自定义组件类
若要自定义 ToggleButtonGroup 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.toggle-button-group {
@apply rounded-lg;
}
.toggle-button-group__separator {
@apply opacity-25;
}
.toggle-button-group--full-width {
@apply w-full;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
ToggleButtonGroup 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/toggle-button-group.css)):
#### 基础与布局类
* `.toggle-button-group` - 容器基础样式
* `.toggle-button-group--horizontal` - 水平方向
* `.toggle-button-group--vertical` - 垂直方向
* `.toggle-button-group--full-width` - 全宽修饰符
* `.toggle-button-group__separator` - 按钮之间的分隔线元素
#### 修饰符类
* `.toggle-button-group--detached` - 分离模式(按钮间有间隔)
## API 参考
### ToggleButtonGroup Props
继承自 [React Aria ToggleButtonGroup](https://react-aria.adobe.com/ToggleButtonGroup)。
| Prop | 类型 | 默认值 | 描述 |
| ------------------------ | ---------------------------- | -------------- | --------------------- |
| `selectionMode` | `"single" \| "multiple"` | `"single"` | 是否允许选中一个或多个按钮 |
| `selectedKeys` | `Iterable` | - | 受控的选中状态 |
| `defaultSelectedKeys` | `Iterable` | - | 默认选中 key(非受控) |
| `onSelectionChange` | `(keys: Set) => void` | - | 选中变化时调用 |
| `disallowEmptySelection` | `boolean` | `false` | 是否禁止清空所有选中 |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | 布局方向 |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | 传递给子 ToggleButton 的尺寸 |
| `isDetached` | `boolean` | `false` | 按钮是否以间隔分离显示 |
| `fullWidth` | `boolean` | `false` | 按钮组是否占满可用宽度 |
| `isDisabled` | `boolean` | `false` | 是否禁用组内全部按钮 |
| `className` | `string` | - | 额外的 CSS 类 |
### ToggleButtonGroup.Separator Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------- | --- | --------- |
| `className` | `string` | - | 额外的 CSS 类 |
### 说明
* ToggleButtonGroup 使用 React Context 将 `size` 传递给所有子 ToggleButton 组件
* 每个 ToggleButton 都必须有唯一 `id` prop,并与 `selectedKeys` / `defaultSelectedKeys` 中使用的 key 对应
* `isDisabled` prop 由 React Aria 原生处理,会禁用所有子 ToggleButton;单个按钮可通过设置 `isDisabled={false}` 覆盖
* 组件会自动处理按钮之间的圆角
* 在每个 ToggleButton(第一个除外)内添加 ` `,可在按钮之间显示分隔线
* 将 `disallowEmptySelection` 与 `selectionMode="single"` 一起使用,可确保始终有一个选项被选中
# ToggleButton 切换按钮
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/toggle-button
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(buttons)/toggle-button.mdx
> 用于在开启/关闭或已选中/未选中状态之间切换的交互式切换控件。
## 引入
```tsx
import { ToggleButton } from '@heroui/react';
```
### 用法
```tsx
import {Heart} from "@gravity-ui/icons";
import {ToggleButton} from "@heroui/react";
export function Basic() {
return (
点赞
);
}
```
### 变体
```tsx
import {Heart} from "@gravity-ui/icons";
import {ToggleButton} from "@heroui/react";
export function Variants() {
return (
默认
幽灵
);
}
```
### 仅图标
```tsx
import {Bookmark, Heart} from "@gravity-ui/icons";
import {ToggleButton} from "@heroui/react";
export function IconOnly() {
return (
);
}
```
### 尺寸
```tsx
import {Heart} from "@gravity-ui/icons";
import {ToggleButton} from "@heroui/react";
export function Sizes() {
return (
);
}
```
### 受控
```tsx
"use client";
import {Heart, HeartFill} from "@gravity-ui/icons";
import {ToggleButton} from "@heroui/react";
import {useState} from "react";
export function Controlled() {
const [isSelected, setIsSelected] = useState(false);
return (
{({isSelected: selected}) => (
<>
{selected ? : }
{selected ? "已点赞" : "点赞"}
>
)}
状态:{isSelected ? "已选" : "未选"}
);
}
```
### 禁用
```tsx
import {Heart, HeartFill} from "@gravity-ui/icons";
import {ToggleButton} from "@heroui/react";
export function Disabled() {
return (
点赞
点赞
);
}
```
## Related Components
* **Button**: Allows a user to perform an action
* **Switch**: Toggle between two states
* **Checkbox**: Binary choice input control
## 样式
### 传入 Tailwind CSS 类
```tsx
import { ToggleButton } from '@heroui/react';
function CustomToggleButton() {
return (
Toggle
);
}
```
### 自定义组件类
若要自定义 ToggleButton 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.toggle-button {
@apply bg-purple-500 text-white;
}
.toggle-button--icon-only {
@apply rounded-lg;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
ToggleButton 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/toggle-button.css)):
#### 基础与尺寸类
* `.toggle-button` - 切换按钮基础样式
* `.toggle-button--sm` - 小尺寸变体
* `.toggle-button--md` - 中尺寸变体(默认)
* `.toggle-button--lg` - 大尺寸变体
#### 变体类
* `.toggle-button--default` - 默认变体(填充背景)
* `.toggle-button--ghost` - 幽灵变体(透明背景)
#### 修饰符类
* `.toggle-button--icon-only` - 仅图标切换按钮
* `.toggle-button--icon-only.toggle-button--sm` - 小尺寸仅图标
* `.toggle-button--icon-only.toggle-button--lg` - 大尺寸仅图标
### 交互状态
该切换按钮同时支持 CSS 伪类与 data 属性,以便灵活控制状态:
* **已选中**:`[data-selected="true"]`(强调色背景与前景)
* **悬停**:`:hover` 或 `[data-hovered="true"]`
* **激活/按下**:`:active` 或 `[data-pressed="true"]`(包含缩放变换)
* **聚焦**:`:focus-visible` 或 `[data-focus-visible="true"]`(显示焦点环)
* **禁用**:`:disabled` 或 `[aria-disabled="true"]`(降低透明度,禁用指针事件)
## API 参考
### ToggleButton Props
继承自 [React Aria ToggleButton](https://react-spectrum.adobe.com/react-aria/ToggleButton.html)。
| Prop | 类型 | 默认值 | 描述 |
| ----------------- | ------------------------------------------------------------------------- | ----------- | --------------- |
| `variant` | `'default' \| 'ghost'` | `'default'` | 视觉样式变体 |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 切换按钮尺寸 |
| `isIconOnly` | `boolean` | `false` | 按钮是否仅包含图标 |
| `isSelected` | `boolean` | - | 受控的已选中状态 |
| `defaultSelected` | `boolean` | `false` | 默认已选中状态(非受控) |
| `isDisabled` | `boolean` | `false` | 是否禁用切换按钮 |
| `onChange` | `(isSelected: boolean) => void` | - | 已选中状态变化时调用的处理函数 |
| `onPress` | `(e: PressEvent) => void` | - | 按钮按下时调用的处理函数 |
| `children` | `React.ReactNode \| (values: ToggleButtonRenderProps) => React.ReactNode` | - | 按钮内容或渲染 prop |
### ToggleButtonRenderProps
使用渲染 prop 模式时,会提供以下值:
| Prop | 类型 | 描述 |
| ---------------- | --------- | ------------ |
| `isSelected` | `boolean` | 按钮当前是否已选中 |
| `isPressed` | `boolean` | 按钮当前是否处于按下状态 |
| `isHovered` | `boolean` | 按钮是否处于悬停状态 |
| `isFocused` | `boolean` | 按钮是否处于聚焦状态 |
| `isFocusVisible` | `boolean` | 按钮是否应显示焦点指示 |
| `isDisabled` | `boolean` | 按钮是否被禁用 |
# Dropdown 下拉菜单
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/dropdown
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(collections)/dropdown.mdx
> 下拉菜单展示一组可供用户选择的操作或选项。
## 引入
```tsx
import { Dropdown } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {Button, Dropdown, Label} from "@heroui/react";
export function Default() {
return (
操作
console.log(`Selected: ${key}`)}>
新建文件
复制链接
编辑文件
删除文件
);
}
```
### 组件结构
引入 Dropdown 组件并通过点语法访问所有子部分。
```tsx
import { Dropdown, Button, Label, Description, Header, Kbd, Separator } from '@heroui/react';
export default () => (
)
```
### 带单选
```tsx
"use client";
import type {Selection} from "@heroui/react";
import {Button, Dropdown, Header, Label} from "@heroui/react";
import {useState} from "react";
export function WithSingleSelection() {
const [selected, setSelected] = useState(new Set(["apple"]));
return (
水果
苹果
香蕉
樱桃
橙子
梨
);
}
```
### 单选且自定义指示器
```tsx
"use client";
import type {Selection} from "@heroui/react";
import {Button, Dropdown, Header, Label} from "@heroui/react";
import {useState} from "react";
export function SingleWithCustomIndicator() {
const [selected, setSelected] = useState(new Set(["apple"]));
const CustomCheckmarkIcon = (
);
return (
水果
{({isSelected}) => (isSelected ? CustomCheckmarkIcon : null)}
苹果
{({isSelected}) => (isSelected ? CustomCheckmarkIcon : null)}
香蕉
{({isSelected}) => (isSelected ? CustomCheckmarkIcon : null)}
樱桃
{({isSelected}) => (isSelected ? CustomCheckmarkIcon : null)}
橙子
{({isSelected}) => (isSelected ? CustomCheckmarkIcon : null)}
梨
);
}
```
### 带多选
```tsx
"use client";
import type {Selection} from "@heroui/react";
import {Button, Dropdown, Header, Label} from "@heroui/react";
import {useState} from "react";
export function WithMultipleSelection() {
const [selected, setSelected] = useState(new Set(["apple"]));
return (
喜爱的水果
苹果
香蕉
樱桃
橙子
梨
);
}
```
### 带分组级选择
```tsx
"use client";
import type {Selection} from "@heroui/react";
import {Button, Dropdown, Header, Kbd, Label, Separator} from "@heroui/react";
import {useState} from "react";
export function WithSectionLevelSelection() {
const [textStyles, setTextStyles] = useState(new Set(["bold", "italic"]));
const [textAlignment, setTextAlignment] = useState(new Set(["left"]));
return (
样式
剪切
X
复制
C
粘贴
U
粗体
B
斜体
I
下划线
U
左对齐
A
居中
H
右对齐
D
);
}
```
### 带键盘快捷键
```tsx
"use client";
import {Button, Dropdown, Kbd, Label} from "@heroui/react";
export function WithKeyboardShortcuts() {
return (
操作
console.log(`Selected: ${key}`)}>
新建
N
打开
O
保存
S
删除
D
);
}
```
### 带图标
```tsx
"use client";
import {FloppyDisk, FolderOpen, SquarePlus, TrashBin} from "@gravity-ui/icons";
import {Button, Dropdown, Kbd, Label} from "@heroui/react";
export function WithIcons() {
return (
操作
console.log(`Selected: ${key}`)}>
新建文件
N
打开文件
O
保存文件
S
删除文件
D
);
}
```
### 长按触发
```tsx
import {Button, Dropdown, Label} from "@heroui/react";
export function LongPressTrigger() {
return (
长按
新建文件
打开文件
保存文件
删除文件
);
}
```
### 带描述
```tsx
"use client";
import {FloppyDisk, FolderOpen, SquarePlus, TrashBin} from "@gravity-ui/icons";
import {Button, Description, Dropdown, Kbd, Label} from "@heroui/react";
export function WithDescriptions() {
return (
操作
console.log(`Selected: ${key}`)}>
新建文件
创建新文件
N
打开文件
打开已有文件
O
保存文件
保存当前文件
S
删除文件
移至废纸篓
D
);
}
```
### 带分组
```tsx
"use client";
import {EllipsisVertical, Pencil, SquarePlus, TrashBin} from "@gravity-ui/icons";
import {Button, Description, Dropdown, Header, Kbd, Label, Separator} from "@heroui/react";
export function WithSections() {
return (
console.log(`Selected: ${key}`)}>
新建文件
创建新文件
N
编辑文件
进行修改
E
删除文件
移至废纸篓
D
);
}
```
### 带禁用项
```tsx
"use client";
import {Bars, Pencil, SquarePlus, TrashBin} from "@gravity-ui/icons";
import {Button, Description, Dropdown, Header, Kbd, Label, Separator} from "@heroui/react";
export function WithDisabledItems() {
return (
console.log(`Selected: ${key}`)}
>
新建文件
创建新文件
N
编辑文件
进行修改
E
删除文件
移至废纸篓
D
);
}
```
### 带子菜单
```tsx
"use client";
import {Button, Dropdown, Label} from "@heroui/react";
export function WithSubmenus() {
return (
分享
console.log(`Selected: ${key}`)}>
复制链接
Facebook
其他
WhatsApp
Telegram
Discord
Email
工作邮箱
个人邮箱
);
}
```
### 带自定义子菜单指示器
```tsx
"use client";
import {ArrowRight} from "@gravity-ui/icons";
import {Button, Dropdown, Label} from "@heroui/react";
export function WithCustomSubmenuIndicator() {
return (
分享
console.log(`Selected: ${key}`)}>
复制链接
Facebook
更多选项
WhatsApp
Telegram
Email
工作邮箱
个人邮箱
Discord
其他(默认指示器)
SMS
);
}
```
### 受控
```tsx
"use client";
import type {Selection} from "@heroui/react";
import {Button, Dropdown, Label} from "@heroui/react";
import {useState} from "react";
export function Controlled() {
const [selected, setSelected] = useState(new Set(["bold"]));
const selectedItems = Array.from(selected);
return (
已选:{selectedItems.length > 0 ? selectedItems.join("、") : "无"}
操作
粗体
斜体
下划线
);
}
```
### 受控展开状态
```tsx
"use client";
import {Button, Dropdown, Label} from "@heroui/react";
import {useState} from "react";
export function ControlledOpenState() {
const [open, setOpen] = useState(false);
return (
下拉菜单:{open ? "打开" : "关闭"}
操作
新建文件
打开文件
保存文件
删除文件
);
}
```
### 自定义触发器
```tsx
import {ArrowRightFromSquare, Gear, Persons} from "@gravity-ui/icons";
import {Avatar, Dropdown, Label} from "@heroui/react";
export function CustomTrigger() {
return (
JD
JD
Jane Doe
jane@example.com
仪表盘
个人资料
设置
);
}
```
## Related Components
* **Button**: Allows a user to perform an action
* **Popover**: Displays content in context with a trigger
* **Separator**: Visual divider between content
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Dropdown, Button } from '@heroui/react';
function CustomDropdown() {
return (
Actions
Item 1
);
}
```
### 自定义组件类
若要自定义 Dropdown 组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.dropdown {
@apply flex flex-col gap-1;
}
.dropdown__trigger {
@apply outline-none;
}
.dropdown__popover {
@apply rounded-lg border border-border bg-overlay p-2;
}
.dropdown__menu {
@apply flex flex-col gap-1;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体和状态可复用且易于自定义。
### CSS 类
Dropdown 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/dropdown.css)):
#### 基础类
* `.dropdown` - Dropdown 根容器
* `.dropdown__trigger` - 用于触发 Dropdown 的按钮或元素
* `.dropdown__popover` - Popover 容器
* `.dropdown__menu` - Popover 内的菜单容器
#### 状态类
* `.dropdown__trigger[data-focus-visible="true"]` - 触发器聚焦状态
* `.dropdown__trigger[data-disabled="true"]` - 触发器禁用状态
* `.dropdown__trigger[data-pressed="true"]` - 触发器按下状态
* `.dropdown__popover[data-entering]` - 进入动画状态
* `.dropdown__popover[data-exiting]` - 退出动画状态
* `.dropdown__menu[data-selection-mode="single"]` - 单选模式
* `.dropdown__menu[data-selection-mode="multiple"]` - 多选模式
### 菜单组件类
Dropdown 使用 Menu、MenuItem 与 MenuSection 作为底层组件。以下类名也可用于自定义:
#### Menu 类
* `.menu` - 菜单容器([menu.css](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/menu.css))
* `[data-slot="separator"]` - 菜单内的分隔线元素
#### MenuItem 类
* `.menu-item` - 菜单项容器([menu-item.css](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/menu-item.css))
* `.menu-item__indicator` - 选中指示器(对勾或圆点)
* `[data-slot="menu-item-indicator--checkmark"]` - 对勾指示器 SVG
* `[data-slot="menu-item-indicator--dot"]` - 圆点指示器 SVG
* `.menu-item__indicator--submenu` - 子菜单指示器(箭头)
* `.menu-item--default` - 默认样式变体
* `.menu-item--danger` - 危险样式变体
#### MenuItem 状态类
* `.menu-item[data-focus-visible="true"]` - 聚焦状态(键盘焦点)
* `.menu-item[data-focus="true"]` - 聚焦状态
* `.menu-item[data-pressed]` - 按下状态
* `.menu-item[data-hovered]` - 悬停状态
* `.menu-item[data-selected="true"]` - 选中状态
* `.menu-item[data-disabled]` - 禁用状态
* `.menu-item[data-has-submenu="true"]` - 带子菜单的项
* `.menu-item[data-selection-mode="single"]` - 单选模式
* `.menu-item[data-selection-mode="multiple"]` - 多选模式
* `.menu-item[aria-checked="true"]` - 已勾选(ARIA)
* `.menu-item[aria-selected="true"]` - 已选中(ARIA)
#### MenuSection 类
* `.menu-section` - 菜单分区容器([menu-section.css](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/menu-section.css))
### 交互状态
该组件同时支持 CSS 伪类和 data 属性,便于灵活组合:
* **悬停**:触发器与菜单项上 `:hover` 或 `[data-hovered="true"]`
* **聚焦**:触发器与菜单项上 `:focus-visible` 或 `[data-focus-visible="true"]`
* **禁用**:触发器与菜单项上 `:disabled` 或 `[data-disabled="true"]`
* **按下**:触发器与菜单项上 `:active` 或 `[data-pressed="true"]`
* **选中**:菜单项上 `[data-selected="true"]` 或 `[aria-selected="true"]`
## API 参考
### Dropdown Props
| Prop | 类型 | 默认值 | 描述 |
| -------------- | --------------------------- | --------- | ------------------- |
| `isOpen` | `boolean` | - | 设置菜单展开状态(受控)。 |
| `defaultOpen` | `boolean` | - | 设置菜单默认展开状态(非受控)。 |
| `onOpenChange` | `(isOpen: boolean) => void` | - | 展开状态变化时调用的事件处理函数。 |
| `trigger` | `"press" \| "longPress"` | `"press"` | 触发菜单的交互类型。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | Dropdown 内容。 |
### Dropdown.Trigger Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------------------- | --- | ------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | 触发器内容或渲染函数。 |
使用 Button 作为触发器时,同样支持所有 [Button](https://react-spectrum.adobe.com/react-aria/Button.html) props。
### Dropdown.Popover Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ------------------- |
| `placement` | `"bottom" \| "bottom left" \| "bottom right" \| "bottom start" \| "bottom end" \| "top" \| "top left" \| "top right" \| "top start" \| "top end" \| "left" \| "left top" \| "left bottom" \| "start" \| "start top" \| "start bottom" \| "right" \| "right top" \| "right bottom" \| "end" \| "end top" \| "end bottom"` | `"bottom"` | 相对于触发器的 Popover 位置。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 子内容。 |
同样支持所有 [Popover](https://react-spectrum.adobe.com/react-aria/Popover.html) props。
### Dropdown.Menu Props
| Prop | 类型 | 默认值 | 描述 |
| --------------------- | ---------------------------------- | -------- | ------------------- |
| `selectionMode` | `"single" \| "multiple" \| "none"` | `"none"` | 是否启用单选、多选或不启用选择。 |
| `selectedKeys` | `Iterable` | - | 当前选中的 key(受控)。 |
| `defaultSelectedKeys` | `Iterable` | - | 初始选中的 key(非受控)。 |
| `onSelectionChange` | `(keys: Selection) => void` | - | 选中变化时调用的事件处理函数。 |
| `disabledKeys` | `Iterable` | - | 禁用项的 key。 |
| `onAction` | `(key: Key) => void` | - | 激活菜单项时调用的事件处理函数。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 菜单内容。 |
同样支持所有 [Menu](https://react-spectrum.adobe.com/react-aria/Menu.html#menu) props。
### Dropdown.Section Props
| Prop | 类型 | 默认值 | 描述 |
| --------------------- | --------------------------- | --- | ------------------- |
| `selectionMode` | `"single" \| "multiple"` | - | 该分组内菜单项的选择模式。 |
| `selectedKeys` | `Iterable` | - | 当前选中的 key(受控)。 |
| `defaultSelectedKeys` | `Iterable` | - | 初始选中的 key(非受控)。 |
| `onSelectionChange` | `(keys: Selection) => void` | - | 选中变化时调用的事件处理函数。 |
| `disabledKeys` | `Iterable` | - | 禁用项的 key。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 分组内容。 |
同样支持所有 [MenuSection](https://react-spectrum.adobe.com/react-aria/Menu.html#menusection) props。
### Dropdown.Item Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------------------- | ----------- | ------------------- |
| `id` | `Key` | - | 菜单项唯一标识。 |
| `textValue` | `string` | - | 用于首字母导航的文本内容。 |
| `variant` | `"default" \| "danger"` | `"default"` | 菜单项视觉变体。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | 菜单项内容或渲染函数。 |
同样支持所有 [MenuItem](https://react-spectrum.adobe.com/react-aria/Menu.html#menuitem) props。
### Dropdown.ItemIndicator Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------------------- | ------------- | ------------------- |
| `type` | `"checkmark" \| "dot"` | `"checkmark"` | 指示器类型。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | 自定义指示器内容或渲染函数。 |
使用渲染函数时,会传入以下值:
| Prop | 类型 | 描述 |
| ----------------- | --------- | ------------ |
| `isSelected` | `boolean` | 该项是否选中。 |
| `isIndeterminate` | `boolean` | 该项是否处于不确定状态。 |
### Dropdown.SubmenuIndicator Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 自定义指示器内容。 |
### Dropdown.SubmenuTrigger Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 子菜单触发器内容。 |
同样支持所有 [SubmenuTrigger](https://react-spectrum.adobe.com/react-aria/Menu.html#submenutrigger) props。
### RenderProps
在 Dropdown.Item 中使用渲染函数时,会传入以下值:
| Prop | 类型 | 描述 |
| ------------ | --------- | ----------- |
| `isSelected` | `boolean` | 该项是否选中。 |
| `isFocused` | `boolean` | 该项是否聚焦。 |
| `isDisabled` | `boolean` | 该项是否禁用。 |
| `isPressed` | `boolean` | 该项是否处于按下状态。 |
## 示例
### 基础用法
```tsx
import { Dropdown, Button, Label } from '@heroui/react';
Actions
alert(`Selected: ${key}`)}>
New file
Open file
Delete file
```
### 带分组
```tsx
import { Dropdown, Button, Label, Header, Separator } from '@heroui/react';
Actions
alert(`Selected: ${key}`)}>
New file
Edit file
Delete file
```
### 受控选择
```tsx
import type { Selection } from '@heroui/react';
import { Dropdown, Button, Label } from '@heroui/react';
import { useState } from 'react';
function ControlledDropdown() {
const [selected, setSelected] = useState(new Set(['bold']));
return (
Actions
Bold
Italic
);
}
```
### 带子菜单
```tsx
import { Dropdown, Button, Label } from '@heroui/react';
Share
alert(`Selected: ${key}`)}>
Copy Link
Other
WhatsApp
Telegram
```
## 无障碍
Dropdown 组件实现 ARIA 菜单模式,并提供:
* 完整键盘导航(方向键、Home/End、首字母导航)
* 屏幕阅读器对操作与选中变化的播报
* 合理的焦点管理
* 禁用态支持
* 长按交互支持
* 子菜单导航
更多信息见 [React Aria Menu 文档](https://react-spectrum.adobe.com/react-aria/Menu.html#menu)。
# ListBox 列表框
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/list-box
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(collections)/list-box.mdx
> 列表框展示一组选项,并允许用户选择一个或多个。
## 引入
```tsx
import { ListBox } from '@heroui/react';
```
### 用法
```tsx
import {Avatar, Description, Label, ListBox} from "@heroui/react";
export function Default() {
return (
B
Bob
bob@heroui.com
F
Fred
fred@heroui.com
M
Martha
martha@heroui.com
);
}
```
### 组件结构
引入 ListBox 组件并通过点语法访问所有子部分。
```tsx
import { ListBox, Label, Description, Header } from '@heroui/react';
export default () => (
)
```
### 带分组
```tsx
"use client";
import {Pencil, SquarePlus, TrashBin} from "@gravity-ui/icons";
import {Description, Header, Kbd, Label, ListBox, Separator, Surface} from "@heroui/react";
export function WithSections() {
return (
alert(`已选项目:${key}`)}
>
新建文件
创建新文件
N
编辑文件
进行修改
E
删除文件
移至废纸篓
D
);
}
```
### 多选
```tsx
import {Avatar, Description, Label, ListBox, Surface} from "@heroui/react";
export function MultiSelect() {
return (
B
Bob
bob@heroui.com
F
Fred
fred@heroui.com
M
Martha
martha@heroui.com
);
}
```
### 带禁用项
```tsx
"use client";
import {Pencil, SquarePlus, TrashBin} from "@gravity-ui/icons";
import {Description, Header, Kbd, Label, ListBox, Separator, Surface} from "@heroui/react";
export function WithDisabledItems() {
return (
alert(`已选项目:${key}`)}
>
新建文件
创建新文件
N
编辑文件
进行修改
E
删除文件
移至废纸篓
D
);
}
```
### 自定义勾选图标
```tsx
"use client";
import {Check} from "@gravity-ui/icons";
import {Avatar, Description, Label, ListBox, Surface} from "@heroui/react";
export function CustomCheckIcon() {
return (
B
Bob
bob@heroui.com
{({isSelected}) => (isSelected ? : null)}
F
Fred
fred@heroui.com
{({isSelected}) => (isSelected ? : null)}
M
Martha
martha@heroui.com
{({isSelected}) => (isSelected ? : null)}
);
}
```
### 受控
```tsx
"use client";
import type {Selection} from "@heroui/react";
import {Check} from "@gravity-ui/icons";
import {Avatar, Description, Label, ListBox, Surface} from "@heroui/react";
import {useState} from "react";
export function Controlled() {
const [selected, setSelected] = useState(new Set(["1"]));
const selectedItems = Array.from(selected);
return (
B
Bob
bob@heroui.com
{({isSelected}) => (isSelected ? : null)}
F
Fred
fred@heroui.com
{({isSelected}) => (isSelected ? : null)}
M
Martha
martha@heroui.com
{({isSelected}) => (isSelected ? : null)}
已选:{selectedItems.length > 0 ? selectedItems.join("、") : "无"}
);
}
```
### 自定义渲染函数
```tsx
"use client";
import {Avatar, Description, Label, ListBox} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
selectionMode="single"
>
}
textValue="Bob"
>
B
Bob
bob@heroui.com
}
textValue="Fred"
>
F
Fred
fred@heroui.com
}
textValue="Martha"
>
M
Martha
martha@heroui.com
);
}
```
### 虚拟化
ListBox 通过 [Virtualizer](https://react-aria.adobe.com/Virtualizer) 支持虚拟化,仅渲染视口内可见的行,从而高效展示大数据集。
```tsx
"use client";
import {Description, Label, ListBox, ListLayout, Virtualizer} from "@heroui/react";
interface User {
id: number;
name: string;
email: string;
}
export function Virtualization() {
const firstNames = [
"Emma",
"Liam",
"Olivia",
"Noah",
"Ava",
"James",
"Sophia",
"Oliver",
"Isabella",
"Lucas",
"Mia",
"Ethan",
"Charlotte",
"Mason",
"Amelia",
"Logan",
"Harper",
"Alexander",
"Ella",
"Benjamin",
];
const lastNames = [
"Smith",
"Johnson",
"Williams",
"Brown",
"Jones",
"Garcia",
"Miller",
"Davis",
"Rodriguez",
"Martinez",
"Anderson",
"Taylor",
"Thomas",
"Jackson",
"White",
"Harris",
"Clark",
"Lewis",
"Robinson",
"Walker",
];
function generateUsers(n: number): User[] {
const users: User[] = [];
for (let i = 0; i < n; i++) {
const firstName = firstNames[i % firstNames.length];
const lastName = lastNames[Math.floor(i / firstNames.length) % lastNames.length];
const name = `${firstName} ${lastName}`;
users.push({
email: `${firstName?.toLowerCase()}.${lastName?.toLowerCase()}@acme.com`,
id: i + 1,
name,
});
}
return users;
}
const users = generateUsers(1000);
return (
{(user) => (
{user.name}
{user.email}
)}
);
}
```
## Related Components
* **Select**: Dropdown select control
* **ComboBox**: Text input with searchable dropdown list
* **Avatar**: Display user profile images
## 样式
### 传入 Tailwind CSS 类
```tsx
import { ListBox } from '@heroui/react';
function CustomListBox() {
return (
Item 1
);
}
```
### 自定义组件类
若要自定义 ListBox 组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.list-box {
@apply rounded-lg border border-border bg-surface p-2;
}
.list-box-item {
@apply rounded px-2 py-1 cursor-pointer;
}
.list-box-item--danger {
@apply text-danger;
}
.list-box-item__indicator {
@apply text-accent;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
ListBox 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/list-box.css)):
#### 基础类
* `.list-box` - ListBox 根容器
* `.list-box-item` - 单个列表项
* `.list-box-item__indicator` - 选中指示图标
* `.list-box-section` - 用于分组的区块容器
#### 变体类
* `.list-box--default` - 默认变体样式
* `.list-box--danger` - 危险变体样式
* `.list-box-item--default` - 列表项默认变体
* `.list-box-item--danger` - 列表项危险变体
#### 状态类
* `.list-box-item[data-selected="true"]` - 选中状态
* `.list-box-item[data-focus-visible="true"]` - 聚焦状态
* `.list-box-item[data-disabled="true"]` - 禁用状态
* `.list-box-item__indicator[data-visible="true"]` - 指示器可见状态
### 交互状态
该组件同时支持 CSS 伪类与 data 属性:
* **悬停**:列表项上 `:hover` 或 `[data-hovered="true"]`
* **聚焦**:列表项上 `:focus-visible` 或 `[data-focus-visible="true"]`
* **已选中**:列表项上 `[data-selected="true"]`
* **禁用**:列表项上 `:disabled` 或 `[data-disabled="true"]`
## API 参考
### ListBox Props
| Prop | 类型 | 默认值 | 描述 |
| --------------------- | -------------------------------------------------------------------------- | ----------- | --------------------- |
| `aria-label` | `string` | - | ListBox 的无障碍标签。 |
| `aria-labelledby` | `string` | - | 标注 ListBox 的元素 id。 |
| `selectionMode` | `"none" \| "single" \| "multiple"` | `"single"` | 选择行为。 |
| `selectedKeys` | `Selection` | - | 受控的选中 key。 |
| `defaultSelectedKeys` | `Selection` | - | 初始选中 key。 |
| `onSelectionChange` | `(keys: Selection) => void` | - | 选中变化时调用的事件处理函数。 |
| `disabledKeys` | `Iterable` | - | 禁用项的 key。 |
| `onAction` | `(key: Key) => void` | - | 激活某项时调用的事件处理函数。 |
| `variant` | `"default" \| "danger"` | `"default"` | 视觉变体。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | ListBox 项与分组。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### ListBox.Item Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | --------------------- |
| `id` | `Key` | - | 列表项唯一标识。 |
| `textValue` | `string` | - | 用于无障碍与首字母导航的文本值。 |
| `isDisabled` | `boolean` | `false` | 是否禁用该项。 |
| `variant` | `"default" \| "danger"` | `"default"` | 视觉变体。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | 列表项内容或渲染函数。 |
| `render` | `(props: DetailedHTMLProps \| React.JSX.IntrinsicElements[keyof React.JSX.IntrinsicElements], renderProps: ListBoxItemRenderProps) => ReactElement` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### ListBox.ItemIndicator Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------------------- | --- | ------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | 自定义指示器内容或渲染函数。 |
### ListBox.Section Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | -------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 分组内容,包含 Header 与列表项。 |
### RenderProps
在 ListBox.Item 或 ListBox.ItemIndicator 中使用渲染函数时,会传入以下值:
| Prop | 类型 | 描述 |
| ------------ | --------- | ----------- |
| `isSelected` | `boolean` | 该项是否选中。 |
| `isFocused` | `boolean` | 该项是否聚焦。 |
| `isDisabled` | `boolean` | 该项是否禁用。 |
| `isPressed` | `boolean` | 该项是否处于按下状态。 |
### ListLayout
| Name | 类型 | 默认值 | 描述 |
| ------------------------ | --------------------- | --- | ------------------------------------------- |
| `rowHeight` | `number \| undefined` | 48 | 行固定高度(px)。 |
| `estimatedRowHeight` | `number \| undefined` | — | 行高可变时的估算高度。 |
| `headingHeight` | `number \| undefined` | 48 | 分组标题固定高度(px)。 |
| `estimatedHeadingHeight` | `number \| undefined` | — | 标题高度可变时的估算高度。 |
| `loaderHeight` | `number \| undefined` | 48 | 加载器元素固定高度(px)。该加载器用于在根级或嵌套行/分组中渲染「加载更多」等内容。 |
| `dropIndicatorThickness` | `number \| undefined` | 2 | 放置指示线厚度。 |
| `gap` | `number \| undefined` | 0 | 项之间的间距。 |
| `padding` | `number \| undefined` | 0 | 列表内边距。 |
## 示例
### 基础用法
```tsx
import { ListBox, Label, Description } from '@heroui/react';
Bob
bob@heroui.com
Alice
alice@heroui.com
```
### 带分组
```tsx
import { ListBox, Header, Separator } from '@heroui/react';
console.log(key)}>
New file
Edit file
Delete
```
### 受控选择
```tsx
import { ListBox, Selection } from '@heroui/react';
import { useState } from 'react';
function ControlledListBox() {
const [selected, setSelected] = useState(new Set(["1"]));
return (
Option 1
Option 2
Option 3
);
}
```
### 自定义指示器
```tsx
import { ListBox, ListBoxItemIndicator } from '@heroui/react';
import { Icon } from '@iconify/react';
Option 1
{({isSelected}) =>
isSelected ? : null
}
```
## 无障碍
ListBox 组件实现 ARIA listbox 模式,并提供:
* 完整键盘导航支持
* 屏幕阅读器对选中变化的播报
* 合理的焦点管理
* 禁用状态支持
* 首字母导航(typeahead)搜索能力
更多信息见 [React Aria ListBox 文档](https://react-spectrum.adobe.com/react-aria/ListBox.html)。
# TagGroup 标签组
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/tag-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(collections)/tag-group.mdx
> 可聚焦的标签列表,支持键盘导航、选择与移除。
## 引入
```tsx
import { TagGroup } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {PlanetEarth, Rocket, ShoppingBag, SquareArticle} from "@gravity-ui/icons";
import {Tag, TagGroup} from "@heroui/react";
export function TagGroupBasic() {
return (
资讯
旅行
游戏
购物
);
}
```
### 组件结构
```tsx
import { TagGroup, Tag, Label, Description, ErrorMessage } from '@heroui/react';
export default () => (
)
```
### 尺寸
```tsx
"use client";
import {Label, Tag, TagGroup} from "@heroui/react";
export function TagGroupSizes() {
return (
小
资讯
旅行
游戏
中
资讯
旅行
游戏
大
资讯
旅行
游戏
);
}
```
### 变体
```tsx
"use client";
import {Label, Tag, TagGroup} from "@heroui/react";
export function TagGroupVariants() {
return (
默认
资讯
旅行
游戏
表面
资讯
旅行
游戏
);
}
```
### 禁用
```tsx
"use client";
import {Description, Label, Tag, TagGroup} from "@heroui/react";
export function TagGroupDisabled() {
return (
已禁用的标签
资讯
旅行
游戏
部分标签已禁用
禁用的键
资讯
旅行
游戏
通过 disabledKeys 属性禁用的标签
);
}
```
### 选择模式
```tsx
"use client";
import type {Key} from "@heroui/react";
import {Description, Label, Tag, TagGroup} from "@heroui/react";
import {useState} from "react";
export function TagGroupSelectionModes() {
const [singleSelected, setSingleSelected] = useState>(new Set(["news"]));
const [multipleSelected, setMultipleSelected] = useState>(
new Set(["news", "travel"]),
);
return (
setSingleSelected(keys)}
>
单选
资讯
旅行
游戏
购物
选择一个分类
setMultipleSelected(keys)}
>
多选
资讯
旅行
游戏
购物
选择多个分类
);
}
```
### 受控
```tsx
"use client";
import type {Key} from "@heroui/react";
import {Description, Label, Tag, TagGroup} from "@heroui/react";
import {useState} from "react";
export function TagGroupControlled() {
const [selected, setSelected] = useState>(new Set(["news", "travel"]));
return (
setSelected(keys)}
>
分类(受控)
资讯
旅行
游戏
购物
已选:{Array.from(selected).length > 0 ? Array.from(selected).join(", ") : "无"}
);
}
```
### 带错误信息
```tsx
"use client";
import type {Key} from "@heroui/react";
import {Description, ErrorMessage, Label, Tag, TagGroup} from "@heroui/react";
import {useMemo, useState} from "react";
export function TagGroupWithErrorMessage() {
const [selected, setSelected] = useState>(new Set());
const isInvalid = useMemo(() => Array.from(selected).length === 0, [selected]);
return (
setSelected(keys)}
>
设施
洗衣
健身中心
停车
游泳池
早餐
{isInvalid ? "请至少选择一个分类" : "已选:" + Array.from(selected).join(", ")}
{!!isInvalid && <>请至少选择一个分类>}
);
}
```
### 带前缀
```tsx
"use client";
import {PlanetEarth, Rocket, ShoppingBag, SquareArticle} from "@gravity-ui/icons";
import {Avatar, Description, Label, Tag, TagGroup} from "@heroui/react";
export function TagGroupWithPrefix() {
return (
带图标
News
Travel
Gaming
Shopping
带图标的标签
带头像
F
Fred
M
Michael
J
Jane
带头像的标签
);
}
```
### 带移除按钮
```tsx
"use client";
import type {Key} from "@heroui/react";
import {CircleXmarkFill} from "@gravity-ui/icons";
import {Description, EmptyState, Label, Tag, TagGroup} from "@heroui/react";
import {useState} from "react";
export function TagGroupWithRemoveButton() {
type TagItem = {id: string; name: string};
const [tags, setTags] = useState([
{id: "news", name: "资讯"},
{id: "travel", name: "旅行"},
{id: "gaming", name: "游戏"},
{id: "shopping", name: "购物"},
]);
const [frameworks, setFrameworks] = useState([
{id: "react", name: "React"},
{id: "vue", name: "Vue"},
{id: "angular", name: "Angular"},
{id: "svelte", name: "Svelte"},
]);
const onRemoveTags = (keys: Set) => {
setTags(tags.filter((tag) => !keys.has(tag.id)));
};
const onRemoveFrameworks = (keys: Set) => {
setFrameworks(frameworks.filter((framework) => !keys.has(framework.id)));
};
return (
默认移除按钮
未找到分类 }
>
{(tag) => (
{tag.name}
)}
点击 × 移除标签
自定义移除按钮
未找到框架 }
>
{(tag) => (
{(renderProps) => (
<>
{tag.name}
{!!renderProps.allowsRemoving && (
)}
>
)}
)}
带图标的自定义移除按钮
);
}
```
### 带列表数据
```tsx
"use client";
import type {Key} from "@heroui/react";
import {Avatar, Description, EmptyState, Label, Tag, TagGroup, useListData} from "@heroui/react";
export function TagGroupWithListData() {
type User = {
id: string;
name: string;
avatar: string;
fallback: string;
};
const list = useListData({
getKey: (item) => item.id,
initialItems: [
{
avatar: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg",
fallback: "F",
id: "fred",
name: "Fred",
},
{
avatar: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg",
fallback: "M",
id: "michael",
name: "Michael",
},
{
avatar: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg",
fallback: "J",
id: "jane",
name: "Jane",
},
{
avatar: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg",
fallback: "A",
id: "alice",
name: "Alice",
},
{
avatar: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg",
fallback: "B",
id: "bob",
name: "Bob",
},
{
avatar: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/black.jpg",
fallback: "C",
id: "charlie",
name: "Charlie",
},
],
initialSelectedKeys: new Set(["fred", "michael"]),
});
const onRemove = (keys: Set) => {
list.remove(...keys);
};
return (
list.setSelectedKeys(keys)}
>
团队成员
暂无团队成员 }
>
{(user) => (
{user.fallback}
{user.name}
)}
为项目选择团队成员
{list.selectedKeys !== "all" && Array.from(list.selectedKeys).length > 0 && (
已选:
{Array.from(list.selectedKeys).map((key) => {
const user = list.getItem(key);
if (!user) return null;
return (
{user.fallback}
{user.name}
);
})}
)}
);
}
```
### 自定义渲染函数
```tsx
"use client";
import {PlanetEarth, Rocket, ShoppingBag, SquareArticle} from "@gravity-ui/icons";
import {Tag, TagGroup} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
selectionMode="single"
>
资讯
旅行
游戏
购物
);
}
```
## Related Components
* **Label**: Accessible label for form controls
* **Description**: Helper text for form fields
* **ErrorMessage**: Displays validation error messages for components with validation support
## 样式
### 传入 Tailwind CSS 类
```tsx
import { TagGroup, Tag, Label } from '@heroui/react';
function CustomTagGroup() {
return (
Categories
Custom Styled
);
}
```
### 自定义组件类
若要自定义 TagGroup 组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.tag-group {
@apply flex flex-col gap-2;
}
.tag-group__list {
@apply flex flex-wrap gap-2;
}
.tag {
@apply rounded-full px-3 py-1;
}
.tag__remove-button {
@apply ml-1;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
TagGroup 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/tag-group.css) 与 [tag.css](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/tag.css)):
#### 基础类
* `.tag-group` - TagGroup 根容器
* `.tag-group__list` - 标签列表容器
* `.tag` - 标签基础样式
* `.tag__remove-button` - 移除按钮触发器
#### 插槽类
* `.tag-group [slot="description"]` - Description 插槽样式
* `.tag-group [slot="errorMessage"]` - ErrorMessage 插槽样式
#### 尺寸类
* `.tag--sm` - 小尺寸标签
* `.tag--md` - 中尺寸标签(默认)
* `.tag--lg` - 大尺寸标签
#### 变体类
* `.tag--default` - 默认变体
* `.tag--surface` - 带 Surface 背景的变体
#### 状态类
* `.tag[data-selected="true"]` - 选中状态
* `.tag[data-disabled="true"]` - 禁用状态
* `.tag[data-hovered="true"]` - 悬停状态
* `.tag[data-pressed="true"]` - 按下状态
* `.tag[data-focus-visible="true"]` - 聚焦状态(键盘焦点)
### 交互状态
该组件同时支持 CSS 伪类与 data 属性:
* **悬停**:标签上 `:hover` 或 `[data-hovered="true"]`
* **聚焦**:标签上 `:focus-visible` 或 `[data-focus-visible="true"]`
* **按下**:标签上 `:active` 或 `[data-pressed="true"]`
* **已选中**:标签上 `[data-selected="true"]` 或 `[aria-selected="true"]`
* **禁用**:标签上 `:disabled` 或 `[data-disabled="true"]`
## API 参考
### TagGroup Props
| Prop | 类型 | 默认值 | 描述 |
| --------------------- | ----------------------------------------------------------------- | ----------- | --------------------- |
| `selectionMode` | `"none" \| "single" \| "multiple"` | `"none"` | 允许的选择类型。 |
| `selectedKeys` | `Selection` | - | 当前选中的 key(受控)。 |
| `defaultSelectedKeys` | `Selection` | - | 初始选中的 key(非受控)。 |
| `onSelectionChange` | `(keys: Selection) => void` | - | 选中变化时调用的事件处理函数。 |
| `disabledKeys` | `Iterable` | - | 禁用标签的 key。 |
| `isDisabled` | `boolean` | - | 是否禁用整个 TagGroup。 |
| `onRemove` | `(keys: Set) => void` | - | 移除标签时调用的事件处理函数。 |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | 组内标签尺寸。 |
| `variant` | `"default" \| "surface"` | `"default"` | 标签视觉变体。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | TagGroup 内容或渲染函数。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### TagGroup.List Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | -------------------------------------------------------------------------- | --- | --------------------- |
| `items` | `Iterable` | - | 标签列表要展示的数据项。 |
| `renderEmptyState` | `() => ReactNode` | - | 列表为空时的渲染函数。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | 标签列表内容或渲染函数。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Tag Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | ---------------------------------------------------------------------- | --- | --------------------- |
| `id` | `Key` | - | 标签唯一标识。 |
| `textValue` | `string` | - | 标签内容的字符串表示,用于无障碍。 |
| `isDisabled` | `boolean` | - | 是否禁用该标签。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | 标签内容或渲染函数。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
**提示:** `size`、`variant` 由父级 `TagGroup` 继承,无法在单个 `Tag` 上直接设置。
### Tag.RemoveButton Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 自定义移除按钮内容(默认为关闭图标)。 |
**提示:** `Tag.RemoveButton` 支持类似 `SearchField.ClearButton` 的定制方式。当为 `TagGroup` 提供 `onRemove` 时:
* **自动渲染**:若 `Tag` 的子节点中未包含自定义 `Tag.RemoveButton`,会自动渲染默认移除按钮。
* **自定义按钮**:若在 `Tag` 下提供了自定义 `Tag.RemoveButton`,将替换自动渲染的按钮。
* **自定义图标**:可向 `Tag.RemoveButton` 传入自定义子内容(如图标)以改变外观。
**示例 — 自动渲染(默认)**:
```tsx
News
{/* Remove button is automatically rendered */}
```
**示例 — 自定义 RemoveButton(带图标)**:
```tsx
News
```
**示例 — 在 render props 中使用自定义 RemoveButton**:
```tsx
{(renderProps) => (
<>
News
{!!renderProps.allowsRemoving && (
)}
>
)}
```
### RenderProps
在 TagGroup.List 中使用渲染函数时,会传入以下值:
| Prop | 类型 | 描述 |
| ---------------- | --------- | ------------ |
| `isSelected` | `boolean` | 标签是否选中。 |
| `isDisabled` | `boolean` | 标签是否禁用。 |
| `isHovered` | `boolean` | 标签是否悬停。 |
| `isPressed` | `boolean` | 标签是否按下。 |
| `isFocused` | `boolean` | 标签是否聚焦。 |
| `isFocusVisible` | `boolean` | 标签是否为可见键盘焦点。 |
# ColorArea 颜色区域
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/color-area
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(colors)/color-area.mdx
> 二维颜色选择器,用户可在渐变区域内选取颜色。
## 引入
```tsx
import { ColorArea } from '@heroui/react';
```
### 用法
```tsx
import {ColorArea} from "@heroui/react";
export function ColorAreaBasic() {
return (
);
}
```
### 组件结构
```tsx
import { ColorArea } from '@heroui/react';
export default () => (
);
```
### 显示点阵
```tsx
import {ColorArea} from "@heroui/react";
export function ColorAreaWithDots() {
return (
);
}
```
### 受控
```tsx
"use client";
import type {Color} from "@heroui/react";
import {ColorArea, ColorSwatch, parseColor} from "@heroui/react";
import {useState} from "react";
export function ColorAreaControlled() {
const [color, setColor] = useState(parseColor("#9B80FF"));
return (
Current color:{" "}
{color ? color.toString("hex") : "(empty)"}
);
}
```
### 颜色空间与通道
使用 `colorSpace` 设置颜色空间(RGB、HSL、HSB),并通过 `xChannel` / `yChannel` prop 自定义横纵轴展示的颜色通道。
```tsx
"use client";
import type {ColorSpace, Key} from "@heroui/react";
import {ColorArea, Label, ListBox, Select, parseColor} from "@heroui/react";
import {useState} from "react";
type ColorChannel = "hue" | "saturation" | "brightness" | "lightness" | "red" | "green" | "blue";
interface ChannelOption {
id: ColorChannel;
name: string;
}
const colorSpaces: Array<{id: ColorSpace; name: string}> = [
{id: "rgb", name: "RGB"},
{id: "hsl", name: "HSL"},
{id: "hsb", name: "HSB"},
];
const channelsBySpace: Record = {
hsb: [
{id: "hue", name: "Hue"},
{id: "saturation", name: "Saturation"},
{id: "brightness", name: "Brightness"},
],
hsl: [
{id: "hue", name: "Hue"},
{id: "saturation", name: "Saturation"},
{id: "lightness", name: "Lightness"},
],
rgb: [
{id: "red", name: "Red"},
{id: "green", name: "Green"},
{id: "blue", name: "Blue"},
],
};
export function ColorAreaSpaceAndChannels() {
const [colorSpace, setColorSpace] = useState("hsb");
const [color, setColor] = useState(() => parseColor("hsb(219, 58%, 93%)"));
const channels = channelsBySpace[colorSpace];
const defaultX = colorSpace === "rgb" ? "blue" : "saturation";
const defaultY =
colorSpace === "rgb" ? "green" : colorSpace === "hsl" ? "lightness" : "brightness";
const [xChannel, setXChannel] = useState(defaultX);
const [yChannel, setYChannel] = useState(defaultY);
const handleColorSpaceChange = (newSpace: Key | null) => {
if (!newSpace) return;
const space = newSpace as ColorSpace;
setColorSpace(space);
// Reset channels to appropriate defaults for the new color space
if (space === "rgb") {
setXChannel("blue");
setYChannel("green");
} else if (space === "hsl") {
setXChannel("saturation");
setYChannel("lightness");
} else {
setXChannel("saturation");
setYChannel("brightness");
}
};
// Filter out the other channel from options (can't have same channel on both axes)
const xChannelOptions = channels.filter((c) => c.id !== yChannel);
const yChannelOptions = channels.filter((c) => c.id !== xChannel);
return (
{/* Controls */}
{/* Color Space Select */}
Color Space
{colorSpaces.map((space) => (
{space.name}
))}
{/* X Channel Select */}
value && setXChannel(value as ColorChannel)}
>
X Axis
{xChannelOptions.map((channel) => (
{channel.name}
))}
{/* Y Channel Select */}
value && setYChannel(value as ColorChannel)}
>
Y Axis
{yChannelOptions.map((channel) => (
{channel.name}
))}
{/* Color Area */}
{/* Color Value Display */}
{color.toString(colorSpace)}
);
}
```
### 禁用
```tsx
import {ColorArea} from "@heroui/react";
export function ColorAreaDisabled() {
return (
);
}
```
### 自定义渲染函数
```tsx
"use client";
import {ColorArea} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
} />
);
}
```
## Related Components
* **ColorSwatch**: Visual preview of a color value
* **ColorSwatchPicker**: Color swatch selection from a list of colors
* **ColorField**: Input for entering color values with hex format
## 样式
### 传入 Tailwind CSS 类
```tsx
import { ColorArea } from '@heroui/react';
function CustomColorArea() {
return (
);
}
```
### 自定义组件类
若要自定义 ColorArea 组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.color-area {
@apply rounded-3xl;
}
.color-area__thumb {
@apply size-5 border-4;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
ColorArea 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/color-area.css)):
#### 基础类
* `.color-area` - 基础样式,含渐变背景与内阴影
* `.color-area--show-dots` - 叠加点阵网格,便于精确取色
#### 元素类
* `.color-area__thumb` - 可拖动的 thumb 指示器
### 交互状态
该组件同时支持 CSS 伪类与 data 属性:
* **禁用**:`[data-disabled="true"]`
* **聚焦**:`[data-focus-visible="true"]`
* **拖拽**:`[data-dragging="true"]`(仅 thumb)
## API 参考
### ColorArea Props
继承自 [React Aria ColorArea](https://react-spectrum.adobe.com/react-aria/ColorArea.html)。
| Prop | 类型 | 默认值 | 描述 |
| -------------- | ---------------------------------------------------------------------------- | -------------- | --------------------- |
| `value` | `string \| Color` | - | 当前颜色值(受控)。 |
| `defaultValue` | `string \| Color` | - | 默认颜色值(非受控)。 |
| `onChange` | `(color: Color) => void` | - | 拖拽过程中颜色变化时调用的事件处理函数。 |
| `onChangeEnd` | `(color: Color) => void` | - | 用户结束拖拽时调用的事件处理函数。 |
| `xChannel` | `ColorChannel` | `"saturation"` | 水平轴对应的颜色通道。 |
| `yChannel` | `ColorChannel` | `"brightness"` | 垂直轴对应的颜色通道。 |
| `colorSpace` | `ColorSpace` | - | 通道所在的颜色空间。 |
| `isDisabled` | `boolean` | `false` | 是否禁用 ColorArea。 |
| `showDots` | `boolean` | `false` | 是否显示点阵网格叠加层。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### ColorArea.Thumb Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------------------------------------------------------------------- | --- | --------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `style` | `CSSProperties \| ((renderProps) => CSSProperties)` | - | 行内样式或渲染函数。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
# ColorField 颜色输入框
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/color-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(colors)/color-field.mdx
> 基于 React Aria ColorField 的颜色输入字段,包含标签、说明与校验能力。
## 引入
```tsx
import { ColorField, parseColor } from '@heroui/react';
```
### 用法
```tsx
"use client";
import type {Color} from "@heroui/react";
import {ColorField, ColorSwatch, Label, parseColor} from "@heroui/react";
import {useState} from "react";
export function Basic() {
const [color, setColor] = useState(parseColor("#0485F7"));
return (
颜色
);
}
```
### 组件结构
```tsx
import {ColorField, Label, ColorSwatch, Description, FieldError, parseColor} from '@heroui/react';
export default () => (
)
```
> **ColorField** 将标签、颜色输入、说明与错误信息组合为单个可访问组件。
### 带说明
```tsx
import {ColorField, Description, Label} from "@heroui/react";
export function WithDescription() {
return (
主色
输入品牌主色
强调色
用于高亮与行动按钮
);
}
```
### 必填字段
```tsx
import {ColorField, Description, Label} from "@heroui/react";
export function Required() {
return (
品牌色
主题色
必填项
);
}
```
### 校验
将 `isInvalid` 与 `FieldError` 配合使用,以展示校验信息。
```tsx
import {ColorField, FieldError, Label} from "@heroui/react";
export function Invalid() {
return (
颜色
请输入有效的十六进制颜色
背景色
颜色格式无效,请使用十六进制(例如 #FF5733)
);
}
```
### 通道编辑
通过设置 `colorSpace` 与 `channel`,ColorField 支持编辑单个颜色通道(hue、saturation、lightness、红、绿、蓝、alpha)。
```tsx
"use client";
import type {Color} from "@heroui/react";
import {ColorField, ColorSwatch, Label, parseColor} from "@heroui/react";
import {useState} from "react";
export function ChannelEditing() {
const [color, setColor] = useState(parseColor("#7F007F"));
return (
分别编辑 HSL 通道:
色相
饱和度
%
明度
%
当前:{color ? color.toString("hex") : "(空)"}
);
}
```
### 受控
控制数值以与其他组件或状态管理同步。
```tsx
"use client";
import type {Color} from "@heroui/react";
import {Button, ColorField, ColorSwatch, Description, Label, parseColor} from "@heroui/react";
import {useState} from "react";
export function Controlled() {
const [value, setValue] = useState(parseColor("#0485F7"));
return (
颜色
当前值:{value ? value.toString("hex") : "(空)"}
setValue(parseColor("#EF4444"))}>
设为红色
setValue(parseColor("#10B981"))}>
设为绿色
setValue(null)}>
清空
);
}
```
### 禁用状态
```tsx
"use client";
import {ColorField, Description, Label} from "@heroui/react";
export function Disabled() {
return (
颜色
该颜色字段已禁用
颜色
该颜色字段已禁用
);
}
```
### 全宽
```tsx
import {ColorField, Label} from "@heroui/react";
export function FullWidth() {
return (
品牌色
主题色
);
}
```
### 变体
ColorField.Group 支持两种视觉变体:
* **`primary`**(默认)— 带阴影的标准样式,适用于大多数场景
* **`secondary`** — 低强调、无阴影的变体,适合用在 Surface 组件内
```tsx
import {ColorField, Label} from "@heroui/react";
export function Variants() {
return (
主要变体
次要变体
);
}
```
### On Surface
在 [Surface](/docs/components/surface) 内使用时,请在 ColorField.Group 上使用 `variant="secondary"`,以应用适合表面背景的低强调变体。
```tsx
import {ColorField, Description, Label, Surface} from "@heroui/react";
export function OnSurface() {
return (
主题色
选择你的主题色
);
}
```
### 表单示例
包含校验与提交处理的完整表单示例。
```tsx
"use client";
import type {Color} from "@heroui/react";
import {Button, ColorField, ColorSwatch, Description, Form, Label} from "@heroui/react";
import {useState} from "react";
export function FormExample() {
const [value, setValue] = useState(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!value) {
return;
}
setIsSubmitting(true);
// Simulate API call
setTimeout(() => {
console.log("已提交颜色:", {color: value.toString("hex")});
setValue(null);
setIsSubmitting(false);
}, 1500);
};
return (
品牌色
选择品牌主色
{isSubmitting ? "保存中…" : "保存颜色"}
);
}
```
### 自定义渲染函数
```tsx
"use client";
import type {Color} from "@heroui/react";
import {ColorField, ColorSwatch, Label, parseColor} from "@heroui/react";
import {useState} from "react";
export function CustomRenderFunction() {
const [color, setColor] = useState(parseColor("#0485F7"));
return (
}
value={color}
onChange={setColor}
>
颜色
}>
);
}
```
## Related Components
* **ColorSwatch**: Visual preview of a color value
* **ColorSwatchPicker**: Color swatch selection from a list of colors
* **ColorPicker**: Composable color picker with popover
## 样式
### 传入 Tailwind CSS 类
```tsx
import {ColorField, Label, ColorSwatch, Description} from '@heroui/react';
function CustomColorField() {
return (
Brand Color
Select your brand's primary color.
);
}
```
### 自定义组件类
ColorField 的默认样式非常克制。你可以覆盖 `.color-field` 类来自定义容器样式。
```css
@layer components {
.color-field {
@apply flex flex-col gap-1;
&[data-invalid="true"],
&[aria-invalid="true"] {
[data-slot="description"] {
@apply hidden;
}
}
[data-slot="label"] {
@apply w-fit;
}
[data-slot="description"] {
@apply px-1;
}
}
}
```
### CSS 类
* `.color-field` – 根容器,样式非常克制(`flex flex-col gap-1`)
> **说明:** 子组件([Label](/docs/components/label)、[Description](/docs/components/description)、[FieldError](/docs/components/field-error))拥有各自的 CSS 类与样式。自定义方式请参见对应文档。ColorField.Group 的样式见下文 API 参考中的 **ColorField.Group Styling** 小节。
### 交互状态
ColorField 会根据状态自动管理以下 data 属性:
* **Invalid**:`[data-invalid="true"]` 或 `[aria-invalid="true"]` – 无效时会自动隐藏 description 插槽
* **Required**:`[data-required="true"]` – 当 `isRequired` 为 true 时应用
* **Disabled**:`[data-disabled="true"]` – 当 `isDisabled` 为 true 时应用
* **Focus Within**:`[data-focus-within="true"]` – 当任意子输入聚焦时应用
## API 参考
### ColorField Props
ColorField 继承 React Aria [ColorField](https://react-aria.adobe.com/ColorField.md) 组件的全部 props。
#### Base Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------------- | ------- | ----------------------------------- |
| `children` | `React.ReactNode \| (values: ColorFieldRenderProps) => React.ReactNode` | - | 子组件(Label、ColorField.Group 等)或渲染函数。 |
| `className` | `string \| (values: ColorFieldRenderProps) => string` | - | 用于样式的 CSS 类,支持渲染 prop。 |
| `style` | `React.CSSProperties \| (values: ColorFieldRenderProps) => React.CSSProperties` | - | 行内样式,支持渲染 prop。 |
| `fullWidth` | `boolean` | `false` | 颜色字段是否占满容器宽度 |
| `id` | `string` | - | 元素的唯一标识符。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
#### Value Props
| Prop | 类型 | 默认值 | 描述 |
| -------------- | -------------------------------- | --- | -------------- |
| `value` | `Color \| null` | - | 当前值(受控)。 |
| `defaultValue` | `Color \| null` | - | 默认值(非受控)。 |
| `onChange` | `(color: Color \| null) => void` | - | 值变化时触发的事件处理函数。 |
#### Channel Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | -------------- | --- | ---------------------------- |
| `colorSpace` | `ColorSpace` | - | 当提供 `channel` 时,颜色字段所处的色彩空间。 |
| `channel` | `ColorChannel` | - | 要编辑的颜色通道。未提供时编辑十六进制值。 |
#### Validation Props
| Prop | 类型 | 默认值 | 描述 |
| -------------------- | ---------------------------------------------------------------- | ---------- | ------------------------ |
| `isRequired` | `boolean` | `false` | 提交表单前是否要求用户输入。 |
| `isInvalid` | `boolean` | - | 当前值是否无效。 |
| `validate` | `(value: Color) => ValidationError \| true \| null \| undefined` | - | 自定义校验函数。 |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | 使用原生 HTML 表单校验或 ARIA 属性。 |
#### State Props
| Prop | 类型 | 默认值 | 描述 |
| ----------------- | --------- | --- | ----------- |
| `isDisabled` | `boolean` | - | 是否禁用输入。 |
| `isReadOnly` | `boolean` | - | 是否可选中但不可修改。 |
| `isWheelDisabled` | `boolean` | - | 是否禁用滚轮改变数值。 |
#### Form Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | --------- | --- | ------------------------- |
| `name` | `string` | - | input 元素的名称,用于 HTML 表单提交。 |
| `autoFocus` | `boolean` | - | 元素渲染后是否应获得焦点。 |
#### Accessibility Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | -------- | --- | -------------- |
| `aria-label` | `string` | - | 没有可见标签时的无障碍标签。 |
| `aria-labelledby` | `string` | - | 用于标注该字段的元素 ID。 |
| `aria-describedby` | `string` | - | 用于描述该字段的元素 ID。 |
| `aria-details` | `string` | - | 包含更多详情的元素 ID。 |
### Composition Components
ColorField 需要与以下独立组件组合使用,请分别导入并直接使用:
* **Label** – 字段标签组件(`@heroui/react`)
* **ColorField.Group** – 颜色输入分组组件(见下文)
* **ColorField.Input** – ColorField.Group 内的输入元素
* **ColorField.Prefix** / **ColorField.Suffix** – 输入组的前缀与后缀插槽
* **ColorSwatch** – 颜色预览组件(`@heroui/react`)
* **Description** – 辅助说明文本组件(`@heroui/react`)
* **FieldError** – 校验错误信息组件(`@heroui/react`)
这些组件各自拥有 props API。请直接在 ColorField 内组合使用:
```tsx
import {ColorField, Label, ColorSwatch, Description, FieldError, parseColor} from '@heroui/react';
Brand Color
Select your brand's primary color.
Please enter a valid color.
```
### Color Types
ColorField 使用来自 React Aria Components 的 `Color` 对象:
```tsx
import {parseColor} from '@heroui/react';
// Parse from hex string
const color = parseColor('#3B82F6');
// Get hex string from color
const hex = color.toString('hex'); // "#3b82f6"
// Get RGB values
const rgb = color.toString('rgb'); // "rgb(59, 130, 246)"
// Use in ColorField
{/* ... */}
```
### ColorFieldRenderProps
在 `className`、`style` 或 `children` 上使用渲染 prop 时,可使用以下值:
| Prop | 类型 | 描述 |
| ---------------- | --------- | -------------- |
| `isDisabled` | `boolean` | 字段是否禁用。 |
| `isInvalid` | `boolean` | 字段当前是否无效。 |
| `isReadOnly` | `boolean` | 字段是否只读。 |
| `isRequired` | `boolean` | 字段是否必填。 |
| `isFocused` | `boolean` | 字段是否聚焦。 |
| `isFocusWithin` | `boolean` | 是否有任意子元素聚焦。 |
| `isFocusVisible` | `boolean` | 是否为可见焦点(键盘导航)。 |
### ColorField.Group Props
ColorField.Group 接受 React Aria `Group` 组件的全部 props,以及:
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------ | ----------- | ----------------------------------------------------------------- |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
| `fullWidth` | `boolean` | `false` | 颜色输入组是否占满容器宽度 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 组件的视觉变体。`primary` 为默认带阴影样式。`secondary` 为低强调、无阴影变体,适合用在 surface 上。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### ColorField.Input Props
ColorField.Input 接受 React Aria `Input` 组件的全部 props,以及:
| Prop | 类型 | 默认值 | 描述 |
| ------------- | -------- | --- | ------------------------ |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
| `placeholder` | `string` | - | 为空时显示的占位符文本。 |
### ColorField.Prefix Props
ColorField.Prefix 接受标准 HTML `div` 属性:
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------------ |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 前缀插槽中要展示的内容。 |
### ColorField.Suffix Props
ColorField.Suffix 接受标准 HTML `div` 属性:
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------------ |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 后缀插槽中要展示的内容。 |
## ColorField.Group Styling
### 自定义组件类
基础类会作用于每个实例。你可以在 `@layer components` 中一次性覆盖它们。
```css
@layer components {
.color-input-group {
@apply inline-flex h-9 items-center overflow-hidden rounded-field border bg-field text-sm text-field-foreground shadow-field outline-none;
&:hover,
&[data-hovered="true"] {
@apply bg-field-hover;
}
&[data-focus-within="true"],
&:focus-within {
@apply status-focused-field;
}
&[data-invalid="true"] {
@apply status-invalid-field;
}
&[data-disabled="true"],
&[aria-disabled="true"] {
@apply status-disabled;
}
}
.color-input-group__input {
@apply flex flex-1 items-center rounded-none border-0 bg-transparent px-3 py-2 shadow-none outline-none;
}
.color-input-group__prefix,
.color-input-group__suffix {
@apply shrink-0 text-field-placeholder flex items-center;
}
}
```
### ColorField.Group CSS Classes
* `.color-input-group` – 根容器样式
* `.color-input-group__input` – 输入区域包裹样式
* `.color-input-group__prefix` – 前缀元素样式
* `.color-input-group__suffix` – 后缀元素样式
### ColorField.Group Interactive States
* **Hover**:`:hover` 或 `[data-hovered="true"]`
* **Focus Within**:`[data-focus-within="true"]` 或 `:focus-within`
* **Invalid**:`[data-invalid="true"]`(也会与 `aria-invalid` 同步)
* **Disabled**:`[data-disabled="true"]` 或 `[aria-disabled="true"]`
# ColorPicker 颜色选择器
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/color-picker
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(colors)/color-picker.mdx
> 可组合的 ColorPicker,在多个颜色组件之间同步颜色值。
## 引入
```tsx
import {
ColorPicker,
ColorArea,
ColorSlider,
ColorSwatch,
ColorField,
ColorSwatchPicker,
} from '@heroui/react';
```
### 用法
```tsx
import {ColorArea, ColorPicker, ColorSlider, ColorSwatch, Label} from "@heroui/react";
export function Basic() {
return (
选择颜色
色相
);
}
```
### 组件结构
ColorPicker 是一个可组合组件,会组合多个颜色相关子组件:
```tsx
import { ColorPicker, ColorArea, ColorSlider, ColorSwatch, Label } from '@heroui/react';
export default () => (
Pick a color
);
```
### 受控
```tsx
"use client";
import {
Button,
ColorArea,
ColorField,
ColorPicker,
ColorSlider,
ColorSwatch,
ColorSwatchPicker,
Label,
parseColor,
} from "@heroui/react";
import {Icon} from "@iconify/react";
import {useState} from "react";
export function Controlled() {
const [color, setColor] = useState(parseColor("#325578"));
const colorPresets = [
"#ef4444",
"#f97316",
"#eab308",
"#22c55e",
"#06b6d4",
"#3b82f6",
"#8b5cf6",
"#ec4899",
"#f43f5e",
];
const shuffleColor = () => {
const randomHue = Math.floor(Math.random() * 360);
const randomSaturation = 50 + Math.floor(Math.random() * 50); // 50-100%
const randomLightness = 40 + Math.floor(Math.random() * 30); // 40-70%
setColor(parseColor(`hsl(${randomHue}, ${randomSaturation}%, ${randomLightness}%)`));
};
return (
选择颜色
{colorPresets.map((preset) => (
))}
已选:{color.toString("hex")}
);
}
```
### 带 swatch
```tsx
import {
ColorArea,
ColorPicker,
ColorSlider,
ColorSwatch,
ColorSwatchPicker,
Label,
} from "@heroui/react";
export function WithSwatches() {
const presets = [
"#ef4444",
"#f97316",
"#eab308",
"#22c55e",
"#06b6d4",
"#3b82f6",
"#8b5cf6",
"#ec4899",
"#f43f5e",
];
return (
品牌色
色相
{presets.map((preset) => (
))}
);
}
```
### 带输入字段
使用 `ColorField` 让用户编辑各个颜色通道的数值,并可配合 `Select` 切换色彩空间。
```tsx
"use client";
import type {ColorChannel, ColorSpace} from "@heroui/react";
import {
ColorArea,
ColorField,
ColorPicker,
ColorSlider,
ColorSwatch,
Label,
ListBox,
Select,
} from "@heroui/react";
import {useState} from "react";
const CHANNEL_LABELS: Record = {
alpha: "透明度",
blue: "蓝",
brightness: "亮度",
green: "绿",
hue: "色相",
lightness: "明度",
red: "红",
saturation: "饱和度",
};
export function WithFields() {
const [colorSpace, setColorSpace] = useState("hsl");
const colorChannelsByColorSpace: Record = {
hsb: ["hue", "saturation", "brightness"],
hsl: ["hue", "saturation", "lightness"],
rgb: ["red", "green", "blue"],
};
return (
选择颜色
色相
setColorSpace(value as ColorSpace)}
>
{Object.keys(colorChannelsByColorSpace).map((space) => (
{space}
))}
{colorChannelsByColorSpace[colorSpace].map((channel) => (
))}
);
}
```
### 带滑块
使用多个 `ColorSlider` 来调整颜色值的各个通道。
```tsx
"use client";
import type {ColorChannel, ColorSpace} from "@heroui/react";
import {ColorPicker, ColorSlider, ColorSwatch, Label, ListBox, Select} from "@heroui/react";
import {useState} from "react";
const CHANNEL_LABELS: Record = {
alpha: "透明度",
blue: "蓝",
brightness: "亮度",
green: "绿",
hue: "色相",
lightness: "明度",
red: "红",
saturation: "饱和度",
};
export function WithSliders() {
const [colorSpace, setColorSpace] = useState("hsl");
const colorChannelsByColorSpace: Record = {
hsb: ["hue", "saturation", "brightness", "alpha"],
hsl: ["hue", "saturation", "lightness", "alpha"],
rgb: ["red", "green", "blue", "alpha"],
};
return (
选择颜色
setColorSpace(value as ColorSpace)}
>
{Object.keys(colorChannelsByColorSpace).map((space) => (
{space}
))}
{colorChannelsByColorSpace[colorSpace].map((channel: ColorChannel) => (
// @ts-expect-error - TypeScript can't correlate dynamic colorSpace with channel type
{CHANNEL_LABELS[channel]}
))}
);
}
```
## Related Components
* **ColorArea**: 2D color picker for selecting colors from a gradient area
* **ColorSlider**: Slider for adjusting individual color channel values
* **ColorSwatch**: Visual preview of a color value
## 样式
### 传入 Tailwind CSS 类
```tsx
import { ColorPicker, ColorArea, ColorSlider, ColorSwatch, Label } from '@heroui/react';
function CustomColorPicker() {
return (
Pick a color
);
}
```
### 自定义组件类
要自定义 ColorPicker 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.color-picker {
@apply inline-flex;
}
.color-picker__trigger {
@apply inline-flex items-center gap-4 rounded-lg;
}
.color-picker__popover {
@apply p-4 rounded-xl;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,以确保组件变体与状态可复用且易于自定义。
### CSS 类
ColorPicker 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/color-picker.css)):
#### 基础类
* `.color-picker` - 基础容器
* `.color-picker__trigger` - 触发按钮
* `.color-picker__popover` - Popover 容器
### 交互状态
组件同时支持 CSS 伪类与 data 属性,便于灵活定制:
* **Focus**:`:focus-visible` 或 `[data-focus-visible="true"]`
* **Disabled**:`:disabled` 或 `[data-disabled="true"]`
## API 参考
### ColorPicker Props
继承自 [React Aria ColorPicker](https://react-spectrum.adobe.com/react-aria/ColorPicker.html)。
| Prop | 类型 | 默认值 | 描述 |
| -------------- | ------------------------ | --- | -------------------------- |
| `value` | `string \| Color` | - | 当前颜色值(受控) |
| `defaultValue` | `string \| Color` | - | 默认颜色值(非受控) |
| `onChange` | `(color: Color) => void` | - | 颜色变化时触发的事件处理函数 |
| `children` | `React.ReactNode` | - | 颜色选择器内容(Trigger、Popover 等) |
| `className` | `string` | - | 额外的 CSS 类 |
### ColorPicker.Trigger Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------- | --- | ------------- |
| `children` | `React.ReactNode \| ((renderProps) => React.ReactNode)` | - | 触发器内容或渲染 prop |
| `className` | `string` | - | 额外的 CSS 类 |
### ColorPicker.Popover Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --------------- | ------------- |
| `placement` | `Placement` | `"bottom left"` | Popover 的放置位置 |
| `children` | `React.ReactNode` | - | Popover 内容 |
| `className` | `string` | - | 额外的 CSS 类 |
### Related Types
#### Color
表示颜色值。完整 API 见 [React Aria Color](https://react-spectrum.adobe.com/react-aria/ColorPicker.html#color)。
| Method | 描述 |
| ---------------------------------- | ----------------------------------- |
| `toString(format)` | 将颜色转换为指定格式的字符串(hex、rgb、hsl、hsb、css) |
| `toFormat(format)` | 将颜色转换为指定格式并返回新的 Color 对象 |
| `getChannelValue(channel)` | 返回指定通道的数值 |
| `withChannelValue(channel, value)` | 设置通道数值并返回新的 Color |
#### parseColor
```tsx
import { parseColor } from 'react-aria-components';
// Parse from string
const color = parseColor('#ff0000');
const hslColor = parseColor('hsl(0, 100%, 50%)');
```
# ColorSlider 颜色滑块
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/color-slider
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(colors)/color-slider.mdx
> ColorSlider 允许用户调整颜色值的单个通道。
## 引入
```tsx
import { ColorSlider, Label } from '@heroui/react';
```
### 用法
```tsx
import {ColorSlider, Label} from "@heroui/react";
export function Basic() {
return (
色相
);
}
```
### 组件结构
导入 ColorSlider 组件后,可通过点号访问各个子部分。
```tsx
import { ColorSlider, Label } from '@heroui/react';
export default () => (
Hue
)
```
### Vertical
```tsx
import {ColorSlider} from "@heroui/react";
export function Vertical() {
return (
);
}
```
### Disabled
```tsx
import {ColorSlider, Label} from "@heroui/react";
export function Disabled() {
return (
色相
);
}
```
### Controlled
```tsx
"use client";
import {ColorSlider, ColorSwatch, Label} from "@heroui/react";
import {useState} from "react";
import {parseColor} from "react-aria-components";
export function Controlled() {
const [color, setColor] = useState(parseColor("hsl(200, 100%, 50%)"));
return (
色相
当前颜色:{color.toString("hsl")}
);
}
```
### HSL Channels
使用多个 ColorSlider 控制同一颜色值的不同通道。这些滑块可以共享同一个颜色值,从而组成完整的颜色选择器。
```tsx
"use client";
import {ColorSlider, ColorSwatch, Label} from "@heroui/react";
import {useState} from "react";
import {parseColor} from "react-aria-components";
export function Channels() {
const [color, setColor] = useState(parseColor("hsl(0, 100%, 50%)"));
return (
色相
饱和度
明度
当前颜色:{color.toString("hsl")}
);
}
```
### Alpha Channel
alpha 通道滑块会显示透明度棋盘格背景,以帮助可视化透明度。
```tsx
import {ColorSlider, Label} from "@heroui/react";
export function AlphaChannel() {
return (
透明度
);
}
```
### RGB Channels
你也可以使用 RGB 色彩空间,并分别控制红、绿、蓝通道。
```tsx
"use client";
import {ColorSlider, ColorSwatch, Label} from "@heroui/react";
import {useState} from "react";
import {parseColor} from "react-aria-components";
export function RGBChannels() {
const [color, setColor] = useState(parseColor("rgb(255, 100, 50)"));
return (
红
绿
蓝
当前颜色:{color.toString("rgb")}
);
}
```
### 自定义渲染函数
```tsx
"use client";
import {ColorSlider, Label} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
色相
);
}
```
## Related Components
* **ColorSwatch**: Visual preview of a color value
* **ColorSwatchPicker**: Color swatch selection from a list of colors
* **ColorPicker**: Composable color picker with popover
## 样式
### 传入 Tailwind CSS 类
```tsx
import { ColorSlider, Label } from '@heroui/react';
function CustomColorSlider() {
return (
Hue
);
}
```
### 自定义组件类
要自定义 ColorSlider 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.color-slider {
@apply flex flex-col gap-2;
}
.color-slider__output {
@apply text-muted text-sm;
}
.color-slider__track {
@apply relative h-5 w-full rounded-full;
}
.color-slider__thumb {
@apply size-4 rounded-full border-3 border-white shadow-overlay;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,以确保组件变体与状态可复用且易于自定义。
### CSS 类
ColorSlider 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/color-slider.css)):
#### 基础类
* `.color-slider` - 基础滑块容器
* `.color-slider__output` - 显示当前数值的输出元素
* `.color-slider__track` - 带颜色渐变的轨道元素
* `.color-slider__thumb` - 显示当前颜色的滑块(thumb)
#### 状态类
* `.color-slider[data-disabled="true"]` - 禁用状态
* `.color-slider[data-orientation="vertical"]` - 纵向方向
* `.color-slider__thumb[data-dragging="true"]` - 正在拖动 thumb
* `.color-slider__thumb[data-focus-visible="true"]` - thumb 的键盘焦点
* `.color-slider__thumb[data-disabled="true"]` - thumb 禁用状态
### 交互状态
组件同时支持 CSS 伪类与 data 属性,便于灵活定制:
* **Hover**:thumb 上 `:hover` 或 `[data-hovered="true"]`
* **Focus**:thumb 上 `:focus-visible` 或 `[data-focus-visible="true"]`
* **Dragging**:thumb 上 `[data-dragging="true"]`
* **Disabled**:滑块或 thumb 上 `:disabled` 或 `[data-disabled="true"]`
## API 参考
### ColorSlider Props
继承自 [React Aria ColorSlider](https://react-spectrum.adobe.com/react-aria/ColorSlider.html)。
| Prop | 类型 | 默认值 | 描述 |
| -------------- | ------------------------------------------------------------------------------ | -------------- | ----------------------------------------------------------------- |
| `channel` | `ColorChannel` | - | 滑块操作的通道(hue、saturation、lightness、brightness、alpha、red、green、blue) |
| `colorSpace` | `ColorSpace` | - | 色彩空间(hsl、hsb、rgb)。默认取当前值的色彩空间 |
| `value` | `string \| Color` | - | 当前颜色值(受控) |
| `defaultValue` | `string \| Color` | - | 默认颜色值(非受控) |
| `onChange` | `(value: Color) => void` | - | 拖动过程中数值变化时触发的事件处理函数 |
| `onChangeEnd` | `(value: Color) => void` | - | 拖动结束时触发的事件处理函数 |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | 滑块方向 |
| `isDisabled` | `boolean` | - | 是否禁用滑块 |
| `name` | `string` | - | 用于表单提交的 input 名称 |
| `aria-label` | `string` | - | 滑块的无障碍标签 |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode \| RenderFunction` | - | 滑块内容或渲染函数 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### ColorSlider.Output Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------------------- | --- | --------- |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode \| RenderFunction` | - | 输出内容或渲染函数 |
### ColorSlider.Track Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | --------------------------------- | --- | --------- |
| `className` | `string` | - | 额外的 CSS 类 |
| `style` | `CSSProperties \| RenderFunction` | - | 行内样式或渲染函数 |
| `children` | `ReactNode \| RenderFunction` | - | 轨道内容或渲染函数 |
### ColorSlider.Thumb Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | --------------------------------- | --- | ------------- |
| `className` | `string` | - | 额外的 CSS 类 |
| `style` | `CSSProperties \| RenderFunction` | - | 行内样式或渲染函数 |
| `children` | `ReactNode \| RenderFunction` | - | thumb 内容或渲染函数 |
### RenderProps
使用渲染函数时,会提供以下值:
| Prop | 类型 | 描述 |
| ------------- | ---------------------------- | --------------- |
| `state` | `ColorSliderState` | ColorSlider 的状态 |
| `color` | `Color` | 当前颜色值 |
| `orientation` | `"horizontal" \| "vertical"` | 滑块方向 |
| `isDisabled` | `boolean` | 是否禁用滑块 |
## 无障碍
ColorSlider 实现了 ARIA slider 模式,并提供:
* 完整的键盘导航支持(方向键、Home、End、Page Up/Down)
* 屏幕阅读器对数值变化的播报
* 合理的焦点管理
* 禁用状态支持
* 通过隐藏 input 元素与 HTML 表单集成
* 结合区域设置进行数值格式化的国际化支持
更多信息见 [React Aria ColorSlider 文档](https://react-spectrum.adobe.com/react-aria/ColorSlider.html)。
# ColorSwatchPicker 颜色色块选择器
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/color-swatch-picker
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(colors)/color-swatch-picker.mdx
> 允许用户从预置调色板中选择颜色的 swatch 列表。
## 引入
```tsx
import { ColorSwatchPicker, parseColor } from '@heroui/react';
```
### 用法
```tsx
import {ColorSwatchPicker} from "@heroui/react";
const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"];
export function Basic() {
return (
{colors.map((color) => (
))}
);
}
```
### 组件结构
导入 ColorSwatchPicker 组件,并通过点语法访问所有子部分。
```tsx
import { ColorSwatchPicker } from '@heroui/react';
export default () => (
);
```
### 变体
```tsx
import {ColorSwatchPicker} from "@heroui/react";
const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"];
export function Variants() {
return (
圆形(默认)
{colors.map((color) => (
))}
方形
{colors.map((color) => (
))}
);
}
```
### 尺寸
```tsx
import {ColorSwatchPicker} from "@heroui/react";
const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"];
const sizes = ["xs", "sm", "md", "lg", "xl"] as const;
const SIZE_LABELS: Record<(typeof sizes)[number], string> = {
lg: "大",
md: "中",
sm: "小",
xl: "特大",
xs: "特小",
};
export function Sizes() {
return (
{sizes.map((size) => (
{SIZE_LABELS[size]}
{colors.map((color) => (
))}
))}
);
}
```
### 堆叠布局
```tsx
import {ColorSwatchPicker} from "@heroui/react";
const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"];
export function StackLayout() {
return (
{colors.map((color) => (
))}
);
}
```
### 默认值
```tsx
import {ColorSwatchPicker} from "@heroui/react";
const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"];
export function DefaultValue() {
return (
{colors.map((color) => (
))}
);
}
```
### 受控
```tsx
"use client";
import {ColorSwatchPicker, parseColor} from "@heroui/react";
import {useState} from "react";
const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"];
export function Controlled() {
const [value, setValue] = useState(parseColor("#F43F5E"));
return (
{colors.map((color) => (
))}
已选:{value.toString("hex")}
);
}
```
### 禁用
```tsx
import {ColorSwatchPicker} from "@heroui/react";
const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"];
export function Disabled() {
return (
{colors.map((color) => (
))}
);
}
```
### 自定义指示器
```tsx
import {HeartFill} from "@gravity-ui/icons";
import {ColorSwatchPicker} from "@heroui/react";
export function CustomIndicator() {
const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"];
return (
{colors.map((color) => (
))}
);
}
```
### 自定义渲染函数
```tsx
"use client";
import {ColorSwatchPicker} from "@heroui/react";
const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"];
export function CustomRenderFunction() {
return (
}>
{colors.map((color) => (
))}
);
}
```
## Related Components
* **ColorSwatch**: Visual preview of a color value
* **ColorField**: Input for entering color values with hex format
* **ColorArea**: 2D color picker for selecting colors from a gradient area
## 样式
### 传入 Tailwind CSS 类
你可以使用 `className` props 自定义 ColorSwatchPicker:
```tsx
import { ColorSwatchPicker } from '@heroui/react';
function CustomColorSwatchPicker() {
return (
);
}
```
### 自定义组件类
若要自定义 ColorSwatchPicker 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.color-swatch-picker {
@apply gap-4;
}
.color-swatch-picker__item {
@apply shadow-md;
}
.color-swatch-picker__swatch {
@apply border-2 border-white;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
ColorSwatchPicker 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/color-swatch-picker.css)):
#### 基础与结构
* `.color-swatch-picker` - 基础容器(flex 布局)
* `.color-swatch-picker__item` - 单个 swatch 包裹层
* `.color-swatch-picker__swatch` - swatch 视觉元素
#### 尺寸类
* `.color-swatch-picker--xs` - 特小(16px)
* `.color-swatch-picker--sm` - 小(24px)
* `.color-swatch-picker--md` - 中(32px,默认)
* `.color-swatch-picker--lg` - 大(36px)
* `.color-swatch-picker--xl` - 特大(40px)
#### 形状变体
* `.color-swatch-picker--circle` - 圆形(默认)
* `.color-swatch-picker--square` - 圆角方形
#### 布局类
* `.color-swatch-picker--grid` - 横向换行网格(默认)
* `.color-swatch-picker--stack` - 纵向堆叠
### 交互状态
组件同时支持 CSS 伪类与 data 属性,以便灵活控制状态:
* **悬停**:`:hover` 或 `[data-hovered="true"]` — 缩放至 1.1(仅在未选中时)
* **聚焦**:`:focus-visible` 或 `[data-focus-visible="true"]` — 焦点环
* **已选中**:`[data-selected="true"]` — 与 swatch 同色的内边框
* **禁用**:`[data-disabled="true"]` — 降低透明度
## API 参考
### ColorSwatchPicker Props
继承自 [React Aria ColorSwatchPicker](https://react-spectrum.adobe.com/react-aria/ColorSwatchPicker.html)。
| Prop | 类型 | 默认值 | 描述 |
| -------------- | ------------------------------------------------------------------------------------ | ---------- | ------------------------- |
| `value` | `string \| Color` | - | 当前选中颜色(受控) |
| `defaultValue` | `string \| Color` | - | 默认选中颜色(非受控) |
| `onChange` | `(value: Color) => void` | - | 选中变化时调用的处理函数 |
| `size` | `"xs" \| "sm" \| "md" \| "lg" \| "xl"` | `"md"` | swatch 尺寸 |
| `variant` | `"circle" \| "square"` | `"circle"` | swatch 形状 |
| `layout` | `"grid" \| "stack"` | `"grid"` | 布局方向 |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `React.ReactNode` | - | ColorSwatchPicker.Item 元素 |
| `render` | `DOMRenderFunction` | - | 通过自定义渲染函数覆盖默认的 DOM 元素。 |
### ColorSwatchPicker.Item Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | ---------------------------------------------------------------------------------------- | ------- | --------------------------- |
| `color` | `string \| Color` | **必填** | swatch 颜色 |
| `isDisabled` | `boolean` | `false` | 是否禁用该项 |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `React.ReactNode` | - | ColorSwatchPicker.Swatch 元素 |
| `render` | `DOMRenderFunction` | - | 通过自定义渲染函数覆盖默认的 DOM 元素。 |
### ColorSwatchPicker.Swatch Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------- | --- | --------- |
| `className` | `string` | - | 额外的 CSS 类 |
### parseColor
为方便使用,从 React Aria Components 重新导出 `parseColor` 函数:
```tsx
import { parseColor } from '@heroui/react';
// 解析十六进制颜色
const red = parseColor('#ff0000');
// 解析 RGB
const green = parseColor('rgb(0, 255, 0)');
// 解析 HSL
const blue = parseColor('hsl(240, 100%, 50%)');
```
# ColorSwatch 颜色色块
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/color-swatch
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(colors)/color-swatch.mdx
> 颜色值的视觉预览,并提供无障碍支持。
## 引入
```tsx
import { ColorSwatch } from '@heroui/react';
```
### 用法
```tsx
import {ColorSwatch} from "@heroui/react";
export function ColorSwatchBasic() {
return (
);
}
```
### Sizes
```tsx
import {ColorSwatch} from "@heroui/react";
export function ColorSwatchSizes() {
return (
);
}
```
### Shapes
```tsx
import {ColorSwatch} from "@heroui/react";
export function ColorSwatchShapes() {
return (
);
}
```
### Transparency
```tsx
import {ColorSwatch} from "@heroui/react";
export function ColorSwatchTransparency() {
return (
);
}
```
### Custom Styles with Render Props
你可以使用 `style` 渲染 prop 来读取颜色值并创建自定义视觉效果。
```tsx
"use client";
import {ColorSwatch} from "@heroui/react";
export function ColorSwatchCustomStyles() {
const colors = ["#0485F7", "#EF4444", "#F59E0B", "#10B981", "#D946EF"];
return (
{/* 发光效果 */}
发光效果
{colors.map((color) => (
({
boxShadow: `0 0 20px 2px ${color}`,
})}
/>
))}
{/* 渐变色块 */}
渐变
{colors.map((color) => (
({
background: `linear-gradient(135deg, ${c.toString("css")}, white)`,
})}
/>
))}
);
}
```
### Accessibility
使用 `colorName` 为颜色提供自定义可访问名称,并使用 `aria-label` 补充颜色用途的上下文。
```tsx
import {ColorSwatch} from "@heroui/react";
export function ColorSwatchAccessibility() {
return (
);
}
```
### 自定义渲染函数
```tsx
"use client";
import {ColorSwatch} from "@heroui/react";
export function CustomRenderFunction() {
return (
);
}
```
## Related Components
* **ColorSwatchPicker**: Color swatch selection from a list of colors
* **ColorField**: Input for entering color values with hex format
* **ColorArea**: 2D color picker for selecting colors from a gradient area
## 样式
### 传入 Tailwind CSS 类
```tsx
import {ColorSwatch} from '@heroui/react';
function CustomColorSwatch() {
return (
);
}
```
### 自定义组件类
要自定义 ColorSwatch 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.color-swatch {
@apply border-2 border-white;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,以确保组件变体与状态可复用且易于自定义。
### CSS 类
ColorSwatch 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/color-swatch.css)):
#### 基础类
* `.color-swatch` - 基础 swatch(色板)样式,透明区域使用棋盘格背景
#### 形状类
* `.color-swatch--circle` - 圆形(默认)
* `.color-swatch--square` - 圆角方形
#### 尺寸类
* `.color-swatch--xs` - 特小(16px)
* `.color-swatch--sm` - 小(24px)
* `.color-swatch--md` - 中(32px,默认)
* `.color-swatch--lg` - 大(36px)
* `.color-swatch--xl` - 特大(40px)
## API 参考
### ColorSwatch Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | ------------------------------------------------------------------------------ | ---------- | ------------------------ |
| `color` | `string \| Color` | - | 要展示的颜色值(hex、rgb、hsl 等) |
| `colorName` | `string` | - | 颜色的可访问名称(会覆盖自动生成的描述) |
| `className` | `string` | - | 额外的 CSS 类 |
| `shape` | `"circle" \| "square"` | `"circle"` | swatch(色板)形状 |
| `size` | `"xs" \| "sm" \| "md" \| "lg" \| "xl"` | `"md"` | swatch(色板)尺寸 |
| `style` | `CSSProperties \| ((renderProps) => CSSProperties)` | - | 行内样式,或带颜色访问能力的渲染 prop 函数 |
| `aria-label` | `string` | - | swatch 的无障碍标签 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Style Render Props
当把 `style` 作为函数传入时,你会获得包含颜色对象在内的渲染参数:
```tsx
({
boxShadow: `0 4px 14px ${color.toString("css")}80`,
})}
/>
```
`color` 对象提供例如:
* `color.toString("css")` - 返回 CSS 颜色字符串
* `color.toString("hex")` - 返回十六进制颜色字符串
* `color.getChannelValue("alpha")` - 返回 alpha 通道数值
# Slider 滑块
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/slider
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(controls)/slider.mdx
> Slider 允许用户在范围内选择一个或多个值。
## 引入
```tsx
import { Slider } from '@heroui/react';
```
### 用法
```tsx
import {Label, Slider} from "@heroui/react";
export function Default() {
return (
音量
);
}
```
### 组件结构
引入 Slider 组件,并通过点语法访问各部分。
```tsx
import { Slider, Label } from '@heroui/react';
export default () => (
)
```
### 范围滑块组件结构
```tsx
import { Slider, Label } from '@heroui/react';
export default () => (
{({state}) => (
<>
{state.values.map((_, i) => (
))}
>
)}
)
```
### 纵向
```tsx
import {Label, Slider} from "@heroui/react";
export function Vertical() {
return (
音量
);
}
```
### 范围
```tsx
"use client";
import {Label, Slider} from "@heroui/react";
export function Range() {
return (
价格区间
{({state}) => (
<>
{state.values.map((_, i) => (
))}
>
)}
);
}
```
### 禁用
```tsx
import {Label, Slider} from "@heroui/react";
export function Disabled() {
return (
音量
);
}
```
### 自定义渲染函数
```tsx
"use client";
import {Label, Slider} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
音量
);
}
```
## Related Components
* **Label**: Accessible label for form controls
* **Form**: Form validation and submission handling
* **Description**: Helper text for form fields
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Slider, Label } from '@heroui/react';
function CustomSlider() {
return (
Volume
);
}
```
### 自定义组件类
若要自定义 Slider 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.slider {
@apply flex flex-col gap-2;
}
.slider__output {
@apply text-muted-fg text-sm;
}
.slider-track {
@apply relative h-2 w-full rounded-full bg-surface-secondary;
}
.slider-fill {
@apply absolute h-full rounded-full bg-accent;
}
.slider-thumb {
@apply size-4 rounded-full bg-accent border-2 border-background;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Slider 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/slider.css)):
#### 基础类
* `.slider` - Slider 根容器
* `.slider__output` - 显示当前值的输出元素
* `.slider-track` - 包含填充与滑块的轨道元素
* `.slider-fill` - 显示已选范围的填充元素
* `.slider-thumb` - 单个滑块控制点
#### 状态类
* `.slider[data-disabled="true"]` - 禁用状态
* `.slider[data-orientation="vertical"]` - 纵向方向
* `.slider-thumb[data-dragging="true"]` - 滑块正在拖动
* `.slider-thumb[data-focus-visible="true"]` - 滑块键盘聚焦
* `.slider-thumb[data-disabled="true"]` - 滑块禁用状态
* `.slider-track[data-fill-start="true"]` - 填充从起点开始
* `.slider-track[data-fill-end="true"]` - 填充在终点结束
### 交互状态
该组件同时支持 CSS 伪类与 data 属性,以提供更灵活的状态控制:
* **悬停**:滑块上的 `:hover` 或 `[data-hovered="true"]`
* **聚焦**:滑块上的 `:focus-visible` 或 `[data-focus-visible="true"]`
* **拖动**:滑块上的 `[data-dragging="true"]`
* **禁用**:Slider 或滑块上的 `:disabled` 或 `[data-disabled="true"]`
## API 参考
### Slider Props
| Prop | 类型 | 默认值 | 描述 |
| ----------------- | ------------------------------------------------------------------------- | -------------- | --------------------- |
| `value` | `number \| number[]` | - | 当前值(受控)。 |
| `defaultValue` | `number \| number[]` | - | 默认值(非受控)。 |
| `onChange` | `(value: number \| number[]) => void` | - | 值变化时的事件处理函数。 |
| `onChangeEnd` | `(value: number \| number[]) => void` | - | 拖动结束时的事件处理函数。 |
| `minValue` | `number` | `0` | Slider 的最小值。 |
| `maxValue` | `number` | `100` | Slider 的最大值。 |
| `step` | `number` | `1` | Slider 的步进值。 |
| `formatOptions` | `Intl.NumberFormatOptions` | - | 数值标签的显示格式。 |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Slider 的方向。 |
| `isDisabled` | `boolean` | - | Slider 是否禁用。 |
| `aria-label` | `string` | - | Slider 的无障碍标签。 |
| `aria-labelledby` | `string` | - | 标注 Slider 的元素 ID。 |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | Slider 内容或渲染函数。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Slider.Output Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------------- | --- | --------------------- |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | 输出内容或渲染函数。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Slider.Track Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------------ | --- | --------------------- |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | 轨道内容或渲染函数。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Slider.Fill Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | --------------- | --- | ---------- |
| `className` | `string` | - | 额外的 CSS 类。 |
| `style` | `CSSProperties` | - | 行内样式。 |
### Slider.Thumb Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | ------------------------------------------------------------------------------ | --- | --------------------- |
| `index` | `number` | `0` | 滑块在 Slider 内的索引。 |
| `isDisabled` | `boolean` | - | 该滑块是否禁用。 |
| `name` | `string` | - | 输入元素名称,用于提交 HTML 表单。 |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | 滑块内容或渲染函数。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### RenderProps
对 `Slider.Output` 或 `Slider.Track` 使用渲染函数时,会提供以下值:
| Prop | 类型 | 描述 |
| -------------------- | ---------------------------- | --------------- |
| `state` | `SliderState` | Slider 的状态。 |
| `values` | `number[]` | 按滑块索引管理的数值。 |
| `getThumbValueLabel` | `(index: number) => string` | 返回指定滑块数值的字符串标签。 |
| `orientation` | `"horizontal" \| "vertical"` | Slider 的方向。 |
| `isDisabled` | `boolean` | Slider 是否禁用。 |
## 示例
### 基础用法
```tsx
import { Slider, Label } from '@heroui/react';
Volume
```
### 范围滑块
```tsx
import { Slider, Label } from '@heroui/react';
Price Range
{({state}) => (
<>
{state.values.map((_, i) => (
))}
>
)}
```
### 受控值
```tsx
import { Slider, Label } from '@heroui/react';
import { useState } from 'react';
function ControlledSlider() {
const [value, setValue] = useState(25);
return (
<>
Volume
Current value: {value}
>
);
}
```
### 自定义数值格式
```tsx
import { Slider, Label } from '@heroui/react';
Price
```
### 纵向方向
```tsx
import { Slider, Label } from '@heroui/react';
Volume
```
### 自定义输出展示
```tsx
import { Slider, Label } from '@heroui/react';
Range
{({state}) =>
state.values.map((_, i) => state.getThumbValueLabel(i)).join(' – ')
}
{({state}) => (
<>
{state.values.map((_, i) => (
))}
>
)}
```
## 无障碍
Slider 组件实现 ARIA slider 模式,并提供:
* 完整的键盘导航支持(方向键、Home、End、Page Up/Down)
* 数值变化时的屏幕阅读器播报
* 合理的焦点管理
* 禁用状态支持
* 通过隐藏 input 元素与 HTML 表单集成
* 结合区域设置进行数值格式化的国际化支持
* 从右到左(RTL)语言支持
更多信息见 [React Aria Slider 文档](https://react-spectrum.adobe.com/react-aria/Slider.html)。
# Switch 开关
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/switch
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(controls)/switch.mdx
> 用于布尔状态的开关组件。
## 引入
```tsx
import { Switch, SwitchGroup, Label } from '@heroui/react';
```
### 用法
```tsx
import {Switch} from "@heroui/react";
export function Basic() {
return (
启用通知
);
}
```
### 组件结构
引入 Switch 组件,并通过点语法访问各部分。
```tsx
import { Switch, Description, FieldError } from '@heroui/react';
export default () => (
{/* 可选 */}
Label {/* 纯文本 —— 可点击的标签,同时作为无障碍名称 */}
{/* 可选 — 字段级帮助文本 */}
{/* 可选 — 校验错误信息 */}
);
```
要对多个 Switch 进行分组,请使用 `SwitchGroup` 组件:
```tsx
import { Switch, SwitchGroup, Label } from '@heroui/react';
export default () => (
Option 1
Option 2
);
```
### 禁用
```tsx
import {Switch} from "@heroui/react";
export function Disabled() {
return (
启用通知
);
}
```
### 默认选中
```tsx
import {Switch} from "@heroui/react";
export function DefaultSelected() {
return (
启用通知
);
}
```
### 受控
```tsx
"use client";
import {Switch} from "@heroui/react";
import React from "react";
export function Controlled() {
const [isSelected, setIsSelected] = React.useState(false);
return (
启用通知
开关{isSelected ? "已打开" : "已关闭"}
);
}
```
### 无标签
```tsx
import {Switch} from "@heroui/react";
export function WithoutLabel() {
return (
);
}
```
### 尺寸
```tsx
import {Switch} from "@heroui/react";
export function Sizes() {
return (
小
中
大
);
}
```
### 标签位置
```tsx
import {Switch} from "@heroui/react";
export function LabelPosition() {
return (
标签在后
标签在前
);
}
```
### 带图标
```tsx
"use client";
import {
BellFill,
BellSlash,
Check,
Microphone,
MicrophoneSlash,
Moon,
Power,
Sun,
VolumeFill,
VolumeSlashFill,
} from "@gravity-ui/icons";
import {Switch} from "@heroui/react";
export function WithIcons() {
const icons = {
check: {
off: Power,
on: Check,
selectedControlClass: "bg-green-500/80",
},
darkMode: {
off: Moon,
on: Sun,
selectedControlClass: "",
},
microphone: {
off: Microphone,
on: MicrophoneSlash,
selectedControlClass: "bg-red-500/80",
},
notification: {
off: BellSlash,
on: BellFill,
selectedControlClass: "bg-purple-500/80",
},
volume: {
off: VolumeFill,
on: VolumeSlashFill,
selectedControlClass: "bg-blue-500/80",
},
};
return (
{Object.entries(icons).map(([key, value]) => (
{({isSelected}) => (
<>
{isSelected ? (
) : (
)}
>
)}
))}
);
}
```
### 带描述
```tsx
import {Description, Switch} from "@heroui/react";
export function WithDescription() {
return (
公开资料
允许他人查看你的资料信息
);
}
```
### 分组
```tsx
import {Switch, SwitchGroup} from "@heroui/react";
export function Group() {
return (
允许通知
营销邮件
社交媒体更新
);
}
```
### 横向分组
```tsx
import {Switch, SwitchGroup} from "@heroui/react";
export function GroupHorizontal() {
return (
通知
营销
社交
);
}
```
### Render Props
```tsx
"use client";
import {Switch} from "@heroui/react";
export function RenderProps() {
return (
{({isSelected}) => (
{isSelected ? "已开启" : "已关闭"}
)}
);
}
```
### 表单集成
```tsx
"use client";
import {Button, Switch, SwitchGroup} from "@heroui/react";
import React from "react";
export function Form() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
alert(
`表单提交内容:\n${Array.from(formData.entries())
.map(([key, value]) => `${key}: ${value}`)
.join("\n")}`,
);
};
return (
启用通知
订阅新闻简报
接收营销更新
Submit
);
}
```
### 自定义样式
```tsx
"use client";
import {Check, Power} from "@gravity-ui/icons";
import {Switch} from "@heroui/react";
export function CustomStyles() {
return (
{({isSelected}) => (
<>
{isSelected ? (
) : (
)}
>
)}
);
}
```
### 自定义渲染函数
```tsx
"use client";
import {Switch} from "@heroui/react";
export function CustomRenderFunction() {
return (
}>
启用通知
);
}
```
## Related Components
* **Label**: Accessible label for form controls
* **Description**: Helper text for form fields
* **Button**: Allows a user to perform an action
## 样式
### 传入 Tailwind CSS 类
你可以自定义各个 Switch:
```tsx
import { Switch, Label } from '@heroui/react';
function CustomSwitch() {
return (
{({isSelected}) => (
<>
Custom Switch
>
)}
);
}
```
或自定义 SwitchGroup 布局:
```tsx
import { Switch, SwitchGroup, Label } from '@heroui/react';
function CustomSwitchGroup() {
return (
Option 1
Option 2
);
}
```
### 自定义组件类
若要自定义 Switch 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.switch {
@apply inline-flex gap-3 items-center;
}
.switch__control {
@apply h-5 w-8 bg-gray-400 data-[selected=true]:bg-blue-500;
}
.switch__thumb {
@apply bg-white shadow-sm;
}
.switch__content {
@apply items-center gap-3;
}
.switch__icon {
@apply h-3 w-3 text-current;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
#### Switch 类
Switch 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/switch.css)):
* `.switch` - Switch 根容器(字段)
* `.switch__content` - 包裹控件与标签文本的可点击 label
* `.switch__control` - Switch 轨道
* `.switch__thumb` - 可移动的滑块
* `.switch__icon` - 滑块内可选图标
* `.switch--sm` - 小尺寸变体
* `.switch--md` - 中尺寸变体(默认)
* `.switch--lg` - 大尺寸变体
#### SwitchGroup 类
SwitchGroup 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/switch-group.css)):
* `.switch-group` - Switch 组容器
* `.switch-group__items` - Switch 项容器
* `.switch-group--horizontal` - 横向布局
* `.switch-group--vertical` - 纵向布局(默认)
### 交互状态
该 Switch 同时支持 CSS 伪类与 data 属性,以提供更灵活的状态控制:
* **已选中**:`[data-selected="true"]`(滑块位置与背景色变化)
* **悬停**:`:hover` 或 `[data-hovered="true"]`(作用于 `Switch.Control` / 按钮)
* **聚焦**:`:focus-visible` 或 `[data-focus-visible="true"]`(在按钮上显示轨道焦点环)
* **禁用**:`[data-disabled="true"]`(降低透明度,包括帮助文本)
* **按压**:`:active` 或 `[data-pressed="true"]`
## API 参考
### Switch Props
继承自 [React Aria SwitchField](https://react-spectrum.adobe.com/react-aria/Switch.html)。
| Prop | 类型 | 默认值 | 描述 |
| -------------------- | ------------------------------------------------------------------------------ | ---------- | ---------------------- |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Switch 尺寸。 |
| `isSelected` | `boolean` | `false` | Switch 是否打开。 |
| `defaultSelected` | `boolean` | `false` | 默认是否打开(非受控)。 |
| `isDisabled` | `boolean` | `false` | Switch 是否禁用。 |
| `isInvalid` | `boolean` | `false` | Switch 是否无效。 |
| `isReadOnly` | `boolean` | `false` | Switch 是否只读。 |
| `isRequired` | `boolean` | `false` | Switch 是否必须打开。 |
| `validate` | `(value: boolean) => ValidationError \| true \| null \| undefined` | - | 自定义校验函数。 |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | 使用原生 HTML 校验或 ARIA 校验。 |
| `name` | `string` | - | 输入元素名称,用于提交 HTML 表单。 |
| `value` | `string` | - | 输入元素值,用于提交 HTML 表单。 |
| `onChange` | `(isSelected: boolean) => void` | - | Switch 值变化时的事件处理函数。 |
| `onPress` | `(e: PressEvent) => void` | - | Switch 被按下时的事件处理函数。 |
| `children` | `React.ReactNode \| (values: SwitchFieldRenderProps) => React.ReactNode` | - | Switch 内容或字段级渲染 prop。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Switch.Content Props
包裹控件与标签文本的可点击 ``。请把 `Switch.Control` 与 `Label` 放在它内部;`Description`/`FieldError` 作为 `Switch.Content` 的兄弟节点。对于没有标签的 switch,省略 `Label` 并在 `Switch` 上传入 `aria-label`。
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------- | --- | ------------------------- |
| `children` | `React.ReactNode \| (values: SwitchButtonRenderProps) => React.ReactNode` | - | 按钮内容(控件 + 标签),或按钮级渲染 prop |
| `className` | `string \| (values: SwitchButtonRenderProps) => string` | - | 应用到可点击 label 的类名 |
### SwitchFieldRenderProps
在根 `Switch` 上使用渲染 prop 时,提供以下字段级值:
| Prop | 类型 | 描述 |
| ------------ | ------------- | -------------- |
| `isSelected` | `boolean` | Switch 当前是否打开。 |
| `isDisabled` | `boolean` | Switch 是否禁用。 |
| `isReadOnly` | `boolean` | Switch 是否只读。 |
| `isInvalid` | `boolean` | Switch 是否无效。 |
| `isRequired` | `boolean` | Switch 是否必填。 |
| `state` | `ToggleState` | Switch 的状态。 |
### SwitchButtonRenderProps
`Switch.Control` 使用按钮级渲染 prop(`isHovered`、`isPressed`、`isFocusVisible` 等)。将函数作为 `Switch.Control` 的子元素即可访问。
### SwitchGroup Props
| Prop | 类型 | 默认值 | 描述 |
| ------------- | ---------------------------- | ------------ | -------------- |
| `orientation` | `'horizontal' \| 'vertical'` | `'vertical'` | Switch 组方向。 |
| `children` | `React.ReactNode` | - | 要渲染的 Switch 项。 |
| `className` | `string` | - | 额外的 CSS 类。 |
# Badge 徽标
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/badge
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(data-display)/badge.mdx
> 展示相对其他元素定位的小型指示器,常用于未读数、状态点与标签等场景。
## 引入
```tsx
import { Badge } from '@heroui/react';
```
## 组件结构
Badge 通过 `Badge.Anchor` 相对另一个元素定位。纯文本子节点会自动包在 `` 中。
> 若需要独立展示标签,请改用 [Chip](/docs/react/components/chip) 组件。
```tsx
5
```
### 用法
```tsx
import {Avatar, Badge} from "@heroui/react";
const GREEN_AVATAR_URL = "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg";
const ORANGE_AVATAR_URL =
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg";
const BLUE_AVATAR_URL = "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg";
export function BadgeBasic() {
return (
);
}
```
### 颜色
```tsx
import {Avatar, Badge} from "@heroui/react";
const AVATAR_URL = "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg";
export function BadgeColors() {
const colors = ["default", "accent", "success", "warning", "danger"] as const;
return (
{colors.map((color) => (
JD
))}
);
}
```
### 尺寸
```tsx
import {Avatar, Badge} from "@heroui/react";
const AVATAR_URL = "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg";
export function BadgeSizes() {
const sizes = ["sm", "md", "lg"] as const;
return (
{sizes.map((size) => (
JD
5
))}
);
}
```
### 变体
```tsx
import {Avatar, Badge, Separator} from "@heroui/react";
import React from "react";
const AVATAR_URL = "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg";
const variants = ["primary", "secondary", "soft"] as const;
const VARIANT_LABELS: Record<(typeof variants)[number], string> = {
primary: "主色",
secondary: "次色",
soft: "柔和",
};
const colors = ["accent", "default", "success", "warning", "danger"] as const;
export function BadgeVariants() {
return (
{variants.map((variant, index) => (
{VARIANT_LABELS[variant]}
{colors.map((color) => (
JD
5
))}
{index < variants.length - 1 && }
))}
);
}
```
### 位置
```tsx
import {Avatar, Badge} from "@heroui/react";
const AVATAR_URL = "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg";
const placements = ["top-right", "top-left", "bottom-right", "bottom-left"] as const;
const PLACEMENT_LABELS: Record<(typeof placements)[number], string> = {
"bottom-left": "左下",
"bottom-right": "右下",
"top-left": "左上",
"top-right": "右上",
};
export function BadgePlacements() {
return (
{placements.map((placement) => (
JD
{PLACEMENT_LABELS[placement]}
))}
);
}
```
### 带内容
Badge 支持以文本、数字与图标作为内容。未提供子节点时,会渲染为点状指示器。
```tsx
import {Bell} from "@gravity-ui/icons";
import {Avatar, Badge} from "@heroui/react";
const AVATAR_URL = "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg";
export function BadgeWithContent() {
return (
);
}
```
### 点状 Badge
空的 Badge 可作为状态指示器,适用于在线/离线状态或活动信号等场景。
```tsx
import {Avatar, Badge} from "@heroui/react";
const AVATAR_URL = "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg";
export function BadgeDot() {
const colors = ["accent", "success", "warning", "danger"] as const;
return (
{colors.map((color) => (
JD
))}
);
}
```
## Related Components
* **Avatar**: Display user profile images
* **Chip**: Compact elements for tags and filters
## 样式
### 传入 Tailwind CSS 类
你可以为根容器与各插槽分别添加类名:
```tsx
import {Badge, Avatar} from '@heroui/react';
function CustomBadge() {
return (
99+
);
}
```
### 自定义组件类
若要自定义 Badge 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.badge {
@apply rounded-full text-xs;
}
.badge__label {
@apply font-semibold;
}
.badge--accent {
@apply shadow-sm;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Badge 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/badge.css)):
#### 基础类
* `.badge` - Badge 容器基础样式
* `.badge__label` - 标签文本插槽样式
* `.badge-anchor` - 锚定元素的定位包裹层
#### 颜色类
* `.badge--accent` - 强调颜色变体
* `.badge--danger` - 危险颜色变体
* `.badge--default` - 默认颜色变体
* `.badge--success` - 成功颜色变体
* `.badge--warning` - 警告颜色变体
#### 变体类
* `.badge--primary` - Primary 变体,实心背景
* `.badge--secondary` - Secondary 变体,默认背景
* `.badge--soft` - Soft 变体,浅色背景
#### 尺寸类
* `.badge--sm` - 小尺寸
* `.badge--md` - 中尺寸(默认)
* `.badge--lg` - 大尺寸
#### 位置类
* `.badge--top-right` - 右上角(默认)
* `.badge--top-left` - 左上角
* `.badge--bottom-right` - 右下角
* `.badge--bottom-left` - 左下角
#### 复合变体类
Badge 支持组合变体与颜色类(例如 `.badge--primary.badge--accent`)。以下组合定义了默认样式:
**Primary 变体:**
* `.badge--primary.badge--accent` - Primary + 强调色,实心背景
* `.badge--primary.badge--default` - Primary + 默认色,实心背景
* `.badge--primary.badge--success` - Primary + 成功色,实心背景
* `.badge--primary.badge--warning` - Primary + 警告色,实心背景
* `.badge--primary.badge--danger` - Primary + 危险色,实心背景
**Soft 变体:**
* `.badge--soft.badge--accent` - Soft + 强调色,浅色背景
* `.badge--soft.badge--default` - Soft + 默认色,浅色背景
* `.badge--soft.badge--success` - Soft + 成功色,浅色背景
* `.badge--soft.badge--warning` - Soft + 警告色,浅色背景
* `.badge--soft.badge--danger` - Soft + 危险色,浅色背景
## API 参考
### Badge Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------------------------------------------------------------- | ------------- | ----------------------------------- |
| `children` | `React.ReactNode` | - | Badge 内展示的内容(文本、数字或图标)。省略时渲染为点状指示器。 |
| `className` | `string` | - | 根元素的额外 CSS 类。 |
| `color` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"default"` | 颜色变体。 |
| `variant` | `"primary" \| "secondary" \| "soft"` | `"primary"` | 视觉样式变体。 |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | 尺寸。 |
| `placement` | `"top-right" \| "top-left" \| "bottom-right" \| "bottom-left"` | `"top-right"` | 相对锚点的位置。 |
### Badge.Anchor Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | ------------------ |
| `children` | `React.ReactNode` | - | 被锚定的元素以及 Badge 本身。 |
| `className` | `string` | - | 锚点包裹层的额外 CSS 类。 |
### Badge.Label Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | -------------- |
| `children` | `React.ReactNode` | - | 标签文本内容。 |
| `className` | `string` | - | 标签插槽的额外 CSS 类。 |
# Chip 标签
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/chip
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(data-display)/chip.mdx
> 用于展示标签、状态与分类等信息的小型徽标。
## 引入
```tsx
import { Chip } from '@heroui/react';
```
## 组件结构
引入 Chip 组件,并通过点语法访问各部分。
> 纯文本子节点会自动包在 `` 中。
```tsx
Label text
```
### 用法
```tsx
import {Chip} from "@heroui/react";
export function ChipBasic() {
return (
默认
强调
成功
警告
危险
);
}
```
### 变体
```tsx
import {CircleDashed} from "@gravity-ui/icons";
import {Chip, Separator} from "@heroui/react";
import React from "react";
const sizes = ["lg", "md", "sm"] as const;
const SIZE_LABELS: Record<(typeof sizes)[number], string> = {
lg: "大",
md: "中",
sm: "小",
};
const variants = ["primary", "secondary", "tertiary", "soft"] as const;
const VARIANT_LABELS: Record<(typeof variants)[number], string> = {
primary: "主要",
secondary: "次要",
soft: "柔和",
tertiary: "第三",
};
const colors = ["accent", "default", "success", "warning", "danger"] as const;
const COLOR_LABELS: Record<(typeof colors)[number], string> = {
accent: "强调",
danger: "危险",
default: "默认",
success: "成功",
warning: "警告",
};
export function ChipVariants() {
return (
{sizes.map((size, index) => (
{SIZE_LABELS[size]}
{colors.map((color) => (
{COLOR_LABELS[color]}
))}
{variants.map((variant) => (
{VARIANT_LABELS[variant]}
{colors.map((color) => (
标签
))}
))}
{index < sizes.length - 1 && }
))}
);
}
```
### 带图标
```tsx
import {ChevronDown, CircleCheckFill, CircleFill, Clock, Xmark} from "@gravity-ui/icons";
import {Chip} from "@heroui/react";
export function ChipWithIcon() {
return (
信息
已完成
待处理
失败
标签
);
}
```
### 状态
```tsx
import {Ban, Check, CircleFill, CircleInfo, TriangleExclamation} from "@gravity-ui/icons";
import {Chip} from "@heroui/react";
export function ChipStatuses() {
return (
默认
活跃
待处理
未激活
新功能
可用
测试版
已弃用
);
}
```
## Related Components
* **Avatar**: Display user profile images
* **CloseButton**: Button for dismissing overlays
* **Separator**: Visual divider between content
## 样式
### 传入 Tailwind CSS 类
你可以为根容器与各插槽分别添加类名:
```tsx
import {Chip} from '@heroui/react';
function CustomChip() {
return (
Custom Styled
);
}
```
### 自定义组件类
若要自定义 Chip 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.chip {
@apply rounded-full text-xs;
}
.chip__label {
@apply font-medium;
}
.chip--accent {
@apply border-accent/20;
}
.chip--accent .chip__label {
@apply text-accent;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Chip 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/chip.css)):
#### 基础类
* `.chip` - Chip 容器基础样式
* `.chip__label` - 标签文本插槽样式
#### 颜色类
* `.chip--accent` - 强调颜色变体
* `.chip--danger` - 危险颜色变体
* `.chip--default` - 默认颜色变体
* `.chip--success` - 成功颜色变体
* `.chip--warning` - 警告颜色变体
#### 变体类
* `.chip--primary` - Primary 变体,实心背景
* `.chip--secondary` - Secondary 变体,带边框
* `.chip--tertiary` - Tertiary 变体,透明背景
* `.chip--soft` - Soft 变体,浅色背景
#### 尺寸类
* `.chip--sm` - 小尺寸
* `.chip--md` - 中尺寸(默认)
* `.chip--lg` - 大尺寸
#### 复合变体类
Chip 支持组合变体与颜色类(例如 `.chip--secondary.chip--accent`)。以下组合定义了默认样式:
**Primary 变体:**
* `.chip--primary.chip--accent` - Primary + 强调色,实心背景
* `.chip--primary.chip--success` - Primary + 成功色,实心背景
* `.chip--primary.chip--warning` - Primary + 警告色,实心背景
* `.chip--primary.chip--danger` - Primary + 危险色,实心背景
**Soft 变体:**
* `.chip--accent.chip--soft` - Soft + 强调色,浅色背景
* `.chip--success.chip--soft` - Soft + 成功色,浅色背景
* `.chip--warning.chip--soft` - Soft + 警告色,浅色背景
* `.chip--danger.chip--soft` - Soft + 危险色,浅色背景
**说明:** 你也可以在 CSS 中通过 `@layer components` 为任意变体与颜色组合(例如 `.chip--secondary.chip--accent`、`.chip--tertiary.chip--success`)编写自定义样式。
## API 参考
### Chip Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------- | ------------- | ------------ |
| `children` | `React.ReactNode` | - | Chip 内展示的内容 |
| `className` | `string` | - | 根元素的额外 CSS 类 |
| `color` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"default"` | 颜色变体 |
| `variant` | `"primary" \| "secondary" \| "tertiary" \| "soft"` | `"secondary"` | 视觉样式变体 |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | 尺寸 |
### Chip.Label Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | ------------- |
| `children` | `React.ReactNode` | - | 标签文本内容 |
| `className` | `string` | - | 标签插槽的额外 CSS 类 |
# Table 表格
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/table
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(data-display)/table.mdx
> 表格以行和列展示结构化数据,支持排序、选择、列宽调整与无限滚动。
## 引入
```tsx
import { Table } from '@heroui/react';
```
### 用法
```tsx
import {Table} from "@heroui/react";
export function Basic() {
return (
姓名
角色
状态
邮箱
Kate Moore
首席执行官
在职
kate@acme.com
John Smith
首席技术官
在职
john@acme.com
Sara Johnson
首席营销官
休假
sara@acme.com
Michael Brown
首席财务官
在职
michael@acme.com
);
}
```
### 组件结构
引入 Table 组件,并通过点语法访问各部分。
```tsx
import { Table } from '@heroui/react';
export default () => (
{({ sortDirection }) => (
Name
)}
Role
Kate Moore
CEO
{/* Optional footer content */}
);
```
### 次要变体
```tsx
import {Table} from "@heroui/react";
export function SecondaryVariant() {
return (
姓名
角色
状态
邮箱
Kate Moore
首席执行官
在职
kate@acme.com
John Smith
首席技术官
在职
john@acme.com
Sara Johnson
首席营销官
休假
sara@acme.com
Michael Brown
首席财务官
在职
michael@acme.com
);
}
```
### 排序
在 `Table.Column` 上设置 `allowsSorting` 可将列设为可排序。在 `Table.Content` 上使用 `sortDescriptor` 与 `onSortChange` 管理排序状态。使用 `Table.SortableColumnHeader` 包裹列标签,并将列渲染函数中的 `sortDirection` 传入,即可显示默认的升序 / 降序指示器。
```tsx
"use client";
import type {SortDescriptor} from "@heroui/react";
import {Table} from "@heroui/react";
import {useMemo, useState} from "react";
interface User {
id: number;
name: string;
role: string;
status: string;
email: string;
}
const users: User[] = [
{email: "kate@acme.com", id: 1, name: "Kate Moore", role: "首席执行官", status: "在职"},
{email: "john@acme.com", id: 2, name: "John Smith", role: "首席技术官", status: "在职"},
{email: "sara@acme.com", id: 3, name: "Sara Johnson", role: "首席营销官", status: "休假"},
{email: "michael@acme.com", id: 4, name: "Michael Brown", role: "首席财务官", status: "在职"},
{
email: "emily@acme.com",
id: 5,
name: "Emily Davis",
role: "产品经理",
status: "未激活",
},
];
export function Sorting() {
const [sortDescriptor, setSortDescriptor] = useState({
column: "name",
direction: "ascending",
});
const sortedUsers = useMemo(() => {
return [...users].sort((a, b) => {
const col = sortDescriptor.column as keyof User;
const first = String(a[col]);
const second = String(b[col]);
let cmp = first.localeCompare(second);
if (sortDescriptor.direction === "descending") {
cmp *= -1;
}
return cmp;
});
}, [sortDescriptor]);
return (
{({sortDirection}) => (
姓名
)}
{({sortDirection}) => (
角色
)}
{({sortDirection}) => (
状态
)}
{({sortDirection}) => (
邮箱
)}
{sortedUsers.map((user) => (
{user.name}
{user.role}
{user.status}
{user.email}
))}
);
}
```
### 选择
在 `Table.Content` 上设置 `selectionMode` 以启用行选择。全选与每行复选框可使用带 `slot="selection"` 的 `Checkbox`。
```tsx
"use client";
import type {Selection} from "@heroui/react";
import {Checkbox, Table} from "@heroui/react";
import {useState} from "react";
const users = [
{email: "kate@acme.com", id: 1, name: "Kate Moore", role: "首席执行官", status: "在职"},
{email: "john@acme.com", id: 2, name: "John Smith", role: "首席技术官", status: "在职"},
{email: "sara@acme.com", id: 3, name: "Sara Johnson", role: "首席营销官", status: "休假"},
{email: "michael@acme.com", id: 4, name: "Michael Brown", role: "首席财务官", status: "在职"},
];
export function SelectionDemo() {
const [selectedKeys, setSelectedKeys] = useState(new Set());
return (
姓名
角色
状态
邮箱
{users.map((user) => (
{user.name}
{user.role}
{user.status}
{user.email}
))}
已选:{" "}
{selectedKeys === "all"
? "全部"
: selectedKeys.size > 0
? Array.from(selectedKeys).join(", ")
: "无"}
);
}
```
### 自定义单元格
```tsx
"use client";
import type {Selection, SortDescriptor} from "@heroui/react";
import {Avatar, Button, Checkbox, Chip, Table} from "@heroui/react";
import {Icon} from "@iconify/react";
import {useMemo, useState} from "react";
interface User {
id: number;
name: string;
image_url: string;
role: string;
status: "在职" | "未激活" | "休假";
email: string;
}
const statusColorMap: Record = {
休假: "warning",
在职: "success",
未激活: "danger",
};
const users: User[] = [
{
email: "kate@acme.com",
id: 4586932,
image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg",
name: "Kate Moore",
role: "首席执行官",
status: "在职",
},
{
email: "john@acme.com",
id: 5273849,
image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg",
name: "John Smith",
role: "首席技术官",
status: "在职",
},
{
email: "sara@acme.com",
id: 7492836,
image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg",
name: "Sara Johnson",
role: "首席营销官",
status: "休假",
},
{
email: "michael@acme.com",
id: 8293746,
image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg",
name: "Michael Brown",
role: "首席财务官",
status: "在职",
},
{
email: "emily@acme.com",
id: 1234567,
image_url: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg",
name: "Emily Davis",
role: "产品经理",
status: "未激活",
},
];
export function CustomCells() {
const [selectedKeys, setSelectedKeys] = useState(new Set());
const [sortDescriptor, setSortDescriptor] = useState({
column: "name",
direction: "ascending",
});
const sortedUsers = useMemo(() => {
return [...users].sort((a, b) => {
const col = sortDescriptor.column as keyof User;
const first = String(a[col]);
const second = String(b[col]);
let cmp = first.localeCompare(second);
if (sortDescriptor.direction === "descending") {
cmp *= -1;
}
return cmp;
});
}, [sortDescriptor]);
return (
{({sortDirection}) => (
员工 ID
)}
{({sortDirection}) => (
成员
)}
{({sortDirection}) => (
角色
)}
{({sortDirection}) => (
状态
)}
操作
{sortedUsers.map((user) => (
#{user.id.toString()}{" "}
{user.name
.split(" ")
.map((n) => n[0])
.join("")}
{user.name}
{user.email}
{user.role}
{user.status}
))}
);
}
```
### 可展开行
行可以嵌套以展示层级数据。使用 `treeColumn` 指定列,并在该列单元格内渲染带 `slot="chevron"` 的 `Button`,以便用户展开/收起行。使用 `expandedKeys` 控制哪些行处于展开状态。
```tsx
"use client";
import type {Selection} from "@heroui/react";
import {Button, Table, cn} from "@heroui/react";
import {Icon} from "@iconify/react";
import {useState} from "react";
export function ExpandableRows() {
type Row = {
children: Row[];
date: string;
id: string;
title: string;
type: string;
};
const data: Row[] = [
{
children: [
{
children: [
{children: [], date: "7/10/2025", id: "3", title: "周报", type: "文件"},
{children: [], date: "8/20/2025", id: "4", title: "预算", type: "文件"},
],
date: "8/2/2025",
id: "2",
title: "项目",
type: "文件夹",
},
],
date: "10/20/2025",
id: "1",
title: "文档",
type: "文件夹",
},
{
children: [
{children: [], date: "1/23/2026", id: "6", title: "图片 1", type: "文件"},
{children: [], date: "2/3/2026", id: "7", title: "图片 2", type: "文件"},
],
date: "2/3/2026",
id: "5",
title: "照片",
type: "文件夹",
},
];
const [expandedKeys, setExpandedKeys] = useState(() => new Set(["1"]));
const renderExpandableRow = (item: Row) => {
return (
{({hasChildItems, isDisabled, isExpanded, isTreeColumn}) => (
{hasChildItems && isTreeColumn ? (
) : null}
{item.title}
)}
{item.type}
{item.date}
{renderExpandableRow}
);
};
return (
姓名
类型
修改日期
{renderExpandableRow}
);
}
```
### 分页
使用 `Table.Footer` 在表格下方添加分页组件。
```tsx
"use client";
import {Pagination, Table} from "@heroui/react";
import {useMemo, useState} from "react";
const columns = [
{id: "name", name: "姓名"},
{id: "role", name: "角色"},
{id: "status", name: "状态"},
{id: "email", name: "邮箱"},
];
const users = [
{email: "kate@acme.com", id: 1, name: "Kate Moore", role: "首席执行官", status: "在职"},
{email: "john@acme.com", id: 2, name: "John Smith", role: "首席技术官", status: "在职"},
{email: "sara@acme.com", id: 3, name: "Sara Johnson", role: "首席营销官", status: "休假"},
{email: "michael@acme.com", id: 4, name: "Michael Brown", role: "首席财务官", status: "在职"},
{
email: "emily@acme.com",
id: 5,
name: "Emily Davis",
role: "产品经理",
status: "未激活",
},
{email: "davis@acme.com", id: 6, name: "Davis Wilson", role: "首席设计师", status: "在职"},
{
email: "olivia@acme.com",
id: 7,
name: "Olivia Martinez",
role: "前端工程师",
status: "在职",
},
{
email: "james@acme.com",
id: 8,
name: "James Taylor",
role: "后端工程师",
status: "在职",
},
];
const ROWS_PER_PAGE = 4;
export function PaginationDemo() {
const [page, setPage] = useState(1);
const totalPages = Math.ceil(users.length / ROWS_PER_PAGE);
const pages = Array.from({length: totalPages}, (_, i) => i + 1);
const paginatedItems = useMemo(() => {
const start = (page - 1) * ROWS_PER_PAGE;
return users.slice(start, start + ROWS_PER_PAGE);
}, [page]);
const start = (page - 1) * ROWS_PER_PAGE + 1;
const end = Math.min(page * ROWS_PER_PAGE, users.length);
return (
{(column) => (
{column.name}
)}
{(user) => (
{(column) => {user[column.id as keyof typeof user]} }
)}
{start}–{end} / 共 {users.length} 条
setPage((p) => Math.max(1, p - 1))}
>
上一页
{pages.map((p) => (
setPage(p)}>
{p}
))}
setPage((p) => Math.min(totalPages, p + 1))}
>
下一页
);
}
```
### 列宽调整
使用 `Table.ResizableContainer` 包裹表格,并在每个可调整宽度的列中加入 `Table.ColumnResizer`。
```tsx
import {Chip, Table} from "@heroui/react";
export function ColumnResizing() {
return (
姓名
角色
状态
邮箱
Kate Moore
首席执行官
Active
kate@acme.com
John Smith
首席技术官
Active
john@acme.com
Sara Johnson
首席营销官
On Leave
sara@acme.com
Michael Brown
首席财务官
Active
michael@acme.com
Emily Davis
产品经理
Inactive
emily@acme.com
);
}
```
### 空状态
在 `Table.Body` 上使用 `renderEmptyState`,在表格无数据时展示自定义内容。
```tsx
"use client";
import {EmptyState, Table} from "@heroui/react";
import {Icon} from "@iconify/react";
export function EmptyStateDemo() {
return (
姓名
角色
状态
邮箱
(
未找到结果
)}
>
{[]}
);
}
```
### 异步加载
使用 `Table.LoadMore` 实现无限滚动:会渲染一行哨兵节点,在进入视口时触发 `onLoadMore`。
```tsx
"use client";
import {Chip, Spinner, Table} from "@heroui/react";
import {useCallback, useRef, useState} from "react";
interface User {
id: number;
name: string;
role: string;
status: string;
email: string;
}
const statusColorMap: Record = {
休假: "warning",
在职: "success",
未激活: "danger",
};
const allUsers: User[] = [
{email: "kate@acme.com", id: 1, name: "Kate Moore", role: "首席执行官", status: "在职"},
{email: "john@acme.com", id: 2, name: "John Smith", role: "首席技术官", status: "在职"},
{email: "sara@acme.com", id: 3, name: "Sara Johnson", role: "首席营销官", status: "休假"},
{email: "michael@acme.com", id: 4, name: "Michael Brown", role: "首席财务官", status: "在职"},
{
email: "emily@acme.com",
id: 5,
name: "Emily Davis",
role: "产品经理",
status: "未激活",
},
{email: "davis@acme.com", id: 6, name: "Davis Wilson", role: "首席设计师", status: "在职"},
{
email: "olivia@acme.com",
id: 7,
name: "Olivia Martinez",
role: "前端工程师",
status: "在职",
},
{
email: "james@acme.com",
id: 8,
name: "James Taylor",
role: "后端工程师",
status: "在职",
},
{
email: "sophia@acme.com",
id: 9,
name: "Sophia Anderson",
role: "测试工程师",
status: "休假",
},
{email: "liam@acme.com", id: 10, name: "Liam Thomas", role: "DevOps 工程师", status: "在职"},
{
email: "lucas@acme.com",
id: 11,
name: "Lucas Martinez",
role: "产品经理",
status: "在职",
},
{
email: "emma@acme.com",
id: 12,
name: "Emma Johnson",
role: "前端工程师",
status: "在职",
},
{email: "noah@acme.com", id: 13, name: "Noah Davis", role: "后端工程师", status: "在职"},
{email: "ava@acme.com", id: 14, name: "Ava Wilson", role: "首席设计师", status: "在职"},
{
email: "oliver@acme.com",
id: 15,
name: "Oliver Martinez",
role: "前端工程师",
status: "在职",
},
{
email: "isabella@acme.com",
id: 16,
name: "Isabella Johnson",
role: "后端工程师",
status: "在职",
},
{email: "mia@acme.com", id: 17, name: "Mia Davis", role: "首席设计师", status: "在职"},
{
email: "william@acme.com",
id: 18,
name: "William Wilson",
role: "前端工程师",
status: "在职",
},
];
const ITEMS_PER_PAGE = 6;
const columns = [
{id: "name", name: "姓名"},
{id: "role", name: "角色"},
{id: "status", name: "状态"},
{id: "email", name: "邮箱"},
];
export function AsyncLoading() {
const [items, setItems] = useState(() => allUsers.slice(0, ITEMS_PER_PAGE));
const [isLoading, setIsLoading] = useState(false);
const isLoadingRef = useRef(false);
const hasMore = items.length < allUsers.length;
const loadMore = useCallback(() => {
if (!hasMore || isLoadingRef.current) return;
isLoadingRef.current = true;
setIsLoading(true);
setTimeout(() => {
setItems((prev) => allUsers.slice(0, prev.length + ITEMS_PER_PAGE));
setIsLoading(false);
requestAnimationFrame(() => {
isLoadingRef.current = false;
});
}, 1500);
}, [hasMore]);
return (
{columns.map((col) => (
{col.name}
))}
{(user) => (
{user.name}
{user.role}
{user.status}
{user.email}
)}
{!!hasMore && (
)}
);
}
```
### 虚拟化
Table 通过 [Virtualizer](https://react-aria.adobe.com/Virtualizer) 支持虚拟化,仅渲染视口内可见行,从而高效处理大数据集。
```tsx
"use client";
import {Table, TableLayout, Virtualizer} from "@heroui/react";
interface User {
id: number;
name: string;
role: string;
email: string;
}
export function Virtualization() {
const roles = [
"软件工程师",
"高级工程师",
"资深工程师",
"产品经理",
"设计师",
"数据分析师",
"测试工程师",
"DevOps 工程师",
"营销经理",
"销售代表",
];
const firstNames = [
"Emma",
"Liam",
"Olivia",
"Noah",
"Ava",
"James",
"Sophia",
"Oliver",
"Isabella",
"Lucas",
"Mia",
"Ethan",
"Charlotte",
"Mason",
"Amelia",
"Logan",
"Harper",
"Alexander",
"Ella",
"Benjamin",
];
const lastNames = [
"Smith",
"Johnson",
"Williams",
"Brown",
"Jones",
"Garcia",
"Miller",
"Davis",
"Rodriguez",
"Martinez",
"Anderson",
"Taylor",
"Thomas",
"Jackson",
"White",
"Harris",
"Clark",
"Lewis",
"Robinson",
"Walker",
];
function generateUsers(count: number): User[] {
const users: User[] = [];
for (let i = 0; i < count; i++) {
const firstName = firstNames[i % firstNames.length];
const lastName = lastNames[Math.floor(i / firstNames.length) % lastNames.length];
const name = `${firstName} ${lastName}`;
users.push({
email: `${firstName?.toLowerCase()}.${lastName?.toLowerCase()}@acme.com`,
id: i + 1,
name,
role: roles[i % roles.length] || "",
});
}
return users;
}
const virtualizedUsers = generateUsers(1000);
return (
姓名
角色
邮箱
{(user) => (
{user.name}
{user.role}
{user.email}
)}
);
}
```
### TanStack Table
HeroUI 的 Table 可作为无头表格库之上的渲染层。
本示例使用 [TanStack Table](https://tanstack.com/table) 处理列定义、排序与分页,而样式与无障碍由 HeroUI 负责。
```tsx
"use client";
import type {SortDescriptor} from "@heroui/react";
import type {SortingState} from "@tanstack/react-table";
import {Chip, Pagination, Table} from "@heroui/react";
import {
createColumnHelper,
flexRender,
getCoreRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import {useMemo, useState} from "react";
// --- Data -----------------------------------------------------------------
interface User {
id: number;
name: string;
role: string;
status: "在职" | "未激活" | "休假";
email: string;
}
const statusColorMap: Record = {
休假: "warning",
在职: "success",
未激活: "danger",
};
const users: User[] = [
{email: "kate@acme.com", id: 1, name: "Kate Moore", role: "首席执行官", status: "在职"},
{email: "john@acme.com", id: 2, name: "John Smith", role: "首席技术官", status: "在职"},
{email: "sara@acme.com", id: 3, name: "Sara Johnson", role: "首席营销官", status: "休假"},
{email: "michael@acme.com", id: 4, name: "Michael Brown", role: "首席财务官", status: "在职"},
{
email: "emily@acme.com",
id: 5,
name: "Emily Davis",
role: "产品经理",
status: "未激活",
},
{email: "davis@acme.com", id: 6, name: "Davis Wilson", role: "首席设计师", status: "在职"},
{
email: "olivia@acme.com",
id: 7,
name: "Olivia Martinez",
role: "前端工程师",
status: "在职",
},
{
email: "james@acme.com",
id: 8,
name: "James Taylor",
role: "后端工程师",
status: "在职",
},
];
// --- TanStack Column Definitions ------------------------------------------
const columnHelper = createColumnHelper();
const columns = [
columnHelper.accessor("name", {header: "姓名"}),
columnHelper.accessor("role", {header: "角色"}),
columnHelper.accessor("status", {
cell: (info) => (
{info.getValue()}
),
header: "状态",
}),
columnHelper.accessor("email", {header: "邮箱"}),
];
// --- Sorting Bridge -------------------------------------------------------
// Convert TanStack SortingState → React Aria SortDescriptor
function toSortDescriptor(sorting: SortingState): SortDescriptor | undefined {
const first = sorting[0];
if (!first) return undefined;
return {
column: first.id,
direction: first.desc ? "descending" : "ascending",
};
}
// Convert React Aria SortDescriptor → TanStack SortingState
function toSortingState(descriptor: SortDescriptor): SortingState {
return [{desc: descriptor.direction === "descending", id: descriptor.column as string}];
}
// --- Component ------------------------------------------------------------
const PAGE_SIZE = 4;
export function TanstackTable() {
const [sorting, setSorting] = useState([]);
// eslint-disable-next-line react-hooks/incompatible-library
const table = useReactTable({
columns,
data: users,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
initialState: {pagination: {pageSize: PAGE_SIZE}},
onSortingChange: setSorting,
state: {sorting},
});
const sortDescriptor = useMemo(() => toSortDescriptor(sorting), [sorting]);
const {pageIndex} = table.getState().pagination;
const pageCount = table.getPageCount();
const pages = Array.from({length: pageCount}, (_, i) => i + 1);
const start = pageIndex * PAGE_SIZE + 1;
const end = Math.min((pageIndex + 1) * PAGE_SIZE, users.length);
return (
setSorting(toSortingState(d))}
>
{table.getHeaderGroups()[0]!.headers.map((header) => (
))}
{table.getRowModel().rows.map((row) => (
{row.getVisibleCells().map((cell) => (
{flexRender(cell.column.columnDef.cell, cell.getContext())}
))}
))}
{start}–{end} / 共 {users.length} 条
table.previousPage()}
>
上一页
{pages.map((p) => (
table.setPageIndex(p - 1)}
>
{p}
))}
table.nextPage()}
>
下一页
);
}
```
## Related Components
* **Pagination**: Page navigation with composable page links and controls
* **Checkbox**: Binary choice input control
* **Chip**: Compact elements for tags and filters
## 样式
### 传入 Tailwind CSS 类
你可以为 Table 的各个部分分别传入类名:
```tsx
import { Table } from '@heroui/react';
function CustomTable() {
return (
);
}
```
### 自定义组件类
若要自定义 Table 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.table-root {
@apply relative grid w-full overflow-clip;
}
.table__header {
@apply bg-gray-100;
}
.table__column {
@apply px-4 py-2.5 text-left text-xs font-medium text-gray-600;
}
.table__row {
@apply bg-white border-b border-gray-200;
}
.table__cell {
@apply px-4 py-3 text-sm;
}
.table__footer {
@apply flex items-center px-4 py-2.5;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Table 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/table.css)):
#### 基础类
* `.table-root` - 根容器(命名为 `table-root` 而非 `table`,因为 `table` 是 Tailwind CSS 内置的 `display: table` 工具类)
* `.table__scroll-container` - 横向滚动包裹层与自定义滚动条
* `.table__content` - `` 元素
* `.table__header` - 表头行(``)
* `.table__column` - 列表头单元格(``)
* `.table__body` - 表体(` `)
* `.table__row` - 行(``)
* `.table__cell` - 数据单元格(``)
* `.table__footer` - 表底容器(位于 table 外部)
#### 进阶类
* `.table__column-resizer` - 列宽拖拽手柄
* `.table__resizable-container` - 启用列宽调整的包裹层
* `.table__load-more` - 无限滚动的哨兵行
* `.table__load-more-content` - 加载指示器的样式容器
* `.table__sortable-column-header` - 可排序列标签与指示器的包裹层
* `.table__sortable-column-indicator` - 排序方向 chevron(通过 `[data-direction="descending"]` 翻转)
#### 变体类
* `.table-root--primary` - 灰色背景容器与卡片式表体(默认)
* `.table-root--secondary` - 无背景,独立圆角表头
### 交互状态
Table 同时支持 CSS 伪类与 data 属性,以提供更灵活的状态控制:
* **悬停**:`:hover` 或 `[data-hovered="true"]`(行背景变化)
* **已选中**:`[data-selected="true"]`(行高亮)
* **聚焦**:`:focus-visible` 或 `[data-focus-visible="true"]`(行、列与单元格的内嵌焦点环)
* **禁用**:`:disabled` 或 `[aria-disabled="true"]`(降低透明度)
* **可排序**:`[data-allows-sorting="true"]`(列上的交互指针样式)
* **拖动中**:`[data-dragging="true"]`(降低透明度)
* **放置目标**:`[data-drop-target="true"]`(强调色背景)
## API 参考
### Table Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------------------------- | ----------- | -------------------------------------- |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 视觉变体。Primary 为灰色背景容器;Secondary 为扁平透明行。 |
| `className` | `string` | - | 根容器的额外 CSS 类。 |
| `children` | `React.ReactNode` | - | 表格内容(ScrollContainer、Footer 等)。 |
### Table.ScrollContainer Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | ------------------- |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `React.ReactNode` | - | `Table.Content` 元素。 |
### Table.Content Props
继承自 [React Aria Table](https://react-spectrum.adobe.com/react-aria/Table.html)。
| Prop | 类型 | 默认值 | 描述 |
| ------------------- | -------------------------------------- | -------- | ------------- |
| `aria-label` | `string` | - | 表格的无障碍标签。 |
| `selectionMode` | `"none" \| "single" \| "multiple"` | `"none"` | 选择行为。 |
| `selectedKeys` | `Selection` | - | 受控的已选中 key。 |
| `onSelectionChange` | `(keys: Selection) => void` | - | 选择变化时的事件处理函数。 |
| `sortDescriptor` | `SortDescriptor` | - | 当前排序状态。 |
| `onSortChange` | `(descriptor: SortDescriptor) => void` | - | 排序变化时的事件处理函数。 |
| `className` | `string` | - | 额外的 CSS 类。 |
### Table.Header Props
继承自 [React Aria TableHeader](https://react-spectrum.adobe.com/react-aria/Table.html#tableheader)。
| Prop | 类型 | 默认值 | 描述 |
| ---------- | --------------------------------------------------- | --- | -------------- |
| `columns` | `T[]` | - | 渲染函数模式下的动态列数据。 |
| `children` | `React.ReactNode \| (column: T) => React.ReactNode` | - | 静态列或渲染函数。 |
### Table.Column Props
继承自 [React Aria Column](https://react-spectrum.adobe.com/react-aria/Table.html#column)。
| Prop | 类型 | 默认值 | 描述 |
| --------------- | ------------------------------------------------------------------- | ------- | --------------- |
| `id` | `string` | - | 列标识符。 |
| `allowsSorting` | `boolean` | `false` | 列是否可排序。 |
| `isRowHeader` | `boolean` | `false` | 该列是否作为行表头。 |
| `defaultWidth` | `string \| number` | - | 可调整列的默认宽度。 |
| `minWidth` | `number` | - | 可调整列的最小宽度。 |
| `children` | `React.ReactNode \| (values: ColumnRenderProps) => React.ReactNode` | - | 列内容或带排序方向的渲染函数。 |
### Table.Body Props
继承自 [React Aria TableBody](https://react-spectrum.adobe.com/react-aria/Table.html#tablebody)。
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | ------------------------------------------------- | --- | -------------- |
| `items` | `T[]` | - | 渲染函数模式下的动态行数据。 |
| `renderEmptyState` | `() => React.ReactNode` | - | 表格为空时展示的内容。 |
| `children` | `React.ReactNode \| (item: T) => React.ReactNode` | - | 静态行或渲染函数。 |
### Table.Row Props
继承自 [React Aria Row](https://react-spectrum.adobe.com/react-aria/Table.html#row)。
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------ | --- | ---------- |
| `id` | `string \| number` | - | 行标识符。 |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `React.ReactNode` | - | 行单元格。 |
### Table.Cell Props
继承自 [React Aria Cell](https://react-spectrum.adobe.com/react-aria/Table.html#cell)。
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | ---------- |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `React.ReactNode` | - | 单元格内容。 |
### Table.SortableColumnHeader Props
渲染可排序列的标签与升序 / 降序指示器。请在 `Table.Column` 的渲染函数回调中使用,并将 `sortDirection` 透传进来。
| Prop | 类型 | 默认值 | 描述 |
| --------------- | ----------------------------- | ------ | ---------------------------------------------------- |
| `sortDirection` | `"ascending" \| "descending"` | - | 当前排序方向。请从 `Table.Column` 的渲染函数中透传。 |
| `showIndicator` | `boolean` | `true` | 当存在排序方向时是否渲染指示器图标。 |
| `indicator` | `React.ReactNode` | - | 自定义指示器元素。会覆盖默认的 chevron,并会被自动注入 `data-direction` 属性。 |
| `className` | `string` | - | 包裹元素的额外 CSS 类。 |
| `children` | `React.ReactNode` | - | 列标签内容。 |
### Table.Footer Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | ----------- |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `React.ReactNode` | - | 表底内容(例如分页)。 |
### Table.ColumnResizer Props
继承自 [React Aria ColumnResizer](https://react-spectrum.adobe.com/react-aria/Table.html#columnresizer)。
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------- | --- | ---------- |
| `className` | `string` | - | 额外的 CSS 类。 |
### Table.ResizableContainer Props
继承自 [React Aria ResizableTableContainer](https://react-spectrum.adobe.com/react-aria/Table.html#resizabletablecontainer)。
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | ------------------- |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `React.ReactNode` | - | `Table.Content` 元素。 |
### Table.LoadMore Props
继承自 [React Aria TableLoadMoreItem](https://react-spectrum.adobe.com/react-aria/Table.html)。
| Prop | 类型 | 默认值 | 描述 |
| ------------ | ----------------- | ------- | -------------- |
| `isLoading` | `boolean` | `false` | 数据是否正在加载。 |
| `onLoadMore` | `() => void` | - | 哨兵行可见时的事件处理函数。 |
| `children` | `React.ReactNode` | - | 加载指示器内容。 |
### Table.LoadMoreContent Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | -------------------- |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `React.ReactNode` | - | 加载指示器内容(例如 Spinner)。 |
### Table.Collection Props
由 React Aria `Collection` 重新导出。用于在行内与静态单元格并存时渲染动态单元格(例如复选框)。
| Prop | 类型 | 默认值 | 描述 |
| ---------- | ------------------------------ | --- | ---------- |
| `items` | `T[]` | - | 集合条目。 |
| `children` | `(item: T) => React.ReactNode` | - | 每个条目的渲染函数。 |
### TableLayout
| Name | 类型 | 默认值 | 描述 |
| ------------------------ | --------------------- | --- | --------------------------------------------- |
| `rowHeight` | `number \| undefined` | 48 | 行的固定高度(px)。 |
| `estimatedRowHeight` | `number \| undefined` | — | 行高可变时的估算高度。 |
| `headingHeight` | `number \| undefined` | 48 | 分区表头的固定高度(px)。 |
| `estimatedHeadingHeight` | `number \| undefined` | — | 表头高度可变时的估算高度。 |
| `loaderHeight` | `number \| undefined` | 48 | 加载器元素的固定高度(px)。该加载器用于在根级或嵌套行/分区中渲染「加载更多」等加载行。 |
| `dropIndicatorThickness` | `number \| undefined` | 2 | 放置指示器的线条粗细。 |
| `gap` | `number \| undefined` | 0 | 条目之间的间距。 |
| `padding` | `number \| undefined` | 0 | 列表的内边距。 |
# Calendar 日历
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/calendar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(date-and-time)/calendar.mdx
> 基于 React Aria Calendar 的可组合日期选择器,包含月份网格、导航与年份选择器支持。
## 引入
```tsx
import { Calendar } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {Calendar} from "@heroui/react";
export function Basic() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### 组件结构
```tsx
import {Calendar} from '@heroui/react';
export default () => (
{(day) => {day} }
{(date) => }
)
```
### 年份选择器
`Calendar.YearPickerTrigger`、`Calendar.YearPickerGrid` 以及对应的 body/cell 子组件提供一体化的年份导航模式。
```tsx
"use client";
import {Calendar} from "@heroui/react";
export function YearPicker() {
return (
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### 默认值
```tsx
"use client";
import {Calendar} from "@heroui/react";
import {parseDate} from "@internationalized/date";
export function DefaultValue() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### 受控
使用受控的 `value` 与 `focusedValue` 与外部状态协同,并支持自定义快捷键。
```tsx
"use client";
import type {CalendarDate} from "@internationalized/date";
import {Button, ButtonGroup, Calendar, Description} from "@heroui/react";
import {
getLocalTimeZone,
parseDate,
startOfMonth,
startOfWeek,
today,
} from "@internationalized/date";
import {useState} from "react";
import {useLocale} from "react-aria-components";
export function Controlled() {
const [value, setValue] = useState(null);
const [focusedDate, setFocusedDate] = useState(parseDate("2025-12-25"));
const {locale} = useLocale();
return (
{
const todayDate = today(getLocalTimeZone());
setValue(todayDate);
setFocusedDate(todayDate);
}}
>
今天
{
const nextWeekStart = startOfWeek(today(getLocalTimeZone()), locale);
setValue(nextWeekStart);
setFocusedDate(nextWeekStart);
}}
>
本周
{
const nextMonthStart = startOfMonth(today(getLocalTimeZone()));
setValue(nextMonthStart);
setFocusedDate(nextMonthStart);
}}
>
本月
{(day) => {day} }
{(date) => }
已选日期:{value ? value.toString() : "(未选)"}
{
const todayDate = today(getLocalTimeZone());
setValue(todayDate);
setFocusedDate(todayDate);
}}
>
设为今天
{
const christmasDate = parseDate("2025-12-25");
setValue(christmasDate);
setFocusedDate(christmasDate);
}}
>
设为圣诞节
setValue(null)}>
清空
);
}
```
### 最小与最大日期
```tsx
"use client";
import {Calendar, Description} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function MinMaxDates() {
const now = today(getLocalTimeZone());
const minDate = now;
const maxDate = now.add({months: 3});
return (
{(day) => {day} }
{(date) => }
请在今天与 {maxDate.toString()} 之间选择日期。
);
}
```
### 不可用日期
使用 `isDateUnavailable` 禁用周末、节假日或已被预订等日期。
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Calendar, Description} from "@heroui/react";
import {isWeekend} from "@internationalized/date";
import {useLocale} from "react-aria-components";
export function UnavailableDates() {
const {locale} = useLocale();
const isDateUnavailable = (date: DateValue) => isWeekend(date, locale);
return (
{(day) => {day} }
{(date) => }
周末不可选
);
}
```
### 固定周数
将 `weeksInMonth` 设为固定值(例如 `6`),可在月份切换时保持网格高度稳定。在非公历场景中请谨慎使用,与 `firstDayOfWeek` 类似。
```tsx
"use client";
import {Calendar, Description} from "@heroui/react";
export function WeeksInMonth() {
return (
{(day) => {day} }
{(date) => }
每月固定显示 6 周,切换月份时避免布局跳动
);
}
```
### 周视图
设置 `visibleDuration={{ weeks: n }}` 可一次显示一个或多个周。翻页会按可见周范围前进。显示多周时可配合 `pageBehavior="single"` 每次仅移动一周。
```tsx
"use client";
import {Calendar, Label, ListBox, Select} from "@heroui/react";
import {useState} from "react";
const weekOptions = [
{id: "1", name: "1 周"},
{id: "2", name: "2 周"},
{id: "3", name: "3 周"},
{id: "4", name: "4 周"},
{id: "5", name: "5 周"},
{id: "6", name: "6 周"},
{id: "8", name: "8 周"},
] as const;
export function WeekView() {
const [weeks, setWeeks] = useState(1);
return (
value && setWeeks(Number(value))}
>
可见周数
{weekOptions.map((option) => (
{option.name}
))}
{(day) => {day} }
{(date) => }
);
}
```
### 日视图
设置 `visibleDuration={{ days: n }}` 可显示连续多天的滚动窗口。翻页会按可见天数范围前进。显示多天时配合 `pageBehavior="single"` 可每次仅移动一天。
```tsx
"use client";
import {Calendar, Label, ListBox, Select} from "@heroui/react";
import {useState} from "react";
const dayOptions = [
{id: "1", name: "1 天"},
{id: "5", name: "5 天"},
{id: "7", name: "7 天"},
{id: "8", name: "8 天"},
{id: "10", name: "10 天"},
{id: "14", name: "14 天"},
{id: "21", name: "21 天"},
] as const;
export function DayView() {
const [days, setDays] = useState(5);
return (
value && setDays(Number(value))}
>
可见天数
{dayOptions.map((option) => (
{option.name}
))}
{(day) => {day} }
{(date) => }
);
}
```
### 多选
设置 `selectionMode="multiple"` 以选择多个日期。此时 `value`、`defaultValue` 与 `onChange` 使用日期数组。
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Calendar, Description} from "@heroui/react";
import {useState} from "react";
export function MultipleSelection() {
const [value, setValue] = useState([]);
return (
{(day) => {day} }
{(date) => }
{value?.length ? `已选择 ${value.length} 个日期` : "可选择多个日期"}
);
}
```
### 禁用
```tsx
"use client";
import {Calendar, Description} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function Disabled() {
return (
{(day) => {day} }
{(date) => }
日历已禁用
);
}
```
### 只读
```tsx
"use client";
import {Calendar, Description} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function ReadOnly() {
return (
{(day) => {day} }
{(date) => }
日历为只读
);
}
```
### 焦点值
使用 `focusedValue` 与 `onFocusChange` 以编程方式控制焦点落在哪一天。
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Button, Calendar, Description} from "@heroui/react";
import {parseDate} from "@internationalized/date";
import {useState} from "react";
export function FocusedValue() {
const [focusedDate, setFocusedDate] = useState(parseDate("2025-06-15"));
return (
{(day) => {day} }
{(date) => }
聚焦:{focusedDate.toString()}
setFocusedDate(parseDate("2025-01-01"))}
>
跳转到一月
setFocusedDate(parseDate("2025-06-15"))}
>
跳转到六月
setFocusedDate(parseDate("2025-12-25"))}
>
跳转到圣诞节
);
}
```
### 单元格指示器
你可以自定义 `Calendar.Cell` 的子节点,并使用 `Calendar.CellIndicator` 展示活动等元数据。
```tsx
"use client";
import {Calendar} from "@heroui/react";
import {getLocalTimeZone, isToday} from "@internationalized/date";
const datesWithEvents = [3, 7, 12, 15, 21, 28];
export function WithIndicators() {
return (
{(day) => {day} }
{(date) => (
{({formattedDate}) => (
<>
{formattedDate}
{(isToday(date, getLocalTimeZone()) || datesWithEvents.includes(date.day)) && (
)}
>
)}
)}
);
}
```
### 多个月份
使用 `visibleDuration` 与 `offset` 渲染多个月份网格,适用于预订与规划场景。在各列头部为 `Calendar.Heading` 设置 `offset`(例如 `offset={{ months: 1 }}`)以显示对应月份标题。
```tsx
"use client";
import {Calendar} from "@heroui/react";
export function MultipleMonths() {
return (
{(day) => {day} }
{(date) => }
{(day) => {day} }
{(date) => }
);
}
```
### 国际化日历
默认情况下,Calendar 使用用户语言环境对应的历法系统显示日期。你可以使用 `I18nProvider` 包裹 Calendar,并通过 [Unicode 历法语言扩展](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string) 覆盖。
下方示例展示印度历法系统:
```tsx
"use client";
import {Calendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
import {I18nProvider} from "react-aria-components";
export function InternationalCalendar() {
return (
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
**提示:** `onChange` 事件始终返回与 `value` 或 `defaultValue` 相同历法系统中的日期(若未提供值则为公历),与界面展示的语言环境无关。这样应用逻辑可以始终使用单一历法系统,同时仍可按用户偏好的格式展示日期。
### 自定义导航图标
向 `Calendar.NavButton` 传入子节点即可替换默认的箭头图标。
```tsx
"use client";
import {Calendar} from "@heroui/react";
export function CustomIcons() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### 真实场景示例
```tsx
"use client";
import type {CalendarDate, DateValue} from "@internationalized/date";
import {Button, Calendar} from "@heroui/react";
import {getLocalTimeZone, isWeekend, today} from "@internationalized/date";
import {useState} from "react";
import {useLocale} from "react-aria-components";
export function BookingCalendar() {
const [selectedDate, setSelectedDate] = useState(null);
const {locale} = useLocale();
const bookedDates = [5, 6, 12, 13, 14, 20];
const isDateUnavailable = (date: DateValue) => {
return isWeekend(date, locale) || bookedDates.includes(date.day);
};
return (
{(day) => {day} }
{(date) => (
{({formattedDate, isUnavailable}) => (
<>
{formattedDate}
{!isUnavailable &&
!isWeekend(date, locale) &&
bookedDates.includes(date.day) && }
>
)}
)}
已有预订
周末/不可用
{selectedDate ? (
预订 {selectedDate.toString()}
) : null}
);
}
```
### 自定义样式
```tsx
"use client";
import {Calendar} from "@heroui/react";
export function CustomStyles() {
return (
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
## Related Components
* **RangeCalendar**: Interactive month grid for selecting date ranges
* **DateField**: Date input field with labels, descriptions, and validation
* **DatePicker**: Composable date picker with date field trigger and calendar popover
## 样式
### 传入 Tailwind CSS 类
```tsx
import {Calendar} from '@heroui/react';
function CustomCalendar() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### 自定义组件类
```css
@layer components {
.calendar {
@apply w-72 rounded-2xl border border-border bg-surface p-3 shadow-sm;
}
.calendar__heading {
@apply text-sm font-semibold text-default-700;
}
.calendar__cell[data-selected="true"] {
@apply bg-accent text-accent-foreground;
}
}
```
### CSS 类
Calendar 在 `packages/styles/components/calendar.css` 与 `packages/styles/components/calendar-year-picker.css` 中使用以下类:
* `.calendar` - 根容器。
* `.calendar__header` - 包含导航按钮与标题的头部行。
* `.calendar__heading` - 当前月份标签。
* `.calendar__nav-button` - 上一月/下一月导航控件。
* `.calendar__grid` - 主体日期网格。
* `.calendar__grid-header` - 星期标题行容器。
* `.calendar__grid-body` - 日期行容器。
* `.calendar__header-cell` - 星期标题单元格。
* `.calendar__cell` - 可交互的日期单元格。
* `.calendar__cell-indicator` - 日期单元格内的点状指示器。
* `.calendar-year-picker__trigger` - 年份选择器切换按钮。
* `.calendar-year-picker__trigger-heading` - 年份选择触发器内的标题文本。
* `.calendar-year-picker__trigger-indicator` - 年份选择触发器内的指示图标。
* `.calendar-year-picker__year-grid` - 可选年份的覆盖网格。
* `.calendar-year-picker__year-cell` - 单个年份选项。
### 交互状态
Calendar 同时支持伪类与 React Aria 的 data 属性:
* **已选中**:`[data-selected="true"]`
* **今天**:`[data-today="true"]`
* **不可用**:`[data-unavailable="true"]`
* **跨月**:`[data-outside-month="true"]`
* **悬停**:`:hover` 或 `[data-hovered="true"]`
* **按下**:`:active` 或 `[data-pressed="true"]`
* **可见焦点**:`:focus-visible` 或 `[data-focus-visible="true"]`
* **禁用**:`:disabled` 或 `[data-disabled="true"]`
## API 参考
### Calendar Props
Calendar 继承 React Aria [Calendar](https://react-spectrum.adobe.com/react-aria/Calendar.html) 的全部 props。
| Prop | 类型 | 默认值 | 描述 |
| ------------------------ | --------------------------------------------------------------------- | ------------------ | ---------------------------------------------------------------------- |
| `selectionMode` | `'single' \| 'multiple'` | `'single'` | 单选或多选日期。 |
| `value` | `DateValue \| null` 或 `DateValue[] \| null` | - | 受控的选中日期。`selectionMode="multiple"` 时使用数组。 |
| `defaultValue` | `DateValue \| null` 或 `DateValue[] \| null` | - | 初始选中日期(非受控)。 |
| `onChange` | `(value: DateValue \| null)` 或 `(value: DateValue[] \| null) => void` | - | 选中变化时调用。 |
| `focusedValue` | `DateValue` | - | 受控的焦点日期。 |
| `onFocusChange` | `(value: DateValue) => void` | - | 焦点移动到其它日期时调用。 |
| `minValue` | `DateValue` | 历法感知的 `1900-01-01` | 最早可选日期。 |
| `maxValue` | `DateValue` | 历法感知的 `2099-12-31` | 最晚可选日期。 |
| `weeksInMonth` | `number` | - | 一个月的周数。该值会覆盖区域设置的默认值。 |
| `isDateUnavailable` | `(date: DateValue) => boolean` | - | 将日期标记为不可用。 |
| `firstDayOfWeek` | `'sun' \| 'mon' \| 'tue' \| 'wed' \| 'thu' \| 'fri' \| 'sat'` | - | 覆盖区域设置的一周起始日。 |
| `pageBehavior` | `'visible' \| 'single'` | `'visible'` | 翻页按可见范围或单步前进。 |
| `selectionAlignment` | `'start' \| 'center' \| 'end'` | `'center'` | 初始渲染时按选中项对齐可见范围。 |
| `isDisabled` | `boolean` | `false` | 禁用交互与选择。 |
| `isReadOnly` | `boolean` | `false` | 内容只读,无法更改选中。 |
| `isInvalid` | `boolean` | `false` | 将日历标记为无效以配合校验 UI。 |
| `visibleDuration` | `{months?: number; weeks?: number; days?: number}` | `{months: 1}` | 可见时间范围。使用 `{ months: n }` 为月视图,`{ weeks: n }` 为周视图,`{ days: n }` 为日视图。 |
| `defaultYearPickerOpen` | `boolean` | `false` | 内置年份选择器的初始展开状态。 |
| `isYearPickerOpen` | `boolean` | - | 受控的年份选择器展开状态。 |
| `onYearPickerOpenChange` | `(isOpen: boolean) => void` | - | 年份选择器展开状态变化时调用。 |
### 组合部件
| Component | 描述 |
| ------------------------------------- | --------------------------------------------------- |
| `Calendar.Header` | 导航与标题的头部容器。 |
| `Calendar.Heading` | 可见范围的格式化标题。支持 `offset`(多月份布局)与 `format`(月/年/日格式选项)。 |
| `Calendar.NavButton` | 上一月/下一月导航控件(`slot="previous"` 或 `slot="next"`)。 |
| `Calendar.Grid` | 单个月的日期网格(多月份布局支持 `offset`)。 |
| `Calendar.GridHeader` | 星期标题容器。 |
| `Calendar.GridBody` | 日期单元格主体容器。 |
| `Calendar.HeaderCell` | 星期标签单元格。 |
| `Calendar.Cell` | 单个日期单元格。 |
| `Calendar.CellIndicator` | 用于自定义元数据的可选指示元素。 |
| `Calendar.YearPickerTrigger` | 切换年份选择模式的触发器。 |
| `Calendar.YearPickerTriggerHeading` | 年份选择触发器内的本地化标题内容。 |
| `Calendar.YearPickerTriggerIndicator` | 年份选择触发器内的切换图标。 |
| `Calendar.YearPickerGrid` | 年份选择覆盖网格容器。 |
| `Calendar.YearPickerGridBody` | 年份网格单元格的 body 渲染器。 |
| `Calendar.YearPickerCell` | 单个年份选项单元格。 |
### 年份选择器子组件
年份选择器子组件继承 React Aria [`useCalendarHeading`](https://react-aria.adobe.com/useCalendar#usecalendarheading) 与 [`useCalendarYearPicker`](https://react-aria.adobe.com/useCalendar#usecalendaryearpicker) 的格式化属性。
| 组件 | 属性 | 类型 | 默认值 | 描述 |
| ----------------------------------- | -------------- | ---------------------- | ------------------- | ---------------------------------------------------------- |
| `Calendar.YearPickerTriggerHeading` | `format` | `DateFormatterOptions` | - | 自定义月/年标题(如 `{month: 'short'}`)。 |
| `Calendar.YearPickerTriggerHeading` | `offset` | `{months?: number}` | - | 相对聚焦日期偏移标题(多月布局)。 |
| `Calendar.YearPickerGrid` | `format` | `DateFormatterOptions` | `{year: 'numeric'}` | 自定义年份单元格标签(纪元、历法系统等)。 |
| `Calendar.YearPickerGrid` | `visibleYears` | `number` | min–max 跨度或 `20` | 滑动窗口中显示的年份数量。当同时设置 `minValue` 与 `maxValue` 时,默认为二者之间的完整范围。 |
### Calendar.Cell Render Props
当 `Calendar.Cell` 的 `children` 为函数时,可使用 React Aria 的渲染参数:
| Prop | 类型 | 描述 |
| ---------------- | --------- | ------------ |
| `formattedDate` | `string` | 单元格日期的本地化标签。 |
| `isSelected` | `boolean` | 该日期是否被选中。 |
| `isUnavailable` | `boolean` | 该日期是否不可用。 |
| `isDisabled` | `boolean` | 单元格是否禁用。 |
| `isOutsideMonth` | `boolean` | 是否属于相邻月份。 |
支持的历法系统及其标识符完整列表见:
* [React Aria Calendar Implementations](https://react-aria.adobe.com/internationalized/date/Calendar#implementations)
* [React Aria International Calendars](https://react-aria.adobe.com/Calendar#international-calendars)
### Related packages
* [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) — 各日期组件共用的日期类型(`CalendarDate`、`CalendarDateTime`、`ZonedDateTime`)与工具函数
* [`I18nProvider`](https://react-aria.adobe.com/I18nProvider) — 为子树覆盖语言环境
* [`useLocale`](https://react-aria.adobe.com/useLocale) — 读取当前语言环境与书写方向
# DateField 日期字段
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/date-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(date-and-time)/date-field.mdx
> 基于 React Aria DateField 的日期输入字段,包含标签、说明与校验。
## 引入
```tsx
import { DateField } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {DateField, Label} from "@heroui/react";
export function Basic() {
return (
日期
{(segment) => }
);
}
```
### 组件结构
```tsx
import {DateField, Label, Description, FieldError} from '@heroui/react';
export default () => (
{(segment) => }
)
```
> **DateField** 将标签、日期输入、说明与错误信息组合为单个无障碍组件。
### 带描述
```tsx
"use client";
import {DateField, Description, Label} from "@heroui/react";
export function WithDescription() {
return (
出生日期
{(segment) => }
输入出生日期
预约日期
{(segment) => }
输入预约日期
);
}
```
### 必填字段
```tsx
"use client";
import {DateField, Description, Label} from "@heroui/react";
export function Required() {
return (
日期
{(segment) => }
开始日期
{(segment) => }
必填项
);
}
```
### 校验
配合 `FieldError`,使用 `isInvalid` 展示校验信息。
```tsx
"use client";
import {DateField, FieldError, Label} from "@heroui/react";
export function Invalid() {
return (
日期
{(segment) => }
请输入有效日期
日期
{(segment) => }
日期须为将来
);
}
```
### 带校验
DateField 支持使用 `minValue`、`maxValue` 及自定义校验逻辑。
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {DateField, Description, FieldError, Label} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
import {useState} from "react";
export function WithValidation() {
const [value, setValue] = useState(null);
const todayDate = today(getLocalTimeZone());
const isInvalid = value !== null && value.compare(todayDate) < 0;
return (
日期
{(segment) => }
{isInvalid ? (
日期须为今天或将来
) : (
输入日期 from today onwards
)}
);
}
```
### 粒度
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {CircleQuestion} from "@gravity-ui/icons";
import {DateField, Label, ListBox, Select, Tooltip} from "@heroui/react";
import {parseDate, parseZonedDateTime} from "@internationalized/date";
import {useState} from "react";
export function Granularity() {
const granularityOptions = [
{id: "day", label: "日"},
{id: "hour", label: "时"},
{id: "minute", label: "分"},
{id: "second", label: "秒"},
] as const;
const [granularity, setGranularity] = useState<"day" | "hour" | "minute" | "second">("day");
// Determine appropriate default value based on granularity
let defaultValue: DateValue;
if (granularity === "day") {
defaultValue = parseDate("2025-02-03");
} else {
// hour, minute, second
defaultValue = parseZonedDateTime("2025-02-03T08:45:00[America/Los_Angeles]");
}
return (
预约日期
{(segment) => }
粒度
决定日期选择器显示的最小单位。默认情况下,日期为「日」,时间为「分」。
setGranularity(value as typeof granularity)}
>
{granularityOptions.map((option) => (
{option.label}
))}
);
}
```
### 受控
通过受控 `value` 与其它组件或状态管理同步。
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Button, DateField, Description, Label} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
import {useState} from "react";
export function Controlled() {
const [value, setValue] = useState(null);
return (
日期
{(segment) => }
当前值:{value ? value.toString() : "(空)"}
setValue(today(getLocalTimeZone()))}>
设为今天
setValue(null)}>
清空
);
}
```
### 禁用状态
```tsx
"use client";
import {DateField, Description, Label} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function Disabled() {
return (
日期
{(segment) => }
该日期字段已禁用
日期
{(segment) => }
该日期字段已禁用
);
}
```
### 带图标
通过前缀或后缀图标增强日期输入。
```tsx
"use client";
import {Calendar} from "@gravity-ui/icons";
import {DateField, Label} from "@heroui/react";
export function WithPrefixIcon() {
return (
日期
{(segment) => }
);
}
```
```tsx
"use client";
import {Calendar} from "@gravity-ui/icons";
import {DateField, Label} from "@heroui/react";
export function WithSuffixIcon() {
return (
日期
{(segment) => }
);
}
```
```tsx
"use client";
import {Calendar, ChevronDown} from "@gravity-ui/icons";
import {DateField, Description, Label} from "@heroui/react";
export function WithPrefixAndSuffix() {
return (
日期
{(segment) => }
输入日期
);
}
```
### 全宽
```tsx
"use client";
import {Calendar, ChevronDown} from "@gravity-ui/icons";
import {DateField, Label} from "@heroui/react";
export function FullWidth() {
return (
日期
{(segment) => }
日期
{(segment) => }
);
}
```
### 变体
`DateField.Group` 提供两种视觉变体:
* **`primary`**(默认):带阴影的标准样式,适用于大多数场景
* **`secondary`**:低强调、无阴影,适合放在 Surface 等表面背景上
```tsx
"use client";
import {DateField, Label} from "@heroui/react";
export function Variants() {
return (
主要变体
{(segment) => }
次要变体
{(segment) => }
);
}
```
### 在 Surface 中
在 [Surface](/docs/components/surface) 内使用时,请在 `DateField.Group` 上使用 `variant="secondary"`,以应用适合表面背景的低强调变体。
```tsx
"use client";
import {Calendar} from "@gravity-ui/icons";
import {DateField, Description, Label, Surface} from "@heroui/react";
export function OnSurface() {
return (
日期
{(segment) => }
输入日期
预约日期
{(segment) => }
输入预约日期
);
}
```
### 表单示例
包含校验与提交的完整表单示例。
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Calendar} from "@gravity-ui/icons";
import {Button, DateField, Description, FieldError, Form, Label} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
import {useState} from "react";
export function FormExample() {
const [value, setValue] = useState(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const todayDate = today(getLocalTimeZone());
const isInvalid = value !== null && value.compare(todayDate) < 0;
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!value || isInvalid) {
return;
}
setIsSubmitting(true);
// Simulate API call
setTimeout(() => {
console.log("已提交日期:", {date: value});
setValue(null);
setIsSubmitting(false);
}, 1500);
};
return (
预约日期
{(segment) => }
{isInvalid ? (
日期须为今天或将来
) : (
输入日期 from today onwards
)}
{isSubmitting ? "提交中…" : "Submit"}
);
}
```
## Related Components
* **DatePicker**: Composable date picker with date field trigger and calendar popover
* **Calendar**: Interactive month grid for selecting dates
* **Label**: Accessible label for form controls
### 自定义渲染函数
```tsx
"use client";
import {DateField, Label} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
}>日期
}>
}>
{(segment) => }
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import {DateField, Label, Description} from '@heroui/react';
function CustomDateField() {
return (
Appointment date
{(segment) => }
Select a date for your appointment.
);
}
```
### 自定义组件类
DateField 的默认样式很轻量。覆盖 `.date-field` 类即可自定义容器样式。
```css
@layer components {
.date-field {
@apply flex flex-col gap-1;
&[data-invalid="true"],
&[aria-invalid="true"] {
[data-slot="description"] {
@apply hidden;
}
}
[data-slot="label"] {
@apply w-fit;
}
[data-slot="description"] {
@apply px-1;
}
}
}
```
### CSS 类
* `.date-field` – 轻量样式的根容器(`flex flex-col gap-1`)
> **说明:** 子组件([Label](/docs/components/label)、[Description](/docs/components/description)、[FieldError](/docs/components/field-error))拥有各自的 CSS 类与样式。自定义方式请参阅对应文档。`DateField.Group` 的样式见下文 API 参考。
### 交互状态
DateField 会根据状态自动设置以下 data 属性:
* **无效**:`[data-invalid="true"]` 或 `[aria-invalid="true"]` – 无效时自动隐藏 description 插槽
* **必填**:`[data-required="true"]` – 当 `isRequired` 为 true 时添加
* **禁用**:`[data-disabled="true"]` – 当 `isDisabled` 为 true 时添加
* **焦点在内**:`[data-focus-within="true"]` – 任一子输入聚焦时添加
## API 参考
### DateField Props
DateField 继承 React Aria [DateField](https://react-aria.adobe.com/DateField.md) 的全部 props。
#### Base Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------------ | ------- | ---------------------------------- |
| `children` | `React.ReactNode \| (values: DateFieldRenderProps) => React.ReactNode` | - | 子组件(Label、DateField.Group 等)或渲染函数。 |
| `className` | `string \| (values: DateFieldRenderProps) => string` | - | 用于样式的 CSS 类,支持渲染 prop。 |
| `style` | `React.CSSProperties \| (values: DateFieldRenderProps) => React.CSSProperties` | - | 内联样式,支持渲染 prop。 |
| `fullWidth` | `boolean` | `false` | 日期字段是否占满容器宽度。 |
| `id` | `string` | - | 元素的唯一 id。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
#### Value Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | ------------------------------------ | --- | ----------------------------------------------------------------------------------------------- |
| `value` | `DateValue \| null` | - | 当前值(受控)。类型见 [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/)。 |
| `defaultValue` | `DateValue \| null` | - | 默认值(非受控)。类型见 [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/)。 |
| `onChange` | `(value: DateValue \| null) => void` | - | 值变化时触发的事件处理函数。 |
| `placeholderValue` | `DateValue \| null` | - | 影响占位符格式的占位日期。 |
#### Validation Props
| Prop | 类型 | 默认值 | 描述 |
| -------------------- | -------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------ |
| `isRequired` | `boolean` | `false` | 是否在提交表单前要求用户输入。 |
| `isInvalid` | `boolean` | - | 值是否无效。 |
| `minValue` | `DateValue \| null` | - | 用户可选择最早日期。类型见 [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/)。 |
| `maxValue` | `DateValue \| null` | - | 用户可选择最晚日期。类型见 [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/)。 |
| `isDateUnavailable` | `(date: DateValue) => boolean` | - | 针对每个日期调用;返回 true 表示该日期不可用。 |
| `validate` | `(value: DateValue) => ValidationError \| true \| null \| undefined` | - | 自定义校验函数。 |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | 使用原生 HTML 表单校验还是 ARIA 属性。 |
#### Format Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------------- | ------------- | ------- | --------------------------------------- |
| `granularity` | `Granularity` | - | 显示的最小单位。日期默认为 `"day"`,时间默认为 `"minute"`。 |
| `hourCycle` | `12 \| 24` | - | 以 12 或 24 小时制显示时间;默认由语言环境决定。 |
| `hideTimeZone` | `boolean` | `false` | 是否隐藏时区缩写。 |
| `shouldForceLeadingZeros` | `boolean` | - | 是否始终为月、日、小时等显示前导零。 |
#### State Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | --------- | --- | ----------- |
| `isDisabled` | `boolean` | - | 是否禁用输入。 |
| `isReadOnly` | `boolean` | - | 是否可选中但不可修改。 |
#### Form Props
| Prop | 类型 | 默认值 | 描述 |
| -------------- | --------- | --- | ----------------------------------------- |
| `name` | `string` | - | 输入元素的 name,用于 HTML 表单提交;以 ISO 8601 字符串提交。 |
| `autoFocus` | `boolean` | - | 是否在渲染后自动聚焦该元素。 |
| `autoComplete` | `string` | - | 输入应提供的自动完成类型。 |
#### Accessibility Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | -------- | --- | ------------- |
| `aria-label` | `string` | - | 无可见标签时的无障碍标签。 |
| `aria-labelledby` | `string` | - | 标注该字段的元素 id。 |
| `aria-describedby` | `string` | - | 描述该字段的元素 id。 |
| `aria-details` | `string` | - | 包含额外详情的元素 id。 |
### 组合组件
DateField 与以下独立组件配合使用,请分别导入并直接使用:
* **Label** – 来自 `@heroui/react` 的字段标签
* **DateField.Group** – 日期输入分组(详见下文)
* **DateField.Input** – 来自 `@heroui/react` 的分段位编辑输入
* **DateField.InputContainer** – 可横向滚动的容器,用于组合多个输入(例如开始/结束范围)
* **DateField.Segment** – 单个日期段位(年、月、日等)
* **DateField.Prefix** / **DateField.Suffix** – 输入组的前缀与后缀插槽
* **Description** – 来自 `@heroui/react` 的辅助说明
* **FieldError** – 来自 `@heroui/react` 的校验错误信息
这些组件各自有独立的 props API。在 DateField 中直接组合使用:
```tsx
import {parseDate} from '@internationalized/date';
import {DateField, Label, Description, FieldError} from '@heroui/react';
Appointment Date
{(segment) => }
Select a date from today onwards.
Please select a valid date.
```
### DateValue 类型
DateField 使用 [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) 中的类型:
* `CalendarDate` – 不含时间与时区的日期
* `CalendarDateTime` – 含时间、不含时区
* `ZonedDateTime` – 含时间与时区
* `Time` – 仅时间
示例:
```tsx
import {parseDate, today, getLocalTimeZone} from '@internationalized/date';
// Parse from string
const date = parseDate('2024-01-15');
// Today's date
const todayDate = today(getLocalTimeZone());
// Use in DateField
{/* ... */}
```
> **说明:** DateField 依赖 [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) 进行解析、运算与类型定义。更多类型与函数见 [Internationalized Date 文档](https://react-aria.adobe.com/internationalized/date/)。
### DateFieldRenderProps
对 `className`、`style` 或 `children` 使用渲染 prop 时,可使用以下值:
| Prop | 类型 | 描述 |
| ---------------- | --------- | ------------- |
| `isDisabled` | `boolean` | 字段是否禁用。 |
| `isInvalid` | `boolean` | 字段当前是否无效。 |
| `isReadOnly` | `boolean` | 字段是否只读。 |
| `isRequired` | `boolean` | 字段是否必填。 |
| `isFocused` | `boolean` | 字段是否聚焦。 |
| `isFocusWithin` | `boolean` | 是否有子元素聚焦。 |
| `isFocusVisible` | `boolean` | 焦点是否可见(键盘导航)。 |
### DateField.Group Props
DateField.Group 继承 React Aria `Group` 的全部 props,并额外支持:
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------------------------- | ----------- | ---------------------------------------------------------- |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
| `fullWidth` | `boolean` | `false` | 日期输入组是否占满容器宽度。 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 视觉变体。`primary` 为默认带阴影样式;`secondary` 为低强调、无阴影,适合用于 Surface。 |
### DateField.Input Props
DateField.Input 继承 React Aria `DateInput` 的全部 props,并额外支持:
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------------------------- | ----------- | ------------------------------------------------------------- |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 输入的视觉变体。`primary` 为默认带阴影样式;`secondary` 为低强调、无阴影,适合用于 Surface。 |
`DateField.Input` 接受渲染函数作为子节点,函数参数为日期段位;每个段位对应日期的一部分(年、月、日等)。
### DateField.Segment Props
DateField.Segment 继承 React Aria `DateSegment` 的全部 props:
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------- | --- | ------------------------------------------ |
| `segment` | `DateSegment` | - | 来自 DateField.Input 渲染函数的 `DateSegment` 对象。 |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
### DateField.InputContainer Props
DateField.InputContainer 接受标准 HTML `div` 属性:
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ---------------------------------- |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 滚动容器中的内容(通常为多个 `DateField.Input`)。 |
### DateField.Prefix Props
DateField.Prefix 接受标准 HTML `div` 属性:
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------------ |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 前缀插槽中要显示的内容。 |
### DateField.Suffix Props
DateField.Suffix 接受标准 HTML `div` 属性:
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------------ |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 后缀插槽中要显示的内容。 |
## DateField.Group 样式
### 自定义组件类
基础类作用于所有实例,可通过 `@layer components` 一次性覆盖。
```css
@layer components {
.date-input-group {
@apply inline-flex h-9 items-center overflow-hidden rounded-field border bg-field text-sm text-field-foreground shadow-field outline-none;
&:hover,
&[data-hovered="true"] {
@apply bg-field-hover;
}
&[data-focus-within="true"],
&:focus-within {
@apply status-focused-field;
}
&[data-invalid="true"] {
@apply status-invalid-field;
}
&[data-disabled="true"],
&[aria-disabled="true"] {
@apply status-disabled;
}
}
.date-input-group__input {
@apply flex flex-1 items-center gap-px rounded-none border-0 bg-transparent px-3 py-2 shadow-none outline-none;
}
.date-input-group__segment {
@apply inline-block rounded-md px-0.5 text-end tabular-nums outline-none;
&:focus,
&[data-focused="true"] {
@apply bg-accent-soft text-accent-soft-foreground;
}
}
.date-input-group__input-container {
@apply flex flex-1 items-center;
overflow-x: auto;
overflow-y: clip;
scrollbar-width: none;
}
.date-input-group__prefix,
.date-input-group__suffix {
@apply pointer-events-none shrink-0 text-field-placeholder flex items-center;
}
}
```
### DateField.Group CSS 类
* `.date-input-group` – 根容器样式
* `.date-input-group__input` – 输入包裹层样式
* `.date-input-group__input-container` – 用于组合多个输入的滚动容器
* `.date-input-group__segment` – 单个日期段位样式
* `.date-input-group__prefix` – 前缀元素样式
* `.date-input-group__suffix` – 后缀元素样式
### DateField.Group 交互状态
* **悬停**:`:hover` 或 `[data-hovered="true"]`
* **焦点在内**:`[data-focus-within="true"]` 或 `:focus-within`
* **无效**:`[data-invalid="true"]`(同时与 `aria-invalid` 同步)
* **禁用**:`[data-disabled="true"]` 或 `[aria-disabled="true"]`
* **段位聚焦**:段位上的 `:focus` 或 `[data-focused="true"]`
* **段位占位符**:段位上的 `[data-placeholder="true"]`
# DatePicker 日期选择器
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/date-picker
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(date-and-time)/date-picker.mdx
> 可组合的日期选择器,基于 React Aria DatePicker,通过 DateField 与 Calendar 组合实现。
## 引入
```tsx
import { DatePicker, DateField, Calendar, Label } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {Calendar, DateField, DatePicker, Label} from "@heroui/react";
export function Basic() {
return (
日期
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### 组件结构
`DatePicker` 采用组合优先的 API。请显式组合 `DateField` 与 `Calendar`,以便完全控制结构与样式。
```tsx
import {Calendar, DateField, DatePicker, Label} from '@heroui/react';
export default () => (
{(segment) => }
{(day) => {day} }
{(date) => }
)
```
### 受控
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Button, Calendar, DateField, DatePicker, Description, Label} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
import {useState} from "react";
export function Controlled() {
const [value, setValue] = useState(today(getLocalTimeZone()));
return (
日期
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
当前值:{value ? value.toString() : "(空)"}
setValue(today(getLocalTimeZone()))}>
设为今天
setValue(null)}>
清空
);
}
```
### 校验
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Calendar, DateField, DatePicker, FieldError, Label} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
import {useState} from "react";
export function WithValidation() {
const [value, setValue] = useState(null);
const currentDate = today(getLocalTimeZone());
const isInvalid = value != null && value.compare(currentDate) < 0;
return (
预约日期
{(segment) => }
日期须为今天或将来。
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### 格式选项
使用 `granularity`、`hourCycle`、`hideTimeZone`、`shouldForceLeadingZeros` 等 props 控制 DatePicker 值的展示方式。
```tsx
"use client";
import type {TimeValue} from "@heroui/react";
import type {DateValue} from "@internationalized/date";
import {
Calendar,
DateField,
DatePicker,
Label,
ListBox,
Select,
Switch,
TimeField,
} from "@heroui/react";
import {getLocalTimeZone, parseDate, parseZonedDateTime} from "@internationalized/date";
import {useMemo, useState} from "react";
type Granularity = "day" | "hour" | "minute" | "second";
type HourCycle = 12 | 24;
const granularityOptions: {label: string; value: Granularity}[] = [
{label: "日", value: "day"},
{label: "时", value: "hour"},
{label: "分", value: "minute"},
{label: "秒", value: "second"},
];
const hourCycleOptions: {label: string; value: HourCycle}[] = [
{label: "12 小时制", value: 12},
{label: "24 小时制", value: 24},
];
export function FormatOptions() {
const [granularity, setGranularity] = useState("minute");
const [hourCycle, setHourCycle] = useState(12);
const [hideTimeZone, setHideTimeZone] = useState(false);
const [shouldForceLeadingZeros, setShouldForceLeadingZeros] = useState(false);
const timeGranularity = granularity !== "day" ? granularity : undefined;
const showTimeField = !!timeGranularity;
const defaultValue = useMemo(() => {
const localTimeZone = getLocalTimeZone();
if (granularity === "day") {
return parseDate("2026-02-03");
}
return parseZonedDateTime(`2026-02-03T08:45:00[${localTimeZone}]`);
}, [granularity]);
return (
{({state}) => (
<>
日期和时间
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
{!!showTimeField && (
时间
state.setTimeValue(v as TimeValue)}
>
{(segment) => }
)}
>
)}
setGranularity(value as Granularity)}
>
粒度
{granularityOptions.map((option) => (
{option.label}
))}
setHourCycle(Number(value) as HourCycle)}
>
小时制
{hourCycleOptions.map((option) => (
{option.label}
))}
隐藏时区
强制前导零
);
}
```
### 禁用
```tsx
"use client";
import {Calendar, DateField, DatePicker, Description, Label} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function Disabled() {
return (
日期
{(segment) => }
该日期选择器已禁用。
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### 自定义指示器
未提供子节点时,`DatePicker.TriggerIndicator` 会渲染默认的 `IconCalendar`。传入子节点即可替换。
```tsx
"use client";
import {Calendar, DateField, DatePicker, Description, Label} from "@heroui/react";
import {Icon} from "@iconify/react";
export function WithCustomIndicator() {
return (
日期
{(segment) => }
通过传入自定义子元素替换默认日历图标。
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### 表单示例
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {
Button,
Calendar,
DateField,
DatePicker,
Description,
FieldError,
Form,
Label,
} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
import {useState} from "react";
export function FormExample() {
const [value, setValue] = useState(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const currentDate = today(getLocalTimeZone());
const isInvalid = value != null && value.compare(currentDate) < 0;
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
if (!value || isInvalid) {
return;
}
setIsSubmitting(true);
setTimeout(() => {
setValue(null);
setIsSubmitting(false);
}, 1200);
};
return (
预约日期
{(segment) => }
{isInvalid ? (
日期须为今天或将来。
) : (
请选择有效的预约日期。
)}
{(day) => {day} }
{(date) => }
{({year}) => }
{isSubmitting ? "提交中…" : "提交"}
);
}
```
### 国际化历法
默认情况下,DatePicker 会使用用户语言环境对应的历法显示日期。你可以使用 `I18nProvider` 包裹 DatePicker,并通过 [Unicode 历法语言扩展](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string) 覆盖。
下方示例展示印度历法系统:
```tsx
"use client";
import {Calendar, DateField, DatePicker, Label} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
import {I18nProvider} from "react-aria-components";
export function InternationalCalendar() {
return (
活动日期
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
**说明:** `onChange` 事件返回的日期始终与 `value` 或 `defaultValue` 使用同一历法系统(未提供值时为公历),与界面展示的本地化格式无关。这能确保应用逻辑在单一历法系统下保持一致,同时仍可按用户偏好展示日期。
支持的历法系统及其标识符完整列表见:
* [React Aria Calendar Implementations](https://react-aria.adobe.com/internationalized/date/Calendar#implementations)
* [React Aria International Calendars](https://react-aria.adobe.com/Calendar#international-calendars)
### 自定义渲染函数
```tsx
"use client";
import {Calendar, DateField, DatePicker, Label} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
}>日期
}
>
}>
{(segment) => (
}
segment={segment}
/>
)}
}
>
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
## Related Components
* **Calendar**: Interactive month grid for selecting dates
* **RangeCalendar**: Interactive month grid for selecting date ranges
* **DateField**: Date input field with labels, descriptions, and validation
## 样式
### 传入 Tailwind CSS 类
你可以分别为各个组合部分添加样式:
```tsx
import {Calendar, DateField, DatePicker, Label} from '@heroui/react';
function CustomDatePicker() {
return (
Date
{(segment) => }
{/* Calendar parts */}
);
}
```
### 自定义组件类
要自定义 DatePicker 的基础类,请使用 `@layer components`。
```css
@layer components {
.date-picker {
@apply inline-flex flex-col gap-1;
}
.date-picker__trigger {
@apply inline-flex items-center justify-between;
}
.date-picker__trigger-indicator {
@apply text-muted;
}
.date-picker__popover {
@apply min-w-[var(--trigger-width)] p-0;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 命名,便于复写与定制。
### CSS 类
DatePicker 在 `packages/styles/components/date-picker.css` 中使用以下类:
* `.date-picker` - 根包裹层。
* `.date-picker__trigger` - 打开弹出层的触发区域。
* `.date-picker__trigger-indicator` - 默认或自定义指示器插槽。
* `.date-picker__popover` - 弹出层内容包裹。
### 交互状态
DatePicker 支持 React Aria 的 data 属性与伪类状态:
* **展开**:触发器上的 `[data-open="true"]`。
* **禁用**:触发器上的 `[data-disabled="true"]` 或 `[aria-disabled="true"]`。
* **焦点可见**:触发器上的 `:focus-visible` 或 `[data-focus-visible="true"]`。
* **悬停**:触发器上的 `:hover` 或 `[data-hovered="true"]`。
## API 参考
### DatePicker Props
DatePicker 继承 React Aria [DatePicker](https://react-aria.adobe.com/DatePicker.md) 的全部 props。
| Prop | 类型 | 默认值 | 描述 |
| -------------- | ----------------------------------------------------------------------------- | ------- | --------------------- |
| `value` | `DateValue \| null` | - | 受控的选中日期值。 |
| `defaultValue` | `DateValue \| null` | - | 非受控模式下的默认选中值。 |
| `onChange` | `(value: DateValue \| null) => void` | - | 选中日期变化时调用。 |
| `isOpen` | `boolean` | - | 受控的弹出层打开状态。 |
| `defaultOpen` | `boolean` | `false` | 弹出层初始打开状态。 |
| `onOpenChange` | `(isOpen: boolean) => void` | - | 弹出层打开状态变化时调用。 |
| `isDisabled` | `boolean` | `false` | 禁用日期选择与触发器交互。 |
| `isInvalid` | `boolean` | - | 将字段标记为无效以呈现校验状态。 |
| `minValue` | `DateValue` | - | 可选择的最小日期。 |
| `maxValue` | `DateValue` | - | 可选择的最大日期。 |
| `name` | `string` | - | HTML 表单提交使用的 name。 |
| `children` | `ReactNode \| (values: DatePickerRenderProps) => ReactNode` | - | 组合内容或渲染函数。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### 组合部件
| 组件 | 描述 |
| ----------------------------- | -------------------------------- |
| `DatePicker.Root` | 根日期选择器容器与状态持有者。 |
| `DatePicker.Trigger` | 触发按钮,通常渲染在 `DateField.Suffix` 内。 |
| `DatePicker.TriggerIndicator` | 带默认日历图标的指示器插槽。 |
| `DatePicker.Popover` | 包裹 `Calendar` 内容的弹出层。 |
### 相关包
* [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) — 所有日期组件共用的日期类型(`CalendarDate`、`CalendarDateTime`、`ZonedDateTime`)与工具函数
* [`I18nProvider`](https://react-aria.adobe.com/I18nProvider) — 为子树覆盖语言环境
* [`useLocale`](https://react-aria.adobe.com/useLocale) — 读取当前语言环境与布局方向
# DateRangePicker 日期范围选择器
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/date-range-picker
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(date-and-time)/date-range-picker.mdx
> 基于 React Aria DateRangePicker 的可组合日期范围选择器,由 DateField 与 RangeCalendar 组合而成。
## 引入
```tsx
import { DateField, DateRangePicker, Label, RangeCalendar } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {DateField, DateRangePicker, Label, RangeCalendar} from "@heroui/react";
export function Basic() {
return (
出行日期
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### 组件结构
`DateRangePicker` 采用组合优先的 API。请显式组合 `DateField` 与 `RangeCalendar`,以便完全控制结构与样式。
```tsx
import {DateField, DateRangePicker, Label, RangeCalendar} from '@heroui/react';
export default () => (
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
)
```
### 受控
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Button, DateField, DateRangePicker, Description, Label, RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
import {useState} from "react";
type DateRange = {
start: DateValue;
end: DateValue;
};
export function Controlled() {
const start = today(getLocalTimeZone());
const [value, setValue] = useState({end: start.add({days: 4}), start});
return (
出行日期
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
当前值:{value ? `${value.start.toString()} 至 ${value.end.toString()}` : "(空)"}
{
const nextStart = today(getLocalTimeZone());
setValue({end: nextStart.add({days: 6}), start: nextStart});
}}
>
设为一周
setValue(null)}>
清空
);
}
```
### 校验
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {DateField, DateRangePicker, FieldError, Label, RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
import {useState} from "react";
type DateRange = {
start: DateValue;
end: DateValue;
};
export function WithValidation() {
const [value, setValue] = useState(null);
const currentDate = today(getLocalTimeZone());
const isInvalid =
value != null && (value.start.compare(currentDate) < 0 || value.end.compare(value.start) < 0);
return (
预订时段
{(segment) => }
{(segment) => }
请选择从今天起的有效日期范围。
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### 格式选项
使用 `granularity`、`hourCycle`、`hideTimeZone`、`shouldForceLeadingZeros` 等 props 控制 DateRangePicker 值的展示格式。
```tsx
"use client";
import type {TimeValue} from "@heroui/react";
import type {DateValue} from "@internationalized/date";
import {
DateField,
DateRangePicker,
Label,
ListBox,
RangeCalendar,
Select,
Separator,
Switch,
TimeField,
useLocale,
} from "@heroui/react";
import {
DateFormatter,
getLocalTimeZone,
parseDate,
parseZonedDateTime,
} from "@internationalized/date";
import {useMemo, useState} from "react";
type Granularity = "day" | "hour" | "minute" | "second";
type HourCycle = 12 | 24;
type DateRange = {
start: DateValue;
end: DateValue;
};
const granularityOptions: {label: string; value: Granularity}[] = [
{label: "日", value: "day"},
{label: "时", value: "hour"},
{label: "分", value: "minute"},
{label: "秒", value: "second"},
];
const hourCycleOptions: {label: string; value: HourCycle}[] = [
{label: "12 小时制", value: 12},
{label: "24 小时制", value: 24},
];
export function FormatOptions() {
const [granularity, setGranularity] = useState("minute");
const [hourCycle, setHourCycle] = useState(12);
const [hideTimeZone, setHideTimeZone] = useState(false);
const [shouldForceLeadingZeros, setShouldForceLeadingZeros] = useState(false);
const {locale} = useLocale();
const dateFormatter = new DateFormatter(locale, {
day: "numeric",
month: "short",
year: "numeric",
});
const formatDate = (date: DateRange) => {
const localTimeZone = getLocalTimeZone();
const start = date.start.toDate(localTimeZone);
const end = date.end.toDate(localTimeZone);
return dateFormatter.formatRange(start, end);
};
const defaultValue = useMemo(() => {
const localTimeZone = getLocalTimeZone();
if (granularity === "day") {
return {
end: parseDate("2025-02-10"),
start: parseDate("2025-02-03"),
};
}
return {
end: parseZonedDateTime(`2026-02-10T18:45:00[${localTimeZone}]`),
start: parseZonedDateTime(`2026-02-03T08:45:00[${localTimeZone}]`),
};
}, [granularity]);
const timeGranularity = granularity !== "day" ? granularity : undefined;
const showTimeField = !!timeGranularity;
return (
{({state}) => (
<>
日期范围
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
{!!showTimeField && (
开始时间
state.setTimeRange({
end: state.timeRange?.end as TimeValue,
start: v as TimeValue,
})
}
>
{(segment) => }
结束时间
state.setTimeRange({
end: v as TimeValue,
start: state.timeRange?.start as TimeValue,
})
}
>
{(segment) => }
)}
已选:{" "}
{state.value && state.value.start && state.value.end
? formatDate({end: state.value.end, start: state.value.start})
: "未选择日期"}
>
)}
格式选项
setGranularity(value as Granularity)}
>
粒度
{granularityOptions.map((option) => (
{option.label}
))}
setHourCycle(Number(value) as HourCycle)}
>
小时制
{hourCycleOptions.map((option) => (
{option.label}
))}
隐藏时区
强制前导零
);
}
```
### 禁用
```tsx
"use client";
import {DateField, DateRangePicker, Description, Label, RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function Disabled() {
const start = today(getLocalTimeZone());
return (
出行日期
{(segment) => }
{(segment) => }
该日期范围选择器已禁用。
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### 自定义指示器
未传入子节点时,`DateRangePicker.TriggerIndicator` 会渲染默认的 `IconCalendar`。传入子节点即可替换。
```tsx
"use client";
import {DateField, DateRangePicker, Description, Label, RangeCalendar} from "@heroui/react";
import {Icon} from "@iconify/react";
export function WithCustomIndicator() {
return (
出行日期
{(segment) => }
{(segment) => }
通过传入自定义子元素替换默认日历图标。
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### 表单示例
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {
Button,
DateField,
DateRangePicker,
Description,
FieldError,
Form,
Label,
RangeCalendar,
} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
import {useState} from "react";
type DateRange = {
start: DateValue;
end: DateValue;
};
export function FormExample() {
const [value, setValue] = useState(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const currentDate = today(getLocalTimeZone());
const isInvalid =
value != null && (value.start.compare(currentDate) < 0 || value.end.compare(value.start) < 0);
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
if (!value || isInvalid) return;
setIsSubmitting(true);
setTimeout(() => {
setValue(null);
setIsSubmitting(false);
}, 1200);
};
return (
出行日期
{(segment) => }
{(segment) => }
{isInvalid ? (
请选择从今天起的有效日期范围。
) : (
选择入住与退房日期。
)}
{(day) => {day} }
{(date) => }
{({year}) => }
{isSubmitting ? "提交中…" : "提交"}
);
}
```
### 国际化历法
默认情况下,DateRangePicker 按用户语言环境的历法显示日期。你可以使用 `I18nProvider` 包裹 DateRangePicker,并通过 [Unicode 历法语言扩展](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string) 覆盖。
下方示例展示印度历法系统:
```tsx
"use client";
import {DateField, DateRangePicker, Label, RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
import {I18nProvider} from "react-aria-components";
export function InternationalCalendar() {
const start = today(getLocalTimeZone());
return (
出行日期
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
**说明:** `onChange` 事件始终返回与 `value` 或 `defaultValue` 相同历法系统中的日期(若未提供值则为公历),与界面展示的本地化格式无关。
支持的历法系统及其标识符完整列表见:
* [React Aria Calendar Implementations](https://react-aria.adobe.com/internationalized/date/Calendar#implementations)
* [React Aria International Calendars](https://react-aria.adobe.com/Calendar#international-calendars)
### 自定义渲染函数
```tsx
"use client";
import {DateField, DateRangePicker, Label, RangeCalendar} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
startName="startDate"
>
出行日期
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
## Related Components
* **RangeCalendar**: Interactive month grid for selecting date ranges
* **Calendar**: Interactive month grid for selecting dates
* **DateField**: Date input field with labels, descriptions, and validation
## 样式
### 传入 Tailwind CSS 类
你可以独立为每个组合部件添加样式:
```tsx
import {DateField, DateRangePicker, Label, RangeCalendar} from '@heroui/react';
function CustomDateRangePicker() {
return (
Trip dates
{(segment) => }
{(segment) => }
{/* RangeCalendar parts */}
);
}
```
### 自定义组件类
若要自定义 DateRangePicker 基础类,请使用 `@layer components`。
```css
@layer components {
.date-range-picker {
@apply inline-flex flex-col gap-1;
}
.date-range-picker__trigger {
@apply inline-flex items-center justify-between;
}
.date-range-picker__trigger-indicator {
@apply text-muted;
}
.date-range-picker__range-separator {
@apply px-2 text-default;
}
.date-range-picker__popover {
@apply min-w-[var(--trigger-width)] p-0;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 命名,以便复写与自定义。
### CSS 类
DateRangePicker 在 `packages/styles/components/date-range-picker.css` 中使用以下类:
* `.date-range-picker` - 根包裹层。
* `.date-range-picker__trigger` - 打开弹出层的触发区域。
* `.date-range-picker__trigger-indicator` - 默认或自定义指示器插槽。
* `.date-range-picker__range-separator` - 开始与结束日期输入之间的分隔。
* `.date-range-picker__popover` - 弹出层内容包裹。
### 交互状态
DateRangePicker 支持 React Aria 的 data 属性与伪类状态:
* **展开**:触发器上的 `[data-open="true"]`。
* **禁用**:触发器上的 `[data-disabled="true"]` 或 `[aria-disabled="true"]`。
* **焦点可见**:触发器上的 `:focus-visible` 或 `[data-focus-visible="true"]`。
* **悬停**:触发器上的 `:hover` 或 `[data-hovered="true"]`。
## API 参考
### DateRangePicker Props
DateRangePicker 继承 React Aria [DateRangePicker](https://react-aria.adobe.com/DateRangePicker) 的全部 props。
| Prop | 类型 | 默认值 | 描述 |
| -------------- | ---------------------------------------------------------------------------------- | ------- | ---------------------- |
| `value` | `{ start: DateValue; end: DateValue } \| null` | - | 受控的选中日期范围值。 |
| `defaultValue` | `{ start: DateValue; end: DateValue } \| null` | - | 非受控模式下的默认范围。 |
| `onChange` | `(value: { start: DateValue; end: DateValue } \| null) => void` | - | 选中范围变化时调用。 |
| `isOpen` | `boolean` | - | 受控的弹出层展开状态。 |
| `defaultOpen` | `boolean` | `false` | 弹出层初始是否展开。 |
| `onOpenChange` | `(isOpen: boolean) => void` | - | 弹出层展开状态变化时调用。 |
| `isDisabled` | `boolean` | `false` | 禁用范围选择与触发器交互。 |
| `isInvalid` | `boolean` | - | 标记字段无效以呈现校验状态。 |
| `minValue` | `DateValue` | - | 可选的最小日期。 |
| `maxValue` | `DateValue` | - | 可选的最大日期。 |
| `startName` | `string` | - | HTML 表单提交时开始日期字段名。 |
| `endName` | `string` | - | HTML 表单提交时结束日期字段名。 |
| `children` | `ReactNode \| (values: DateRangePickerRenderProps) => ReactNode` | - | 组合内容或渲染函数。 |
| `render` | `DOMRenderFunction` | - | 通过自定义渲染函数覆盖默认的 DOM 元素。 |
### 组合部件
| 组件 | 描述 |
| ---------------------------------- | ------------------------------- |
| `DateRangePicker.Root` | 根日期范围选择器容器与状态持有者。 |
| `DateRangePicker.Trigger` | 触发按钮,通常放在 `DateField.Suffix` 内。 |
| `DateRangePicker.TriggerIndicator` | 带默认日历图标的指示器插槽。 |
| `DateRangePicker.RangeSeparator` | 开始与结束日期输入之间的分隔部件。 |
| `DateRangePicker.Popover` | 包裹 `RangeCalendar` 内容的弹出层。 |
### Related packages
* [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) — 各日期组件共用的日期类型(`CalendarDate`、`CalendarDateTime`、`ZonedDateTime`)与工具函数
* [`I18nProvider`](https://react-aria.adobe.com/I18nProvider) — 为子树覆盖语言环境
* [`useLocale`](https://react-aria.adobe.com/useLocale) — 读取当前语言环境与书写方向
# RangeCalendar 范围日历
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/range-calendar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(date-and-time)/range-calendar.mdx
> 基于 React Aria RangeCalendar 的可组合日期范围选择器,包含月份网格、导航与年份选择支持。
## 引入
```tsx
import { RangeCalendar } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
export function Basic() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### 组件结构
```tsx
import {RangeCalendar} from '@heroui/react';
export default () => (
{(day) => {day} }
{(date) => }
)
```
### 年份选择
`RangeCalendar.YearPickerTrigger`、`RangeCalendar.YearPickerGrid` 及其 body/cell 子组件提供一体化的年份导航模式。
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
export function YearPicker() {
return (
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### 默认值
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
import {parseDate} from "@internationalized/date";
export function DefaultValue() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### 受控
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Button, ButtonGroup, Description, RangeCalendar} from "@heroui/react";
import {
getLocalTimeZone,
parseDate,
startOfMonth,
startOfWeek,
today,
} from "@internationalized/date";
import {useState} from "react";
import {useLocale} from "react-aria-components";
type DateRange = {
start: DateValue;
end: DateValue;
};
export function Controlled() {
const [value, setValue] = useState(null);
const [focusedDate, setFocusedDate] = useState(parseDate("2025-12-25"));
const {locale} = useLocale();
return (
{
const start = today(getLocalTimeZone());
setFocusedDate(start);
}}
>
本周
{
const nextWeekStart = startOfWeek(today(getLocalTimeZone()).add({weeks: 1}), locale);
setFocusedDate(nextWeekStart);
}}
>
下周
{
const nextMonthStart = startOfMonth(today(getLocalTimeZone()).add({months: 1}));
setFocusedDate(nextMonthStart);
}}
>
下月
{(day) => {day} }
{(date) => }
已选区间: {value ? `${value.start.toString()} -> ${value.end.toString()}` : "(无)"}
{
const start = today(getLocalTimeZone());
setValue({end: start.add({days: 6}), start});
setFocusedDate(start);
}}
>
设为 1 周
{
const start = parseDate("2025-12-20");
setValue({end: parseDate("2025-12-31"), start});
setFocusedDate(start);
}}
>
设为节假日
setValue(null)}>
清空
);
}
```
### 最小与最大日期
```tsx
"use client";
import {Description, RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function MinMaxDates() {
const now = today(getLocalTimeZone());
const minDate = now;
const maxDate = now.add({months: 3});
return (
{(day) => {day} }
{(date) => }
请在今天与 {maxDate.toString()} 之间选择日期。
);
}
```
### 不可用日期
使用 `isDateUnavailable` 禁用周末、节假日或已被预订的日期等。
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Description, RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function UnavailableDates() {
const now = today(getLocalTimeZone());
const blockedRanges = [
[now.add({days: 2}), now.add({days: 5})],
[now.add({days: 12}), now.add({days: 13})],
] as const;
const isDateUnavailable = (date: DateValue) => {
return blockedRanges.some(([start, end]) => date.compare(start) >= 0 && date.compare(end) <= 0);
};
return (
{(day) => {day} }
{(date) => }
部分日期不可选
);
}
```
### 基于锚点的不可用日期
选择范围时,`isDateUnavailable` 的第二个参数 `anchorDate` 为用户选中的开始日期。可据此限制结束日期(例如仅允许开始日期前后 7 天)。
```tsx
"use client";
import type {CalendarDate, DateValue} from "@internationalized/date";
import {Description, RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function AnchorUnavailableDates() {
const now = today(getLocalTimeZone());
const isDateUnavailable = (date: DateValue, anchorDate: CalendarDate | null) => {
return anchorDate != null && Math.abs(date.compare(anchorDate)) > 7;
};
return (
{(day) => {day} }
{(date) => }
选择开始日期后,仅前后 7 天内的日期可选
);
}
```
### 固定周数
将 `weeksInMonth` 设为固定值(例如 `6`),可在月份切换时保持网格高度稳定。
```tsx
"use client";
import {Description, RangeCalendar} from "@heroui/react";
export function WeeksInMonth() {
return (
{(day) => {day} }
{(date) => }
每月固定显示 6 周,切换月份时避免布局跳动
);
}
```
### 周视图
设置 `visibleDuration={{ weeks: n }}` 可一次显示一个或多个周。翻页会按可见周范围前进。显示多周时可配合 `pageBehavior="single"` 每次仅移动一周。
```tsx
"use client";
import {Label, ListBox, RangeCalendar, Select} from "@heroui/react";
import {useState} from "react";
const weekOptions = [
{id: "1", name: "1 周"},
{id: "2", name: "2 周"},
{id: "3", name: "3 周"},
{id: "4", name: "4 周"},
{id: "5", name: "5 周"},
{id: "6", name: "6 周"},
{id: "8", name: "8 周"},
] as const;
export function WeekView() {
const [weeks, setWeeks] = useState(1);
return (
value && setWeeks(Number(value))}
>
可见周数
{weekOptions.map((option) => (
{option.name}
))}
{(day) => {day} }
{(date) => }
);
}
```
### 日视图
设置 `visibleDuration={{ days: n }}` 可显示连续多天的滚动窗口。翻页会按可见天数范围前进。显示多天时配合 `pageBehavior="single"` 可每次仅移动一天。
```tsx
"use client";
import {Label, ListBox, RangeCalendar, Select} from "@heroui/react";
import {useState} from "react";
const dayOptions = [
{id: "1", name: "1 天"},
{id: "5", name: "5 天"},
{id: "7", name: "7 天"},
{id: "8", name: "8 天"},
{id: "10", name: "10 天"},
{id: "14", name: "14 天"},
{id: "21", name: "21 天"},
] as const;
export function DayView() {
const [days, setDays] = useState(5);
return (
value && setDays(Number(value))}
>
可见天数
{dayOptions.map((option) => (
{option.name}
))}
{(day) => {day} }
{(date) => }
);
}
```
### 允许非连续范围
启用 `allowsNonContiguousRanges`,允许选择跨越不可用日期的范围。
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Description, RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function AllowsNonContiguousRanges() {
const now = today(getLocalTimeZone());
const blockedRanges = [
[now.add({days: 2}), now.add({days: 5})],
[now.add({days: 12}), now.add({days: 13})],
] as const;
const isDateUnavailable = (date: DateValue) => {
return blockedRanges.some(([start, end]) => date.compare(start) >= 0 && date.compare(end) <= 0);
};
return (
{(day) => {day} }
{(date) => }
允许跨不可选日期选择非连续区间
);
}
```
### 禁用
```tsx
"use client";
import {Description, RangeCalendar} from "@heroui/react";
export function Disabled() {
return (
{(day) => {day} }
{(date) => }
区间日历已禁用
);
}
```
### 只读
```tsx
"use client";
import {Description, RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function ReadOnly() {
return (
{(day) => {day} }
{(date) => }
区间日历为只读
);
}
```
### 无效
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Description, RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
import {useState} from "react";
type DateRange = {
start: DateValue;
end: DateValue;
};
export function Invalid() {
const now = today(getLocalTimeZone());
const [value, setValue] = useState({
end: now.add({days: 14}),
start: now.add({days: 6}),
});
const isInvalid = value.end.compare(value.start) > 7;
return (
{(day) => {day} }
{(date) => }
{isInvalid ? (
最长入住时间为 1 周
) : (
请选择最多 7 天的入住区间
)}
);
}
```
### 焦点日期
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Button, Description, RangeCalendar} from "@heroui/react";
import {parseDate} from "@internationalized/date";
import {useState} from "react";
export function FocusedValue() {
const [focusedDate, setFocusedDate] = useState(parseDate("2025-06-15"));
return (
{(day) => {day} }
{(date) => }
聚焦: {focusedDate.toString()}
setFocusedDate(parseDate("2025-01-01"))}
>
跳转到一月
setFocusedDate(parseDate("2025-06-15"))}
>
跳转到六月
setFocusedDate(parseDate("2025-12-25"))}
>
跳转到圣诞节
);
}
```
### 单元格指示器
你可以自定义 `RangeCalendar.Cell` 的子节点,并使用 `RangeCalendar.CellIndicator` 展示活动等元数据。
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, isToday} from "@internationalized/date";
const datesWithEvents = [3, 7, 12, 15, 21, 28];
export function WithIndicators() {
return (
{(day) => {day} }
{(date) => (
{({formattedDate}) => (
<>
{formattedDate}
{(isToday(date, getLocalTimeZone()) || datesWithEvents.includes(date.day)) && (
)}
>
)}
)}
);
}
```
### 多个月份
使用 `visibleDuration` 与 `offset` 渲染多个月份网格,适用于预订与规划场景。在各列头部为 `RangeCalendar.Heading` 设置 `offset`(例如 `offset={{ months: 1 }}`)以显示对应月份标题。
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
export function MultipleMonths() {
return (
{(day) => {day} }
{(date) => }
{(day) => {day} }
{(date) => }
);
}
```
### 国际化历法
默认情况下,RangeCalendar 按用户语言环境的历法显示日期。你可以使用 `I18nProvider` 包裹 RangeCalendar,并通过 [Unicode 历法语言扩展](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string) 覆盖。
下方示例展示印度历法系统:
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
import {I18nProvider} from "react-aria-components";
export function InternationalCalendar() {
return (
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
**说明:** `onChange` 事件始终返回与 `value` 或 `defaultValue` 相同历法系统中的日期(若未提供值则为公历),与界面展示的本地化格式无关。
### 实际场景示例
```tsx
"use client";
import type {DateValue} from "@internationalized/date";
import {Button, RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, isWeekend, today} from "@internationalized/date";
import {useState} from "react";
import {useLocale} from "react-aria-components";
type DateRange = {
start: DateValue;
end: DateValue;
};
export function BookingCalendar() {
const [selectedRange, setSelectedRange] = useState(null);
const {locale} = useLocale();
const blockedDates = [5, 6, 12, 13, 14, 20];
const isDateUnavailable = (date: DateValue) => {
return isWeekend(date, locale) || blockedDates.includes(date.day);
};
return (
{(day) => {day} }
{(date) => (
{({formattedDate, isUnavailable}) => (
<>
{formattedDate}
{!isUnavailable &&
!isWeekend(date, locale) &&
blockedDates.includes(date.day) && }
>
)}
)}
不可订日期
周末/不可用
{selectedRange ? (
预订 {selectedRange.start.toString()} → {selectedRange.end.toString()}
) : null}
);
}
```
## Related Components
* **Calendar**: Interactive month grid for selecting dates
* **DateField**: Date input field with labels, descriptions, and validation
* **DatePicker**: Composable date picker with date field trigger and calendar popover
## 样式
### 传入 Tailwind CSS 类
```tsx
import {RangeCalendar} from '@heroui/react';
function CustomRangeCalendar() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### 自定义组件类
```css
@layer components {
.range-calendar {
@apply w-80 rounded-2xl border border-border bg-surface p-3 shadow-sm;
}
.range-calendar__heading {
@apply text-sm font-semibold text-default;
}
.range-calendar__cell[data-selected="true"] .range-calendar__cell-button {
@apply bg-accent text-accent-foreground;
}
}
```
### CSS 类
RangeCalendar 在 `packages/styles/components/range-calendar.css` 与 `packages/styles/components/calendar-year-picker.css` 中使用以下类:
* `.range-calendar` - 根容器。
* `.range-calendar__header` - 含导航按钮与标题的头部行。
* `.range-calendar__heading` - 当前月份标签。
* `.range-calendar__nav-button` - 上一月/下一月导航控件。
* `.range-calendar__grid` - 主体日期网格。
* `.range-calendar__grid-header` - 星期标题行外层。
* `.range-calendar__grid-body` - 日期行外层。
* `.range-calendar__header-cell` - 星期标题单元格。
* `.range-calendar__cell` - 可交互日期单元格外层。
* `.range-calendar__cell-button` - 单元格内的可交互日期按钮。
* `.range-calendar__cell-indicator` - 日期单元格内的圆点指示器。
* `.calendar-year-picker__trigger` - 年份选择器切换按钮。
* `.calendar-year-picker__trigger-heading` - 年份选择触发器内的标题文案。
* `.calendar-year-picker__trigger-indicator` - 年份选择触发器内的指示图标。
* `.calendar-year-picker__year-grid` - 可选年份的覆盖网格。
* `.calendar-year-picker__year-cell` - 单个年份选项。
### 交互状态
RangeCalendar 同时支持伪类与 React Aria 的 data 属性:
* **已选中**:`[data-selected="true"]`
* **范围起点**:`[data-selection-start="true"]`
* **范围终点**:`[data-selection-end="true"]`
* **范围内**:`[data-selection-in-range="true"]`
* **今天**:`[data-today="true"]`
* **不可用**:`[data-unavailable="true"]`
* **跨月**:`[data-outside-month="true"]`
* **悬停**:`:hover` 或 `[data-hovered="true"]`
* **按下**:`:active` 或 `[data-pressed="true"]`
* **焦点可见**:`:focus-visible` 或 `[data-focus-visible="true"]`
* **禁用**:`:disabled` 或 `[data-disabled="true"]`
## API 参考
### RangeCalendar Props
RangeCalendar 继承 React Aria [RangeCalendar](https://react-spectrum.adobe.com/react-aria/RangeCalendar.html) 的全部 props。
| Prop | 类型 | 默认值 | 描述 |
| --------------------------- | ---------------------------------------------------------------- | ------------------ | ---------------------------------------------------------------------- |
| `value` | `RangeValue \| null` | - | 受控的选中范围。 |
| `defaultValue` | `RangeValue \| null` | - | 初始选中范围(非受控)。 |
| `onChange` | `(value: RangeValue) => void` | - | 选中变化时调用。 |
| `focusedValue` | `DateValue` | - | 受控的焦点日期。 |
| `onFocusChange` | `(value: DateValue) => void` | - | 焦点移动到其它日期时调用。 |
| `minValue` | `DateValue` | 历法感知的 `1900-01-01` | 可选的最早日期。 |
| `maxValue` | `DateValue` | 历法感知的 `2099-12-31` | 可选的最晚日期。 |
| `weeksInMonth` | `number` | - | 一个月的周数。该值会覆盖区域设置的默认值。 |
| `isDateUnavailable` | `(date: DateValue, anchorDate: CalendarDate \| null) => boolean` | - | 将日期标记为不可用。`anchorDate` 为当前范围选择中的首个日期。 |
| `firstDayOfWeek` | `'sun' \| 'mon' \| 'tue' \| 'wed' \| 'thu' \| 'fri' \| 'sat'` | - | 覆盖区域设置的一周起始日。 |
| `pageBehavior` | `'visible' \| 'single'` | `'visible'` | 翻页按可见范围或单步前进。 |
| `selectionAlignment` | `'start' \| 'center' \| 'end'` | `'center'` | 初始渲染时按选中项对齐可见范围。 |
| `allowsNonContiguousRanges` | `boolean` | `false` | 允许范围跨越不可用日期。 |
| `isDisabled` | `boolean` | `false` | 禁用交互与选择。 |
| `isReadOnly` | `boolean` | `false` | 内容只读,不可更改选中。 |
| `isInvalid` | `boolean` | `false` | 标记为无效以配合校验样式。 |
| `visibleDuration` | `{months?: number; weeks?: number; days?: number}` | `{months: 1}` | 可见时间范围。使用 `{ months: n }` 为月视图,`{ weeks: n }` 为周视图,`{ days: n }` 为日视图。 |
| `defaultYearPickerOpen` | `boolean` | `false` | 内置年份选择器的初始展开状态。 |
| `isYearPickerOpen` | `boolean` | - | 受控的年份选择器展开状态。 |
| `onYearPickerOpenChange` | `(isOpen: boolean) => void` | - | 年份选择器展开状态变化时调用。 |
### 组合部件
| 组件 | 描述 |
| ------------------------------------------ | --------------------------------------------------- |
| `RangeCalendar.Header` | 导航与标题的头部容器。 |
| `RangeCalendar.Heading` | 可见范围的格式化标题。支持 `offset`(多月份布局)与 `format`(月/年/日格式选项)。 |
| `RangeCalendar.NavButton` | 上一页/下一页导航(`slot="previous"` 或 `slot="next"`)。 |
| `RangeCalendar.Grid` | 单个月的日期网格(多月份布局支持 `offset`)。 |
| `RangeCalendar.GridHeader` | 星期标题容器。 |
| `RangeCalendar.GridBody` | 日期单元格主体容器。 |
| `RangeCalendar.HeaderCell` | 星期标签单元格。 |
| `RangeCalendar.Cell` | 单个日期单元格。 |
| `RangeCalendar.CellIndicator` | 用于自定义元数据的可选指示元素。 |
| `RangeCalendar.YearPickerTrigger` | 切换年份选择模式的触发器。 |
| `RangeCalendar.YearPickerTriggerHeading` | 年份选择触发器内的本地化标题内容。 |
| `RangeCalendar.YearPickerTriggerIndicator` | 年份选择触发器内的切换图标。 |
| `RangeCalendar.YearPickerGrid` | 年份选择覆盖网格容器。 |
| `RangeCalendar.YearPickerGridBody` | 年份网格单元格的 body 渲染器。 |
| `RangeCalendar.YearPickerCell` | 单个年份选项单元格。 |
### 年份选择器子组件
年份选择器子组件继承 React Aria [`useCalendarHeading`](https://react-aria.adobe.com/useCalendar#usecalendarheading) 与 [`useCalendarYearPicker`](https://react-aria.adobe.com/useCalendar#usecalendaryearpicker) 的格式化属性。
| 组件 | 属性 | 类型 | 默认值 | 描述 |
| ---------------------------------------- | -------------- | ---------------------- | ------------------- | ---------------------------------------------------------- |
| `RangeCalendar.YearPickerTriggerHeading` | `format` | `DateFormatterOptions` | - | 自定义月/年标题(如 `{month: 'short'}`)。 |
| `RangeCalendar.YearPickerTriggerHeading` | `offset` | `{months?: number}` | - | 相对聚焦日期偏移标题(多月布局)。 |
| `RangeCalendar.YearPickerGrid` | `format` | `DateFormatterOptions` | `{year: 'numeric'}` | 自定义年份单元格标签(纪元、历法系统等)。 |
| `RangeCalendar.YearPickerGrid` | `visibleYears` | `number` | min–max 跨度或 `20` | 滑动窗口中显示的年份数量。当同时设置 `minValue` 与 `maxValue` 时,默认为二者之间的完整范围。 |
### RangeCalendar.Cell Render Props
当 `RangeCalendar.Cell` 的 `children` 为函数时,可使用 React Aria 的渲染参数:
| Prop | 类型 | 描述 |
| ------------------ | --------- | ------------ |
| `formattedDate` | `string` | 单元格日期的本地化文案。 |
| `isSelected` | `boolean` | 该日期是否已选中。 |
| `isSelectionStart` | `boolean` | 是否为选中范围的起点。 |
| `isSelectionEnd` | `boolean` | 是否为选中范围的终点。 |
| `isUnavailable` | `boolean` | 该日期是否不可用。 |
| `isDisabled` | `boolean` | 单元格是否禁用。 |
| `isOutsideMonth` | `boolean` | 是否属于相邻月份。 |
支持的历法系统及其标识符完整列表见:
* [React Aria Calendar Implementations](https://react-aria.adobe.com/internationalized/date/Calendar#implementations)
* [React Aria International Calendars](https://react-aria.adobe.com/Calendar#international-calendars)
### Related packages
* [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) — 各日期组件共用的日期类型(`CalendarDate`、`CalendarDateTime`、`ZonedDateTime`)与工具函数
* [`I18nProvider`](https://react-aria.adobe.com/I18nProvider) — 为子树覆盖语言环境
* [`useLocale`](https://react-aria.adobe.com/useLocale) — 读取当前语言环境与书写方向
# TimeField 时间字段
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/time-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(date-and-time)/time-field.mdx
> 基于 React Aria TimeField 的时间输入字段,包含标签、说明与校验。
## 引入
```tsx
import { TimeField } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {Label, TimeField} from "@heroui/react";
export function Basic() {
return (
时间
{(segment) => }
);
}
```
### 组件结构
```tsx
import {TimeField, Label, Description, FieldError} from '@heroui/react';
export default () => (
{(segment) => }
)
```
> **TimeField** 将标签、时间输入、说明与错误信息组合为单个无障碍组件。
### 带描述
```tsx
"use client";
import {Description, Label, TimeField} from "@heroui/react";
export function WithDescription() {
return (
开始时间
{(segment) => }
输入开始时间
结束时间
{(segment) => }
输入结束时间
);
}
```
### 必填字段
```tsx
"use client";
import {Description, Label, TimeField} from "@heroui/react";
export function Required() {
return (
时间
{(segment) => }
预约时间
{(segment) => }
必填项
);
}
```
### 校验
配合 `FieldError`,使用 `isInvalid` 展示校验信息。
```tsx
"use client";
import {FieldError, Label, TimeField} from "@heroui/react";
export function Invalid() {
return (
时间
{(segment) => }
请输入有效时间
时间
{(segment) => }
时间须在工作时间内
);
}
```
### 带校验
TimeField 支持使用 `minValue`、`maxValue` 及自定义校验逻辑。
```tsx
"use client";
import type {Time} from "@internationalized/date";
import {Description, FieldError, Label, TimeField} from "@heroui/react";
import {parseTime} from "@internationalized/date";
import {useState} from "react";
export function WithValidation() {
const [value, setValue] = useState(null);
const minTime = parseTime("09:00");
const maxTime = parseTime("17:00");
const isInvalid = value !== null && (value.compare(minTime) < 0 || value.compare(maxTime) > 0);
return (
时间
{(segment) => }
{isInvalid ? (
时间须在上午 9:00 至下午 5:00 之间
) : (
输入上午 9:00 至下午 5:00 之间的时间
)}
);
}
```
### 受控
通过控制 `value` 与其它组件或状态管理同步。
```tsx
"use client";
import type {TimeValue} from "@heroui/react";
import {Button, Description, Label, TimeField} from "@heroui/react";
import {Time, getLocalTimeZone, now} from "@internationalized/date";
import {useState} from "react";
export function Controlled() {
const [value, setValue] = useState(null);
return (
时间
{(segment) => }
当前值:{value ? value.toString() : "(空)"}
{
const currentTime = now(getLocalTimeZone());
setValue(new Time(currentTime.hour, currentTime.minute, currentTime.second));
}}
>
设为当前时间
setValue(null)}>
清空
);
}
```
### 禁用状态
```tsx
"use client";
import {Description, Label, TimeField} from "@heroui/react";
import {Time, getLocalTimeZone, now} from "@internationalized/date";
export function Disabled() {
const currentTime = now(getLocalTimeZone());
const timeValue = new Time(currentTime.hour, currentTime.minute, currentTime.second);
return (
时间
{(segment) => }
此时间字段已禁用
时间
{(segment) => }
此时间字段已禁用
);
}
```
### 带图标
通过前缀或后缀图标增强时间输入。
```tsx
"use client";
import {Clock} from "@gravity-ui/icons";
import {Label, TimeField} from "@heroui/react";
export function WithPrefixIcon() {
return (
时间
{(segment) => }
);
}
```
```tsx
"use client";
import {Clock} from "@gravity-ui/icons";
import {Label, TimeField} from "@heroui/react";
export function WithSuffixIcon() {
return (
时间
{(segment) => }
);
}
```
```tsx
"use client";
import {ChevronDown, Clock} from "@gravity-ui/icons";
import {Description, Label, TimeField} from "@heroui/react";
export function WithPrefixAndSuffix() {
return (
时间
{(segment) => }
输入时间
);
}
```
### 全宽
```tsx
"use client";
import {ChevronDown, Clock} from "@gravity-ui/icons";
import {Label, TimeField} from "@heroui/react";
export function FullWidth() {
return (
时间
{(segment) => }
时间
{(segment) => }
);
}
```
### 在 Surface 中
在 [Surface](/docs/components/surface) 内使用时,请在 `TimeField.Group` 上使用 `variant="secondary"`,以应用适合表面背景的低强调变体。
```tsx
"use client";
import {Clock} from "@gravity-ui/icons";
import {Description, Label, Surface, TimeField} from "@heroui/react";
export function OnSurface() {
return (
时间
{(segment) => }
输入时间
预约时间
{(segment) => }
输入预约时间
);
}
```
### 表单示例
包含校验与提交的完整表单示例。
```tsx
"use client";
import type {Time} from "@internationalized/date";
import {Clock} from "@gravity-ui/icons";
import {Button, Description, FieldError, Form, Label, TimeField} from "@heroui/react";
import {parseTime} from "@internationalized/date";
import {useState} from "react";
export function FormExample() {
const [value, setValue] = useState(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const minTime = parseTime("09:00");
const maxTime = parseTime("17:00");
const isInvalid = value !== null && (value.compare(minTime) < 0 || value.compare(maxTime) > 0);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!value || isInvalid) {
return;
}
setIsSubmitting(true);
// Simulate API call
setTimeout(() => {
console.log("Time submitted:", {time: value});
setValue(null);
setIsSubmitting(false);
}, 1500);
};
return (
预约时间
{(segment) => }
{isInvalid ? (
时间须在上午 9:00 至下午 5:00 之间
) : (
输入上午 9:00 至下午 5:00 之间的时间
)}
{isSubmitting ? "提交中…" : "提交"}
);
}
```
## Related Components
* **Label**: Accessible label for form controls
* **FieldError**: Inline validation messages for form fields
* **Description**: Helper text for form fields
### 自定义渲染函数
```tsx
"use client";
import {Label, TimeField} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
时间
{(segment) => }
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import {TimeField, Label, Description} from '@heroui/react';
function CustomTimeField() {
return (
Appointment time
{(segment) => }
Select a time for your appointment.
);
}
```
### 自定义组件类
TimeField 的默认样式很轻量。覆盖 `.time-field` 类即可自定义容器样式。
```css
@layer components {
.time-field {
@apply flex flex-col gap-1;
&[data-invalid="true"],
&[aria-invalid="true"] {
[data-slot="description"] {
@apply hidden;
}
}
[data-slot="label"] {
@apply w-fit;
}
[data-slot="description"] {
@apply px-1;
}
}
}
```
### CSS 类
* `.time-field` – 轻量样式的根容器(`flex flex-col gap-1`)
> **说明:** 子组件([Label](/docs/components/label)、[Description](/docs/components/description)、[FieldError](/docs/components/field-error))拥有各自的 CSS 类与样式。自定义方式请参阅对应文档。`TimeField.Group` 的样式见下文 API 参考。
### 交互状态
TimeField 会根据状态自动设置以下 data 属性:
* **无效**:`[data-invalid="true"]` 或 `[aria-invalid="true"]` – 无效时自动隐藏 description 插槽
* **必填**:`[data-required="true"]` – 当 `isRequired` 为 true 时添加
* **禁用**:`[data-disabled="true"]` – 当 `isDisabled` 为 true 时添加
* **焦点在内**:`[data-focus-within="true"]` – 任一子输入聚焦时添加
## API 参考
### TimeField Props
TimeField 继承 React Aria [TimeField](https://react-aria.adobe.com/TimeField) 的全部 props。
#### Base Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------------ | ------- | ---------------------------------- |
| `children` | `React.ReactNode \| (values: TimeFieldRenderProps) => React.ReactNode` | - | 子组件(Label、TimeField.Group 等)或渲染函数。 |
| `className` | `string \| (values: TimeFieldRenderProps) => string` | - | 用于样式的 CSS 类,支持渲染 prop。 |
| `style` | `React.CSSProperties \| (values: TimeFieldRenderProps) => React.CSSProperties` | - | 内联样式,支持渲染 prop。 |
| `fullWidth` | `boolean` | `false` | 时间字段是否占满容器宽度。 |
| `id` | `string` | - | 元素的唯一 id。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
#### Value Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | ------------------------------------ | --- | ----------------------------------------------------------------------------------------------- |
| `value` | `TimeValue \| null` | - | 当前值(受控)。类型见 [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/)。 |
| `defaultValue` | `TimeValue \| null` | - | 默认值(非受控)。类型见 [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/)。 |
| `onChange` | `(value: TimeValue \| null) => void` | - | 值变化时触发的事件处理函数。 |
| `placeholderValue` | `TimeValue \| null` | - | 影响占位符格式的占位时间;默认随小时制为 12:00 AM 或 00:00。 |
#### Validation Props
| Prop | 类型 | 默认值 | 描述 |
| -------------------- | -------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------ |
| `isRequired` | `boolean` | `false` | 是否在提交表单前要求用户输入。 |
| `isInvalid` | `boolean` | - | 值是否无效。 |
| `minValue` | `TimeValue \| null` | - | 用户可选择最早时间。类型见 [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/)。 |
| `maxValue` | `TimeValue \| null` | - | 用户可选择最晚时间。类型见 [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/)。 |
| `validate` | `(value: TimeValue) => ValidationError \| true \| null \| undefined` | - | 自定义校验函数。 |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | 使用原生 HTML 表单校验还是 ARIA 属性。 |
#### Format Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------------- | -------------------------------- | ---------- | ---------------------------- |
| `granularity` | `'hour' \| 'minute' \| 'second'` | `'minute'` | 时间选择器显示的最小单位。 |
| `hourCycle` | `12 \| 24` | - | 以 12 或 24 小时制显示时间;默认由语言环境决定。 |
| `hideTimeZone` | `boolean` | `false` | 是否隐藏时区缩写。 |
| `shouldForceLeadingZeros` | `boolean` | - | 是否始终为小时字段显示前导零;默认由语言环境决定。 |
#### State Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | --------- | --- | ----------- |
| `isDisabled` | `boolean` | - | 是否禁用输入。 |
| `isReadOnly` | `boolean` | - | 是否可选中但不可修改。 |
#### Form Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | --------- | --- | ----------------------------------------- |
| `name` | `string` | - | 输入元素的 name,用于 HTML 表单提交;以 ISO 8601 字符串提交。 |
| `autoFocus` | `boolean` | - | 是否在渲染后自动聚焦该元素。 |
#### Accessibility Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | -------- | --- | ------------- |
| `aria-label` | `string` | - | 无可见标签时的无障碍标签。 |
| `aria-labelledby` | `string` | - | 标注该字段的元素 id。 |
| `aria-describedby` | `string` | - | 描述该字段的元素 id。 |
| `aria-details` | `string` | - | 包含额外详情的元素 id。 |
### 组合组件
TimeField 与以下独立组件配合使用,请分别导入并直接使用:
* **Label** – 来自 `@heroui/react` 的字段标签
* **TimeField.Group** – 时间输入分组(详见下文)
* **TimeField.Input** – 来自 `@heroui/react` 的分段位编辑输入
* **TimeField.Segment** – 单个时间段位(时、分、秒等)
* **TimeField.Prefix** / **TimeField.Suffix** – 输入组的前缀与后缀插槽
* **Description** – 来自 `@heroui/react` 的辅助说明
* **FieldError** – 来自 `@heroui/react` 的校验错误信息
这些组件各自有独立的 props API。在 TimeField 中直接组合使用:
```tsx
import {parseTime} from '@internationalized/date';
import {TimeField, Label, Description, FieldError} from '@heroui/react';
Appointment Time
{(segment) => }
Select a time between 9:00 AM and 5:00 PM.
Please select a valid time.
```
### TimeValue 类型
TimeField 使用 [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) 中的类型:
* `Time` – 仅时间(时、分、秒)
* `CalendarDateTime` – 含日期与时间、不含时区(TimeField 仅展示时间部分)
* `ZonedDateTime` – 含日期、时间与时区(TimeField 仅展示时间部分)
示例:
```tsx
import {parseTime, Time, getLocalTimeZone, now} from '@internationalized/date';
// Parse from string
const time = parseTime('14:30');
// Create from current time
const currentTime = now(getLocalTimeZone());
const timeValue = new Time(currentTime.hour, currentTime.minute, currentTime.second);
// Use in TimeField
{/* ... */}
```
> **说明:** TimeField 使用 [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) 进行时间处理、解析与类型定义。更多类型与函数见 [Internationalized Date 文档](https://react-aria.adobe.com/internationalized/date/)。
### TimeFieldRenderProps
对 `className`、`style` 或 `children` 使用渲染 prop 时,可使用以下值:
| Prop | 类型 | 描述 |
| ---------------- | --------- | ------------- |
| `isDisabled` | `boolean` | 字段是否禁用。 |
| `isInvalid` | `boolean` | 字段当前是否无效。 |
| `isReadOnly` | `boolean` | 字段是否只读。 |
| `isRequired` | `boolean` | 字段是否必填。 |
| `isFocused` | `boolean` | 字段是否聚焦。 |
| `isFocusWithin` | `boolean` | 是否有子元素聚焦。 |
| `isFocusVisible` | `boolean` | 焦点是否可见(键盘导航)。 |
### TimeField.Group Props
TimeField.Group 继承 React Aria `Group` 的全部 props,并额外支持:
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------------------------- | ----------- | ---------------------------------------------------------- |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 视觉变体。`primary` 为默认带阴影样式;`secondary` 为低强调、无阴影,适合用于 Surface。 |
### TimeField.Input Props
TimeField.Input 继承 React Aria `DateInput` 的全部 props,并额外支持:
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------------------------- | ----------- | ------------------------------------------------------------- |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 输入的视觉变体。`primary` 为默认带阴影样式;`secondary` 为低强调、无阴影,适合用于 Surface。 |
`TimeField.Input` 接受渲染函数作为子节点,函数接收日期段位;每个段位表示时间的一部分(时、分、秒等)。
### TimeField.Segment Props
TimeField.Segment 继承 React Aria `DateSegment` 的全部 props:
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------- | --- | ------------------------------------------ |
| `segment` | `DateSegment` | - | 来自 TimeField.Input 渲染函数的 `DateSegment` 对象。 |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
### TimeField.Prefix Props
TimeField.Prefix 接受标准 HTML `div` 属性:
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------------ |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 前缀插槽中要显示的内容。 |
### TimeField.Suffix Props
TimeField.Suffix 接受标准 HTML `div` 属性:
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------------ |
| `className` | `string` | - | 与组件样式合并的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 后缀插槽中要显示的内容。 |
## TimeField.Group 样式
### 自定义组件类
基础类作用于所有实例,可通过 `@layer components` 一次性覆盖。
```css
@layer components {
.date-input-group {
@apply inline-flex h-9 items-center overflow-hidden rounded-field border bg-field text-sm text-field-foreground shadow-field outline-none;
&:hover,
&[data-hovered="true"] {
@apply bg-field-hover;
}
&[data-focus-within="true"],
&:focus-within {
@apply status-focused-field;
}
&[data-invalid="true"] {
@apply status-invalid-field;
}
&[data-disabled="true"],
&[aria-disabled="true"] {
@apply status-disabled;
}
}
.date-input-group__input {
@apply flex flex-1 items-center gap-px rounded-none border-0 bg-transparent px-3 py-2 shadow-none outline-none;
}
.date-input-group__segment {
@apply inline-block rounded-md px-0.5 text-end tabular-nums outline-none;
&:focus,
&[data-focused="true"] {
@apply bg-accent-soft text-accent-soft-foreground;
}
}
.date-input-group__prefix,
.date-input-group__suffix {
@apply pointer-events-none shrink-0 text-field-placeholder flex items-center;
}
}
```
### TimeField.Group CSS 类
* `.date-input-group` – 根容器样式
* `.date-input-group__input` – 输入包裹层样式
* `.date-input-group__segment` – 单个时间段位样式
* `.date-input-group__prefix` – 前缀元素样式
* `.date-input-group__suffix` – 后缀元素样式
### TimeField.Group 交互状态
* **悬停**:`:hover` 或 `[data-hovered="true"]`
* **焦点在内**:`[data-focus-within="true"]` 或 `:focus-within`
* **无效**:`[data-invalid="true"]`(同时与 `aria-invalid` 同步)
* **禁用**:`[data-disabled="true"]` 或 `[aria-disabled="true"]`
* **段位聚焦**:段位上的 `:focus` 或 `[data-focused="true"]`
* **段位占位符**:段位上的 `[data-placeholder="true"]`
# Alert 警告
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/alert
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(feedback)/alert.mdx
> 向用户展示重要消息与通知,并提供状态指示。
## 引入
```tsx
import { Alert } from '@heroui/react';
```
### 用法
```tsx
import {Alert, Button, CloseButton, Spinner} from "@heroui/react";
import React from "react";
export function Basic() {
return (
{/* 默认 — 一般信息 */}
新功能已上线
查看我们的最新更新,包括深色模式支持与改进的无障碍体验。
{/* 强调 — 重要信息含操作 */}
有可用更新
应用有新版本可用。请刷新页面以获取最新功能与问题修复。
刷新
刷新
{/* 危险 — 错误与排查步骤 */}
无法连接到服务器
当前遇到连接问题,请尝试以下操作:
重试
重试
{/* 无描述 */}
个人资料已更新
{/* 自定义指示器 — 加载中 */}
正在处理你的请求
正在同步你的数据,请稍候,这可能需要一点时间。
{/* 无关闭按钮 */}
计划维护
我们将于 UTC 时间 3 月 15 日(周日)凌晨 2:00 至上午 6:00
进行计划维护,期间服务将暂时不可用。
);
}
```
### 组件结构
导入 Alert 组件后,可通过点语法访问所有子部分。
```tsx
import { Alert } from '@heroui/react';
export default () => (
)
```
## Related Components
* **CloseButton**: Button for dismissing overlays
* **Button**: Allows a user to perform an action
* **Spinner**: Loading indicator
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Alert } from "@heroui/react";
function CustomAlert() {
return (
Custom Alert
This alert has custom styling applied
);
}
```
### 自定义组件类
要自定义 Alert 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.alert {
@apply rounded-2xl shadow-lg;
}
.alert__title {
@apply font-bold text-lg;
}
.alert--danger {
@apply border-l-4 border-red-600;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Alert 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/alert.css)):
#### 基础类
* `.alert` — Alert 根容器
* `.alert__indicator` — 图标/指示器容器
* `.alert__content` — 包裹标题与说明的内容容器
* `.alert__title` — Alert 标题文本
* `.alert__description` — Alert 说明文本
#### 状态变体类
* `.alert--default` — 默认灰色状态
* `.alert--accent` — 强调蓝色状态
* `.alert--success` — 成功绿色状态
* `.alert--warning` — 警告黄/橙色状态
* `.alert--danger` — 危险红色状态
### 交互状态
Alert 主要用于信息展示,基础组件本身通常没有交互状态;但它可以包含按钮或关闭按钮等交互元素。
## API 参考
### Alert Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------- | ----------- | ----------- |
| `status` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"default"` | Alert 的视觉状态 |
| `className` | `string` | - | 附加的 CSS 类 |
| `children` | `ReactNode` | - | Alert 内容 |
### Alert.Indicator Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ----------------- |
| `className` | `string` | - | 附加的 CSS 类 |
| `children` | `ReactNode` | - | 自定义指示图标(默认显示状态图标) |
### Alert.Content Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | --------------------------- |
| `className` | `string` | - | 附加的 CSS 类 |
| `children` | `ReactNode` | - | 内容(通常为 Title 与 Description) |
### Alert.Title Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ---------- |
| `className` | `string` | - | 附加的 CSS 类 |
| `children` | `ReactNode` | - | Alert 标题文本 |
### Alert.Description Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ---------- |
| `className` | `string` | - | 附加的 CSS 类 |
| `children` | `ReactNode` | - | Alert 说明文本 |
# Meter 计量条
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/meter
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(feedback)/meter.mdx
> Meter 表示已知范围内的数量,或一个比例值。
## 引入
```tsx
import { Meter, Label } from '@heroui/react';
```
### 用法
```tsx
import {Label, Meter} from "@heroui/react";
export function Basic() {
return (
存储空间
);
}
```
### 组件结构
```tsx
import { Meter, Label } from '@heroui/react';
export default () => (
Storage
);
```
### 尺寸
```tsx
import {Label, Meter} from "@heroui/react";
const SIZE_LABELS = {
lg: "大",
md: "中",
sm: "小",
} as const;
export function Sizes() {
return (
{SIZE_LABELS.sm}
{SIZE_LABELS.md}
{SIZE_LABELS.lg}
);
}
```
### 颜色
```tsx
import {Label, Meter} from "@heroui/react";
const colors = ["default", "accent", "success", "warning", "danger"] as const;
const COLOR_LABELS: Record<(typeof colors)[number], string> = {
accent: "强调",
danger: "危险",
default: "默认",
success: "成功",
warning: "警告",
};
export function Colors() {
return (
{colors.map((color) => (
{COLOR_LABELS[color]}
))}
);
}
```
### 自定义取值范围与格式
使用 `minValue`、`maxValue` 与 `formatOptions` 自定义取值范围与展示格式。
```tsx
import {Label, Meter} from "@heroui/react";
export function CustomValue() {
return (
收入
);
}
```
### 无可见标签
当不需要可见标签时,请使用 `aria-label` 以保证无障碍。
```tsx
import {Meter} from "@heroui/react";
export function WithoutLabel() {
return (
);
}
```
## 样式
### 传入 Tailwind CSS 类
你可以为 Meter 的各个部分分别自定义样式:
```tsx
import { Meter, Label } from '@heroui/react';
function CustomMeter() {
return (
Storage
);
}
```
### 自定义组件类
要自定义 Meter 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.meter {
@apply w-full gap-2;
}
.meter__track {
@apply h-3 rounded-full;
}
.meter__fill {
@apply rounded-full;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Meter 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/meter.css)):
#### 基础与元素类
* `.meter` — 基础容器(grid 布局)
* `.meter__output` — 数值文本展示
* `.meter__track` — 轨道背景
* `.meter__fill` — 轨道已填充部分
#### 尺寸类
* `.meter--sm` — 小尺寸变体(更细的轨道)
* `.meter--md` — 中等尺寸变体(默认)
* `.meter--lg` — 大尺寸变体(更粗的轨道)
#### 颜色类
* `.meter--default` — 默认颜色变体
* `.meter--accent` — 强调色变体
* `.meter--success` — 成功色变体
* `.meter--warning` — 警告色变体
* `.meter--danger` — 危险色变体
## API 参考
### Meter Props
继承自 [React Aria Meter](https://react-spectrum.adobe.com/react-aria/Meter.html)。
| Prop | 类型 | 默认值 | 描述 |
| --------------- | ------------------------------------------------------------- | -------------------- | ---------- |
| `value` | `number` | `0` | 当前值 |
| `minValue` | `number` | `0` | 最小值 |
| `maxValue` | `number` | `100` | 最大值 |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Meter 轨道尺寸 |
| `color` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"accent"` | 填充条颜色 |
| `formatOptions` | `Intl.NumberFormatOptions` | `{style: 'percent'}` | 数值展示的格式化选项 |
| `valueLabel` | `ReactNode` | - | 自定义数值标签内容 |
| `children` | `ReactNode \| (values: MeterRenderProps) => ReactNode` | - | 内容或渲染 prop |
### MeterRenderProps
使用渲染 prop 模式时,会提供以下值:
| Prop | 类型 | 描述 |
| ------------ | -------- | ---------------- |
| `percentage` | `number` | Meter 百分比(0–100) |
| `valueText` | `string` | 格式化后的数值文本 |
# ProgressBar 进度条
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/progress-bar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(feedback)/progress-bar.mdx
> 进度条用于展示某项操作随时间变化的确定或不确定进度。
## 引入
```tsx
import { ProgressBar, Label } from '@heroui/react';
```
### 用法
```tsx
import {Label, ProgressBar} from "@heroui/react";
export function Basic() {
return (
加载中
);
}
```
### 组件结构
```tsx
import { ProgressBar, Label } from '@heroui/react';
export default () => (
Loading
);
```
### 尺寸
```tsx
import {Label, ProgressBar} from "@heroui/react";
const SIZE_LABELS = {
lg: "大",
md: "中",
sm: "小",
} as const;
export function Sizes() {
return (
{SIZE_LABELS.sm}
{SIZE_LABELS.md}
{SIZE_LABELS.lg}
);
}
```
### 颜色
```tsx
import {Label, ProgressBar} from "@heroui/react";
const colors = ["default", "accent", "success", "warning", "danger"] as const;
const COLOR_LABELS: Record<(typeof colors)[number], string> = {
accent: "强调",
danger: "危险",
default: "默认",
success: "成功",
warning: "警告",
};
export function Colors() {
return (
{colors.map((color) => (
{COLOR_LABELS[color]}
))}
);
}
```
### 不确定进度
在无法确定具体进度时,使用 `isIndeterminate`。
```tsx
import {Label, ProgressBar} from "@heroui/react";
export function Indeterminate() {
return (
加载中…
);
}
```
### 自定义数值范围
使用 `minValue`、`maxValue` 与 `formatOptions` 自定义取值范围与展示格式。
```tsx
"use client";
import {Label, ListBox, NumberField, ProgressBar, Select, Separator} from "@heroui/react";
import {useState} from "react";
const formatStyleOptions: {label: string; value: string}[] = [
{label: "货币", value: "currency"},
{label: "百分比", value: "percent"},
{label: "小数", value: "decimal"},
{label: "单位", value: "unit"},
];
const formatOptionsMap: Record = {
currency: {currency: "USD", style: "currency"},
decimal: {style: "decimal"},
percent: {style: "percent"},
unit: {style: "unit", unit: "mile"},
};
export function CustomValue() {
const [value, setValue] = useState(750);
const [minValue, setMinValue] = useState(0);
const [maxValue, setMaxValue] = useState(1000);
const [format, setFormat] = useState("percent");
return (
选项
setValue(v)}
>
值
{
setMinValue(v);
if (value < v) setValue(v);
}}
>
最小值
{
setMaxValue(v);
if (value > v) setValue(v);
}}
>
最大值
setFormat(key as string)}>
格式
{formatStyleOptions.map((option) => (
{option.label}
))}
);
}
```
### 无可见标签
不需要可见标签时,请使用 `aria-label` 保证无障碍。
```tsx
import {ProgressBar} from "@heroui/react";
export function WithoutLabel() {
return (
);
}
```
## 样式
### 传入 Tailwind CSS 类
你可以为 ProgressBar 的各个部分单独添加类名:
```tsx
import { ProgressBar, Label } from '@heroui/react';
function CustomProgressBar() {
return (
Loading
);
}
```
### 自定义组件类
要自定义 ProgressBar 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.progress-bar {
@apply w-full gap-2;
}
.progress-bar__track {
@apply h-3 rounded-full;
}
.progress-bar__fill {
@apply rounded-full;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
ProgressBar 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/progress-bar.css)):
#### 基础与元素类
* `.progress-bar` - 基础容器(网格布局)
* `.progress-bar__output` - 数值文本展示
* `.progress-bar__track` - 轨道背景
* `.progress-bar__fill` - 轨道上已填充部分
#### 尺寸类
* `.progress-bar--sm` - 小尺寸变体(更细的轨道)
* `.progress-bar--md` - 中等尺寸变体(默认)
* `.progress-bar--lg` - 大尺寸变体(更粗的轨道)
#### 颜色类
* `.progress-bar--default` - 默认颜色变体
* `.progress-bar--accent` - 强调色变体
* `.progress-bar--success` - 成功色变体
* `.progress-bar--warning` - 警告色变体
* `.progress-bar--danger` - 危险色变体
## API 参考
### ProgressBar Props
继承自 [React Aria ProgressBar](https://react-spectrum.adobe.com/react-aria/ProgressBar.html)。
| Prop | 类型 | 默认值 | 描述 |
| ----------------- | ------------------------------------------------------------- | -------------------- | ---------- |
| `value` | `number` | `0` | 当前值 |
| `minValue` | `number` | `0` | 最小值 |
| `maxValue` | `number` | `100` | 最大值 |
| `isIndeterminate` | `boolean` | `false` | 是否为不确定进度 |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | 进度轨道尺寸 |
| `color` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"accent"` | 填充条颜色 |
| `formatOptions` | `Intl.NumberFormatOptions` | `{style: 'percent'}` | 数值展示的数字格式 |
| `valueLabel` | `ReactNode` | - | 自定义数值标签内容 |
| `children` | `ReactNode \| (values: ProgressBarRenderProps) => ReactNode` | - | 内容或渲染 prop |
### ProgressBarRenderProps
使用渲染 prop 模式时,会提供以下值:
| Prop | 类型 | 描述 |
| ----------------- | --------- | ------------ |
| `percentage` | `number` | 进度百分比(0–100) |
| `valueText` | `string` | 格式化后的数值文本 |
| `isIndeterminate` | `boolean` | 是否为不确定进度 |
# ProgressCircle 环形进度条
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/progress-circle
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(feedback)/progress-circle.mdx
> 环形进度指示器,用于展示确定或不确定的进度。
## 引入
```tsx
import { ProgressCircle } from '@heroui/react';
```
### 用法
```tsx
import {ProgressCircle} from "@heroui/react";
export function Basic() {
return (
);
}
```
### 组件结构
```tsx
import { ProgressCircle } from '@heroui/react';
export default () => (
);
```
### 尺寸
```tsx
import {ProgressCircle} from "@heroui/react";
const SIZE_LABELS = {
lg: "大",
md: "中",
sm: "小",
} as const;
export function Sizes() {
return (
);
}
```
### 颜色
```tsx
import {ProgressCircle} from "@heroui/react";
const colors = ["default", "accent", "success", "warning", "danger"] as const;
const COLOR_LABELS: Record<(typeof colors)[number], string> = {
accent: "强调",
danger: "危险",
default: "默认",
success: "成功",
warning: "警告",
};
export function Colors() {
return (
{colors.map((color) => (
))}
);
}
```
### 不确定进度
在无法确定具体进度时,使用 `isIndeterminate`。
```tsx
import {ProgressCircle} from "@heroui/react";
export function Indeterminate() {
return (
);
}
```
### 带标签
```tsx
import {Label, ProgressCircle} from "@heroui/react";
export function WithLabel() {
return (
);
}
```
### 自定义 SVG 属性
由于每个部分都是可组合组件,你可以直接覆盖 `strokeWidth`、`r`、`cx`、`cy`、`viewBox` 等 SVG 属性。
```tsx
import {ProgressCircle} from "@heroui/react";
export function CustomSvg() {
return (
);
}
```
## 样式
### 传入 Tailwind CSS 类
你可以分别自定义 ProgressCircle 的各个部分:
```tsx
import { ProgressCircle } from '@heroui/react';
function CustomProgressCircle() {
return (
);
}
```
### 自定义组件类
若要自定义 ProgressCircle 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.progress-circle {
@apply inline-flex;
}
.progress-circle__track {
@apply size-12;
}
.progress-circle__fill-circle {
stroke: purple;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
ProgressCircle 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/progress-circle.css)):
#### 基础与元素类
* `.progress-circle` - 基础容器
* `.progress-circle__track` - SVG 元素
* `.progress-circle__track-circle` - 背景圆环
* `.progress-circle__fill-circle` - 进度弧
#### 尺寸类
* `.progress-circle--sm` - 小尺寸
* `.progress-circle--md` - 中等尺寸(默认)
* `.progress-circle--lg` - 大尺寸
#### 颜色类
* `.progress-circle--default` - 默认颜色
* `.progress-circle--accent` - 强调色
* `.progress-circle--success` - 成功色
* `.progress-circle--warning` - 警告色
* `.progress-circle--danger` - 危险色
## API 参考
### ProgressCircle Props
继承自 [React Aria ProgressBar](https://react-spectrum.adobe.com/react-aria/ProgressBar.html)。
| Prop | 类型 | 默认值 | 描述 |
| ----------------- | ------------------------------------------------------------- | -------------------- | ---------- |
| `value` | `number` | `0` | 当前值 |
| `minValue` | `number` | `0` | 最小值 |
| `maxValue` | `number` | `100` | 最大值 |
| `isIndeterminate` | `boolean` | `false` | 是否为不确定进度 |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | 圆环尺寸 |
| `color` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"accent"` | 进度弧颜色 |
| `formatOptions` | `Intl.NumberFormatOptions` | `{style: 'percent'}` | 数值展示格式 |
| `children` | `ReactNode \| (values: ProgressBarRenderProps) => ReactNode` | - | 内容或渲染 prop |
### ProgressBarRenderProps
使用渲染 prop 模式时,会提供以下值:
| Prop | 类型 | 描述 |
| ----------------- | --------- | ------------ |
| `percentage` | `number` | 进度百分比(0–100) |
| `valueText` | `string` | 格式化后的数值文案 |
| `isIndeterminate` | `boolean` | 是否为不确定进度 |
# Skeleton 骨架屏
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/skeleton
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(feedback)/skeleton.mdx
> Skeleton 用于展示加载状态,并预览组件的预期形状。
## 引入
```tsx
import { Skeleton } from '@heroui/react';
```
### 用法
```tsx
import {Skeleton} from "@heroui/react";
export function Basic() {
return (
);
}
```
### 文本内容
```tsx
import {Skeleton} from "@heroui/react";
export function TextContent() {
return (
);
}
```
### 用户资料
```tsx
import {Skeleton} from "@heroui/react";
export function UserProfile() {
return (
);
}
```
### 列表项
```tsx
import {Skeleton} from "@heroui/react";
export function List() {
return (
{Array.from({length: 3}).map((_, index) => (
))}
);
}
```
### 动画类型
```tsx
import {Skeleton} from "@heroui/react";
export function AnimationTypes() {
return (
);
}
```
### 网格
```tsx
import {Skeleton} from "@heroui/react";
export function Grid() {
return (
);
}
```
### 单次闪烁
一种同步的闪烁效果,会一次性扫过所有骨架元素。请在父容器上应用 `skeleton--shimmer` 类,并将子级 Skeleton 的 `animationType` 设为 `"none"`。
```tsx
import {Skeleton} from "@heroui/react";
export function SingleShimmer() {
return (
);
}
```
## Related Components
* **Card**: Content container with header, body, and footer
* **Avatar**: Display user profile images
## 样式
### 全局动画配置
你可以通过在应用中定义 `--skeleton-animation` CSS 变量,为所有 Skeleton 设置默认动画类型:
```css
/* In your global CSS file */
:root {
/* Possible values: shimmer, pulse, none */
--skeleton-animation: pulse;
}
/* You can also set different values for light/dark themes */
.light, [data-theme="light"] {
--skeleton-animation: shimmer;
}
.dark, [data-theme="dark"] {
--skeleton-animation: pulse;
}
```
在单个组件上指定 `animationType` 时,会覆盖上述全局设置。
### 传入 Tailwind CSS 类
```tsx
import { Skeleton } from '@heroui/react';
function CustomSkeleton() {
return (
);
}
```
### 自定义组件类
若要自定义 Skeleton 的组件类名,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
/* Base skeleton styles */
.skeleton {
@apply bg-surface-secondary/50; /* Change base background */
}
/* Shimmer animation gradient */
.skeleton--shimmer:before {
@apply viasurface; /* Change shimmer gradient color */
}
/* Pulse animation */
.skeleton--pulse {
@apply animate-pulse opacity-75; /* Customize pulse animation */
}
/* No animation variant */
.skeleton--none {
@apply opacity-50; /* Style for static skeleton */
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于定制。
### CSS 类
Skeleton 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/skeleton.css)):
#### 基础类
`.skeleton` - 包含背景与圆角等基础骨架样式
#### 动画变体类
* `.skeleton--shimmer` - 添加带渐变效果的闪烁动画(默认)
* `.skeleton--pulse` - 使用 Tailwind 的 `animate-pulse` 添加脉冲动画
* `.skeleton--none` - 无动画的静态骨架
### 动画
Skeleton 支持三种动画类型,视觉效果各不相同:
#### 闪烁动画
闪烁效果会在骨架元素上移动渐变:
```css
.skeleton--shimmer:before {
@apply animate-skeleton via-surface-3 absolute inset-0 -translate-x-full
bg-gradient-to-r from-transparent to-transparent content-[''];
}
```
闪烁动画在主题中通过以下方式定义:
```css
@theme inline {
--animate-skeleton: skeleton 2s linear infinite;
@keyframes skeleton {
100% {
transform: translateX(200%);
}
}
}
```
#### 脉冲动画
脉冲动画使用 Tailwind 内置的 `animate-pulse` 工具类:
```css
.skeleton--pulse {
@apply animate-pulse;
}
```
#### 无动画
用于不需要任何动画的静态骨架:
```css
.skeleton--none {
/* No animation styles applied */
}
```
## API 参考
### Skeleton Props
| Prop | 类型 | 默认值 | 描述 |
| --------------- | -------------------------------- | -------------------- | ------------------------------------------------------- |
| `animationType` | `"shimmer" \| "pulse" \| "none"` | `"shimmer"` 或 CSS 变量 | Skeleton 的动画类型;也可通过 `--skeleton-animation` CSS 变量进行全局配置 |
| `className` | `string` | - | 额外的 CSS 类名 |
# Spinner 加载指示器
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/spinner
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(feedback)/spinner.mdx
> 用于展示等待或处理中状态的加载指示组件。
## 引入
```tsx
import { Spinner } from '@heroui/react';
```
### 用法
```tsx
import {Spinner} from "@heroui/react";
export function SpinnerBasic() {
return (
);
}
```
### 颜色
```tsx
import {Spinner} from "@heroui/react";
const COLOR_LABELS = {
accent: "强调",
current: "当前",
danger: "危险",
success: "成功",
warning: "警告",
} as const;
const colors = ["current", "accent", "success", "warning", "danger"] as const;
export function SpinnerColors() {
return (
{colors.map((color) => (
{COLOR_LABELS[color]}
))}
);
}
```
### 尺寸
```tsx
import {Spinner} from "@heroui/react";
const SIZE_LABELS = {
lg: "大",
md: "中",
sm: "小",
xl: "特大",
} as const;
const sizes = ["sm", "md", "lg", "xl"] as const;
export function SpinnerSizes() {
return (
{sizes.map((size) => (
{SIZE_LABELS[size]}
))}
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import {Spinner} from '@heroui/react';
function CustomSpinner() {
return (
);
}
```
### 自定义组件类
若要自定义 Spinner 的组件类名,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.spinner {
@apply animate-spin;
}
.spinner--accent {
color: var(--accent);
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于定制。
### CSS 类
Spinner 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/spinner.css)):
#### 基础类与尺寸类
* `.spinner` - 基础样式与默认尺寸
* `.spinner--sm` - 小尺寸变体
* `.spinner--md` - 中等尺寸变体(默认)
* `.spinner--lg` - 大尺寸变体
* `.spinner--xl` - 特大尺寸变体
#### 颜色类
* `.spinner--current` - 继承当前文本颜色
* `.spinner--accent` - 强调色变体
* `.spinner--danger` - 危险色变体
* `.spinner--success` - 成功色变体
* `.spinner--warning` - 警告色变体
## API 参考
### Spinner Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------- | ----------- | ------------- |
| `size` | `"sm" \| "md" \| "lg" \| "xl"` | `"md"` | Spinner 的尺寸 |
| `color` | `"current" \| "accent" \| "success" \| "warning" \| "danger"` | `"current"` | Spinner 的颜色变体 |
| `className` | `string` | - | 额外的 CSS 类名 |
# CheckboxGroup 复选框组
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/checkbox-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/checkbox-group.mdx
> 用于管理多项复选框选择的 CheckboxGroup 组件。
## 引入
```tsx
import { CheckboxGroup, Checkbox, Label, Description } from '@heroui/react';
```
### 用法
```tsx
import {Checkbox, CheckboxGroup, Description, Label} from "@heroui/react";
export function Basic() {
return (
选择你的兴趣
可多选
编程
热爱构建软件
设计
喜欢打造精美界面
写作
热衷于内容创作
);
}
```
### 组件结构
导入 CheckboxGroup 组件,并通过点语法访问所有子部分。
```tsx
import {CheckboxGroup, Checkbox, Label, Description, FieldError} from '@heroui/react';
export default () => (
{/* Optional */}
Label {/* 纯文本 —— 可点击的标签 */}
{/* 可选:单个复选框的帮助文本 */}
{/* Optional */}
);
```
### 在 Surface 内
置于 [Surface](/docs/components/surface) 中时,使用 `variant="secondary"`,以应用适合表面背景的低强调变体。
```tsx
import {Checkbox, CheckboxGroup, Description, Label, Surface} from "@heroui/react";
export function OnSurface() {
return (
选择你的兴趣
可多选
编程
热爱构建软件
设计
喜欢打造精美界面
写作
热衷于内容创作
);
}
```
### 自定义指示器
```tsx
"use client";
import {Checkbox, CheckboxGroup, Description, Label} from "@heroui/react";
export function WithCustomIndicator() {
return (
功能
选择你需要的功能
{({isSelected}) =>
isSelected ? (
) : null
}
邮件通知
通过邮件接收更新
{({isSelected}) =>
isSelected ? (
) : null
}
邮件通讯
每周接收邮件简报
);
}
```
### 不定状态
```tsx
"use client";
import {Checkbox, CheckboxGroup} from "@heroui/react";
import {useState} from "react";
export function Indeterminate() {
const [selected, setSelected] = useState(["coding"]);
const allOptions = ["coding", "design", "writing"];
return (
0 && selected.length < allOptions.length}
isSelected={selected.length === allOptions.length}
name="select-all"
onChange={(isSelected: boolean) => {
setSelected(isSelected ? allOptions : []);
}}
>
全选
编程
设计
写作
);
}
```
### 受控
```tsx
"use client";
import {Checkbox, CheckboxGroup, Label} from "@heroui/react";
import {useState} from "react";
export function Controlled() {
const [selected, setSelected] = useState(["coding", "design"]);
return (
你的技能
编程
设计
写作
已选:{selected.join(", ") || "无"}
);
}
```
### 校验
```tsx
"use client";
import {Button, Checkbox, CheckboxGroup, FieldError, Form, Label} from "@heroui/react";
export function Validation() {
return (
{
e.preventDefault();
const formData = new FormData(e.currentTarget);
const values = formData.getAll("preferences");
alert(`已选偏好:${values.join(", ")}`);
}}
>
偏好设置
邮件通知
短信通知
推送通知
请至少选择一种通知方式。
提交
);
}
```
### 禁用
```tsx
import {Checkbox, CheckboxGroup, Description, Label} from "@heroui/react";
export function Disabled() {
return (
功能
功能选择暂时不可用
功能一
该功能即将推出
功能二
该功能即将推出
);
}
```
### 特性与附加示例
```tsx
import {Bell, Comment, Envelope} from "@gravity-ui/icons";
import {Checkbox, CheckboxGroup, Description, Label} from "@heroui/react";
import clsx from "clsx";
export function FeaturesAndAddOns() {
const addOns = [
{
description: "通过邮件接收更新",
icon: Envelope,
title: "邮件通知",
value: "email",
},
{
description: "即时短信通知",
icon: Comment,
title: "短信提醒",
value: "sms",
},
{
description: "浏览器与移动端推送提醒",
icon: Bell,
title: "推送通知",
value: "push",
},
];
return (
通知偏好
选择接收更新的方式
{addOns.map((addon) => (
{addon.title}
{addon.description}
))}
);
}
```
### 自定义渲染函数
```tsx
"use client";
import {Checkbox, CheckboxGroup, Description, Label} from "@heroui/react";
export function CustomRenderFunction() {
return (
}>
选择你的兴趣
可多选
编程
热爱构建软件
设计
喜欢打造精美界面
写作
热衷于内容创作
);
}
```
## Related Components
* **Checkbox**: Binary choice input control
* **Label**: Accessible label for form controls
* **Fieldset**: Group related form controls with legends
## 样式
### 传入 Tailwind CSS 类
你可以自定义 CheckboxGroup 组件:
```tsx
import { CheckboxGroup, Checkbox, Label } from '@heroui/react';
function CustomCheckboxGroup() {
return (
Option 1
);
}
```
### 自定义组件类
若要自定义 CheckboxGroup 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.checkbox-group {
@apply flex flex-col gap-2;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
CheckboxGroup 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/checkbox-group.css)):
* `.checkbox-group` - 复选框组合容器基础样式
## API 参考
### CheckboxGroup Props
继承自 [React Aria CheckboxGroup](https://react-spectrum.adobe.com/react-aria/CheckboxGroup.html)。
| Prop | 类型 | 默认值 | 描述 |
| -------------- | -------------------------------------------------------------------------------- | ------- | ---------------------- |
| `value` | `string[]` | - | 当前选中值(受控) |
| `defaultValue` | `string[]` | - | 默认选中值(非受控) |
| `onChange` | `(value: string[]) => void` | - | 选中值变化时调用的处理函数 |
| `isDisabled` | `boolean` | `false` | 是否禁用整个复选框组合 |
| `isRequired` | `boolean` | `false` | 是否必填 |
| `isReadOnly` | `boolean` | `false` | 是否只读 |
| `isInvalid` | `boolean` | `false` | 是否处于无效状态 |
| `name` | `string` | - | 提交 HTML 表单时复选框组合的名称 |
| `children` | `React.ReactNode \| (values: CheckboxGroupRenderProps) => React.ReactNode` | - | 复选框组合内容或渲染 prop |
| `render` | `DOMRenderFunction` | - | 通过自定义渲染函数覆盖默认的 DOM 元素。 |
### CheckboxGroupRenderProps
使用渲染 prop 模式时,会提供以下值:
| Prop | 类型 | 描述 |
| ------------ | ---------- | -------- |
| `value` | `string[]` | 当前选中值 |
| `isDisabled` | `boolean` | 是否禁用 |
| `isReadOnly` | `boolean` | 是否只读 |
| `isInvalid` | `boolean` | 是否处于无效状态 |
| `isRequired` | `boolean` | 是否必填 |
# Checkbox 复选框
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/checkbox
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/checkbox.mdx
> 复选框允许用户从多个独立选项中选择多项,或将单个独立选项标记为已选。
## 引入
```tsx
import { Checkbox } from '@heroui/react';
```
### 用法
```tsx
import {Checkbox} from "@heroui/react";
export function Basic() {
return (
接受条款与条件
);
}
```
### 组件结构
引入 Checkbox 后,可通过点语法访问各个部分。
```tsx
import { Checkbox, Description, FieldError } from '@heroui/react';
export default () => (
Label {/* 纯文本 —— 可点击的标签,同时作为无障碍名称 */}
{/* 可选 — 字段级帮助文本 */}
{/* 可选 — 校验错误信息 */}
);
```
### 禁用
```tsx
import {Checkbox, Description} from "@heroui/react";
export function Disabled() {
return (
高级功能
该功能即将推出
);
}
```
### 默认选中
```tsx
import {Checkbox} from "@heroui/react";
export function DefaultSelected() {
return (
启用邮件通知
);
}
```
### 受控
```tsx
"use client";
import {Checkbox} from "@heroui/react";
import {useState} from "react";
export function Controlled() {
const [isSelected, setIsSelected] = useState(true);
return (
邮件通知
状态:{isSelected ? "已勾选" : "未勾选"}
);
}
```
### 不定状态
```tsx
"use client";
import {Checkbox, Description} from "@heroui/react";
import {useState} from "react";
export function Indeterminate() {
const [isIndeterminate, setIsIndeterminate] = useState(true);
const [isSelected, setIsSelected] = useState(false);
return (
{
setIsSelected(selected);
setIsIndeterminate(false);
}}
>
全选
展示部分选中状态(短横线图标)
);
}
```
### 外部标签
```tsx
import {Checkbox, Label} from "@heroui/react";
export function ExternalLabel() {
return (
给我发送营销邮件
);
}
```
### 带说明
```tsx
import {Checkbox, Description} from "@heroui/react";
export function WithDescription() {
return (
邮件通知
当有人在评论中提及您时收到通知
);
}
```
### 渲染 props
```tsx
"use client";
import {Checkbox, Description} from "@heroui/react";
export function RenderProps() {
return (
{({isSelected}) => (
<>
{isSelected ? "已同意条款" : "接受条款"}
{isSelected ? "感谢您的确认" : "请先阅读并接受条款"}
>
)}
);
}
```
### 表单集成
```tsx
"use client";
import {Button, Checkbox} from "@heroui/react";
import React from "react";
export function Form() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
alert(
`表单提交数据:\n${Array.from(formData.entries())
.map(([key, value]) => `${key}: ${value}`)
.join("\n")}`,
);
};
return (
启用通知
订阅新闻通讯
接收营销更新
提交
);
}
```
### 无效
```tsx
import {Checkbox, FieldError} from "@heroui/react";
export function Invalid() {
return (
我同意条款
您必须接受条款才能继续
);
}
```
### 自定义指示器
```tsx
"use client";
import {Checkbox} from "@heroui/react";
export function CustomIndicator() {
return (
{({isSelected}) =>
isSelected ? (
) : null
}
心形
{({isSelected}) =>
isSelected ? (
) : null
}
加号
{({isIndeterminate}) =>
isIndeterminate ? (
) : null
}
部分选中
);
}
```
### 全圆角
```tsx
import {Checkbox, Label} from "@heroui/react";
export function FullRounded() {
return (
);
}
```
### 变体
Checkbox 支持两种视觉变体:
* **`primary`**(默认)— 常规样式与默认背景,适用于大多数场景
* **`secondary`** — 弱强调变体,适合用于 Surface 等组件内部
```tsx
import {Checkbox, Description} from "@heroui/react";
export function Variants() {
return (
);
}
```
### 自定义渲染函数
```tsx
"use client";
import {Checkbox, Label} from "@heroui/react";
export function CustomRenderFunction() {
return (
);
}
```
## Related Components
* **Label**: Accessible label for form controls
* **CheckboxGroup**: Group of checkboxes with shared state
* **Description**: Helper text for form fields
## 样式
### 传入 Tailwind CSS 类
你可以单独定制各个 Checkbox:
```tsx
import { Checkbox, Label } from '@heroui/react';
function CustomCheckbox() {
return (
Custom Checkbox
);
}
```
### 自定义组件类
若要自定义 Checkbox 的组件类名,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.checkbox {
@apply inline-flex gap-3 items-center;
}
.checkbox__control {
@apply size-5 border-2 border-gray-400 rounded data-[selected=true]:bg-blue-500 data-[selected=true]:border-blue-500;
/* Animated background indicator */
&::before {
@apply bg-accent pointer-events-none absolute inset-0 z-0 origin-center scale-50 rounded-md opacity-0 content-[''];
transition:
scale 200ms linear,
opacity 200ms linear,
background-color 200ms ease-out;
}
/* Show indicator when selected */
&[data-selected="true"]::before {
@apply scale-100 opacity-100;
}
}
.checkbox__indicator {
@apply text-white;
}
.checkbox__content {
@apply items-center gap-3;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于定制。
### CSS 类
Checkbox 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/checkbox.css)):
* `.checkbox` - Checkbox 根容器
* `.checkbox__content` - 包裹控件与标签文本的可点击 label
* `.checkbox__control` - Checkbox 控件方框
* `.checkbox__indicator` - Checkbox 勾选指示器
### 交互状态
Checkbox 同时支持 CSS 伪类与 data 属性,以获得更好的灵活性:
* **选中**:`[data-selected="true"]` 或 `[aria-checked="true"]`(显示勾选与背景色变化)
* **不定**:`[data-indeterminate="true"]`(以横线表示不定状态)
* **无效**:`[data-invalid="true"]` 或 `[aria-invalid="true"]`(以危险色显示错误状态)
* **悬停**:`:hover` 或 `[data-hovered="true"]`(交互状态在 `Checkbox.Control` / 按钮上)
* **焦点**:`:focus-visible` 或 `[data-focus-visible="true"]`(显示焦点环,作用于按钮)
* **禁用**:`[data-disabled="true"]`(降低透明度并禁用指针事件)
* **按下**:`:active` 或 `[data-pressed="true"]`
## API 参考
### Checkbox Props
继承自 [React Aria CheckboxField](https://react-spectrum.adobe.com/react-aria/Checkbox.html)。
| Prop | 类型 | 默认值 | 描述 |
| -------------------- | -------------------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------- |
| `isSelected` | `boolean` | `false` | Checkbox 是否选中 |
| `defaultSelected` | `boolean` | `false` | Checkbox 默认是否选中(非受控) |
| `isIndeterminate` | `boolean` | `false` | Checkbox 是否处于不定状态 |
| `isDisabled` | `boolean` | `false` | Checkbox 是否禁用 |
| `isInvalid` | `boolean` | `false` | Checkbox 是否无效 |
| `isReadOnly` | `boolean` | `false` | Checkbox 是否只读 |
| `isRequired` | `boolean` | `false` | Checkbox 是否必须选中 |
| `validate` | `(value: boolean) => ValidationError \| true \| null \| undefined` | - | 自定义校验函数 |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | 使用原生 HTML 校验或 ARIA 校验 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 组件的视觉变体。`primary` 为默认带阴影样式。`secondary` 为弱强调、无阴影变体,适合用于 surface 上。 |
| `name` | `string` | - | input 元素的 name,用于提交 HTML 表单 |
| `value` | `string` | - | input 元素的 value,用于提交 HTML 表单 |
| `onChange` | `(isSelected: boolean) => void` | - | Checkbox 值变化时调用 |
| `children` | `React.ReactNode \| (values: CheckboxFieldRenderProps) => React.ReactNode` | - | Checkbox 内容或字段级渲染 prop |
| `render` | `DOMRenderFunction` | - | 通过自定义渲染函数覆盖默认的 DOM 元素。 |
### Checkbox.Content Props
包裹控件与标签文本的可点击 ``。请把 `Checkbox.Control` 与 `Label` 放在它内部;`Description`/`FieldError` 作为 `Checkbox.Content` 的兄弟节点。对于没有标签的 checkbox,省略 `Label` 并在 `Checkbox` 上传入 `aria-label`。
| Prop | 类型 | 默认值 | 描述 |
| ----------- | --------------------------------------------------------------------------- | --- | ------------------------- |
| `children` | `React.ReactNode \| (values: CheckboxButtonRenderProps) => React.ReactNode` | - | 按钮内容(控件 + 标签),或按钮级渲染 prop |
| `className` | `string \| (values: CheckboxButtonRenderProps) => string` | - | 应用到可点击 label 的类名 |
### CheckboxFieldRenderProps
在根 `Checkbox` 上使用渲染 prop 时,提供以下字段级值:
| Prop | 类型 | 描述 |
| ----------------- | --------- | ----------------- |
| `isSelected` | `boolean` | Checkbox 当前是否选中 |
| `isIndeterminate` | `boolean` | Checkbox 是否处于不定状态 |
| `isDisabled` | `boolean` | Checkbox 是否禁用 |
| `isReadOnly` | `boolean` | Checkbox 是否只读 |
| `isInvalid` | `boolean` | Checkbox 是否无效 |
| `isRequired` | `boolean` | Checkbox 是否必填 |
### CheckboxButtonRenderProps
`Checkbox.Control` 与 `Checkbox.Indicator` 使用按钮级渲染 prop(`isHovered`、`isPressed`、`isFocusVisible` 等)。将函数作为 `Checkbox.Control` 或 `Checkbox.Indicator` 的子元素即可访问。
# Description 描述
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/description
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/description.mdx
> 为表单字段及其他组件提供补充说明文字。
## 引入
```tsx
import { Description } from '@heroui/react';
```
## 用法
```tsx
import {Description, Input, Label} from "@heroui/react";
export function Basic() {
return (
邮箱
我们不会将你的邮箱分享给任何人。
);
}
```
## Related Components
* **TextField**: Composition-friendly fields with labels and validation
* **Input**: Single-line text input built on React Aria
* **TextArea**: Multiline text input with focus management
## API 参考
### Description Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | Description 的内容。 |
## 无障碍
Description 组件通过以下方式增强无障碍:
* 使用语义化 HTML,屏幕阅读器可识别
* 提供 `slot="description"` 属性以便与 React Aria 集成
* 支持适宜的文本对比度
## 样式
Description 组件使用以下 CSS 类:
* `.description` - 基础 Description 样式,使用弱化(muted)文本颜色
## 示例
### 与表单字段一起使用
```tsx
Password
Must be at least 8 characters with one uppercase letter
```
### 与 TextField 集成
```tsx
import {TextField, Label, Input, Description} from '@heroui/react';
Email
We'll never share your email
```
使用 [TextField](./text-field) 组件时,无障碍属性会自动应用到 Label 与 Description 上。
# ErrorMessage 错误信息
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/error-message
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/error-message.mdx
> 用于展示错误信息的底层组件。
## 引入
```tsx
import { ErrorMessage } from '@heroui/react';
```
## 用法
`ErrorMessage` 是基于 React Aria `Text`、并使用 `errorMessage` 插槽的底层组件,适用于 **非表单** 场景(例如 `TagGroup`、`Calendar` 等集合类组件)。
```tsx
"use client";
import type {Key} from "@heroui/react";
import {Description, ErrorMessage, Label, Tag, TagGroup} from "@heroui/react";
import {useMemo, useState} from "react";
export function ErrorMessageBasic() {
const [selected, setSelected] = useState>(new Set());
const isInvalid = useMemo(() => Array.from(selected).length === 0, [selected]);
return (
setSelected(keys)}
>
必选分类
新闻
旅游
游戏
购物
请至少选择一个分类
{!!isInvalid && <>请至少选择一个分类>}
);
}
```
### 组件结构
```tsx
import { TagGroup, Tag, Label, Description, ErrorMessage } from '@heroui/react';
```
## Related Components
* **TagGroup**: Focusable list of tags with selection and removal support
## 何时使用
`ErrorMessage` **不绑定表单**,是用于非表单上下文的通用错误展示组件。
* **推荐用于** 非表单组件(例如 `TagGroup`、`Calendar`、集合类组件)
* **对于表单字段**,我们更推荐使用 [`FieldError`](/docs/components/field-error),它提供表单相关的校验能力与自动错误处理,并遵循标准化的表单校验模式。
## ErrorMessage 与 FieldError
| 组件 | 使用场景 | 表单集成 | 示例组件 |
| -------------- | -------- | ---- | ---------------------------------- |
| `ErrorMessage` | 非表单组件 | 否 | `TagGroup`、`Calendar` |
| `FieldError` | 表单字段(推荐) | 是 | `TextField`、`NumberField`、`Select` |
对于表单校验,我们推荐使用 `FieldError`,因为它遵循标准化的表单校验模式并提供表单相关能力。示例与最佳实践见 [FieldError 文档](/docs/components/field-error) 与 [Form 指南](/docs/components/form)。
## API 参考
### ErrorMessage Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | --------- |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode` | - | 错误信息内容 |
**说明:** `ErrorMessage` 基于 React Aria 的 `Text` 组件,并使用 `slot="errorMessage"`。你可以使用 `[slot=errorMessage]` CSS 选择器进行样式覆盖。
## 无障碍
ErrorMessage 通过以下方式增强无障碍:
* 使用屏幕阅读器可识别的语义化 HTML
* 提供 `slot="errorMessage"` 属性以集成 React Aria
* 为错误状态提供合适的文本对比度
* 遵循 WAI-ARIA 的错误信息最佳实践
## 样式
### 传入 Tailwind CSS 类
```tsx
import { ErrorMessage } from '@heroui/react';
function CustomErrorMessage() {
return (
Custom styled error message
);
}
```
### 自定义组件类
要自定义 ErrorMessage 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.error-message {
@apply text-red-600 text-sm font-medium;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
ErrorMessage 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/error-message.css)):
#### 基础类
* `.error-message` - 危险色与文本截断等基础样式
#### 插槽类
* `[slot="errorMessage"]` - 与 React Aria 集成的 ErrorMessage 插槽样式
# FieldError 字段错误
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/field-error
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/field-error.mdx
> 用于展示表单字段的校验错误信息。
## 引入
```tsx
import { FieldError } from '@heroui/react';
```
## 用法
FieldError 组件用于展示表单字段的校验错误信息。当父级字段被标记为无效时会自动显示,并提供平滑的透明度过渡。
```tsx
"use client";
import {FieldError, Input, Label, TextField} from "@heroui/react";
import {useState} from "react";
export function Basic() {
const [value, setValue] = useState("jr");
const isInvalid = value.length > 0 && value.length < 3;
return (
用户名
setValue(e.target.value)}
/>
用户名至少需要 3 个字符
);
}
```
## Related Components
* **TextField**: Composition-friendly fields with labels and validation
* **Input**: Single-line text input built on React Aria
* **TextArea**: Multiline text input with focus management
## API 参考
### FieldError Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------ | --- | ------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode \| ((validation: ValidationResult) => ReactNode)` | - | 错误信息内容或渲染函数。 |
## 无障碍
FieldError 组件通过以下方式保证无障碍:
* 使用恰当的 ARIA 属性以播报错误
* 通过语义化 HTML 支持屏幕阅读器
* 同时提供视觉与程序化的错误提示
* 根据校验状态自动控制可见性
## 样式
FieldError 组件使用以下 CSS 类:
* `.field-error` - 基础错误样式,使用危险色(danger)
* 仅在存在 `data-visible` 属性时显示
* 长文案会以省略号截断
## 示例
### 基础校验
```tsx
export function Basic() {
const [value, setValue] = useState("");
const isInvalid = value.length > 0 && value.length < 3;
return (
Username
setValue(e.target.value)}
/>
Username must be at least 3 characters
);
}
```
### 动态错误信息
```tsx
0}>
Password
{(validation) => validation.validationErrors.join(', ')}
```
### 自定义校验逻辑
```tsx
function EmailField() {
const [email, setEmail] = useState('');
const isInvalid = email.length > 0 && !email.includes('@');
return (
Email
setEmail(e.target.value)}
/>
Email must include @ symbol
);
}
```
### 多条错误信息
```tsx
Username
{errors.map((error, i) => (
{error}
))}
```
# Fieldset 字段集
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/fieldset
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/fieldset.mdx
> 使用 legend、description 与操作区对相关表单控件进行分组。
## 引入
```tsx
import { Fieldset } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {FloppyDisk} from "@gravity-ui/icons";
import {
Button,
Description,
FieldError,
FieldGroup,
Fieldset,
Form,
Input,
Label,
TextArea,
TextField,
} from "@heroui/react";
export function Basic() {
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: Record = {};
// Convert FormData to plain object
formData.forEach((value, key) => {
data[key] = value.toString();
});
alert("表单提交成功!");
};
return (
个人资料设置
更新你的个人资料信息。
{
if (value.length < 3) {
return "姓名至少需要 3 个字符";
}
return null;
}}
>
姓名
邮箱
{
if (value.length < 10) {
return "简介至少需要 10 个字符";
}
return null;
}}
>
简介
至少 10 个字符
保存更改
取消
);
}
```
### 在 Surface 内
在 [Surface](/docs/components/surface) 组件内部使用时,请在表单控件(Input、TextArea 等)上使用 `variant="secondary"`,以应用适合 surface 背景的弱强调变体。
```tsx
"use client";
import {FloppyDisk} from "@gravity-ui/icons";
import {
Button,
Description,
FieldError,
Fieldset,
Form,
Input,
Label,
Surface,
TextArea,
TextField,
} from "@heroui/react";
import React from "react";
export function OnSurface() {
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: Record = {};
// Convert FormData to plain object
formData.forEach((value, key) => {
data[key] = value.toString();
});
alert("表单提交成功!");
};
return (
个人资料设置
更新你的个人资料信息。
{
if (value.length < 3) {
return "姓名至少需要 3 个字符";
}
return null;
}}
>
姓名
邮箱
{
if (value.length < 10) {
return "简介至少需要 10 个字符";
}
return null;
}}
>
简介
至少 10 个字符
保存更改
取消
);
}
```
### 组件结构
引入 Fieldset 后,可通过点语法访问各个部分。
```tsx
import { Fieldset } from '@heroui/react';
export default () => (
{/* form fields go here */}
{/* action buttons go here */}
)
```
## Related Components
* **TextField**: Composition-friendly fields with labels and validation
* **Label**: Accessible label for form controls
* **CheckboxGroup**: Group of checkboxes with shared state
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Fieldset, TextField, Label, Input } from '@heroui/react';
function CustomFieldset() {
return (
Team members
First name
Last name
{/* Action buttons */}
);
}
```
### 自定义组件类
使用 `@layer components` 指令,针对 Fieldset 的 [BEM](https://getbem.com/) 风格类名进行定制。
```css
@layer components {
.fieldset {
@apply gap-5 rounded-xl border border-border/60 bg-surface p-6 shadow-field;
}
.fieldset__legend {
@apply text-lg font-semibold;
}
.fieldset__field_group {
@apply gap-3 md:grid md:grid-cols-2;
}
.fieldset__actions {
@apply flex justify-end gap-2 pt-2;
}
}
```
### CSS 类
Fieldset 复合组件暴露以下 CSS 选择器:
* `.fieldset` – 根容器
* `.fieldset__legend` – Legend 元素
* `.fieldset__field_group` – 分组字段的包裹层
* `.fieldset__actions` – 字段下方的操作栏
## API 参考
### Fieldset Props
| Prop | 类型 | 默认值 | 描述 |
| ------------- | ------------------------------------------- | --------------------- | ---------------------------------------- |
| `className` | `string` | - | 应用到根元素上的 Tailwind CSS 类。 |
| `children` | `React.ReactNode` | - | Fieldset 内容(legend、分组、description、操作区等)。 |
| `nativeProps` | `React.HTMLAttributes` | 支持原生 fieldset 的属性与事件。 | |
### Fieldset.Legend Props
| Prop | 类型 | 默认值 | 描述 |
| ------------- | ----------------------------------------- | --- | ---------------------- |
| `className` | `string` | - | legend 元素的 Tailwind 类。 |
| `children` | `React.ReactNode` | - | Legend 内容,通常为纯文本。 |
| `nativeProps` | `React.HTMLAttributes` | - | 原生 legend 属性。 |
### Fieldset.Group Props
| Prop | 类型 | 默认值 | 描述 |
| ------------- | -------------------------------------- | --- | -------------------- |
| `className` | `string` | - | 分组字段的布局与间距类。 |
| `children` | `React.ReactNode` | - | 在 fieldset 内分组的表单控件。 |
| `nativeProps` | `React.HTMLAttributes` | - | 原生 div 属性。 |
### Fieldset.Actions Props
| Prop | 类型 | 默认值 | 描述 |
| ------------- | -------------------------------------- | --- | -------------------------- |
| `className` | `string` | - | 用于对齐操作按钮或辅助文本的 Tailwind 类。 |
| `children` | `React.ReactNode` | - | 操作按钮或辅助文本。 |
| `nativeProps` | `React.HTMLAttributes` | - | 原生 div 属性。 |
# Form 表单
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/form
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/form.mdx
> 用于表单校验与提交处理的包装组件。
## 引入
```tsx
import { Form } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {Check} from "@gravity-ui/icons";
import {Button, Description, FieldError, Form, Input, Label, TextField} from "@heroui/react";
export function Basic() {
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: Record = {};
// Convert FormData to plain object
formData.forEach((value, key) => {
data[key] = value.toString();
});
alert(`表单提交数据:${JSON.stringify(data, null, 2)}`);
};
return (
{
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)) {
return "请输入有效的邮箱地址";
}
return null;
}}
>
邮箱
{
if (value.length < 8) {
return "密码至少需要 8 个字符";
}
if (!/[A-Z]/.test(value)) {
return "密码至少需要包含一个大写字母";
}
if (!/[0-9]/.test(value)) {
return "密码至少需要包含一个数字";
}
return null;
}}
>
密码
至少 8 个字符,且包含 1 个大写字母和 1 个数字
提交
重置
);
}
```
### 组件结构
引入所有组件部分,并自由组合:
```tsx
import {Form, Button} from '@heroui/react';
export default () => (
{/* Form fields go here */}
)
```
### 自定义渲染函数
```tsx
"use client";
import {Check} from "@gravity-ui/icons";
import {Button, Description, FieldError, Form, Input, Label, TextField} from "@heroui/react";
export function CustomRenderFunction() {
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: Record = {};
// Convert FormData to plain object
formData.forEach((value, key) => {
data[key] = value.toString();
});
alert(`表单提交数据:${JSON.stringify(data, null, 2)}`);
};
return (
}
onSubmit={onSubmit}
>
{
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)) {
return "请输入有效的邮箱地址";
}
return null;
}}
>
邮箱
{
if (value.length < 8) {
return "密码至少需要 8 个字符";
}
if (!/[A-Z]/.test(value)) {
return "密码至少需要包含一个大写字母";
}
if (!/[0-9]/.test(value)) {
return "密码至少需要包含一个数字";
}
return null;
}}
>
密码
至少 8 个字符,且包含 1 个大写字母和 1 个数字
提交
重置
);
}
```
## Related Components
* **Button**: Allows a user to perform an action
* **Fieldset**: Group related form controls with legends
* **TextField**: Composition-friendly fields with labels and validation
## 样式
### 传入 Tailwind CSS 类
```tsx
import {Form, TextField, Label, Input, FieldError, Button} from '@heroui/react';
function CustomForm() {
return (
Email
Submit
);
}
```
## API 参考
### Form Props
Form 组件是对 React Aria `Form` 原语的封装,提供表单校验与提交处理能力。
| Prop | 类型 | 默认值 | 描述 |
| -------------------- | ------------------------------------------------------------------------------ | ---------- | -------------------------------------------------------------- |
| `action` | `string \| FormHTMLAttributes['action']` | - | 表单数据提交的目标 URL。 |
| `className` | `string` | - | 应用到 form 元素上的 Tailwind CSS 类。 |
| `children` | `React.ReactNode` | - | 表单内容(字段、按钮等)。 |
| `encType` | `'application/x-www-form-urlencoded' \| 'multipart/form-data' \| 'text/plain'` | - | 表单数据提交时的编码类型。 |
| `method` | `'get' \| 'post'` | - | 提交表单时使用的 HTTP 方法。 |
| `onInvalid` | `(event: FormEvent) => void` | - | 表单校验失败时调用的处理函数。默认会聚焦第一个无效字段,使用 `preventDefault()` 可自定义聚焦行为。 |
| `onReset` | `(event: FormEvent) => void` | - | 表单被重置时调用的处理函数。 |
| `onSubmit` | `(event: FormEvent) => void` | - | 表单被提交时调用的处理函数。 |
| `target` | `'_self' \| '_blank' \| '_parent' \| '_top'` | - | 提交表单后响应的展示位置。 |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | 使用浏览器原生 HTML 校验还是 ARIA 校验。`'native'` 会阻止表单提交,`'aria'` 会实时显示错误。 |
| `validationErrors` | `ValidationErrors` | - | 按字段名映射的服务端校验错误。错误会立即展示,并在用户修改字段后自动清除。 |
| `aria-label` | `string` | - | 表单的无障碍标签。 |
| `aria-labelledby` | `string` | - | 用于为表单提供标签的元素 ID。提供后会创建 form landmark。 |
| `render` | `DOMRenderFunction` | - | 通过自定义渲染函数覆盖默认的 DOM 元素。 |
### 表单校验
Form 组件集成了 React Aria 的校验体系,你可以:
* 使用内置的 HTML5 校验属性(`required`、`minLength`、`pattern` 等)
* 在 TextField 等组件上提供自定义校验函数
* 通过 FieldError 组件展示校验错误
* 在提交时进行完整的校验处理
* 通过 `validationErrors` prop 提供服务端校验错误
#### 校验行为
`validationBehavior` prop 控制校验信息的展示方式:
* **`native`**(默认):使用浏览器原生 HTML 校验,发生错误时阻止表单提交。
* **`aria`**:使用 ARIA 属性进行校验,在用户输入时实时显示错误,且不会阻止提交。
该行为可以在 form 层级设置,也可以在单个字段层级覆盖。
### 表单提交
表单可以通过多种方式提交:
* **传统提交**:设置 `action` prop 提交到一个 URL
* **JavaScript 处理**:使用 `onSubmit` 处理函数处理表单数据
* **FormData API**:在提交处理函数中使用 FormData API 读取表单数据
使用 FormData 的示例:
```tsx
function handleSubmit(e: FormEvent) {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data = Object.fromEntries(formData);
console.log('Form data:', data);
}
```
### 与表单字段集成
Form 组件可与 HeroUI 的所有表单字段组件无缝配合:
* **TextField**:带标签与校验的文本输入
* **Checkbox**:布尔选择
* **RadioGroup**:从多个选项中单选
* **Switch**:切换控件
* **Button**:用于表单提交与重置
所有字段组件在置于 Form 内部时,都会自动接入 Form 的校验与提交行为。
### 无障碍
使用 React Aria 组件时,表单默认即具备良好的无障碍能力,主要特性包括:
* 原生 `` 元素语义
* 通过 `aria-label` 或 `aria-labelledby` 创建 form landmark
* 校验失败时自动聚焦管理
* 设置 `validationBehavior="aria"` 时使用 ARIA 校验属性
### 进阶用法
更高级的使用场景,包括:
* 自定义校验上下文
* Form context provider
* 与第三方库的集成
* 校验错误时的自定义聚焦管理
请参考 [React Aria Form 文档](https://react-spectrum.adobe.com/react-aria/Form.html)。
# InputGroup 输入框组
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/input-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/input-group.mdx
> 将相关输入控件与前后缀元素组合,以增强表单字段。
## 引入
```tsx
import { InputGroup } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {InputGroup, Label, TextField} from "@heroui/react";
export function Default() {
return (
邮箱地址
);
}
```
### 组件结构
```tsx
import {InputGroup, TextField, Label} from '@heroui/react';
export default () => (
{/* Or use InputGroup.TextArea for multiline input */}
)
```
> **InputGroup** 使用可选的前缀与后缀包裹输入框,形成视觉上统一的组合。通常放在 **[TextField](/docs/components/text-field)** 内,用于在输入前后添加图标、文字、按钮等元素。单行输入请使用 **InputGroup.Input**,多行输入请使用 **InputGroup.TextArea**。
### 前缀图标
在输入框前添加图标。
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {Description, InputGroup, Label, TextField} from "@heroui/react";
export function WithPrefixIcon() {
return (
邮箱地址
我们不会将此邮箱分享给任何人
);
}
```
### 后缀图标
在输入框后添加图标。
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {Description, InputGroup, Label, TextField} from "@heroui/react";
export function WithSuffixIcon() {
return (
邮箱地址
我们不会发送垃圾邮件
);
}
```
### 前缀与后缀
同时组合前缀与后缀。
```tsx
"use client";
import {Description, InputGroup, Label, TextField} from "@heroui/react";
export function WithPrefixAndSuffix() {
return (
设置价格
$
USD
客户将支付的价格
);
}
```
### 文字前缀
使用文字作为前缀,例如货币符号或协议前缀。
```tsx
"use client";
import {InputGroup, Label, TextField} from "@heroui/react";
export function WithTextPrefix() {
return (
网站
https://
);
}
```
### 文字后缀
使用文字作为后缀,例如域名后缀或单位。
```tsx
"use client";
import {InputGroup, Label, TextField} from "@heroui/react";
export function WithTextSuffix() {
return (
网站
.com
);
}
```
### 图标前缀与文字后缀
组合图标前缀与文字后缀。
```tsx
"use client";
import {Globe} from "@gravity-ui/icons";
import {InputGroup, Label, TextField} from "@heroui/react";
export function WithIconPrefixAndTextSuffix() {
return (
网站
.com
);
}
```
### 复制按钮后缀
在后缀中加入交互按钮,例如复制按钮。
```tsx
"use client";
import {Copy} from "@gravity-ui/icons";
import {Button, InputGroup, Label, TextField} from "@heroui/react";
export function WithCopySuffix() {
return (
网站
);
}
```
### 图标前缀与复制按钮
组合图标前缀与交互式后缀按钮。
```tsx
"use client";
import {Copy, Globe} from "@gravity-ui/icons";
import {Button, InputGroup, Label, TextField} from "@heroui/react";
export function WithIconPrefixAndCopySuffix() {
return (
网站
);
}
```
### 密码显隐切换
在后缀中使用按钮切换密码可见性。
```tsx
"use client";
import {Eye, EyeSlash} from "@gravity-ui/icons";
import {Button, InputGroup, Label, TextField} from "@heroui/react";
import {useState} from "react";
export function PasswordWithToggle() {
const [isVisible, setIsVisible] = useState(false);
return (
密码
setIsVisible(!isVisible)}
>
{isVisible ? : }
);
}
```
### 加载状态
在后缀显示加载指示器,表示正在处理。
```tsx
"use client";
import {InputGroup, Spinner, TextField} from "@heroui/react";
export function WithLoadingSuffix() {
return (
);
}
```
### 键盘快捷键
使用 [Kbd](/docs/components/kbd) 组件展示键盘快捷键。
```tsx
"use client";
import {InputGroup, Kbd, TextField} from "@heroui/react";
export function WithKeyboardShortcut() {
return (
K
);
}
```
### Badge 后缀
在后缀中加入徽章或 chip,用于展示状态或标签。
```tsx
"use client";
import {Chip, InputGroup, TextField} from "@heroui/react";
export function WithBadgeSuffix() {
return (
Pro
);
}
```
### 必填字段
InputGroup 会遵循父级 TextField 的必填状态。
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {Description, InputGroup, Label, TextField} from "@heroui/react";
export function Required() {
return (
邮箱地址
设置价格
$
USD
客户将支付的价格
);
}
```
### 校验
InputGroup 会自动反映父级 TextField 的无效状态。
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {FieldError, InputGroup, Label, TextField} from "@heroui/react";
export function Invalid() {
return (
邮箱地址
请输入有效的邮箱地址
设置价格
$
USD
价格必须大于 0
);
}
```
### 禁用状态
InputGroup 会遵循父级 TextField 的禁用状态。
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {InputGroup, Label, TextField} from "@heroui/react";
export function Disabled() {
return (
邮箱地址
设置价格
$
USD
);
}
```
### 全宽
```tsx
import {Envelope, Eye} from "@gravity-ui/icons";
import {InputGroup, Label, TextField} from "@heroui/react";
export function FullWidth() {
return (
邮箱地址
密码
);
}
```
### 变体
InputGroup 支持两种视觉变体:
* **`primary`**(默认)— 带阴影的标准样式,适用于大多数场景
* **`secondary`** — 低强调、无阴影的变体,适合用在 Surface 组件内
```tsx
import {Envelope} from "@gravity-ui/icons";
import {InputGroup, Label, TextField} from "@heroui/react";
export function Variants() {
return (
主要变体
次要变体
);
}
```
### 在 Surface 内
在 [Surface](/docs/components/surface) 内使用时,请使用 `variant="secondary"`,以应用适合表面背景的低强调变体。
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {Description, InputGroup, Label, Surface, TextField} from "@heroui/react";
export function OnSurface() {
return (
邮箱地址
我们不会将此邮箱分享给任何人
);
}
```
### 搭配 TextArea
多行输入请使用 **InputGroup.TextArea**,并搭配前缀与后缀。当存在 textarea 时,容器高度会自动适应内容,并将前缀/后缀与顶部对齐。
```tsx
"use client";
import {ArrowUp, At, Microphone, PlugConnection, Plus} from "@gravity-ui/icons";
import {Button, InputGroup, Kbd, Spinner, TextField, Tooltip} from "@heroui/react";
import {useState} from "react";
export function WithTextArea() {
const [value, setValue] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = () => {
if (!value.trim()) return;
setIsSubmitting(true);
setTimeout(() => {
setIsSubmitting(false);
setValue("");
}, 1000);
};
return (
添加上下文
setValue(event.target.value)}
/>
添加文件等
连接应用
语音输入
{({isPending}) => (isPending ? : )}
发送
);
}
```
## Related Components
* **TextField**: Composition-friendly fields with labels and validation
* **Input**: Single-line text input built on React Aria
* **Label**: Accessible label for form controls
## 样式
### 传入 Tailwind CSS 类
```tsx
import {InputGroup, TextField, Label} from '@heroui/react';
function CustomInputGroup() {
return (
Website
https://
.com
);
}
```
### 自定义组件类
InputGroup 使用可自定义的 CSS 类。你可以覆盖这些类名以匹配自己的设计系统。
```css
@layer components {
.input-group {
@apply bg-field text-field-foreground shadow-field rounded-field inline-flex min-h-9 items-center overflow-hidden border text-sm outline-none;
}
.input-group__input {
@apply flex-1 rounded-none border-0 bg-transparent px-3 py-2 shadow-none outline-none;
}
.input-group__prefix {
@apply text-field-placeholder rounded-l-field flex h-full items-center justify-center rounded-r-none bg-transparent px-3;
}
.input-group__suffix {
@apply text-field-placeholder rounded-r-field flex h-full items-center justify-center rounded-l-none bg-transparent px-3;
}
/* Secondary variant */
.input-group--secondary {
@apply shadow-none;
background-color: var(--color-default);
}
}
```
### CSS 类
* `.input-group` – 根容器:带边框、背景与 flex 布局。默认使用 `min-h-9` 与 `items-center`;当存在 textarea 时会切换为 `items-start`。
* `.input-group__input` – 透明背景、无边框的输入元素。textarea 也使用该基础类。
* `.input-group__prefix` – 左侧圆角的前缀容器。与 textarea 搭配时与顶部对齐。
* `.input-group__suffix` – 右侧圆角的后缀容器。与 textarea 搭配时与顶部对齐。
* `.input-group--primary` – 带阴影的主变体(默认)
* `.input-group--secondary` – 无阴影的次变体,适合用在 surface 上
**说明:** 使用 `InputGroup.TextArea` 时,容器会从 `items-center` 切换为 `items-start`,并使用 `height: auto` 替代固定高度。前缀与后缀与顶部对齐,并增加内边距以匹配 textarea 的垂直内边距。textarea 使用相同的 `.input-group__input` 基础类,并通过 `[data-slot="input-group-textarea"]` 选择器应用 textarea 专用样式(最小高度与纵向 resize)。
### 交互状态
InputGroup 会根据状态自动管理以下 data 属性:
* **Hover**:`[data-hovered]` – 悬停在整个组合上时应用
* **Focus Within**:`[data-focus-within]` – 输入框聚焦时应用
* **Invalid**:`[data-invalid]` – 父级 TextField 为无效时应用
* **Disabled**:`[data-disabled]` 或 `[aria-disabled]` – 父级 TextField 为禁用时应用
## API 参考
### InputGroup Props
InputGroup 继承 React Aria [Group](https://react-spectrum.adobe.com/react-aria/Group.html) 组件的全部 props。
#### Base Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------------------------------------------------------------------------- | ------- | --------------------------------------- |
| `children` | `React.ReactNode \| (values: GroupRenderProps) => React.ReactNode` | - | 子组件(Input、TextArea、Prefix、Suffix)或渲染函数。 |
| `className` | `string \| (values: GroupRenderProps) => string` | - | 用于样式的 CSS 类,支持渲染 prop。 |
| `style` | `React.CSSProperties \| (values: GroupRenderProps) => React.CSSProperties` | - | 行内样式,支持渲染 prop。 |
| `fullWidth` | `boolean` | `false` | 输入组是否占满容器宽度 |
| `id` | `string` | - | 元素的唯一标识符。 |
#### Variant Props
| Prop | 类型 | 默认值 | 描述 |
| --------- | -------------------------- | ----------- | ----------------------------------------------------------------- |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 组件的视觉变体。`primary` 为默认带阴影样式。`secondary` 为低强调、无阴影变体,适合用在 surface 上。 |
#### Accessibility Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | --------------------------------------- | --------- | -------------------------------------------------- |
| `aria-label` | `string` | - | 没有可见标签时的无障碍标签。 |
| `aria-labelledby` | `string` | - | 用于标注该组的元素 ID。 |
| `aria-describedby` | `string` | - | 用于描述该组的元素 ID。 |
| `aria-details` | `string` | - | 包含更多详情的元素 ID。 |
| `role` | `'group' \| 'region' \| 'presentation'` | `'group'` | 分组的无障碍角色。重要内容可使用 `region`,纯视觉分组可使用 `presentation`。 |
### Composition Components
InputGroup 与以下子组件配合使用:
* **InputGroup.Root** – 根容器(也可直接写作 `InputGroup`)
* **InputGroup.Input** – 单行输入元素组件
* **InputGroup.TextArea** – 多行 textarea 元素组件
* **InputGroup.Prefix** – 前缀容器组件
* **InputGroup.Suffix** – 后缀容器组件
#### InputGroup.Input Props
InputGroup.Input 继承 React Aria [Input](https://react-spectrum.adobe.com/react-aria/Input.html) 组件的全部 props。
| Prop | 类型 | 默认值 | 描述 |
| -------------- | -------------------------- | ----------- | ----------------------------------------------------------------- |
| `className` | `string` | - | 用于样式的 CSS 类。 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 输入的视觉变体。`primary` 为默认带阴影样式。`secondary` 为低强调、无阴影变体,适合用在 surface 上。 |
| `type` | `string` | `'text'` | 输入类型(text、password、email 等)。 |
| `value` | `string` | - | 当前值(受控)。 |
| `defaultValue` | `string` | - | 默认值(非受控)。 |
| `placeholder` | `string` | - | 占位符文本。 |
| `disabled` | `boolean` | - | 是否禁用输入。 |
| `readOnly` | `boolean` | - | 是否只读。 |
#### InputGroup.TextArea Props
InputGroup.TextArea 继承 React Aria [TextArea](https://react-spectrum.adobe.com/react-aria/TextArea.html) 组件的全部 props。
| Prop | 类型 | 默认值 | 描述 |
| -------------- | -------------------------- | ----------- | ------------------------------------------------------------------------ |
| `className` | `string` | - | 用于样式的 CSS 类。 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | textarea 的视觉变体。`primary` 为默认带阴影样式。`secondary` 为低强调、无阴影变体,适合用在 surface 上。 |
| `value` | `string` | - | 当前值(受控)。 |
| `defaultValue` | `string` | - | 默认值(非受控)。 |
| `placeholder` | `string` | - | 占位符文本。 |
| `rows` | `number` | - | 可见文本行数。 |
| `disabled` | `boolean` | - | 是否禁用 textarea。 |
| `readOnly` | `boolean` | - | 是否只读。 |
#### InputGroup.Prefix Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | ------------------ |
| `children` | `React.ReactNode` | - | 前缀中要展示的内容(图标、文字等)。 |
| `className` | `string` | - | 用于样式的 CSS 类。 |
#### InputGroup.Suffix Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | --------------------- |
| `children` | `React.ReactNode` | - | 后缀中要展示的内容(图标、按钮、徽章等)。 |
| `className` | `string` | - | 用于样式的 CSS 类。 |
### Usage Example
```tsx
import {InputGroup, TextField, Label, Button} from '@heroui/react';
import {Icon} from '@iconify/react';
function Example() {
return (
Email
);
}
```
### TextArea Usage Example
```tsx
import {Envelope} from "@gravity-ui/icons";
import {Description, FieldError, InputGroup, Label, TextField} from "@heroui/react";
import {useState} from "react";
function TextAreaExample() {
const [feedback, setFeedback] = useState("");
return (
500} name="feedback" onChange={setFeedback}>
Your Feedback
Maximum 500 characters.
{feedback.length}/500
Feedback must be less than 500 characters
);
}
```
# InputOTP 一次性密码输入框
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/input-otp
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/input-otp.mdx
> 用于验证码与安全认证等场景的一次性密码输入组件。
## 引入
```tsx
import { InputOTP } from '@heroui/react';
```
### 用法
```tsx
import {InputOTP, Label, Link} from "@heroui/react";
export function Basic() {
return (
验证账户
我们已向 a****@gmail.com 发送验证码
);
}
```
### 组件结构
引入 InputOTP 后,可通过点语法访问各个部分。
```tsx
import { InputOTP } from '@heroui/react';
export default () => (
{/* ...rest of the slots */}
{/* ...rest of the slots */}
)
```
> **InputOTP** 基于 [@guilherme\_rodz](https://twitter.com/guilherme_rodz) 的 [input-otp](https://github.com/guilhermerodz/input-otp) 构建,为 OTP 输入组件提供灵活且无障碍的基础能力。
### 四位数字
```tsx
import {InputOTP, Label} from "@heroui/react";
export function FourDigits() {
return (
输入 PIN
);
}
```
### 禁用状态
```tsx
import {Description, InputOTP, Label} from "@heroui/react";
export function Disabled() {
return (
验证账户
验证码校验当前已禁用
);
}
```
### 使用 pattern
使用 `pattern` prop 限制可输入字符。HeroUI 会导出常用模式,例如 `REGEXP_ONLY_CHARS` 与 `REGEXP_ONLY_DIGITS`。
```tsx
import {Description, InputOTP, Label, REGEXP_ONLY_CHARS} from "@heroui/react";
export function WithPattern() {
return (
输入验证码(仅字母)
仅允许输入字母
);
}
```
### 受控
控制值以同步状态、清空输入或实现自定义校验。
```tsx
"use client";
import {Description, InputOTP, Label} from "@heroui/react";
import React from "react";
export function Controlled() {
const [value, setValue] = React.useState("");
return (
验证账户
{value.length > 0 ? (
<>
值:{value} ({value.length}/6) •{" "}
setValue("")}>
Clear
>
) : (
"请输入 6 位验证码"
)}
);
}
```
### 带校验
将 `isInvalid` 与校验消息一起使用以展示错误。
```tsx
"use client";
import {Button, Description, Form, InputOTP, Label} from "@heroui/react";
import React from "react";
export function WithValidation() {
const [value, setValue] = React.useState("");
const [isInvalid, setIsInvalid] = React.useState(false);
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const code = formData.get("code");
if (code !== "123456") {
setIsInvalid(true);
return;
}
setIsInvalid(false);
setValue("");
alert("验证码校验成功!");
};
const handleChange = (val: string) => {
setValue(val);
setIsInvalid(false);
};
return (
验证账户
提示:验证码为 123456
验证码无效,请重试。
提交
);
}
```
### 完成回调
在所有槽位填满时使用 `onComplete` 回调触发逻辑。
```tsx
"use client";
import {Button, Form, InputOTP, Label, Spinner} from "@heroui/react";
import React from "react";
export function OnComplete() {
const [value, setValue] = React.useState("");
const [isComplete, setIsComplete] = React.useState(false);
const [isSubmitting, setIsSubmitting] = React.useState(false);
const handleComplete = (code: string) => {
setIsComplete(true);
console.log("Code complete:", code);
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setIsSubmitting(true);
// Simulate API call
setTimeout(() => {
setIsSubmitting(false);
setValue("");
setIsComplete(false);
}, 2000);
};
return (
验证账户
{
setValue(val);
setIsComplete(false);
}}
>
{isSubmitting ? (
<>
验证中…
>
) : (
"验证验证码"
)}
);
}
```
### 表单示例
包含校验与提交的完整双因素认证表单。
```tsx
"use client";
import {Button, Description, Form, InputOTP, Label, Link, Spinner} from "@heroui/react";
import React from "react";
export function FormExample() {
const [value, setValue] = React.useState("");
const [error, setError] = React.useState("");
const [isSubmitting, setIsSubmitting] = React.useState(false);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setError("");
if (value.length !== 6) {
setError("请输入全部 6 位数字");
return;
}
setIsSubmitting(true);
// Simulate API call
setTimeout(() => {
if (value === "123456") {
console.log("Code verified successfully!");
setValue("");
} else {
setError("验证码无效,请重试。");
}
setIsSubmitting(false);
}, 1500);
};
return (
双重身份验证
请输入身份验证器应用中的 6 位验证码
{
setValue(val);
setError("");
}}
>
{error}
{isSubmitting ? (
<>
验证中…
>
) : (
"验证"
)}
);
}
```
### 变体
InputOTP 支持两种视觉变体:
* **`primary`**(默认)— 常规带阴影样式,适用于大多数场景
* **`secondary`** — 弱强调、无阴影变体,适合用于 Surface 组件内部
```tsx
import {InputOTP, Label} from "@heroui/react";
export function Variants() {
return (
);
}
```
### 在 Surface 内
在 [Surface](/docs/components/surface) 组件内部使用时,请使用 `variant="secondary"`,以应用适合 surface 背景的弱强调变体。
```tsx
import {InputOTP, Label, Link, Surface} from "@heroui/react";
export function OnSurface() {
return (
验证账户
我们已向 a****@gmail.com 发送验证码
);
}
```
## Related Components
* **Input**: Single-line text input built on React Aria
* **Form**: Form validation and submission handling
* **Surface**: Base container surface
## 样式
### 传入 Tailwind CSS 类
```tsx
import {InputOTP, Label} from '@heroui/react';
function CustomInputOTP() {
return (
Enter verification code
);
}
```
### 自定义组件类
若要自定义 InputOTP 的组件类名,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.input-otp {
@apply gap-3;
}
.input-otp__slot {
@apply size-12 rounded-xl border-2 font-bold;
}
.input-otp__slot[data-active="true"] {
@apply border-primary-500 ring-2 ring-primary-200;
}
.input-otp__separator {
@apply w-2 h-1 bg-border-strong rounded-full;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于定制。
### CSS 类
InputOTP 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/input-otp.css)):
#### 基础类
* `.input-otp` - 根容器
* `.input-otp__container` - input-otp 库提供的内层容器
* `.input-otp__group` - 槽位分组
* `.input-otp__slot` - 单个输入槽位
* `.input-otp__slot-value` - 槽位内的字符
* `.input-otp__caret` - 闪烁的光标指示器
* `.input-otp__separator` - 分组之间的视觉分隔符
#### 状态类
* `.input-otp__slot[data-active="true"]` - 当前激活的槽位
* `.input-otp__slot[data-filled="true"]` - 已填入字符的槽位
* `.input-otp__slot[data-disabled="true"]` - 禁用的槽位
* `.input-otp__slot[data-invalid="true"]` - 无效的槽位
* `.input-otp__container[data-disabled="true"]` - 禁用的容器
### 交互状态
组件同时支持 CSS 伪类与 data 属性,以获得更好的灵活性:
* **悬停**:槽位上的 `:hover` 或 `[data-hovered="true"]`
* **激活**:槽位上的 `[data-active="true"]`(当前聚焦)
* **已填**:槽位上的 `[data-filled="true"]`(包含字符)
* **禁用**:容器与槽位上的 `[data-disabled="true"]`
* **无效**:槽位上的 `[data-invalid="true"]`
## API 参考
### InputOTP Props
InputOTP 在 [input-otp](https://github.com/guilhermerodz/input-otp) 库之上构建,并增加了额外能力。
#### Base Props
| Prop | 类型 | 默认值 | 描述 |
| -------------------- | -------------------------- | ----------- | ----------------------------------------------------------------- |
| `maxLength` | `number` | - | **必填。** 输入槽位数量。 |
| `value` | `string` | - | 受控值(未提供则为非受控)。 |
| `onChange` | `(value: string) => void` | - | 值变化时调用。 |
| `onComplete` | `(value: string) => void` | - | 所有槽位填满时调用。 |
| `className` | `string` | - | 容器的额外 CSS 类名。 |
| `containerClassName` | `string` | - | 内层容器的 CSS 类名。 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 组件的视觉变体。`primary` 为默认带阴影样式。`secondary` 为弱强调、无阴影变体,适合用于 surface 上。 |
| `children` | `React.ReactNode` | - | InputOTP.Group、InputOTP.Slot 与 InputOTP.Separator 组件。 |
#### Validation Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------- | --------------- | ------- | ------------ |
| `isDisabled` | `boolean` | `false` | 是否禁用输入。 |
| `isInvalid` | `boolean` | `false` | 输入是否处于无效状态。 |
| `validationErrors` | `string[]` | - | 服务端或自定义校验错误。 |
| `validationDetails` | `ValidityState` | - | HTML5 校验详情。 |
#### Input Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | --------------------------------------------------------------------------- | ----------- | ------------------------------------ |
| `pattern` | `string` | - | 允许字符的正则表达式(例如 `REGEXP_ONLY_DIGITS`)。 |
| `textAlign` | `'left' \| 'center' \| 'right'` | `'left'` | 槽位内文本对齐方式。 |
| `inputMode` | `'numeric' \| 'text' \| 'decimal' \| 'tel' \| 'search' \| 'email' \| 'url'` | `'numeric'` | 移动设备上的虚拟键盘类型。 |
| `placeholder` | `string` | - | 空槽位的占位符文本。 |
| `pasteTransformer` | `(text: string) => string` | - | 转换粘贴文本(例如移除连字符)。 |
#### Form Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | --------- | --- | ----------------- |
| `name` | `string` | - | 表单提交时使用的 name 属性。 |
| `autoFocus` | `boolean` | - | 挂载时是否聚焦第一个槽位。 |
### InputOTP.Group Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | ----------------- |
| `className` | `string` | - | 分组的额外 CSS 类名。 |
| `children` | `React.ReactNode` | - | InputOTP.Slot 组件。 |
### InputOTP.Slot Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------- | --- | -------------------- |
| `index` | `number` | - | **必填。** 槽位从 0 开始的索引。 |
| `className` | `string` | - | 槽位的额外 CSS 类名。 |
### InputOTP.Separator Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------- | --- | -------------- |
| `className` | `string` | - | 分隔符的额外 CSS 类名。 |
### 导出的 pattern
HeroUI 会为了方便而从 input-otp 再导出常用正则 pattern:
```tsx
import { REGEXP_ONLY_DIGITS, REGEXP_ONLY_CHARS, REGEXP_ONLY_DIGITS_AND_CHARS } from '@heroui/react';
// Use with pattern prop
{/* ... */}
```
* **REGEXP\_ONLY\_DIGITS** — 仅数字字符(0-9)
* **REGEXP\_ONLY\_CHARS** — 仅字母字符(a-z、A-Z)
* **REGEXP\_ONLY\_DIGITS\_AND\_CHARS** — 字母数字字符(0-9、a-z、A-Z)
# Input 输入框
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/input
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/input.mdx
> 单行文本输入原语,可接受标准 HTML 属性。
## 引入
```tsx
import { Input } from '@heroui/react';
```
关于校验、标签与错误信息,请参见 **[TextField](/docs/components/text-field)**。
### 用法
```tsx
import {Input} from "@heroui/react";
export function Basic() {
return ;
}
```
### Input 类型
```tsx
import {Input, Label} from "@heroui/react";
export function Types() {
return (
);
}
```
### 受控
```tsx
"use client";
import {Input} from "@heroui/react";
import React from "react";
export function Controlled() {
const [value, setValue] = React.useState("heroui.com");
return (
setValue(event.target.value)}
/>
https://{value || "你的域名"}
);
}
```
### 全宽
```tsx
import {Input} from "@heroui/react";
export function FullWidth() {
return (
);
}
```
### 变体
Input 支持两种视觉变体:
* **`primary`**(默认)— 常规样式并带阴影,适用于大多数场景
* **`secondary`** — 弱强调变体,无阴影,适合用于 Surface 组件内
```tsx
import {Input} from "@heroui/react";
export function Variants() {
return (
);
}
```
### 在 Surface 内
在 [Surface](/docs/components/surface) 组件内使用时,请使用 `variant="secondary"`,以应用适合表面背景的弱强调变体。
```tsx
import {Input, Surface} from "@heroui/react";
export function OnSurface() {
return (
);
}
```
## Related Components
* **TextField**: Composition-friendly fields with labels and validation
* **TextArea**: Multiline text input with focus management
* **Label**: Accessible label for form controls
## 样式
### 传入 Tailwind CSS 类
```tsx
import {Input, Label} from '@heroui/react';
function CustomInput() {
return (
Project name
);
}
```
### 自定义组件类
基础类 `.input` 驱动每个实例。使用 `@layer components` 一次性覆盖即可。
```css
@layer components {
.input {
@apply rounded-lg border border-border bgsurface px-4 py-2 text-sm shadow-sm transition-colors;
&:hover,
&[data-hovered="true"] {
@apply bg-surface-secondary border-border/80;
}
&:focus-visible,
&[data-focus-visible="true"] {
@apply border-primary ring-2 ring-primary/20;
}
&[data-invalid="true"] {
@apply border-danger bg-danger-50/10 text-danger;
}
}
}
```
### CSS 类
* `.input` — 原生 input 元素样式
### 交互状态
* **悬停**:`:hover` 或 `[data-hovered="true"]`
* **可见焦点**:`:focus-visible` 或 `[data-focus-visible="true"]`
* **无效**:`[data-invalid="true"]`(并与 `aria-invalid` 同步)
* **禁用**:`:disabled` 或 `[aria-disabled="true"]`
* **只读**:`[aria-readonly="true"]`
## API 参考
### Input Props
除标准 HTML ` ` 属性外,还支持以下 props:
| Prop | 类型 | 默认值 | 描述 |
| -------------- | ------------------------------------------------------ | ----------- | ----------------------------------------------------------------- |
| `className` | `string` | - | 与组件样式合并的 Tailwind 类。 |
| `type` | `string` | `"text"` | Input 类型(text、email、password、number 等)。 |
| `value` | `string` | - | 受控值。 |
| `defaultValue` | `string` | - | 非受控初始值。 |
| `onChange` | `(event: React.ChangeEvent) => void` | - | 变更事件处理函数。 |
| `placeholder` | `string` | - | 占位符文本。 |
| `disabled` | `boolean` | `false` | 禁用输入框。 |
| `readOnly` | `boolean` | `false` | 将输入框设为只读。 |
| `required` | `boolean` | `false` | 将输入框标记为必填。 |
| `name` | `string` | - | 用于表单提交的 name。 |
| `autoComplete` | `string` | - | 浏览器自动完成提示。 |
| `maxLength` | `number` | - | 最大字符数。 |
| `minLength` | `number` | - | 最小字符数。 |
| `pattern` | `string` | - | 用于校验的正则表达式。 |
| `min` | `number \| string` | - | 最小值(用于 number/date 输入)。 |
| `max` | `number \| string` | - | 最大值(用于 number/date 输入)。 |
| `step` | `number \| string` | - | 步进间隔(用于 number 输入)。 |
| `fullWidth` | `boolean` | `false` | 输入框是否占满容器宽度。 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 组件的视觉变体。`primary` 为默认带阴影样式。`secondary` 为无阴影的弱强调变体,适合用于 surface 内。 |
> 如需 `isInvalid`、`isRequired` 等校验相关 props 与错误处理,请使用 **[TextField](/docs/components/text-field)**,并将 Input 作为其子组件。
# Label 标签
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/label
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/label.mdx
> 渲染与表单控件关联的无障碍标签。
## 引入
```tsx
import { Label } from '@heroui/react';
```
## 用法
```tsx
import {Input, Label} from "@heroui/react";
export function Basic() {
return (
姓名
);
}
```
## Related Components
* **Input**: Single-line text input built on React Aria
* **TextArea**: Multiline text input with focus management
* **Fieldset**: Group related form controls with legends
## API 参考
### Label Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | ----------- | ------- | ----------- |
| `htmlFor` | `string` | - | 标签所关联元素的 id |
| `isRequired` | `boolean` | `false` | 是否显示必填指示符 |
| `isDisabled` | `boolean` | `false` | 标签是否处于禁用状态 |
| `isInvalid` | `boolean` | `false` | 标签是否处于无效状态 |
| `className` | `string` | - | 附加的 CSS 类 |
| `children` | `ReactNode` | - | 标签内容 |
## 无障碍
Label 基于原生 HTML ``([MDN 参考](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label)),并遵循 WAI-ARIA 最佳实践:
* 使用 `htmlFor` 与表单控件关联
* 提供语义化的 `` 元素
* 与表单控件关联时支持键盘导航
* 向屏幕阅读器传达必填与无效状态
* 点击标签可聚焦/激活关联的表单控件
## Related Components
* **Input**: Single-line text input built on React Aria
* **TextArea**: Multiline text input with focus management
* **Fieldset**: Group related form controls with legends
## 样式
### CSS 类
Label 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/label.css)):
#### 基础类
* `.label` — 基础标签文本样式
#### 状态修饰类
* `.label--required` 或 `[data-required="true"] > .label` — 显示必填星号
* `.label--disabled` 或 `[data-disabled="true"] .label` — 禁用状态样式
* `.label--invalid` 或 `[data-invalid="true"] .label` 或 `[aria-invalid="true"] .label` — 无效状态样式(危险/红色文本)
**说明:** 必填星号会基于 role 与 `data-slot` 智能应用,并排除:
* `role="group"`、`role="radiogroup"`、`role="checkboxgroup"` 的元素
* `data-slot="radio"` 或 `data-slot="checkbox"` 的元素
从而在分组组件与必填字段组合时避免重复星号。
## 示例
### 带必填指示符
```tsx
Email Address
```
### 禁用状态
```tsx
Username
```
### 无效状态
```tsx
Password
```
# NumberField 数字输入框
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/number-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/number-field.mdx
> 数字输入字段,包含增减按钮、校验与国际化格式化能力。
## 引入
```tsx
import { NumberField } from '@heroui/react';
```
### 用法
```tsx
import {Label, NumberField} from "@heroui/react";
export function Basic() {
return (
宽度
);
}
```
### 组件结构
```tsx
import {NumberField, Label, Description, FieldError} from '@heroui/react';
export default () => (
)
```
> **NumberField** 允许用户输入数值,并可选择是否显示增减按钮。它支持国际化格式化、校验与键盘导航。
### 带说明
```tsx
import {Description, Label, NumberField} from "@heroui/react";
export function WithDescription() {
return (
宽度
以像素为单位输入宽度
百分比
取值须在 0 到 100 之间
);
}
```
### 必填字段
```tsx
import {Description, Label, NumberField} from "@heroui/react";
export function Required() {
return (
数量
评分
评分范围 1 到 10
);
}
```
### 校验
将 `isInvalid` 与 `FieldError` 配合使用,以展示校验信息。
```tsx
import {FieldError, Label, NumberField} from "@heroui/react";
export function Validation() {
return (
数量
数量必须大于或等于 0
百分比
百分比必须在 0 到 100 之间
);
}
```
### 受控
控制值以与其他组件同步,或执行自定义格式化。
```tsx
"use client";
import {Button, Description, Label, NumberField} from "@heroui/react";
import React from "react";
export function Controlled() {
const [value, setValue] = React.useState(1024);
return (
宽度
当前值:{value}
setValue(0)}>
重置为 0
setValue(2048)}>
设为 2048
);
}
```
### 带校验
在受控数值的基础上实现自定义校验逻辑。
```tsx
"use client";
import {Description, FieldError, Label, NumberField} from "@heroui/react";
import React from "react";
export function WithValidation() {
const [value, setValue] = React.useState(undefined);
const isInvalid = value !== undefined && (value < 0 || value > 100);
return (
百分比
{isInvalid ? (
百分比必须在 0 到 100 之间
) : (
请输入 0 到 100 之间的值
)}
);
}
```
### 步进值
配置增减步进值,以实现更精确的控制。
```tsx
import {Description, Label, NumberField} from "@heroui/react";
export function WithStep() {
return (
步长:1
每次增减 1
步长:5
每次增减 5
步长:10
每次增减 10
);
}
```
### 格式化选项
将数字格式化为货币、百分比、小数或单位,并支持国际化。
```tsx
import {Description, Label, NumberField} from "@heroui/react";
export function WithFormatOptions() {
return (
货币(EUR - 会计格式)
欧元会计记账格式
货币(USD)
标准美元货币格式
百分比
百分比格式(0–1,0.5 表示 50%)
小数(保留 2 位)
保留 2 位小数格式
单位(千克)
千克单位格式
);
}
```
### 自定义图标
自定义增减按钮的图标。
```tsx
import {Description, Label, NumberField} from "@heroui/react";
export function CustomIcons() {
return (
);
}
```
### 搭配 Chevron
在纵向布局中使用 chevron 图标,以获得不同的视觉风格。
```tsx
import {Label, NumberField} from "@heroui/react";
export function WithChevrons() {
return (
带 Chevron 的数字输入框
);
}
```
### 禁用状态
```tsx
import {Description, Label, NumberField} from "@heroui/react";
export function Disabled() {
return (
宽度
以像素为单位输入宽度
百分比
取值须在 0 到 100 之间
);
}
```
### 全宽
```tsx
import {Label, NumberField} from "@heroui/react";
export function FullWidth() {
return (
宽度
);
}
```
### 变体
NumberField 支持两种视觉变体:
* **`primary`**(默认)— 带阴影的标准样式,适用于大多数场景
* **`secondary`** — 低强调、无阴影的变体,适合用在 Surface 组件内
```tsx
import {Label, NumberField} from "@heroui/react";
export function Variants() {
return (
主要变体
次要变体
);
}
```
### 在 Surface 内
在 [Surface](/docs/components/surface) 内使用时,请使用 `variant="secondary"`,以应用适合表面背景的低强调变体。
```tsx
import {Description, Label, NumberField, Surface} from "@heroui/react";
export function OnSurface() {
return (
宽度
以像素为单位输入宽度
百分比
取值须在 0 到 100 之间
);
}
```
### 表单示例
包含校验与提交处理的完整表单集成示例。
```tsx
"use client";
import {Button, Description, FieldError, Form, Label, NumberField, Spinner} from "@heroui/react";
import React from "react";
export function FormExample() {
const [value, setValue] = React.useState(undefined);
const [isSubmitting, setIsSubmitting] = React.useState(false);
const STOCK_AVAILABLE = 3;
const isOutOfStock = value !== undefined && value > STOCK_AVAILABLE;
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (value === undefined || value === null || value < 1 || value > STOCK_AVAILABLE) {
return;
}
setIsSubmitting(true);
// Simulate API call
setTimeout(() => {
console.log("Order submitted:", {quantity: value});
setValue(undefined);
setIsSubmitting(false);
}, 1500);
};
return (
订购数量
{isOutOfStock ? (
仅剩 {STOCK_AVAILABLE} 件库存
) : (
仅剩 {STOCK_AVAILABLE} 件可购
)}
STOCK_AVAILABLE}
isPending={isSubmitting}
type="submit"
variant="primary"
>
{isSubmitting ? (
<>
处理中…
>
) : (
"下单"
)}
);
}
```
## Related Components
* **Label**: Accessible label for form controls
* **Description**: Helper text for form fields
* **FieldError**: Inline validation messages for form fields
### 自定义渲染函数
```tsx
"use client";
import {Label, NumberField} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
宽度
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import {NumberField, Label} from '@heroui/react';
function CustomNumberField() {
return (
Quantity
);
}
```
### 自定义组件类
NumberField 使用可自定义的 CSS 类。你可以覆盖这些类名以匹配自己的设计系统。
```css
@layer components {
.number-field {
@apply flex flex-col gap-1;
}
/* When invalid, the description is hidden automatically */
.number-field[data-invalid="true"] [data-slot="description"],
.number-field[aria-invalid="true"] [data-slot="description"] {
@apply hidden;
}
.number-field__group {
@apply bg-field text-field-foreground shadow-field rounded-field inline-flex h-9 items-center overflow-hidden border;
}
.number-field__input {
@apply flex-1 rounded-none border-0 bg-transparent px-3 py-2 tabular-nums;
}
.number-field__increment-button,
.number-field__decrement-button {
@apply flex h-full w-10 items-center justify-center rounded-none bg-transparent;
}
}
```
### CSS 类
* `.number-field` – 根容器,样式非常克制(`flex flex-col gap-1`)
* `.number-field__group` – 输入与按钮的容器,包含边框与背景样式
* `.number-field__input` – 数字输入字段
* `.number-field__increment-button` – 用于增加数值的按钮
* `.number-field__decrement-button` – 用于减少数值的按钮
* `.number-field--primary` – 带阴影的主变体(默认)
* `.number-field--secondary` – 无阴影的次变体,适合用在 surface 上
> **说明:** 子组件([Label](/docs/components/label)、[Description](/docs/components/description)、[FieldError](/docs/components/field-error))拥有各自的 CSS 类与样式。自定义方式请参见对应文档。
### 交互状态
NumberField 会根据状态自动管理以下 data 属性:
* **Invalid**:`[data-invalid="true"]` 或 `[aria-invalid="true"]` – 无效时会自动隐藏 description 插槽
* **Disabled**:`[data-disabled="true"]` – 当 `isDisabled` 为 true 时应用
* **Focus Within**:`[data-focus-within="true"]` – 当输入框或按钮聚焦时应用
* **Focus Visible**:`[data-focus-visible="true"]` – 当焦点可见(键盘导航)时应用
* **Hovered**:`[data-hovered="true"]` – 当悬停在按钮上时应用
更多属性可通过渲染 prop 获得(见下方的 NumberFieldRenderProps)。
## API 参考
### NumberField Props
NumberField 继承 React Aria [NumberField](https://react-spectrum.adobe.com/react-aria/NumberField.html) 组件的全部 props。
#### Base Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------------------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------- |
| `children` | `React.ReactNode \| (values: NumberFieldRenderProps) => React.ReactNode` | - | 子组件(Label、Group、Input 等)或渲染函数。 |
| `className` | `string \| (values: NumberFieldRenderProps) => string` | - | 用于样式的 CSS 类,支持渲染 prop。 |
| `style` | `React.CSSProperties \| (values: NumberFieldRenderProps) => React.CSSProperties` | - | 行内样式,支持渲染 prop。 |
| `fullWidth` | `boolean` | `false` | 数字字段是否占满容器宽度 |
| `id` | `string` | - | 元素的唯一标识符。 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 组件的视觉变体。`primary` 为默认带阴影样式。`secondary` 为低强调、无阴影变体,适合用在 surface 上。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
#### Value Props
| Prop | 类型 | 默认值 | 描述 |
| -------------- | -------------------------------------- | --- | -------------- |
| `value` | `number` | - | 当前值(受控)。 |
| `defaultValue` | `number` | - | 默认值(非受控)。 |
| `onChange` | `(value: number \| undefined) => void` | - | 值变化时触发的事件处理函数。 |
#### Formatting Props
| Prop | 类型 | 默认值 | 描述 |
| --------------- | -------------------------- | --- | ----------------------- |
| `formatOptions` | `Intl.NumberFormatOptions` | - | 数字格式化选项(货币、百分比、小数、单位等)。 |
| `locale` | `string` | - | 数字格式化的区域设置。 |
#### Validation Props
| Prop | 类型 | 默认值 | 描述 |
| -------------------- | ----------------------------------------------------------------- | ---------- | ------------------------ |
| `isRequired` | `boolean` | `false` | 提交表单前是否要求用户输入。 |
| `isInvalid` | `boolean` | - | 当前值是否无效。 |
| `validate` | `(value: number) => ValidationError \| true \| null \| undefined` | - | 自定义校验函数。 |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | 使用原生 HTML 表单校验或 ARIA 属性。 |
| `validationErrors` | `string[]` | - | 服务端校验错误。 |
#### Range Props
| Prop | 类型 | 默认值 | 描述 |
| ---------- | -------- | --- | --------- |
| `minValue` | `number` | - | 允许的最小值。 |
| `maxValue` | `number` | - | 允许的最大值。 |
| `step` | `number` | `1` | 增减操作的步进值。 |
#### State Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | --------- | --- | ----------- |
| `isDisabled` | `boolean` | - | 是否禁用输入。 |
| `isReadOnly` | `boolean` | - | 是否可选中但不可修改。 |
#### Form Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | --------- | --- | ------------------------- |
| `name` | `string` | - | input 元素的名称,用于 HTML 表单提交。 |
| `autoFocus` | `boolean` | - | 元素渲染后是否应获得焦点。 |
#### Accessibility Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | -------- | --- | -------------- |
| `aria-label` | `string` | - | 没有可见标签时的无障碍标签。 |
| `aria-labelledby` | `string` | - | 用于标注该字段的元素 ID。 |
| `aria-describedby` | `string` | - | 用于描述该字段的元素 ID。 |
| `aria-details` | `string` | - | 包含更多详情的元素 ID。 |
### Composition Components
NumberField 需要与以下独立组件组合使用,请分别导入并直接使用:
* **NumberField.Group** – 输入与按钮的容器
* **NumberField.Input** – 数字输入字段
* **NumberField.IncrementButton** – 用于增加数值的按钮
* **NumberField.DecrementButton** – 用于减少数值的按钮
* **Label** – 字段标签组件(`@heroui/react`)
* **Description** – 辅助说明文本组件(`@heroui/react`)
* **FieldError** – 校验错误信息组件(`@heroui/react`)
这些组件各自拥有 props API。请直接在 NumberField 内组合使用:
```tsx
Quantity
Enter a value between 0 and 100
Value must be between 0 and 100
```
#### NumberField.Group Props
NumberField.Group 继承 React Aria [Group](https://react-spectrum.adobe.com/react-aria/Group.html) 组件的 props。
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------ | --- | ------------------------ |
| `children` | `React.ReactNode \| (values: GroupRenderProps) => React.ReactNode` | - | 子组件(Input、Buttons)或渲染函数。 |
| `className` | `string \| (values: GroupRenderProps) => string` | - | 用于样式的 CSS 类。 |
#### NumberField.Input Props
NumberField.Input 继承 React Aria [Input](https://react-spectrum.adobe.com/react-aria/Input.html) 组件的 props。
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------------------------- | ----------- | ----------------------------------------------------------------- |
| `className` | `string` | - | 用于样式的 CSS 类。 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 输入的视觉变体。`primary` 为默认带阴影样式。`secondary` 为低强调、无阴影变体,适合用在 surface 上。 |
#### NumberField.IncrementButton Props
NumberField.IncrementButton 继承 React Aria [Button](https://react-spectrum.adobe.com/react-aria/Button.html) 组件的 props。
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | -------------- | --------------------------- |
| `children` | `React.ReactNode` | ` ` | 按钮的图标或内容。默认为加号图标。 |
| `className` | `string` | - | 用于样式的 CSS 类。 |
| `slot` | `"increment"` | `"increment"` | 必须设置为 `"increment"`(会自动设置)。 |
#### NumberField.DecrementButton Props
NumberField.DecrementButton 继承 React Aria [Button](https://react-spectrum.adobe.com/react-aria/Button.html) 组件的 props。
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --------------- | --------------------------- |
| `children` | `React.ReactNode` | ` ` | 按钮的图标或内容。默认为减号图标。 |
| `className` | `string` | - | 用于样式的 CSS 类。 |
| `slot` | `"decrement"` | `"decrement"` | 必须设置为 `"decrement"`(会自动设置)。 |
### NumberFieldRenderProps
在 `className`、`style` 或 `children` 上使用渲染 prop 时,可使用以下值:
| Prop | 类型 | 描述 |
| ---------------- | --------------------- | -------------------------------- |
| `isDisabled` | `boolean` | 字段是否禁用。 |
| `isInvalid` | `boolean` | 字段当前是否无效。 |
| `isReadOnly` | `boolean` | 字段是否只读。 |
| `isRequired` | `boolean` | 字段是否必填。 |
| `isFocused` | `boolean` | 字段是否聚焦(已弃用,请使用 `isFocusWithin`)。 |
| `isFocusWithin` | `boolean` | 是否有任意子元素聚焦。 |
| `isFocusVisible` | `boolean` | 是否为可见焦点(键盘导航)。 |
| `value` | `number \| undefined` | 当前值。 |
| `minValue` | `number \| undefined` | 允许的最小值。 |
| `maxValue` | `number \| undefined` | 允许的最大值。 |
| `step` | `number` | 增减步进值。 |
# RadioGroup 单选框组
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/radio-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/radio-group.mdx
> 用于从列表中选择单个选项的单选组。
## 引入
```tsx
import { RadioGroup, Radio } from '@heroui/react';
```
### 用法
```tsx
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Basic() {
return (
选择套餐
选择最适合你的套餐
基础版
每月包含 100 条消息
高级版
每月包含 200 条消息
商业版
无限消息
);
}
```
### 组件结构
导入 RadioGroup 组件后,可通过点号访问各个子部分。
```tsx
import {RadioGroup, Radio, Label, Description, FieldError} from '@heroui/react';
export default () => (
{/* 可点击区域:control + label */}
✓ {/* Custom indicator (optional) */}
Label {/* 纯文本 —— 可点击的标签 */}
{/* 兄弟节点 — 位于按钮外部(通过 aria-describedby 关联) */}
{/* 可选 — 单选项校验错误信息 */}
{/* 可选 — 组级校验 */}
)
```
### 自定义指示器
```tsx
"use client";
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function CustomIndicator() {
return (
选择套餐
选择最适合你的套餐
{({isSelected}) =>
isSelected ? ✓ : null
}
基础版
每月包含 100 条消息
{({isSelected}) =>
isSelected ? ✓ : null
}
高级版
每月包含 200 条消息
{({isSelected}) =>
isSelected ? ✓ : null
}
商业版
无限消息
);
}
```
### 水平排列
```tsx
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Horizontal() {
return (
订阅套餐
入门版
适合副项目
专业版
高级报表
团队版
最多 10 名队友
);
}
```
### 受控
```tsx
"use client";
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
import React from "react";
export function Controlled() {
const [value, setValue] = React.useState("pro");
return (
订阅套餐
入门版
适合副项目和小型团队
专业版
高级报表与分析
团队版
最多可与 10 名队友共享访问权限
已选套餐: {value}
);
}
```
### 非受控
当你只需要响应更新时,可组合使用 `defaultValue` 与 `onChange`。
```tsx
"use client";
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
import React from "react";
export function Uncontrolled() {
const [selection, setSelection] = React.useState("pro");
return (
setSelection(nextValue)}
>
订阅套餐
入门版
适合副项目和小型团队
专业版
高级报表与分析
团队版
最多可与 10 名队友共享访问权限
上次选择的套餐: {selection}
);
}
```
### 校验
```tsx
"use client";
import {Button, Description, FieldError, Form, Label, Radio, RadioGroup} from "@heroui/react";
import React from "react";
export function Validation() {
const [message, setMessage] = React.useState(null);
return (
{
e.preventDefault();
const formData = new FormData(e.currentTarget);
const value = formData.get("plan-validation");
setMessage(`你选择的套餐是: ${value}`);
}}
>
订阅套餐
入门版
适合副项目和小型团队
专业版
高级报表与分析
团队版
最多可与 10 名队友共享访问权限
请先选择订阅套餐再继续。
Submit
{!!message && {message}
}
);
}
```
### 禁用
```tsx
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Disabled() {
return (
订阅套餐
我们正在发布更新,暂时无法更改套餐。
入门版
适合副项目和小型团队
专业版
高级报表与分析
团队版
最多可与 10 名队友共享访问权限
);
}
```
### 变体
RadioGroup 支持两种视觉变体:
* **`primary`**(默认)— 带默认背景的标准样式,适用于大多数场景
* **`secondary`** — 低强调变体,适合用在 Surface 组件内
```tsx
import {Description, Radio, RadioGroup} from "@heroui/react";
export function Variants() {
return (
主要变体
选项 1
默认背景的标准样式
选项 2
另一种主要样式选项
次要变体
选项 1
用于表面上的低强调变体
选项 2
另一种次要样式选项
);
}
```
### 在 Surface 内
在 [Surface](/docs/components/surface) 内使用时,请使用 `variant="secondary"`,以应用适合表面背景的低强调变体。
```tsx
import {Description, Label, Radio, RadioGroup, Surface} from "@heroui/react";
export function OnSurface() {
return (
选择套餐
选择最适合你的套餐
基础版
每月包含 100 条消息
高级版
每月包含 200 条消息
商业版
无限消息
);
}
```
### 配送与支付
## Related Components
* **Fieldset**: Group related form controls with legends
* **Surface**: Base container surface
* **Description**: Helper text for form fields
### 自定义渲染函数
```tsx
"use client";
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
选择套餐
选择最适合你的套餐
基础版
每月包含 100 条消息
高级版
每月包含 200 条消息
商业版
无限消息
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import { RadioGroup, Radio } from '@heroui/react';
export default () => (
Basic Plan
Premium Plan
Business Plan
);
```
### 自定义组件类
要自定义 RadioGroup 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.radio-group {
@apply gap-2;
}
.radio {
@apply gap-4 rounded-lg border border-border p-3 hover:bg-surface-hovered;
}
.radio__control {
@apply border-2 border-primary;
}
.radio__indicator {
@apply bg-primary;
}
.radio__content {
@apply gap-1;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,以确保组件变体与状态可复用且易于自定义。
### CSS 类
RadioGroup 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/radio-group.css)):
#### 基础类
* `.radio-group` - 单选组基础容器
* `.radio` - 单个单选项
* `.radio__control` - 单选控件(圆形按钮)
* `.radio__indicator` - 单选指示器(内部圆点)
* `.radio__content` - 单选内容包裹层
#### 修饰类
* `.radio--disabled` - 禁用状态
### 交互状态
单选项同时支持 CSS 伪类与 data 属性,便于灵活定制:
* **Selected**:`[aria-checked="true"]` 或 `[data-selected="true"]`(显示指示器)
* **Hover**:`:hover` 或 `[data-hovered="true"]`(边框颜色变化)
* **Focus**:`:focus-visible` 或 `[data-focus-visible="true"]`(显示焦点环)
* **Pressed**:`:active` 或 `[data-pressed="true"]`(缩放变换)
* **Disabled**:`:disabled` 或 `[aria-disabled="true"]`(降低透明度并禁用指针事件)
* **Invalid**:`[data-invalid="true"]` 或 `[aria-invalid="true"]`(错误边框颜色)
## API 参考
### RadioGroup Props
| Prop | 类型 | 默认值 | 描述 |
| -------------- | ----------------------------------------------------------------------------- | ------------ | ----------------------------------------------------------------- |
| `value` | `string` | - | 当前值(受控) |
| `defaultValue` | `string` | - | 默认值(非受控) |
| `onChange` | `(value: string) => void` | - | 值变化时触发的事件处理函数 |
| `isDisabled` | `boolean` | `false` | 是否禁用整个单选组 |
| `isRequired` | `boolean` | `false` | 是否必填 |
| `isReadOnly` | `boolean` | `false` | 是否只读 |
| `isInvalid` | `boolean` | `false` | 是否处于无效状态 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 组件的视觉变体。`primary` 为默认带阴影样式。`secondary` 为低强调、无阴影变体,适合用在 surface 上。 |
| `name` | `string` | - | 单选组的名称,用于提交 HTML 表单 |
| `orientation` | `'horizontal' \| 'vertical'` | `'vertical'` | 单选组的排列方向 |
| `children` | `React.ReactNode \| (values: RadioGroupRenderProps) => React.ReactNode` | - | 单选组内容或渲染 prop |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Radio Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | ----------------------------------------------------------------------------- | ------- | --------------------- |
| `value` | `string` | - | 单选项的值 |
| `isDisabled` | `boolean` | `false` | 是否禁用该单选项 |
| `name` | `string` | - | 单选项名称,用于提交 HTML 表单 |
| `children` | `React.ReactNode \| (values: RadioFieldRenderProps) => React.ReactNode` | - | 单选内容或字段级渲染 prop |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Radio.Control Props
继承 `React.HTMLAttributes`。
| Prop | 类型 | 默认值 | 描述 |
| ---------- | ----------------- | --- | --------------------------------- |
| `children` | `React.ReactNode` | - | 控件包裹层内要渲染的内容(通常为 Radio.Indicator) |
### Radio.Indicator Props
继承 `React.HTMLAttributes`。
| Prop | 类型 | 默认值 | 描述 |
| ---------- | ------------------------------------------------------------------------ | --- | ------------------------ |
| `children` | `React.ReactNode \| (values: RadioButtonRenderProps) => React.ReactNode` | - | 可选内容或接收当前单选按钮状态的渲染 prop。 |
### Radio.Content Props
单选项的可点击区域(包裹隐藏 input 的 ``)。请将 `Radio.Control` 与 `Label` 放在其中。`className` 支持接收 `RadioButtonRenderProps` 的渲染函数。
| Prop | 类型 | 默认值 | 描述 |
| ---------- | ------------------------------------------------------------------------ | --- | -------------------------------- |
| `children` | `React.ReactNode \| (values: RadioButtonRenderProps) => React.ReactNode` | - | 可点击内容(通常为 Radio.Control 与 Label) |
### RadioFieldRenderProps
在根级 `Radio` 上使用渲染 prop 时,会提供以下字段级值:
| Prop | 类型 | 描述 |
| ------------ | --------- | -------- |
| `isSelected` | `boolean` | 单选项是否已选中 |
| `isDisabled` | `boolean` | 是否禁用 |
| `isReadOnly` | `boolean` | 是否只读 |
| `isInvalid` | `boolean` | 是否无效 |
| `isRequired` | `boolean` | 是否必填 |
### RadioButtonRenderProps
`Radio.Control` 和 `Radio.Indicator` 使用按钮级渲染 prop(`isHovered`、`isPressed`、`isFocusVisible` 等)。将函数作为 `Radio.Control` 子节点或传给 `Radio.Indicator` 即可访问它们。
# SearchField 搜索框
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/search-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/search-field.mdx
> 搜索输入字段,包含清除按钮与搜索图标。
## 引入
```tsx
import { SearchField } from '@heroui/react';
```
### 用法
```tsx
import {Label, SearchField} from "@heroui/react";
export function Basic() {
return (
搜索
);
}
```
### 组件结构
```tsx
import {SearchField, Label, Description, FieldError} from '@heroui/react';
export default () => (
)
```
> **SearchField** 允许用户输入并清空搜索关键词。它包含搜索图标,并提供可选的清除按钮以便快速重置。
### 带说明
```tsx
import {Description, Label, SearchField} from "@heroui/react";
export function WithDescription() {
return (
搜索产品
输入关键词进行搜索 for products
搜索用户
按姓名、邮箱或用户名搜索
);
}
```
### 必填字段
```tsx
import {Description, Label, SearchField} from "@heroui/react";
export function Required() {
return (
搜索
搜索内容
至少需要 3 个字符
);
}
```
### 校验
将 `isInvalid` 与 `FieldError` 配合使用,以展示校验信息。
```tsx
import {FieldError, Label, SearchField} from "@heroui/react";
export function Validation() {
return (
搜索
搜索内容至少需要 3 个字符
搜索
搜索内容包含无效字符
);
}
```
### 禁用状态
```tsx
import {Description, Label, SearchField} from "@heroui/react";
export function Disabled() {
return (
搜索
此搜索框已禁用
搜索
此搜索框已禁用
);
}
```
### 受控
控制值以与其他组件同步,或执行自定义格式化。
```tsx
"use client";
import {Button, Description, Label, SearchField} from "@heroui/react";
import React from "react";
export function Controlled() {
const [value, setValue] = React.useState("");
return (
搜索
当前值: {value || "(空)"}
setValue("")}>
Clear
setValue("示例查询")}>
设置示例
);
}
```
### 带校验
在受控数值的基础上实现自定义校验逻辑。
```tsx
"use client";
import {Description, FieldError, Label, SearchField} from "@heroui/react";
import React from "react";
export function WithValidation() {
const [value, setValue] = React.useState("");
const isInvalid = value.length > 0 && value.length < 3;
return (
搜索
{isInvalid ? (
搜索内容至少需要 3 个字符
) : (
请输入至少 3 个字符后再搜索
)}
);
}
```
### 自定义图标
自定义搜索图标与清除按钮图标。
```tsx
import {Description, Label, SearchField} from "@heroui/react";
export function CustomIcons() {
return (
);
}
```
### 全宽
```tsx
import {Label, SearchField} from "@heroui/react";
export function FullWidth() {
return (
搜索
);
}
```
### 变体
SearchField 支持两种视觉变体:
* **`primary`**(默认)— 带阴影的标准样式,适用于大多数场景
* **`secondary`** — 低强调、无阴影的变体,适合用在 Surface 组件内
```tsx
import {Label, SearchField} from "@heroui/react";
export function Variants() {
return (
主要变体
次要变体
);
}
```
### 在 Surface 内
在 [Surface](/docs/components/surface) 内使用时,请使用 `variant="secondary"`,以应用适合表面背景的低强调变体。
```tsx
import {Description, Label, SearchField, Surface} from "@heroui/react";
export function OnSurface() {
return (
搜索
输入关键词进行搜索
高级搜索
使用筛选条件细化搜索
);
}
```
### 表单示例
包含校验与提交处理的完整表单集成示例。
```tsx
"use client";
import {Button, Description, FieldError, Form, Label, SearchField, Spinner} from "@heroui/react";
import React from "react";
export function FormExample() {
const [value, setValue] = React.useState("");
const [isSubmitting, setIsSubmitting] = React.useState(false);
const MIN_LENGTH = 3;
const isInvalid = value.length > 0 && value.length < MIN_LENGTH;
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (value.length < MIN_LENGTH) {
return;
}
setIsSubmitting(true);
// Simulate API call
setTimeout(() => {
console.log("Search submitted:", {query: value});
setValue("");
setIsSubmitting(false);
}, 1500);
};
return (
搜索产品
{isInvalid ? (
搜索内容至少需要 {MIN_LENGTH} 个字符
) : (
请输入至少 {MIN_LENGTH} 个字符后再搜索
)}
{isSubmitting ? (
<>
搜索中…
>
) : (
"搜索"
)}
);
}
```
### 键盘快捷键
添加快捷键以快速聚焦搜索字段。
```tsx
"use client";
import {Description, Kbd, Label, SearchField} from "@heroui/react";
import React from "react";
export function WithKeyboardShortcut() {
const inputRef = React.useRef(null);
const [value, setValue] = React.useState("");
React.useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
// Check for Shift+S
if (e.shiftKey && e.key === "S" && !e.metaKey && !e.ctrlKey && !e.altKey) {
e.preventDefault();
inputRef.current?.focus();
}
// Check for ESC key to blur the input
if (e.key === "Escape" && document.activeElement === inputRef.current) {
inputRef.current?.blur();
}
};
// Add global event listener
window.addEventListener("keydown", handleKeyDown);
// Cleanup on unmount
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, []);
return (
搜索
使用键盘快捷键快速聚焦此输入框
按
S
聚焦搜索框
);
}
```
## Related Components
* **Label**: Accessible label for form controls
* **Description**: Helper text for form fields
* **FieldError**: Inline validation messages for form fields
### 自定义渲染函数
```tsx
"use client";
import {Label, SearchField} from "@heroui/react";
export function CustomRenderFunction() {
return (
}>
搜索
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import {SearchField, Label} from '@heroui/react';
function CustomSearchField() {
return (
Search
);
}
```
### 自定义组件类
SearchField 使用可自定义的 CSS 类。你可以覆盖这些类名以匹配自己的设计系统。
```css
@layer components {
.search-field {
@apply flex flex-col gap-1;
}
/* When invalid, the description is hidden automatically */
.search-field[data-invalid],
.search-field[aria-invalid] {
[data-slot="description"] {
@apply hidden;
}
}
.search-field__group {
@apply bg-field text-field-foreground shadow-field rounded-field inline-flex h-9 items-center overflow-hidden border;
}
.search-field__input {
@apply flex-1 rounded-none border-0 bg-transparent px-3 py-2 shadow-none outline-none;
}
.search-field__search-icon {
@apply text-field-placeholder pointer-events-none shrink-0 ml-3 mr-0 size-4;
}
.search-field__clear-button {
@apply mr-1 shrink-0;
}
}
```
### CSS 类
* `.search-field` – 根容器,样式非常克制(`flex flex-col gap-1`)
* `.search-field__group` – 搜索图标、输入框与清除按钮的容器,包含边框与背景样式
* `.search-field__input` – 搜索输入字段
* `.search-field__search-icon` – 左侧显示的搜索图标
* `.search-field__clear-button` – 用于清空搜索字段的按钮
* `.search-field--primary` – 带阴影的主变体(默认)
* `.search-field--secondary` – 无阴影的次变体,适合用在 surface 上
> **说明:** 子组件([Label](/docs/components/label)、[Description](/docs/components/description)、[FieldError](/docs/components/field-error))拥有各自的 CSS 类与样式。自定义方式请参见对应文档。
### 交互状态
SearchField 会根据状态自动管理以下 data 属性:
* **Invalid**:`[data-invalid="true"]` 或 `[aria-invalid="true"]` – 无效时会自动隐藏 description 插槽
* **Disabled**:`[data-disabled="true"]` – 当 `isDisabled` 为 true 时应用
* **Focus Within**:`[data-focus-within="true"]` – 当输入框聚焦时应用
* **Focus Visible**:`[data-focus-visible="true"]` – 当焦点可见(键盘导航)时应用
* **Hovered**:`[data-hovered="true"]` – 当悬停在整个组合上时应用
* **Empty**:`[data-empty="true"]` – 当字段为空时应用(会隐藏清除按钮)
更多属性可通过渲染 prop 获得(见下方的 SearchFieldRenderProps)。
## API 参考
### SearchField Props
SearchField 继承 React Aria [SearchField](https://react-spectrum.adobe.com/react-aria/SearchField.html) 组件的全部 props。
#### Base Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------------------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------- |
| `children` | `React.ReactNode \| (values: SearchFieldRenderProps) => React.ReactNode` | - | 子组件(Label、Group、Input 等)或渲染函数。 |
| `className` | `string \| (values: SearchFieldRenderProps) => string` | - | 用于样式的 CSS 类,支持渲染 prop。 |
| `style` | `React.CSSProperties \| (values: SearchFieldRenderProps) => React.CSSProperties` | - | 行内样式,支持渲染 prop。 |
| `fullWidth` | `boolean` | `false` | 搜索字段是否占满容器宽度 |
| `id` | `string` | - | 元素的唯一标识符。 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 组件的视觉变体。`primary` 为默认带阴影样式。`secondary` 为低强调、无阴影变体,适合用在 surface 上。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
#### Value Props
| Prop | 类型 | 默认值 | 描述 |
| -------------- | ------------------------- | --- | -------------- |
| `value` | `string` | - | 当前值(受控)。 |
| `defaultValue` | `string` | - | 默认值(非受控)。 |
| `onChange` | `(value: string) => void` | - | 值变化时触发的事件处理函数。 |
#### Validation Props
| Prop | 类型 | 默认值 | 描述 |
| -------------------- | ----------------------------------------------------------------- | ---------- | ------------------------ |
| `isRequired` | `boolean` | `false` | 提交表单前是否要求用户输入。 |
| `isInvalid` | `boolean` | - | 当前值是否无效。 |
| `validate` | `(value: string) => ValidationError \| true \| null \| undefined` | - | 自定义校验函数。 |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | 使用原生 HTML 表单校验或 ARIA 属性。 |
| `validationErrors` | `string[]` | - | 服务端校验错误。 |
#### State Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | --------- | --- | ----------- |
| `isDisabled` | `boolean` | - | 是否禁用输入。 |
| `isReadOnly` | `boolean` | - | 是否可选中但不可修改。 |
#### Form Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | --------- | --- | ------------------------- |
| `name` | `string` | - | input 元素的名称,用于 HTML 表单提交。 |
| `autoFocus` | `boolean` | - | 元素渲染后是否应获得焦点。 |
#### Event Props
| Prop | 类型 | 默认值 | 描述 |
| ---------- | ------------------------- | --- | ------------------------ |
| `onSubmit` | `(value: string) => void` | - | 用户提交搜索(Enter)时触发的事件处理函数。 |
| `onClear` | `() => void` | - | 按下清除按钮时触发的事件处理函数。 |
#### Accessibility Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | -------- | --- | -------------- |
| `aria-label` | `string` | - | 没有可见标签时的无障碍标签。 |
| `aria-labelledby` | `string` | - | 用于标注该字段的元素 ID。 |
| `aria-describedby` | `string` | - | 用于描述该字段的元素 ID。 |
| `aria-details` | `string` | - | 包含更多详情的元素 ID。 |
### Composition Components
SearchField 需要与以下独立组件组合使用,请分别导入并直接使用:
* **SearchField.Group** – 搜索图标、输入框与清除按钮的容器
* **SearchField.Input** – 搜索输入字段
* **SearchField.SearchIcon** – 左侧显示的搜索图标
* **SearchField.ClearButton** – 用于清空搜索字段的按钮
* **Label** – 字段标签组件(`@heroui/react`)
* **Description** – 辅助说明文本组件(`@heroui/react`)
* **FieldError** – 校验错误信息组件(`@heroui/react`)
这些组件各自拥有 props API。请直接在 SearchField 内组合使用:
```tsx
Search
Enter keywords to search
Search query is required
```
#### SearchField.Group Props
SearchField.Group 继承 React Aria [Group](https://react-spectrum.adobe.com/react-aria/Group.html) 组件的 props。
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------ | --- | --------------------------------------- |
| `children` | `React.ReactNode \| (values: GroupRenderProps) => React.ReactNode` | - | 子组件(SearchIcon、Input、ClearButton)或渲染函数。 |
| `className` | `string \| (values: GroupRenderProps) => string` | - | 用于样式的 CSS 类。 |
#### SearchField.Input Props
SearchField.Input 继承 React Aria [Input](https://react-spectrum.adobe.com/react-aria/Input.html) 组件的 props。
| Prop | 类型 | 默认值 | 描述 |
| ------------- | -------------------------- | ----------- | ----------------------------------------------------------------- |
| `className` | `string` | - | 用于样式的 CSS 类。 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 输入的视觉变体。`primary` 为默认带阴影样式。`secondary` 为低强调、无阴影变体,适合用在 surface 上。 |
| `placeholder` | `string` | - | 输入为空时显示的占位符文本。 |
| `type` | `string` | `"search"` | 输入类型(会自动设置为 `"search"`)。 |
#### SearchField.SearchIcon Props
SearchField.SearchIcon 是一个用于渲染搜索图标的自定义组件。
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | ---------------- | ---------------- |
| `children` | `React.ReactNode` | ` ` | 自定义图标元素。默认为搜索图标。 |
| `className` | `string` | - | 用于样式的 CSS 类。 |
#### SearchField.ClearButton Props
SearchField.ClearButton 继承 React Aria [Button](https://react-spectrum.adobe.com/react-aria/Button.html) 组件的 props。
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | ---------------------- | ----------------------- |
| `children` | `React.ReactNode` | ` ` | 按钮的图标或内容。默认为关闭图标。 |
| `className` | `string` | - | 用于样式的 CSS 类。 |
| `slot` | `"clear"` | `"clear"` | 必须设置为 `"clear"`(会自动设置)。 |
### SearchFieldRenderProps
在 `className`、`style` 或 `children` 上使用渲染 prop 时,可使用以下值:
| Prop | 类型 | 描述 |
| ---------------- | --------- | -------------------------------- |
| `isDisabled` | `boolean` | 字段是否禁用。 |
| `isInvalid` | `boolean` | 字段当前是否无效。 |
| `isReadOnly` | `boolean` | 字段是否只读。 |
| `isRequired` | `boolean` | 字段是否必填。 |
| `isFocused` | `boolean` | 字段是否聚焦(已弃用,请使用 `isFocusWithin`)。 |
| `isFocusWithin` | `boolean` | 是否有任意子元素聚焦。 |
| `isFocusVisible` | `boolean` | 是否为可见焦点(键盘导航)。 |
| `value` | `string` | 当前值。 |
| `isEmpty` | `boolean` | 字段是否为空。 |
# TextArea 多行文本框
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/text-area
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/text-area.mdx
> 原语级多行文本输入组件,可接受标准 HTML 属性。
## 引入
```tsx
import { TextArea } from '@heroui/react';
```
关于校验、标签与错误信息,请参阅 **[TextField](/docs/components/text-field)**。
### 用法
```tsx
import {TextArea} from "@heroui/react";
export function Basic() {
return (
);
}
```
### 受控
```tsx
"use client";
import {Description, TextArea} from "@heroui/react";
import React from "react";
export function Controlled() {
const [value, setValue] = React.useState("");
return (
setValue(event.target.value)}
/>
字符数: {value.length} / 280
);
}
```
### 行数与尺寸调整
```tsx
import {Label, TextArea} from "@heroui/react";
export function Rows() {
return (
);
}
```
### 全宽
```tsx
import {TextArea} from "@heroui/react";
export function FullWidth() {
return (
);
}
```
### 变体
TextArea 支持两种视觉变体:
* **`primary`**(默认)— 常规带阴影样式,适用于大多数场景
* **`secondary`** — 弱强调、无阴影变体,适合用于 Surface 组件内部
```tsx
import {TextArea} from "@heroui/react";
export function Variants() {
return (
);
}
```
### 在 Surface 内
在 [Surface](/docs/components/surface) 组件内部使用时,请使用 `variant="secondary"`,以应用适合 surface 背景的弱强调变体。
```tsx
import {Surface, TextArea} from "@heroui/react";
export function OnSurface() {
return (
);
}
```
## Related Components
* **TextField**: Composition-friendly fields with labels and validation
* **Input**: Single-line text input built on React Aria
* **Label**: Accessible label for form controls
## 样式
### 传入 Tailwind CSS 类
```tsx
import {Label, TextArea} from '@heroui/react';
function CustomTextArea() {
return (
Message
);
}
```
### 自定义组件类
使用 Tailwind 的 `@layer components` 一次性覆盖共享的 `.textarea` 类。
```css
@layer components {
.textarea {
@apply rounded-xl border border-border bgsurface px-4 py-3 text-sm leading-6 shadow-sm;
&:hover,
&[data-hovered="true"] {
@apply bg-surface-secondary border-border/80;
}
&:focus-visible,
&[data-focus-visible="true"] {
@apply border-primary ring-2 ring-primary/20;
}
&[data-invalid="true"] {
@apply border-danger bg-danger-50/10 text-danger;
}
}
}
```
### CSS 类
* `.textarea` – 底层 `` 元素样式
### 交互状态
* **悬停**:`:hover` 或 `[data-hovered="true"]`
* **可见焦点**:`:focus-visible` 或 `[data-focus-visible="true"]`
* **无效**:`[data-invalid="true"]`
* **禁用**:`:disabled` 或 `[aria-disabled="true"]`
## API 参考
### TextArea Props
TextArea 接受所有标准 HTML `` 属性,以及以下属性:
| Prop | 类型 | 默认值 | 描述 |
| -------------- | --------------------------------------------------------- | ----------- | ----------------------------------------------------------------- |
| `className` | `string` | - | 与基础样式合并的 Tailwind 类。 |
| `rows` | `number` | `3` | 可见文本行数。 |
| `cols` | `number` | - | 文本控件的可见宽度。 |
| `value` | `string` | - | TextArea 的受控值。 |
| `defaultValue` | `string` | - | 非受控初始值。 |
| `onChange` | `(event: React.ChangeEvent) => void` | - | 变更处理函数。 |
| `placeholder` | `string` | - | 占位符文本。 |
| `disabled` | `boolean` | `false` | 禁用 TextArea。 |
| `readOnly` | `boolean` | `false` | 将 TextArea 设为只读。 |
| `required` | `boolean` | `false` | 将 TextArea 标记为必填。 |
| `name` | `string` | - | 表单提交时使用的 name。 |
| `autoComplete` | `string` | - | 浏览器自动完成提示。 |
| `maxLength` | `number` | - | 最大字符数。 |
| `minLength` | `number` | - | 最小字符数。 |
| `wrap` | `'soft' \| 'hard'` | - | 提交时文本如何换行。 |
| `fullWidth` | `boolean` | `false` | TextArea 是否占满容器宽度 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 组件的视觉变体。`primary` 为默认带阴影样式。`secondary` 为弱强调、无阴影变体,适合用于 surface 上。 |
> 对于 `isInvalid`、`isRequired` 等校验 prop 以及错误处理,请将 TextArea 作为子组件与 **[TextField](/docs/components/text-field)** 一起使用。
# TextField 文本输入框
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/text-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(forms)/text-field.mdx
> 便于组合的文本字段,包含标签、说明与内联校验。
## 引入
```tsx
import { TextField } from '@heroui/react';
```
### 用法
```tsx
import {Input, Label, TextField} from "@heroui/react";
export function Basic() {
return (
邮箱
);
}
```
### 组件结构
```tsx
import {TextField, Label, Input, Description, FieldError} from '@heroui/react';
export default () => (
)
```
> **TextField** 将标签、输入、说明与错误信息整合为单个无障碍组件。若只需独立输入,请使用 **[Input](/docs/components/input)** 或 **[TextArea](/docs/components/textarea)**。
### 带说明
```tsx
import {Description, Input, Label, TextField} from "@heroui/react";
export function WithDescription() {
return (
用户名
为你的账户选择一个唯一的用户名
);
}
```
### 必填字段
```tsx
import {Description, Input, Label, TextField} from "@heroui/react";
export function Required() {
return (
全名
此字段为必填项
);
}
```
### 校验
使用 `isInvalid` 与 `FieldError` 展示校验信息。
```tsx
"use client";
import {Description, FieldError, Input, Label, TextArea, TextField} from "@heroui/react";
import React from "react";
export function Validation() {
const [username, setUsername] = React.useState("");
const [bio, setBio] = React.useState("");
const isUsernameInvalid = username.length > 0 && username.length < 3;
const isBioInvalid = bio.length > 0 && bio.length < 20;
return (
用户名
{isUsernameInvalid ? (
用户名至少需要 3 个字符。
) : (
为你的资料选择一个唯一的用户名。
)}
个人简介
{isBioInvalid ? (
个人简介至少需要 20 个字符。
) : (
至少 20 个字符 ({bio.length}/20).
)}
);
}
```
### 受控
通过受控 `value` 同步计数器、预览或格式化。
```tsx
"use client";
import {Description, Input, Label, TextArea, TextField} from "@heroui/react";
import React from "react";
export function Controlled() {
const [name, setName] = React.useState("");
const [bio, setBio] = React.useState("");
return (
显示名称
字符数: {name.length}
个人简介
字符数: {bio.length} / 200
);
}
```
### 错误信息
```tsx
import {FieldError, Input, Label, TextField} from "@heroui/react";
export function WithError() {
return (
邮箱
请输入有效的邮箱地址
);
}
```
### 禁用状态
```tsx
import {Description, Input, Label, TextField} from "@heroui/react";
export function Disabled() {
return (
账户 ID
此字段不可编辑
);
}
```
### TextArea
多行内容请使用 [TextArea](/docs/components/textarea) 替代 [Input](/docs/components/input)。
```tsx
import {Description, Label, TextArea, TextField} from "@heroui/react";
export function TextAreaExample() {
return (
消息
最多 500 个字符
);
}
```
### Input 类型
```tsx
import {Input, Label, TextField} from "@heroui/react";
export function InputTypes() {
return (
密码
年龄
邮箱
网站
电话
);
}
```
### 全宽
```tsx
import {FieldError, Input, Label, TextField} from "@heroui/react";
export function FullWidth() {
return (
你的姓名
密码
密码长度必须超过 8 个字符
);
}
```
### 在 Surface 内
置于 [Surface](/docs/components/surface) 中时,请在 Input 或 TextArea 上使用 `variant="secondary"`,以应用适合表面背景的弱强调变体。
```tsx
import {Description, Input, Label, Surface, TextArea, TextField} from "@heroui/react";
export function OnSurface() {
return (
你的姓名
我们绝不会与他人分享此信息
邮箱
个人简介
至少 4 行
);
}
```
## Related Components
* **Input**: Single-line text input built on React Aria
* **TextArea**: Multiline text input with focus management
* **Fieldset**: Group related form controls with legends
### 自定义渲染函数
```tsx
"use client";
import {Input, Label, TextField} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
type="email"
>
邮箱
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import {TextField, Label, Input, Description} from '@heroui/react';
function CustomTextField() {
return (
Project name
Keep it short and memorable.
);
}
```
### 自定义组件类
TextField 默认样式很少。覆盖 `.textfield` 类即可自定义容器样式。
```css
@layer components {
.textfield {
@apply flex flex-col gap-1;
}
/* 无效时自动隐藏说明 */
.textfield[data-invalid="true"] [data-slot="description"],
.textfield[aria-invalid="true"] [data-slot="description"] {
@apply hidden;
}
/* Description 默认内边距 */
.textfield [data-slot="description"] {
@apply px-1;
}
}
```
### CSS 类
* `.textfield` – 根容器,样式极少(`flex flex-col gap-1`)
> **提示:** 子组件([Label](/docs/components/label)、[Input](/docs/components/input)、[TextArea](/docs/components/textarea)、[Description](/docs/components/description)、[FieldError](/docs/components/field-error))各自拥有 CSS 类与样式,定制方式请参见对应文档。
### 交互状态
TextField 会根据状态自动管理以下 data 属性:
* **无效**:`[data-invalid="true"]` 或 `[aria-invalid="true"]` — 无效时自动隐藏 description 插槽
* **禁用**:`[data-disabled="true"]` — 在 `isDisabled` 为 true 时应用
* **焦点在内部**:`[data-focus-within="true"]` — 任一子级 input 聚焦时应用
* **可见焦点**:`[data-focus-visible="true"]` — 键盘导航产生可见焦点时应用
更多属性可通过 render prop 获取(见下文 TextFieldRenderProps)。
## API 参考
### TextField Props
继承 React Aria [TextField](https://react-spectrum.adobe.com/react-aria/TextField.html) 的全部 props。
#### Base Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------------ | ------- | ------------------------ |
| `children` | `React.ReactNode \| (values: TextFieldRenderProps) => React.ReactNode` | - | 子组件(Label、Input 等)或渲染函数。 |
| `className` | `string \| (values: TextFieldRenderProps) => string` | - | 用于样式的 CSS 类,支持渲染 prop。 |
| `style` | `React.CSSProperties \| (values: TextFieldRenderProps) => React.CSSProperties` | - | 行内样式,支持渲染 prop。 |
| `fullWidth` | `boolean` | `false` | TextField 是否占满容器宽度。 |
| `id` | `string` | - | 元素的唯一 id。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
#### Validation Props
| Prop | 类型 | 默认值 | 描述 |
| -------------------- | ----------------------------------------------------------------- | ---------- | ------------------------- |
| `isRequired` | `boolean` | `false` | 提交表单前是否必须填写。 |
| `isInvalid` | `boolean` | - | 当前值是否无效。 |
| `validate` | `(value: string) => ValidationError \| true \| null \| undefined` | - | 自定义校验函数。 |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | 使用原生 HTML 表单校验还是 ARIA 属性。 |
| `validationErrors` | `string[]` | - | 服务端校验错误。 |
#### Value Props
| Prop | 类型 | 默认值 | 描述 |
| -------------- | ------------------------- | --- | -------------- |
| `value` | `string` | - | 当前值(受控)。 |
| `defaultValue` | `string` | - | 默认值(非受控)。 |
| `onChange` | `(value: string) => void` | - | 值变化时调用的事件处理函数。 |
#### State Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | --------- | --- | ----------- |
| `isDisabled` | `boolean` | - | 是否禁用输入。 |
| `isReadOnly` | `boolean` | - | 是否可选中但不可修改。 |
#### Form Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | --------- | --- | ----------------------- |
| `name` | `string` | - | 用于 HTML 表单提交的 input 名称。 |
| `autoFocus` | `boolean` | - | 是否在挂载时自动聚焦。 |
#### Accessibility Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | -------- | --- | -------------- |
| `aria-label` | `string` | - | 无可见标签时的无障碍标签。 |
| `aria-labelledby` | `string` | - | 用于标注该字段的元素 id。 |
| `aria-describedby` | `string` | - | 用于描述该字段的元素 id。 |
| `aria-details` | `string` | - | 提供附加详情的元素 id。 |
### Composition Components
TextField 与以下独立组件配合使用,请直接按需引入并组合:
* **Label** — `@heroui/react` 的字段标签组件
* **Input** — `@heroui/react` 的单行文本输入
* **TextArea** — `@heroui/react` 的多行文本输入
* **Description** — `@heroui/react` 的辅助说明组件
* **FieldError** — `@heroui/react` 的校验错误信息组件
这些组件各自有独立的 props API,请在 TextField 内直接使用:
```tsx
Email Address
setEmail(e.target.value)} />
We'll never share your email.
Please enter a valid email address.
```
### TextFieldRenderProps
对 `className`、`style` 或 `children` 使用渲染 prop 时,可使用以下值:
| Prop | 类型 | 描述 |
| ---------------- | --------- | ---------------------------------- |
| `isDisabled` | `boolean` | 字段是否禁用。 |
| `isInvalid` | `boolean` | 字段当前是否无效。 |
| `isReadOnly` | `boolean` | 字段是否只读。 |
| `isRequired` | `boolean` | 字段是否必填。 |
| `isFocused` | `boolean` | 字段是否聚焦(已弃用 — 请使用 `isFocusWithin`)。 |
| `isFocusWithin` | `boolean` | 是否有任一子元素聚焦。 |
| `isFocusVisible` | `boolean` | 是否为可见键盘焦点。 |
# Card 卡片
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/card
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(layout)/card.mdx
> 用于分组相关内容与操作的灵活容器组件。
## 引入
```tsx
import { Card } from "@heroui/react";
```
### 用法
```tsx
import {CircleDollar} from "@gravity-ui/icons";
import {Card, Link} from "@heroui/react";
export function Default() {
return (
成为 Acme 创作者!
前往 Acme 创作者中心立即注册,开始从粉丝与支持者处获得积分奖励。
创作者中心
);
}
```
### 组件结构
导入 Card 组件后,可通过点语法访问所有子部分。
```tsx
import { Card } from "@heroui/react";
export default () => (
);
```
### 变体
卡片提供语义化变体,用于表达层级强弱而非固定视觉样式,主题可按需诠释:
```tsx
import {Card} from "@heroui/react";
export function Variants() {
return (
透明
背景透明,视觉层级较低(transparent)
适合次要内容或嵌套在其它容器中的卡片
默认
标准外观(bg-surface)
大多数场景的默认卡片变体
次要
中等强调(bg-surface-secondary)
用于需要适度吸引注意力的内容
第三
更高强调(bg-surface-tertiary)
适合主要内容或需要突出的展示位
);
}
```
* **`transparent`** — 层次最低,透明背景(适合嵌套卡片)
* **`default`** — 常规卡片,适用于大多数场景(surface-secondary)
* **`secondary`** — 中等突出,吸引适度注意(surface-tertiary)
* **`tertiary`** — 更高突出,用于重要内容(surface-tertiary)
### 横向布局
```tsx
import {Button, Card, CloseButton} from "@heroui/react";
export function Horizontal() {
return (
成为 ACME 创作者!
这是一段占位说明文字,用于展示横向卡片布局、配图与右上角关闭按钮的排版效果。
仅剩 10 个名额
报名截止:10 月 10 日
立即申请
);
}
```
### 带头像
```tsx
import {Avatar, Card} from "@heroui/react";
export function WithAvatar() {
return (
Indie Hackers
148 位成员
IH
创建者:玛莎
AI Builders
362 位成员
B
创建者:约翰
);
}
```
### 带图片
```tsx
import {CircleDollar} from "@gravity-ui/icons";
import {Avatar, Button, Card, CloseButton, Link} from "@heroui/react";
export function WithImages() {
return (
{/* 第 1 行:大图商品卡 */}
成为 ACME 创作者!
这是一段占位说明文字,用于展示横向卡片布局、配图与右上角关闭按钮的排版效果。
仅剩 10 个名额
报名截止:10 月 10 日
立即申请
{/* 第 2 行 */}
{/* 左栏 */}
{/* 上方卡片 */}
支付
现已支持加密货币提现
在设置中添加钱包即可提现
前往设置
{/* 下方小卡 */}
{/* 左卡 */}
JK
Indie Hackers
148 位成员
JK
创建者:约翰
{/* 右卡 */}
AB
AI Builders
362 位成员
M
创建者:玛莎
{/* 右栏 */}
{/* 背景图 */}
{/* 标题区 */}
NEO
家用机器人
{/* 底部 */}
通知我
{/* 第 3 行 */}
{/* 左:大图卡 */}
立即购买
{/* 右:堆叠小卡 */}
{/* 1 */}
连接未来
今天 18:30
{/* 2 */}
牛油果黑客松
周三 16:30
{/* 3 */}
Sound Electro|超越艺术
周五 20:00
);
}
```
### 带表单
```tsx
"use client";
import {Button, Card, Form, Input, Label, Link, TextField} from "@heroui/react";
export function WithForm() {
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: Record = {};
// Convert FormData to plain object
formData.forEach((value, key) => {
data[key] = value.toString();
});
alert("表单提交成功!");
};
return (
登录
输入账号信息以访问您的账户
邮箱
密码
登录
忘记密码?
);
}
```
## 无障碍
```tsx
import { Card } from '@heroui/react';
import { cardVariants } from '@heroui/styles';
// 语义化标记
Article Title
// 可交互卡片
Product Name
```
## Related Components
* **Surface**: Base container surface
* **Avatar**: Display user profile images
* **Form**: Form validation and submission handling
## 样式
### 组件定制
```tsx
Custom Styled Card
Custom colors applied
Content with custom styling
```
### CSS 变量覆盖
```css
/* 覆盖特定变体 */
.card--secondary {
@apply bg-gradient-to-br from-blue-50 to-purple-50;
}
/* 自定义元素样式 */
.card__title {
@apply text-xl font-bold;
}
```
## CSS 类
Card 使用 [BEM](https://getbem.com/) 命名以便样式可预期([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/card.css)):
#### 基础类
* `.card` — 基础容器,含内边距与边框
* `.card__header` — 头部区域容器
* `.card__title` — 标题的基础字号与字重
* `.card__description` — 弱化说明文字
* `.card__content` — 弹性主内容区
* `.card__footer` — 底部行布局
#### 变体类
* `.card--transparent` — 层次最低,透明背景(对应 `transparent` 变体)
* `.card--default` — 常规外观,surface-secondary(默认)
* `.card--secondary` — 中等突出,surface-tertiary(对应 `secondary` 变体)
* `.card--tertiary` — 更高突出,surface-tertiary(对应 `tertiary` 变体)
## API 参考
### Card
| Prop | 类型 | 默认值 | 描述 |
| ----------- | --------------------------------------------------------- | ----------- | ----------- |
| `variant` | `"transparent" \| "default" \| "secondary" \| "tertiary"` | `"default"` | 表示层次强弱的语义变体 |
| `className` | `string` | - | 附加的 CSS 类 |
| `children` | `React.ReactNode` | - | 卡片内容 |
### Card.Header
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | --------- |
| `className` | `string` | - | 附加的 CSS 类 |
| `children` | `React.ReactNode` | - | 头部内容 |
### Card.Title
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | -------------- |
| `className` | `string` | - | 附加的 CSS 类 |
| `children` | `React.ReactNode` | - | 标题内容(渲染为 `h3`) |
### Card.Description
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | ------------- |
| `className` | `string` | - | 附加的 CSS 类 |
| `children` | `React.ReactNode` | - | 说明内容(渲染为 `p`) |
### Card.Content
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | --------- |
| `className` | `string` | - | 附加的 CSS 类 |
| `children` | `React.ReactNode` | - | 主内容 |
### Card.Footer
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | --------- |
| `className` | `string` | - | 附加的 CSS 类 |
| `children` | `React.ReactNode` | - | 底部内容 |
# Separator 分隔符
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/separator
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(layout)/separator.mdx
> 在内容区块之间进行视觉分隔。
## 引入
```tsx
import { Separator } from '@heroui/react';
```
### 用法
```tsx
import {Separator} from "@heroui/react";
export function Basic() {
return (
HeroUI v3 组件
美观、快速、现代的 React UI 库。
);
}
```
### 垂直方向
```tsx
import {Separator} from "@heroui/react";
export function Vertical() {
return (
);
}
```
### 带内容
```tsx
import {Separator} from "@heroui/react";
const items = [
{
iconUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/bell-small.png",
subtitle: "接收账户活动更新",
title: "设置通知",
},
{
iconUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/compass-small.png",
subtitle: "将浏览器连接到你的账户",
title: "设置浏览器扩展",
},
{
iconUrl:
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/mint-collective-small.png",
subtitle: "创建你的第一件收藏品",
title: "铸造收藏品",
},
];
export function WithContent() {
return (
{items.map((item, index) => (
{item.title}
{item.subtitle}
{index < items.length - 1 &&
}
))}
);
}
```
### 变体
```tsx
import {Separator} from "@heroui/react";
export function Variants() {
return (
);
}
```
### 与 Surface 组合
Separator 会适配不同的 surface 背景,以获得更好的可见性。
```tsx
import {Separator, Surface} from "@heroui/react";
export function WithSurface() {
return (
);
}
```
## Related Components
* **Card**: Content container with header, body, and footer
* **Chip**: Compact elements for tags and filters
* **Avatar**: Display user profile images
### 自定义渲染函数
```tsx
"use client";
import {Separator} from "@heroui/react";
export function CustomRenderFunction() {
return (
HeroUI v3 组件
美观、快速、现代的 React UI 库。
} />
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import {Separator} from '@heroui/react';
function CustomSeparator() {
return (
);
}
```
### 自定义组件类
若要自定义 Separator 的组件类名,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.separator {
@apply bg-accent h-[2px];
}
.separator--vertical {
@apply bg-accent w-[2px];
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于定制。
### CSS 类
Separator 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/separator.css)):
#### 基础类与方向类
* `.separator` - 基础 Separator 样式,默认水平方向
* `.separator--horizontal` - 水平方向(全宽,高度 1px)
* `.separator--vertical` - 垂直方向(全高,宽度 1px)
#### 变体类
* `.separator--default` - 默认变体,标准对比度
* `.separator--secondary` - 次要变体,中等对比度
* `.separator--tertiary` - 第三级变体,较弱对比度
## API 参考
### Separator Props
| Prop | 类型 | 默认值 | 描述 |
| ------------- | ----------------------------------------------------------------- | -------------- | ---------------------- |
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Separator 的方向 |
| `variant` | `'default' \| 'secondary' \| 'tertiary'` | `'default'` | Separator 的视觉变体 |
| `className` | `string` | - | 额外的 CSS 类名 |
| `render` | `DOMRenderFunction` | - | 通过自定义渲染函数覆盖默认的 DOM 元素。 |
# Surface 表面
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/surface
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(layout)/surface.mdx
> 提供表面级样式与子组件上下文的容器组件。
## 引入
```tsx
import { Surface } from '@heroui/react';
```
### 用法
```tsx
import {Surface} from "@heroui/react";
export function Variants() {
return (
默认
表面内容
这是默认表面变体,使用 bg-surface 样式。
次要
表面内容
这是次要表面变体,使用 bg-surface-secondary 样式。
第三
表面内容
这是第三表面变体,使用 bg-surface-tertiary 样式。
透明
表面内容
这是透明表面变体,无背景,适用于遮罩层和自定义背景的卡片。
);
}
```
## 概述
Surface 组件是语义化容器,通过变体提供不同的视觉层次。
### 变体
Surface 提供描述视觉层次的语义化变体:
* **`default`** — 标准表面外观(bg-surface)
* **`secondary`** — 中等层次(bg-surface-secondary)
* **`tertiary`** — 更高层次(bg-surface-tertiary)
## 与表单组件配合
在 Surface 内使用表单组件时,请为这些组件设置 `variant="secondary"`,以应用适合表面背景的低强调变体。
```tsx
import { Surface, Input, TextArea } from '@heroui/react';
function App() {
return (
);
}
```
## Related Components
* **CheckboxGroup**: Group of checkboxes with shared state
* **Fieldset**: Group related form controls with legends
* **InputOTP**: One-time password input
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Surface } from '@heroui/react';
function CustomSurface() {
return (
Custom Styled Surface
Content goes here
);
}
```
### 自定义组件类
若要自定义 Surface 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.surface {
@apply rounded-2xl border border-border;
}
.surface--secondary {
@apply bg-gradient-to-br from-blue-50 to-purple-50;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Surface 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/surface.css)):
#### 基础类
* `.surface` - Surface 根容器
#### 变体类
* `.surface--default` - 默认 Surface 变体(bg-surface)
* `.surface--secondary` - Secondary Surface 变体(bg-surface-secondary)
* `.surface--tertiary` - Tertiary Surface 变体(bg-surface-tertiary)
## API 参考
### Surface Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ---------------------------------------------------------- | ----------- | -------------- |
| `variant` | ` "transparent" \| "default" \| "secondary" \| "tertiary"` | `"default"` | Surface 的视觉变体。 |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `ReactNode` | - | Surface 内容。 |
## Context API
### SurfaceContext
子组件可通过 Surface 上下文读取当前变体:
```tsx
import { useContext } from 'react';
import { SurfaceContext } from '@heroui/react';
function MyComponent() {
const { variant } = useContext(SurfaceContext);
// variant 为 "transparent" | "default" | "secondary" | "tertiary" | undefined
}
```
# Toolbar 工具栏
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/toolbar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(layout)/toolbar.mdx
> 用于承载可交互控件的容器,并支持方向键导航。
## 引入
```tsx
import { Toolbar } from '@heroui/react';
```
### 用法
```tsx
import {Bold, Copy, Italic, Scissors, Underline} from "@gravity-ui/icons";
import {
Button,
ButtonGroup,
Separator,
ToggleButton,
ToggleButtonGroup,
Toolbar,
} from "@heroui/react";
export function Basic() {
return (
);
}
```
### 垂直方向
```tsx
import {ArrowUturnCcwLeft, ArrowUturnCwRight, Bold, Italic, Underline} from "@gravity-ui/icons";
import {
Button,
ButtonGroup,
Separator,
ToggleButton,
ToggleButtonGroup,
Toolbar,
} from "@heroui/react";
export function Vertical() {
return (
);
}
```
### 与 ButtonGroup 组合
```tsx
import {
ArrowUturnCcwLeft,
ArrowUturnCwRight,
Bold,
Italic,
TextAlignCenter,
TextAlignLeft,
TextAlignRight,
Underline,
} from "@gravity-ui/icons";
import {
Button,
ButtonGroup,
Separator,
ToggleButton,
ToggleButtonGroup,
Toolbar,
} from "@heroui/react";
export function WithButtonGroup() {
return (
撤销
重做
);
}
```
### Attached
```tsx
import {Bold, Copy, Italic, Scissors, Underline} from "@gravity-ui/icons";
import {
Button,
ButtonGroup,
Separator,
ToggleButton,
ToggleButtonGroup,
Toolbar,
} from "@heroui/react";
export function Attached() {
return (
);
}
```
## Related Components
* **ButtonGroup**: Group related buttons together
* **ToggleButtonGroup**: Group multiple toggle buttons into a unified control
* **Separator**: Visual divider between content
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Toolbar } from '@heroui/react';
function CustomToolbar() {
return (
{/* toolbar content */}
);
}
```
### 自定义组件类
若要自定义 Toolbar 的组件类名,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.toolbar {
@apply gap-4 rounded-lg bg-surface p-3;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于定制。
### CSS 类
Toolbar 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/toolbar.css)):
* `.toolbar` - 基础容器
* `.toolbar--horizontal` - 水平方向(默认)
* `.toolbar--vertical` - 垂直方向
* `.toolbar--attached` - Attached 变体:surface 背景与完全圆角
## API 参考
### Toolbar Props
继承 [React Aria Toolbar](https://react-spectrum.adobe.com/react-aria/Toolbar.html)。
| Prop | 类型 | 默认值 | 描述 |
| ----------------- | -------------------------------------------------------------------- | -------------- | ----------------------------- |
| `isAttached` | `boolean` | `false` | Toolbar 是否使用带完全圆角的 surface 背景 |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Toolbar 的方向 |
| `aria-label` | `string` | - | Toolbar 的无障碍标签 |
| `aria-labelledby` | `string` | - | 用于标注该 Toolbar 的元素 id |
| `children` | `React.ReactNode \| (values: ToolbarRenderProps) => React.ReactNode` | - | 内容或渲染 prop |
| `className` | `string \| (values: ToolbarRenderProps) => string` | - | 额外的 CSS 类名 |
### ToolbarRenderProps
使用渲染 prop 模式时,会提供以下值:
| Prop | 类型 | 描述 |
| ------------- | ---------------------------- | -------------- |
| `orientation` | `"horizontal" \| "vertical"` | 当前 Toolbar 的方向 |
# Avatar 头像
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/avatar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(media)/avatar.mdx
> 展示用户头像图片,并提供可定制的回退内容。
## 引入
```tsx
import { Avatar } from '@heroui/react';
```
### 用法
```tsx
import {Avatar} from "@heroui/react";
export function Basic() {
return (
);
}
```
### 组件结构
引入 Avatar 组件,并通过点语法访问各部分。
```tsx
import { Avatar } from '@heroui/react';
export default () => (
)
```
### 尺寸
```tsx
import {Avatar} from "@heroui/react";
export function Sizes() {
return (
);
}
```
### 颜色
```tsx
import {Avatar} from "@heroui/react";
export function Colors() {
return (
);
}
```
### 变体
```tsx
import {Person} from "@gravity-ui/icons";
import {Avatar, Separator} from "@heroui/react";
const colors = ["accent", "default", "success", "warning", "danger"] as const;
const COLOR_LABELS: Record<(typeof colors)[number], string> = {
accent: "强调",
danger: "危险",
default: "默认",
success: "成功",
warning: "警告",
};
const variants = [
{content: "AG", label: "字母", type: "letter"},
{content: "AG", label: "柔和字母", type: "letter-soft"},
{content: , label: "图标", type: "icon"},
{content: , label: "柔和图标", type: "icon-soft"},
{
content: [
"https://img.heroui.chat/image/avatar?w=400&h=400&u=3",
"https://img.heroui.chat/image/avatar?w=400&h=400&u=4",
"https://img.heroui.chat/image/avatar?w=400&h=400&u=5",
"https://img.heroui.chat/image/avatar?w=400&h=400&u=8",
"https://img.heroui.chat/image/avatar?w=400&h=400&u=16",
],
label: "图片",
type: "img",
},
] as const;
export function Variants() {
return (
{/* 颜色列标题 */}
{colors.map((color) => (
{COLOR_LABELS[color]}
))}
{/* 变体行 */}
{variants.map((variant) => (
{variant.label}
{colors.map((color, colorIndex) => (
{variant.type === "img" ? (
<>
{COLOR_LABELS[color].charAt(0)}
>
) : (
{variant.content}
)}
))}
))}
);
}
```
### 回退内容
```tsx
import {Person} from "@gravity-ui/icons";
import {Avatar} from "@heroui/react";
export function Fallback() {
return (
{/* 文字回退 */}
JD
{/* 图标回退 */}
{/* 延迟显示回退 */}
NA
{/* 自定义样式回退 */}
GB
);
}
```
### 头像组
```tsx
import {Avatar} from "@heroui/react";
const users = [
{
id: 1,
image: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg",
name: "张明",
},
{
id: 2,
image: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg",
name: "李华",
},
{
id: 3,
image: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg",
name: "王芳",
},
{
id: 4,
image: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg",
name: "刘洋",
},
{
id: 5,
image: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg",
name: "陈静",
},
];
function initialsFromName(name: string) {
const parts = name.split(/\s+/).filter(Boolean);
if (parts.length >= 2) {
return parts.map((n) => n[0]).join("");
}
return name.slice(0, 2);
}
export function Group() {
return (
{/* 基础头像组 */}
{users.slice(0, 4).map((user) => (
{initialsFromName(user.name)}
))}
{/* 带头像数量提示的组合 */}
{users.slice(0, 3).map((user) => (
{initialsFromName(user.name)}
))}
+{users.length - 3}
);
}
```
### 自定义样式
```tsx
import {Avatar} from "@heroui/react";
export function CustomStyles() {
return (
{/* 使用 Tailwind 自定义尺寸 */}
XL
{/* 方形头像 */}
SQ
{/* 渐变描边 */}
{/* 在线状态指示 */}
);
}
```
## Related Components
* **Separator**: Visual divider between content
* **Badge**: Small indicator positioned relative to another element
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Avatar } from '@heroui/react';
function CustomAvatar() {
return (
XL
);
}
```
### 自定义组件类
若要自定义 Avatar 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.avatar {
@apply size-16 border-2 border-primary;
}
.avatar__fallback {
@apply bg-gradient-to-br from-purple-500 to-pink-500;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Avatar 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/avatar.css)):
#### 基础类
* `.avatar` - 基础容器,默认尺寸(size-10)
* `.avatar__image` - 图片元素,方形比例
* `.avatar__fallback` - 回退容器,内容居中
#### 尺寸修饰
* `.avatar--sm` - 小尺寸(size-8)
* `.avatar--md` - 中尺寸(默认,无额外样式)
* `.avatar--lg` - 大尺寸(size-12)
#### 变体修饰
* `.avatar--soft` - Soft 变体,背景更浅
#### 颜色修饰
* `.avatar__fallback--default` - 默认文字颜色
* `.avatar__fallback--accent` - 强调文字颜色
* `.avatar__fallback--success` - 成功文字颜色
* `.avatar__fallback--warning` - 警告文字颜色
* `.avatar__fallback--danger` - 危险文字颜色
## API 参考
### Avatar Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------- | ----------- | --------- |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Avatar 尺寸 |
| `color` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | `'default'` | 回退区域的颜色主题 |
| `variant` | `'default' \| 'soft'` | `'default'` | 视觉样式变体 |
| `className` | `string` | - | 额外的 CSS 类 |
### Avatar.Image Props
| Prop | 类型 | 默认值 | 描述 |
| ------------- | --------------------------------------------------- | --- | --------------- |
| `src` | `string` | - | 图片地址 |
| `srcSet` | `string` | - | 响应式图片的 `srcset` |
| `sizes` | `string` | - | 响应式图片的 `sizes` |
| `alt` | `string` | - | 图片替代文本 |
| `onLoad` | `(event: SyntheticEvent) => void` | - | 图片加载成功时的事件处理函数 |
| `onError` | `(event: SyntheticEvent) => void` | - | 图片加载失败时的事件处理函数 |
| `crossOrigin` | `'anonymous' \| 'use-credentials'` | - | 图片请求的 CORS 设置 |
| `loading` | `'eager' \| 'lazy'` | - | 原生懒加载属性 |
| `className` | `string` | - | 额外的 CSS 类 |
### Avatar.Fallback Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------- | --- | ---------------- |
| `delayMs` | `number` | - | 显示回退内容前的延迟(减轻闪烁) |
| `color` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | - | 覆盖父级的颜色 |
| `className` | `string` | - | 额外的 CSS 类 |
# Accordion 手风琴
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/accordion
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(navigation)/accordion.mdx
> 用于在紧凑空间中组织信息的可折叠内容面板。
## 引入
```tsx
import { Accordion } from '@heroui/react';
```
### 用法
```tsx
import {
ArrowsRotateLeft,
Box,
ChevronDown,
CreditCard,
PlanetEarth,
Receipt,
ShoppingBag,
} from "@gravity-ui/icons";
import {Accordion} from "@heroui/react";
const items = [
{
content: "浏览我们的商品,将商品加入购物车并前往结账。完成购买需要提供收货与支付信息。",
icon: ,
title: "如何下单?",
},
{
content: "可以,在订单发货前你可以修改或取消。订单一旦进入处理流程,将无法再更改。",
icon: ,
title: "可以修改或取消订单吗?",
},
{
content: "我们接受主流信用卡,包括 Visa、Mastercard 和 American Express。",
icon: ,
title: "支持哪些支付方式?",
},
{
content: "运费因收货地址与订单体积而异。订单满 50 美元可享受免运费。",
icon: ,
title: "运费如何计算?",
},
{
content: "是的,我们可向多数国家/地区发货。请查看运费说明与政策了解更多信息。",
icon: ,
title: "是否提供国际配送?",
},
{
content: "若对购买不满意,可在购买后 30 天内申请退款。请联系客服团队协助处理。",
icon: ,
title: "如何申请退款?",
},
];
export function Basic() {
return (
{items.map((item, index) => (
{item.icon ? (
{item.icon}
) : null}
{item.title}
{item.content}
))}
);
}
```
### 组件结构
引入 Accordion 组件并通过点语法访问所有子部分。
```tsx
import { Accordion } from '@heroui/react';
export default () => (
)
```
### Surface
```tsx
import {
ArrowsRotateLeft,
Box,
ChevronDown,
CreditCard,
PlanetEarth,
Receipt,
ShoppingBag,
} from "@gravity-ui/icons";
import {Accordion} from "@heroui/react";
const items = [
{
content: "浏览我们的商品,将商品加入购物车并前往结账。完成购买需要提供收货与支付信息。",
icon: ,
title: "如何下单?",
},
{
content: "可以,在订单发货前你可以修改或取消。订单一旦进入处理流程,将无法再更改。",
icon: ,
title: "可以修改或取消订单吗?",
},
{
content: "我们接受主流信用卡,包括 Visa、Mastercard 和 American Express。",
icon: ,
title: "支持哪些支付方式?",
},
{
content: "运费因收货地址与订单体积而异。订单满 50 美元可享受免运费。",
icon: ,
title: "运费如何计算?",
},
{
content: "是的,我们可向多数国家/地区发货。请查看运费说明与政策了解更多信息。",
icon: ,
title: "是否提供国际配送?",
},
{
content: "若对购买不满意,可在购买后 30 天内申请退款。请联系客服团队协助处理。",
icon: ,
title: "如何申请退款?",
},
];
export function Surface() {
return (
{items.map((item, index) => (
{item.icon ? (
{item.icon}
) : null}
{item.title}
{item.content}
))}
);
}
```
### 多项同时展开
```tsx
import {Accordion} from "@heroui/react";
export function Multiple() {
return (
快速开始
了解 HeroUI 的基础知识,以及如何将其集成到你的 React
项目中。本节涵盖安装、配置和你的第一个组件。
核心概念
理解 HeroUI 背后的核心概念,包括复合组件模式、使用 Tailwind CSS
进行样式设计,以及无障碍特性。
高级用法
探索高级特性,例如自定义变体、主题定制,以及与 React 生态中其他库的集成。
最佳实践
遵循我们建议的最佳实践,使用 HeroUI 构建高性能、无障碍且易于维护的应用。
);
}
```
### 受控
```tsx
"use client";
import {ChevronDown, ChevronUp} from "@gravity-ui/icons";
import {Accordion, Button, useDisclosureGroupNavigation} from "@heroui/react";
import React from "react";
const items = [
{
content:
"了解 HeroUI 的基础知识,以及如何将其集成到你的 React 项目中。本节涵盖安装、配置和你的第一个组件。",
id: "getting-started",
title: "快速开始",
},
{
content:
"理解 HeroUI 背后的核心概念,包括复合组件模式、使用 Tailwind CSS 进行样式设计,以及无障碍特性。",
id: "core-concepts",
title: "核心概念",
},
{
content: "探索高级特性,例如自定义变体、主题定制,以及与 React 生态中其他库的集成。",
id: "advanced-usage",
title: "高级用法",
},
];
export function Controlled() {
const [expandedKeys, setExpandedKeys] = React.useState(
new Set(["getting-started"]),
);
const itemIds = items.map((item) => item.id);
const {isNextDisabled, isPrevDisabled, onNext, onPrevious} = useDisclosureGroupNavigation({
expandedKeys,
itemIds,
onExpandedChange: setExpandedKeys,
});
return (
已展开:{[...expandedKeys].join("、") || "无"}
{items.map((item) => (
{item.title}
{item.content}
))}
);
}
```
### 自定义指示器
```tsx
"use client";
import type {Key} from "@heroui/react";
import {ChevronsDown, CircleChevronDown, Minus, Plus} from "@gravity-ui/icons";
import {Accordion} from "@heroui/react";
import React from "react";
export function CustomIndicator() {
const [expandedKeys, setExpandedKeys] = React.useState>(new Set([""]));
return (
使用加号/减号图标
{expandedKeys.has("1") ? : }
折叠时显示加号图标,展开时切换为减号图标。
使用圆形箭头图标
此项使用圆形内的箭头作为指示器,旋转动画会自动应用。
使用双箭头图标
此项使用双箭头图标。传入任意图标后,在条目展开时都会获得旋转动画。
);
}
```
### 禁用状态
```tsx
import {Accordion} from "@heroui/react";
export function Disabled() {
return (
整个手风琴禁用
禁用项 1
手风琴禁用时无法查看此内容。
禁用项 2
手风琴禁用时无法查看此内容。
单独禁用条目
可用项
此项可用,可正常展开与折叠。
禁用项
条目禁用时无法查看此内容。
另一可用项
此项同样可用,可正常切换。
);
}
```
### FAQ 布局
```tsx
import {ChevronDown} from "@gravity-ui/icons";
import {Accordion} from "@heroui/react";
export function FAQ() {
const categories = [
{
items: [
{
content: "浏览我们的商品,将商品加入购物车并前往结账。完成购买需要提供收货与支付信息。",
title: "如何下单?",
},
{
content: "可以,在订单发货前你可以修改或取消。订单一旦进入处理流程,将无法再更改。",
title: "可以修改或取消订单吗?",
},
],
title: "常规",
},
{
items: [
{
content: "你可以直接在官网购买许可证,选择适合的许可证类型后前往结账即可。",
title: "如何购买许可证?",
},
{
content: "标准版适用于个人或小项目;专业版包含商业使用授权与优先支持。",
title: "标准版与专业版有什么区别?",
},
],
title: "许可",
},
{
items: [
{
content: "可通过网站上的联系表单联系支持团队,或直接发送邮件至 support@example.com。",
title: "如何获取支持?",
},
],
title: "支持",
},
];
return (
常见问题
关于许可与使用,你需要了解的内容都在这里。
{categories.map((category) => (
{category.title}
{category.items.map((item, index) => (
{item.title}
{item.content}
))}
))}
);
}
```
### 自定义样式
```tsx
import {ChevronDown} from "@gravity-ui/icons";
import {Accordion, cn} from "@heroui/react";
const items = [
{
content: "通过实时通知及时了解账户动态。",
iconUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/bell-small.png",
subtitle: "接收账户活动更新",
title: "开启通知",
},
{
content: "安装我们的官方浏览器扩展,获得更顺畅的浏览体验。",
iconUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/compass-small.png",
subtitle: "将浏览器连接到你的账户",
title: "安装浏览器扩展",
},
{
content: "创建你的第一件数字藏品,开启数字收藏之旅。",
iconUrl:
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/mint-collective-small.png",
subtitle: "创建你的第一件收藏品",
title: "铸造收藏品",
},
];
export function CustomStyles() {
return (
{items.map((item, index) => (
{item.iconUrl ? (
) : null}
{item.title}
{item.subtitle}
{item.content}
))}
);
}
```
### 无分隔线
```tsx
import {ChevronDown, CreditCard, Receipt, ShoppingBag} from "@gravity-ui/icons";
import {Accordion} from "@heroui/react";
const items = [
{
content: "浏览我们的商品,将商品加入购物车并前往结账。完成购买需要提供收货与支付信息。",
icon: ,
title: "如何下单?",
},
{
content: "可以,在订单发货前你可以修改或取消。订单一旦进入处理流程,将无法再更改。",
icon: ,
title: "可以修改或取消订单吗?",
},
{
content: "我们接受主流信用卡,包括 Visa、Mastercard 和 American Express。",
icon: ,
title: "支持哪些支付方式?",
},
];
export function WithoutSeparator() {
return (
{items.map((item, index) => (
{item.icon ? (
{item.icon}
) : null}
{item.title}
{item.content}
))}
);
}
```
### 自定义渲染函数
```tsx
"use client";
import {
ArrowsRotateLeft,
Box,
ChevronDown,
CreditCard,
PlanetEarth,
Receipt,
ShoppingBag,
} from "@gravity-ui/icons";
import {Accordion} from "@heroui/react";
const items = [
{
content: "浏览我们的商品,将商品加入购物车并前往结账。完成购买需要提供收货与支付信息。",
icon: ,
title: "如何下单?",
},
{
content: "可以,在订单发货前你可以修改或取消。订单一旦进入处理流程,将无法再更改。",
icon: ,
title: "可以修改或取消订单吗?",
},
{
content: "我们接受主流信用卡,包括 Visa、Mastercard 和 American Express。",
icon: ,
title: "支持哪些支付方式?",
},
{
content: "运费因收货地址与订单体积而异。订单满 50 美元可享受免运费。",
icon: ,
title: "运费如何计算?",
},
{
content: "是的,我们可向多数国家/地区发货。请查看运费说明与政策了解更多信息。",
icon: ,
title: "是否提供国际配送?",
},
{
content: "若对购买不满意,可在购买后 30 天内申请退款。请联系客服团队协助处理。",
icon: ,
title: "如何申请退款?",
},
];
export function CustomRenderFunction() {
return (
}
>
{items.map((item, index) => (
}>
}>
}>
{item.icon ? (
{item.icon}
) : null}
{item.title}
}>
{item.content}
))}
);
}
```
## Related Components
* **DisclosureGroup**: Group of collapsible panels
* **Disclosure**: Single collapsible content section
## 样式
### 传入 Tailwind CSS 类
```tsx
"use client";
import { Accordion, cn } from "@heroui/react";
import {Icon} from "@iconify/react";
const items = [
{
content:
"Stay informed about your account activity with real-time notifications. You'll receive instant alerts for important events like transactions, new messages, security updates, and system announcements. ",
iconUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/bell-small.png",
title: "Set Up Notifications",
subtitle: "Receive account activity updates",
},
{
content:
"Enhance your browsing experience by installing our official browser extension. The extension provides seamless integration with your account, allowing you to receive notifications directly in your browser, quickly access your dashboard, and interact with web3 applications securely. Compatible with Chrome, Firefox, Edge, and Brave browsers.",
iconUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/compass-small.png",
title: "Set up Browser Extension",
subtitle: "Connect you browser to your account",
},
{
content:
"Begin your journey into the world of digital collectibles by creating your first NFT. Our intuitive minting process guides you through uploading your artwork, setting metadata, choosing royalty percentages, and deploying to the blockchain. Whether you're an artist, creator, or collector, you'll find all the tools you need to bring your digital assets to life. Your collectibles are stored on IPFS for permanent decentralized storage.",
iconUrl:
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/mint-collective-small.png",
title: "Mint Collectible",
subtitle: "Create your first collectible",
},
];
export function CustomStyles() {
return (
{items.map((item, index) => (
{item.iconUrl ? (
) : null}
{item.title}
{item.subtitle}
{item.content}
))}
);
}
```
### 自定义组件类
若要自定义 Accordion 组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.accordion {
@apply rounded-xl bg-gray-50;
}
.accordion__trigger {
@apply font-semibold text-lg;
}
.accordion--outline {
@apply shadow-lg border-2;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Accordion 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/accordion.css)):
#### 基础类
* `.accordion` - Accordion 根容器
* `.accordion__body` - 正文容器
* `.accordion__heading` - 标题包裹层
* `.accordion__indicator` - 展开/收起指示图标
* `.accordion__item` - 单个 Accordion 项
* `.accordion__panel` - 可折叠面板容器
* `.accordion__trigger` - 可点击的触发按钮
#### 变体类
* `.accordion--outline` - 描边变体(边框与背景)
#### 状态类
* `.accordion__trigger[aria-expanded="true"]` - 展开状态
* `.accordion__panel[aria-hidden="false"]` - 面板可见状态
### 交互状态
该组件同时支持 CSS 伪类与 data 属性:
* **悬停**:触发器上 `:hover` 或 `[data-hovered="true"]`
* **聚焦**:触发器上 `:focus-visible` 或 `[data-focus-visible="true"]`
* **禁用**:触发器上 `:disabled` 或 `[aria-disabled="true"]`
* **展开**:触发器上 `[aria-expanded="true"]`
## API 参考
### Accordion Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------------ | ---------------------------------------------------------------------------- | ----------- | ----------------------- |
| `allowsMultipleExpanded` | `boolean` | `false` | 是否允许多项同时展开。 |
| `defaultExpandedKeys` | `Iterable` | - | 初始展开的 key。 |
| `expandedKeys` | `Iterable` | - | 受控的展开 key。 |
| `onExpandedChange` | `(keys: Set) => void` | - | 展开 key 变化时调用的事件处理函数。 |
| `isDisabled` | `boolean` | `false` | 是否禁用整个 Accordion。 |
| `variant` | `"default" \| "surface"` | `"default"` | Accordion 的视觉变体。 |
| `hideSeparator` | `boolean` | `false` | 是否隐藏 Accordion 项之间的分隔线。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | Accordion 项。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Accordion.Item Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | -------------------------------------------------------------------------------- | ------- | --------------------- |
| `id` | `Key` | - | 该项的唯一标识。 |
| `isDisabled` | `boolean` | `false` | 是否禁用该项。 |
| `defaultExpanded` | `boolean` | `false` | 初始是否展开。 |
| `isExpanded` | `boolean` | - | 受控展开状态。 |
| `onExpandedChange` | `(isExpanded: boolean) => void` | - | 展开状态变化时调用的事件处理函数。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 项内容。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Accordion.Trigger Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | -------------------------------------------------------------------------- | --- | --------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | 触发器内容或渲染函数。 |
| `onPress` | `() => void` | - | 额外的按下事件处理函数。 |
| `isDisabled` | `boolean` | - | 是否禁用触发器。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Accordion.Panel Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | --------------------------------------------------------------------------------- | --- | --------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 面板内容。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Accordion.Indicator Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 自定义指示图标。 |
### Accordion.Body Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 正文内容。 |
# Breadcrumbs 面包屑
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/breadcrumbs
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(navigation)/breadcrumbs.mdx
> 面包屑导航,用于展示当前页面在层级结构中的位置。
## 引入
```tsx
import { Breadcrumbs } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {Breadcrumbs} from "@heroui/react";
export default function BreadcrumbsBasic() {
return (
首页
产品
电子产品
笔记本电脑
);
}
```
### 组件结构
导入 Breadcrumbs 组件后,可通过点语法访问各个子部分。
```tsx
import { Breadcrumbs } from '@heroui/react';
export default () => (
Home
Category
Current Page
)
```
### 导航层级
```tsx
"use client";
import {Breadcrumbs} from "@heroui/react";
export default function BreadcrumbsLevel2() {
return (
首页
当前页面
);
}
```
```tsx
"use client";
import {Breadcrumbs} from "@heroui/react";
export default function BreadcrumbsLevel3() {
return (
首页
分类
当前页面
);
}
```
### 自定义分隔符
```tsx
"use client";
import {Breadcrumbs} from "@heroui/react";
export default function BreadcrumbsCustomSeparator() {
return (
}
>
首页
产品
电子产品
笔记本电脑
);
}
```
### 禁用状态
```tsx
"use client";
import {Breadcrumbs} from "@heroui/react";
export default function BreadcrumbsDisabled() {
return (
首页
产品
电子产品
笔记本电脑
);
}
```
### 自定义渲染函数
```tsx
"use client";
import {Breadcrumbs} from "@heroui/react";
export function CustomRenderFunction() {
return (
}>
}>
首页
}>
产品
}>
电子产品
}>
笔记本电脑
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Breadcrumbs } from '@heroui/react';
function CustomBreadcrumbs() {
return (
Home
Current
);
}
```
### 自定义组件类
要自定义 Breadcrumbs 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.breadcrumbs {
@apply gap-4 text-lg;
}
.breadcrumbs__link {
@apply font-semibold;
}
.breadcrumbs__separator {
@apply text-blue-500;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Breadcrumbs 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/breadcrumbs.css)):
#### 基础类
* `.breadcrumbs` - 面包屑根容器
* `.breadcrumbs__item` - 单个面包屑项的包裹层
* `.breadcrumbs__link` - 面包屑链接元素
* `.breadcrumbs__separator` - 项之间的分隔图标
#### 状态类
* `.breadcrumbs__link[data-current="true"]` - 当前页指示(非链接)
### 交互状态
组件同时支持 CSS 伪类与 data 属性,便于灵活编写样式:
* **当前页**:链接上的 `[data-current="true"]`
* **悬停**:链接元素支持常规悬停态
* **禁用**:`isDisabled` prop 会禁用所有链接
## API 参考
### Breadcrumbs Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | ----------------------------------------------------------------- | ------------------ | --------------------- |
| `separator` | `ReactNode` | chevron-right icon | 面包屑项之间的自定义分隔符 |
| `isDisabled` | `boolean` | `false` | 是否禁用所有面包屑链接 |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode` | - | 面包屑项 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Breadcrumbs.Item Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------------------------------------------------------------------- | --- | --------------------- |
| `href` | `string` | - | 链接 URL(当前页可省略) |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode \| RenderFunction` | - | 项内容或渲染函数 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
## 无障碍
Breadcrumbs 基于 React Aria Components 的 Breadcrumbs 原语,提供:
* 导航地标的合适 ARIA 属性
* 通过 `aria-current="page"` 标示当前页
* 键盘导航支持
* 屏幕阅读器对导航上下文的播报
最后一项(无 `href`)会自动作为当前页指示。
# DisclosureGroup 折叠面板组
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/disclosure-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(navigation)/disclosure-group.mdx
> 管理多个 Disclosure 的容器,用于协调展开状态。
## 引入
```tsx
import { DisclosureGroup } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {QrCode} from "@gravity-ui/icons";
import {Button, Disclosure, DisclosureGroup, Separator} from "@heroui/react";
import {Icon} from "@iconify/react";
import React from "react";
import {cn} from "tailwind-variants";
export function Basic() {
const [expandedKeys, setExpandedKeys] = React.useState(new Set(["preview"]));
return (
预览 HeroUI Native
使用手机相机扫描此二维码,即可预览 HeroUI Native 组件。
设备需已安装 Expo。
在
Expo Go 预览
下载应用
下载 HeroUI Native 应用,即可在设备上直接体验我们的移动端组件。
支持 iOS 和 Android 设备。
在 App Store 下载
);
}
```
### 组件结构
导入所有子部分并组合使用。
```tsx
import {DisclosureGroup, Disclosure} from '@heroui/react';
export default () => (
)
```
### 受控
你可以使用 `expandedKeys` 与 `onExpandedChange` props,通过外部导航控件控制哪些 Disclosure 处于展开状态。
```tsx
"use client";
import {ChevronDown, ChevronUp, QrCode} from "@gravity-ui/icons";
import {
Button,
Disclosure,
DisclosureGroup,
Separator,
useDisclosureGroupNavigation,
} from "@heroui/react";
import {Icon} from "@iconify/react";
import React from "react";
import {cn} from "tailwind-variants";
export function Controlled() {
const [expandedKeys, setExpandedKeys] = React.useState(new Set(["preview"]));
const itemIds = ["preview", "download"]; // Track our disclosure items
const {isNextDisabled, isPrevDisabled, onNext, onPrevious} = useDisclosureGroupNavigation({
expandedKeys,
itemIds,
onExpandedChange: setExpandedKeys,
});
return (
预览 HeroUI Native
使用手机相机扫描此二维码,即可预览 HeroUI Native 组件。
设备需已安装 Expo。
在 Expo
Go 预览
下载 HeroUI Native
使用手机相机扫描此二维码,即可预览 HeroUI Native 组件。
设备需已安装 Expo。
在 App Store 下载
);
}
```
## Related Components
* **Accordion**: Collapsible content sections
* **Disclosure**: Single collapsible content section
* **Button**: Allows a user to perform an action
## 样式
### 传入 Tailwind CSS 类
```tsx
import {
DisclosureGroup,
Disclosure,
DisclosureTrigger,
DisclosurePanel
} from '@heroui/react';
function CustomDisclosureGroup() {
return (
Item 1
Content 1
Item 2
Content 2
);
}
```
### 自定义组件类
若要自定义 DisclosureGroup 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.disclosure-group {
@apply w-full;
/* Performance optimization */
contain: layout style;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
DisclosureGroup 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/disclosure-group.css)):
#### 基础类
* `.disclosure-group` - 带布局 containment 的基础容器样式
### 交互状态
组件同时支持 CSS 伪类与 data 属性,以便灵活控制状态:
* **禁用**:在整个组合上使用 `:disabled` 或 `[aria-disabled="true"]`
* **展开管理**:自动管理子 Disclosure 项上的 `[data-expanded]` 等状态
## API 参考
### DisclosureGroup Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------------ | ----------------------------- | ------- | ------------------- |
| `expandedKeys` | `Set` | - | 当前展开项(受控) |
| `defaultExpandedKeys` | `Iterable` | - | 初始展开项(非受控) |
| `onExpandedChange` | `(keys: Set) => void` | - | 展开项变化时调用的处理函数 |
| `allowsMultipleExpanded` | `boolean` | `false` | 是否允许多项同时展开 |
| `isDisabled` | `boolean` | `false` | 是否禁用组内全部 Disclosure |
| `children` | `ReactNode \| RenderFunction` | - | 要渲染的 Disclosure 项 |
| `className` | `string` | - | 额外的 CSS 类 |
### RenderProps
使用渲染 prop 模式时,会提供以下值:
| Prop | 类型 | 描述 |
| -------------- | ---------- | --------- |
| `expandedKeys` | `Set` | 当前展开的 key |
| `isDisabled` | `boolean` | 组合是否禁用 |
# Disclosure 折叠面板
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/disclosure
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(navigation)/disclosure.mdx
> Disclosure 是一种可折叠区域:头部包含标题与触发按钮,面板包裹正文内容。
## 引入
```tsx
import { Disclosure } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {QrCode} from "@gravity-ui/icons";
import {Button, Disclosure} from "@heroui/react";
import {Icon} from "@iconify/react";
import React from "react";
export function Basic() {
const [isExpanded, setIsExpanded] = React.useState(true);
return (
预览 HeroUI Native
使用手机相机扫描此二维码,即可预览 HeroUI Native 组件。
设备需已安装 Expo。
在 App Store 下载
);
}
```
### 组件结构
导入 Disclosure 组件后,可通过点号访问各个子部分。
```tsx
import { Disclosure } from '@heroui/react';
export default () => (
)
```
## Related Components
* **Accordion**: Collapsible content sections
* **DisclosureGroup**: Group of collapsible panels
* **Button**: Allows a user to perform an action
### 自定义渲染函数
```tsx
"use client";
import {QrCode} from "@gravity-ui/icons";
import {Button, Disclosure} from "@heroui/react";
import {Icon} from "@iconify/react";
import React from "react";
export function CustomRenderFunction() {
const [isExpanded, setIsExpanded] = React.useState(true);
return (
}
onExpandedChange={setIsExpanded}
>
预览 HeroUI Native
}>
使用手机相机扫描此二维码,即可预览 HeroUI Native 组件。
设备需已安装 Expo。
在 App Store 下载
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Disclosure } from '@heroui/react';
function CustomDisclosure() {
return (
Click to expand
Hidden content
);
}
```
### 自定义组件类
要自定义 Disclosure 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.disclosure {
@apply relative;
}
.disclosure__trigger {
@apply cursor-pointer;
}
.disclosure__indicator {
@apply transition-transform duration-300;
}
.disclosure__content {
@apply overflow-hidden transition-all;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,以确保组件变体与状态可复用且易于自定义。
### CSS 类
Disclosure 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/disclosure.css)):
#### 基础类
* `.disclosure` - 基础容器样式
* `.disclosure__heading` - 标题包裹层
* `.disclosure__trigger` - 触发按钮样式
* `.disclosure__indicator` - Chevron 指示器样式
* `.disclosure__content` - 带动画的内容容器
### 交互状态
组件同时支持 CSS 伪类与 data 属性,便于灵活定制:
* **Expanded**:指示器上 `[data-expanded="true"]`,用于旋转等效果
* **Focus**:触发器上 `:focus-visible` 或 `[data-focus-visible="true"]`
* **Disabled**:触发器上 `:disabled` 或 `[aria-disabled="true"]`
* **Hidden**:内容上 `[aria-hidden="false"]` 表示可见
## API 参考
### Disclosure Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | ----------------------------------------------------------------------------- | ------- | --------------------- |
| `isExpanded` | `boolean` | `false` | 控制展开状态 |
| `onExpandedChange` | `(isExpanded: boolean) => void` | - | 展开状态变化时的回调 |
| `isDisabled` | `boolean` | `false` | 是否禁用 Disclosure |
| `children` | `ReactNode \| RenderFunction` | - | 要渲染的内容 |
| `className` | `string` | - | 额外的 CSS 类 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### DisclosureTrigger Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------------------- | --- | --------- |
| `children` | `ReactNode \| RenderFunction` | - | 触发器内容 |
| `className` | `string` | - | 额外的 CSS 类 |
### DisclosureContent Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------------------ | --- | --------------------- |
| `children` | `ReactNode` | - | 要显示/隐藏的内容 |
| `className` | `string` | - | 额外的 CSS 类 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### RenderProps
使用渲染 prop 模式时,会提供以下值:
| Prop | 类型 | 描述 |
| ------------ | --------- | --------------- |
| `isExpanded` | `boolean` | 当前是否展开 |
| `isDisabled` | `boolean` | Disclosure 是否禁用 |
# Link 链接
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/link
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(navigation)/link.mdx
> 用于导航的样式化锚点组件,内置图标支持。
## 引入
```tsx
import { Link } from '@heroui/react';
```
### 用法
```tsx
import {Link} from "@heroui/react";
export function LinkBasic() {
return (
立即行动
);
}
```
### 组件结构
导入 Link 组件后,可通过点语法访问所有子部分。
```tsx
import { Link } from '@heroui/react';
export default () => (
Call to action
);
```
### 自定义图标
```tsx
import {ArrowUpRightFromSquare, Link as LinkIcon} from "@gravity-ui/icons";
import {Link} from "@heroui/react";
export function LinkCustomIcon() {
return (
);
}
```
### 图标位置
```tsx
import {Link} from "@heroui/react";
export function LinkIconPlacement() {
return (
图标在末尾(默认)
图标在开头
);
}
```
### 配合 Tailwind CSS 的文本装饰
Link 默认在悬浮时显示下划线。可使用 Tailwind CSS 的 text-decoration 工具类让下划线始终可见、完全移除,或自定义其颜色、样式、粗细与偏移。
```tsx
import {Link} from "@heroui/react";
export function LinkUnderlineAndOffset() {
return (
调整下划线偏移
偏移 1(1px 间距)
偏移 2(2px 间距)
偏移 3(3px 间距)
偏移 4(4px 间距)
);
}
```
**文本装饰线:**
* `underline` — 始终显示下划线
* `no-underline` — 移除下划线
* 默认 `Link` 样式 — 下划线在悬浮时显示
**文本装饰色:**
* `decoration-primary`、`decoration-secondary` 等 — 使用主题色设置下划线颜色
* `decoration-muted/50` — 使用透明度修饰符实现半透明下划线
**文本装饰样式:**
* `decoration-solid` — 实线(默认)
* `decoration-double` — 双线
* `decoration-dotted` — 点线
* `decoration-dashed` — 虚线
* `decoration-wavy` — 波浪线
**文本装饰粗细:**
* `decoration-1`、`decoration-2`、`decoration-4` 等 — 控制下划线粗细
**下划线偏移:**
* `underline-offset-1`、`underline-offset-2`、`underline-offset-4` 等 — 调整文本与下划线间距
更多说明见 Tailwind CSS 文档:
* [text-decoration-line](https://tailwindcss.com/docs/text-decoration-line)
* [text-decoration-color](https://tailwindcss.com/docs/text-decoration-color)
* [text-decoration-style](https://tailwindcss.com/docs/text-decoration-style)
* [text-decoration-thickness](https://tailwindcss.com/docs/text-decoration-thickness)
* [text-underline-offset](https://tailwindcss.com/docs/text-underline-offset)
可用的 BEM 类:
* 基础:`link`
* 图标:`link__icon`
## Related Components
* **Breadcrumbs**: Display the user's current location within a hierarchy
### 自定义渲染函数
```tsx
"use client";
import {Link} from "@heroui/react";
export function CustomRenderFunction() {
return (
}>
立即行动
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Link } from '@heroui/react';
function CustomLink() {
return (
Custom styled link
);
}
```
### 自定义组件类
要自定义 Link 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.link {
@apply font-semibold;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Link 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/link.css)):
#### 基础类
* `.link` — 链接基础样式
* `.link__icon` — 链接图标样式
### 交互状态
组件同时支持 CSS 伪类与 data 属性,以获得更大灵活性:
* **焦点**:`:focus-visible` 或 `[data-focus-visible="true"]`
* **悬浮**:`:hover` 或 `[data-hovered="true"]`
* **按下**:`:active` 或 `[data-pressed="true"]`
* **禁用**:`:disabled` 或 `[aria-disabled="true"]`
## API 参考
### Link Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | ----------------------------------------------------------------------- | --------- | --------------------- |
| `href` | `string` | - | 锚点的目标 URL |
| `target` | `string` | `"_self"` | 在何处打开链接文档 |
| `rel` | `string` | - | 当前文档与链接文档的关系 |
| `download` | `boolean \| string` | - | 触发下载而非导航 |
| `isDisabled` | `boolean` | `false` | 禁用指针与键盘交互 |
| `className` | `string` | - | 与默认样式合并的自定义类 |
| `children` | `React.ReactNode` | - | 渲染在链接内部的内容 |
| `onPress` | `(e: PressEvent) => void` | - | 链接被激活时触发 |
| `autoFocus` | `boolean` | - | 元素挂载时是否应获得焦点 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Link.Icon Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | ------------------- |
| `children` | `React.ReactNode` | - | 自定义图标元素;省略时使用内置箭头图标 |
| `className` | `string` | - | 附加的 CSS 类 |
### 与路由库配合使用
使用变体函数为框架专用链接(例如 Next.js)添加样式:
```tsx
import { Link } from '@heroui/react';
import { linkVariants } from '@heroui/styles';
import NextLink from 'next/link';
export default function Demo() {
const slots = linkVariants();
return (
About Page
);
}
```
### 直接应用类
由于 HeroUI 使用 [BEM](https://getbem.com/) 类,你可以将 Link 样式直接应用到任意链接元素:
```tsx
import NextLink from 'next/link';
// 直接使用 Tailwind 工具类
export default function Demo() {
return (
About Page
);
}
// 或使用原生
export default function NativeLink() {
return (
About Page
);
}
```
# Pagination 分页
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/pagination
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(navigation)/pagination.mdx
> 分页导航:可组合的页码链接、上一页/下一页按钮与省略号指示器。
## 引入
```tsx
import { Pagination } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {Pagination} from "@heroui/react";
import {useState} from "react";
export function PaginationBasic() {
const [page, setPage] = useState(1);
const totalPages = 3;
return (
setPage((p) => p - 1)}>
上一页
{Array.from({length: totalPages}, (_, i) => i + 1).map((p) => (
setPage(p)}>
{p}
))}
setPage((p) => p + 1)}>
下一页
);
}
```
### 组件结构
导入 Pagination 组件后,可通过点号访问各个子部分。
```tsx
import { Pagination } from '@heroui/react';
export default () => (
Showing 1-10 of 100 results
Previous
1
10
Next
);
```
### 尺寸
```tsx
"use client";
import {Pagination} from "@heroui/react";
import {useState} from "react";
const SIZE_LABELS = {
lg: "大",
md: "中",
sm: "小",
} as const;
function SizePagination({size}: {size: "sm" | "md" | "lg"}) {
const [page, setPage] = useState(1);
const totalPages = 3;
return (
{SIZE_LABELS[size]}
setPage((p) => p - 1)}>
上一页
{Array.from({length: totalPages}, (_, i) => i + 1).map((p) => (
setPage(p)}>
{p}
))}
setPage((p) => p + 1)}>
下一页
);
}
export function PaginationSizes() {
return (
{(["sm", "md", "lg"] as const).map((size) => (
))}
);
}
```
### 带省略号
```tsx
"use client";
import {Pagination} from "@heroui/react";
import {useState} from "react";
export function PaginationWithEllipsis() {
const [page, setPage] = useState(1);
const totalPages = 12;
const getPageNumbers = () => {
const pages: (number | "ellipsis")[] = [];
pages.push(1);
if (page > 3) {
pages.push("ellipsis");
}
const start = Math.max(2, page - 1);
const end = Math.min(totalPages - 1, page + 1);
for (let i = start; i <= end; i++) {
pages.push(i);
}
if (page < totalPages - 2) {
pages.push("ellipsis");
}
pages.push(totalPages);
return pages;
};
return (
setPage((p) => p - 1)}>
上一页
{getPageNumbers().map((p, i) =>
p === "ellipsis" ? (
) : (
setPage(p)}>
{p}
),
)}
setPage((p) => p + 1)}>
下一页
);
}
```
### 简化(上一页 / 下一页)
```tsx
"use client";
import {Pagination} from "@heroui/react";
import {useState} from "react";
export function PaginationSimplePrevNext() {
const [page, setPage] = useState(1);
const totalPages = 10;
const itemsPerPage = 5;
const totalItems = 50;
const startItem = (page - 1) * itemsPerPage + 1;
const endItem = Math.min(page * itemsPerPage, totalItems);
return (
第 {startItem}–{endItem} 条,共 {totalItems} 张发票
setPage((p) => p - 1)}>
上一页
setPage((p) => p + 1)}>
下一页
);
}
```
### 带摘要
```tsx
"use client";
import {Pagination} from "@heroui/react";
import {useState} from "react";
export function PaginationWithSummary() {
const [page, setPage] = useState(1);
const totalPages = 12;
const itemsPerPage = 10;
const totalItems = 120;
const getPageNumbers = () => {
const pages: (number | "ellipsis")[] = [];
pages.push(1);
if (page > 3) {
pages.push("ellipsis");
}
const start = Math.max(2, page - 1);
const end = Math.min(totalPages - 1, page + 1);
for (let i = start; i <= end; i++) {
pages.push(i);
}
if (page < totalPages - 2) {
pages.push("ellipsis");
}
pages.push(totalPages);
return pages;
};
const startItem = (page - 1) * itemsPerPage + 1;
const endItem = Math.min(page * itemsPerPage, totalItems);
return (
显示第 {startItem}–{endItem} 条,共 {totalItems} 条结果
setPage((p) => p - 1)}>
上一页
{getPageNumbers().map((p, i) =>
p === "ellipsis" ? (
) : (
setPage(p)}>
{p}
),
)}
setPage((p) => p + 1)}>
下一页
);
}
```
### 自定义图标
你可以通过为 `PreviousIcon` 与 `NextIcon` 传入自定义子节点来替换默认的 chevron 图标。
```tsx
"use client";
import {Pagination} from "@heroui/react";
import {Icon} from "@iconify/react";
import {useState} from "react";
export function PaginationCustomIcons() {
const [page, setPage] = useState(1);
const totalPages = 3;
return (
setPage((p) => p - 1)}>
返回
{Array.from({length: totalPages}, (_, i) => i + 1).map((p) => (
setPage(p)}>
{p}
))}
setPage((p) => p + 1)}>
前进
);
}
```
### 受控
```tsx
"use client";
import {Pagination} from "@heroui/react";
import {useState} from "react";
export function PaginationControlled() {
const [page, setPage] = useState(1);
const totalPages = 12;
const itemsPerPage = 10;
const totalItems = 120;
const getPageNumbers = () => {
const pages: (number | "ellipsis")[] = [];
if (totalPages <= 7) {
for (let i = 1; i <= totalPages; i++) {
pages.push(i);
}
} else {
pages.push(1);
if (page > 3) {
pages.push("ellipsis");
}
const start = Math.max(2, page - 1);
const end = Math.min(totalPages - 1, page + 1);
for (let i = start; i <= end; i++) {
pages.push(i);
}
if (page < totalPages - 2) {
pages.push("ellipsis");
}
pages.push(totalPages);
}
return pages;
};
const startItem = (page - 1) * itemsPerPage + 1;
const endItem = Math.min(page * itemsPerPage, totalItems);
return (
显示第 {startItem}–{endItem} 条,共 {totalItems} 条结果
setPage((p) => p - 1)}>
上一页
{getPageNumbers().map((p, i) =>
p === "ellipsis" ? (
) : (
setPage(p)}>
{p}
),
)}
setPage((p) => p + 1)}>
下一页
);
}
```
### 禁用
```tsx
"use client";
import {Pagination} from "@heroui/react";
import {useState} from "react";
export function PaginationDisabled() {
const [page, setPage] = useState(1);
const totalPages = 3;
return (
setPage((p) => p - 1)}>
上一页
{Array.from({length: totalPages}, (_, i) => i + 1).map((p) => (
setPage(p)}>
{p}
))}
setPage((p) => p + 1)}>
下一页
);
}
```
## Related Components
* **Button**: Allows a user to perform an action
* **Link**: Styled anchor links
## 样式
### 传入 Tailwind CSS 类
你可以单独定制 Pagination 的各个子部分:
```tsx
import { Pagination } from '@heroui/react';
function CustomPagination() {
return (
1
);
}
```
### 自定义组件类
要自定义 Pagination 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.pagination {
@apply gap-8;
}
.pagination__link {
@apply rounded-md;
}
.pagination__summary {
@apply text-xs font-semibold;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,以确保组件变体与状态可复用且易于自定义。
### CSS 类
Pagination 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/pagination.css)):
#### 基础与布局类
* `.pagination` - 根导航容器(flex 布局)
* `.pagination__summary` - 左侧信息文本容器
* `.pagination__content` - 分页项容器
* `.pagination__item` - 单个分页项包裹层
* `.pagination__link` - 页码按钮(ghost 按钮样式)
* `.pagination__link--nav` - 导航按钮修饰符(Previous/Next)
* `.pagination__ellipsis` - 省略号指示器
#### 尺寸类
* `.pagination--sm` - 小尺寸变体
* `.pagination--md` - 中尺寸变体(默认)
* `.pagination--lg` - 大尺寸变体
### 交互状态
组件同时支持 CSS 伪类与 data 属性,便于灵活定制:
* **Active page**:`[data-active="true"]` 或 `[aria-current="page"]`
* **Hover**:`:hover` 或 `[data-hovered="true"]`
* **Focus**:`:focus-visible` 或 `[data-focus-visible="true"]`
* **Disabled**:`:disabled` 或 `[aria-disabled="true"]`
* **Pressed**:`:active` 或 `[data-pressed="true"]`
## API 参考
### Pagination Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ---------------------- | ------ | ----------------------- |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | 分页控件的尺寸 |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode` | - | 分页部件(Summary、Content 等) |
### Pagination.Summary Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------------------ |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode` | - | 摘要内容(例如 "Showing 1-10 of 120") |
### Pagination.Content Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | --------- |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode` | - | 分页项 |
### Pagination.Item Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ---------------------------------- |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode` | - | 项内容(Link、Previous、Next 或 Ellipsis) |
### Pagination.Link Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | ------------------------- | ------- | ----------------------- |
| `isActive` | `boolean` | `false` | 是否为当前页 |
| `isDisabled` | `boolean` | `false` | 是否禁用链接 |
| `onPress` | `(e: PressEvent) => void` | - | 按下事件处理函数(来自 React Aria) |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode` | - | 页码内容 |
### Pagination.Previous / Pagination.Next Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | ------------------------- | ------- | --------------------------------- |
| `isDisabled` | `boolean` | `false` | 是否禁用按钮 |
| `onPress` | `(e: PressEvent) => void` | - | 按下事件处理函数(来自 React Aria) |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode` | - | 按钮内容(可与 PreviousIcon/NextIcon 组合) |
### Pagination.PreviousIcon / Pagination.NextIcon Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | ------------------- | --------------------- |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode` | Default chevron SVG | 用于替换默认 chevron 的自定义图标 |
### Pagination.Ellipsis Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------- | --- | --------- |
| `className` | `string` | - | 额外的 CSS 类 |
## 无障碍
Pagination 基于 [React Aria 的 Button](https://react-spectrum.adobe.com/react-aria/Button.html) 原语实现所有可交互元素,并提供:
* 语义化 `` 元素,包含 `aria-label="pagination"` 与 `role="navigation"`
* 通过在当前链接上使用 `aria-current="page"` 标示活动页
* 通过 Tab 键在全部可交互元素间进行键盘导航
* 通过 React Aria 在鼠标、触摸与键盘交互之间统一处理按下事件
* 键盘导航时通过 `:focus-visible` 显示焦点环
* 省略号使用 `aria-hidden="true"`,避免干扰屏幕阅读器
* 通过 `isDisabled` 向辅助技术正确传达禁用状态
> **说明:** Pagination 按钮请使用 `onPress` 而不是 `onClick`。React Aria 的 `onPress` 会规范化不同指针类型的按下行为,并提供开箱即用的无障碍改进。
# Tabs 标签页
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/tabs
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(navigation)/tabs.mdx
> Tabs 将内容组织为多个区块,并允许用户在它们之间导航。
## 引入
```tsx
import { Tabs } from '@heroui/react';
```
### 用法
### 组件结构
导入 Tabs 组件后,可通过点语法访问所有子部分。
```tsx
import { Tabs } from '@heroui/react';
export default () => (
{/* Optional */}
)
```
### 垂直布局
### 禁用 Tab
### 带分隔线
在每个 `` 内(第一项除外)添加 ` `,用于在标签之间显示分隔线。
### 自定义样式
### Secondary 变体
### Secondary 变体(垂直)
## Related Components
* **Breadcrumbs**: Display the user's current location within a hierarchy
### 自定义渲染函数
```tsx
"use client";
import {Tabs} from "@heroui/react";
import Link from "next/link";
export function CustomRenderFunction() {
return (
}>
}
>
快速入门
}
>
组件
}
>
发布说明
查看项目概览与近期活动。
跟踪指标并分析性能数据。
生成并下载详细报告。
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Tabs } from '@heroui/react';
function CustomTabs() {
return (
Daily
Weekly
Bi-Weekly
Monthly
Daily
Manage your daily tasks and goals.
Weekly
Manage your weekly tasks and goals.
Bi-Weekly
Manage your bi-weekly tasks and goals.
Monthly
Manage your monthly tasks and goals.
);
}
```
### CSS 类
Tabs 使用以下 CSS 类:
#### 基础类
* `.tabs` — Tabs 根容器
* `.tabs__list-container` — 标签列表容器外层包裹
* `.tabs__list` — 标签列表容器
* `.tabs__tab` — 单个标签按钮
* `.tabs__separator` — 标签之间的分隔线
* `.tabs__panel` — 标签面板内容
* `.tabs__indicator` — 标签指示器
#### 方向属性
* `.tabs[data-orientation="horizontal"]` — 水平标签布局(默认)
* `.tabs[data-orientation="vertical"]` — 垂直标签布局
#### 变体类
* `.tabs--secondary` — Secondary 变体,使用下划线指示器
### 交互状态
组件同时支持 CSS 伪类与 data 属性:
* **已选中**:`[aria-selected="true"]`
* **悬停**:`:hover` 或 `[data-hovered="true"]`
* **焦点**:`:focus-visible` 或 `[data-focus-visible="true"]`
* **禁用**:`[aria-disabled="true"]`
## API 参考
### Tabs Props
| Prop | 类型 | 默认值 | 描述 |
| -------------------- | ----------------------------------------------------------------------- | -------------- | ----------------------------------------- |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 视觉样式变体。Primary 使用填充指示器,Secondary 使用下划线指示器 |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | 标签布局方向 |
| `selectedKey` | `string` | - | 受控选中标签的 key |
| `defaultSelectedKey` | `string` | - | 默认选中标签的 key |
| `onSelectionChange` | `(key: Key) => void` | - | 选中变化事件处理函数 |
| `className` | `string` | - | 附加的 CSS 类 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Tabs.List Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | -------------------------------------------------------------------------- | --- | --------------------- |
| `aria-label` | `string` | - | 标签列表的无障碍标签 |
| `className` | `string` | - | 附加的 CSS 类 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Tabs.Tab Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | ---------------------------------------------------------------------- | ------- | --------------------- |
| `id` | `string` | - | 标签唯一标识 |
| `isDisabled` | `boolean` | `false` | 是否禁用该标签 |
| `className` | `string` | - | 附加的 CSS 类 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Tabs.Separator Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------- | --- | --------- |
| `className` | `string` | - | 附加的 CSS 类 |
### Tabs.Panel Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | --------------------------------------------------------------------------- | --- | --------------------- |
| `id` | `string` | - | 与对应 Tab id 匹配的面板标识 |
| `className` | `string` | - | 附加的 CSS 类 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
# AlertDialog 警告对话框
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/alert-dialog
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(overlays)/alert-dialog.mdx
> 用于关键确认的模态对话框,需要用户关注并执行明确操作。
## 引入
```tsx
import { AlertDialog } from "@heroui/react";
```
### 用法
```tsx
"use client";
import {AlertDialog, Button} from "@heroui/react";
export function Default() {
return (
删除项目
要永久删除项目吗?
此操作将永久删除 我的精彩项目 及其全部数据,且无法撤销。
取消
删除项目
);
}
```
### 组件结构
导入 AlertDialog 组件后,可通过点语法访问各个子部分。
```tsx
import {AlertDialog, Button} from "@heroui/react";
export default () => (
Open Alert Dialog
{/* Optional: Close button */}
{/* Optional: Status icon */}
);
```
### 状态
```tsx
"use client";
import {AlertDialog, Button} from "@heroui/react";
export function Statuses() {
const examples = [
{
actions: {
cancel: "保持登录",
confirm: "退出登录",
},
body: "退出后需要重新登录才能访问账户,未保存的更改将丢失。",
classNames: "bg-accent-soft text-accent-soft-foreground",
header: "要退出当前账户吗?",
status: "accent",
trigger: "退出登录",
},
{
actions: {
cancel: "稍后再说",
confirm: "标记完成",
},
body: "将把该任务标记为完成并通知所有成员,任务会移入已完成列表。",
classNames: "bg-success-soft text-success-soft-foreground",
header: "要完成此任务吗?",
status: "success",
trigger: "完成任务",
},
{
actions: {
cancel: "继续编辑",
confirm: "放弃更改",
},
body: "你有未保存的更改,放弃后将永久丢失。确定要放弃吗?",
classNames: "bg-warning-soft text-warning-soft-foreground",
header: "要放弃未保存的更改吗?",
status: "warning",
trigger: "放弃更改",
},
{
actions: {
cancel: "取消",
confirm: "删除账户",
},
body: "将永久删除你的账户并从服务器移除全部数据,此操作不可恢复。",
classNames: "bg-danger-soft text-danger-soft-foreground",
header: "要删除账户吗?",
status: "danger",
trigger: "删除账户",
},
] as const;
return (
{examples.map(({actions, body, classNames, header, status, trigger}) => (
{trigger}
{header}
{body}
{actions.cancel}
{actions.confirm}
))}
);
}
```
### 位置
```tsx
"use client";
import {AlertDialog, Button} from "@heroui/react";
const PLACEMENT_LABELS = {
auto: "自动",
bottom: "底部",
center: "居中",
top: "顶部",
} as const;
export function Placements() {
const placements = ["auto", "top", "center", "bottom"] as const;
return (
{placements.map((placement) => (
{PLACEMENT_LABELS[placement]}
{placement === "auto" ? "自动定位" : `${PLACEMENT_LABELS[placement]}位置`}
{placement === "auto"
? "在移动端默认靠近底部,在桌面端居中,以获得更合适的阅读与操作体验。"
: `对话框将锚定在视口的「${PLACEMENT_LABELS[placement]}」区域。重要确认通常使用居中 placement 以吸引最多注意。`}
取消
确认
))}
);
}
```
### 背景变体
```tsx
"use client";
import {AlertDialog, Button} from "@heroui/react";
const VARIANT_LABELS = {
blur: "模糊",
opaque: "不透明",
transparent: "透明",
} as const;
export function BackdropVariants() {
const variants = ["opaque", "blur", "transparent"] as const;
return (
{variants.map((variant) => (
{VARIANT_LABELS[variant]}
背景:{VARIANT_LABELS[variant]}
{variant === "opaque"
? "不透明的深色背景会完全遮挡背后内容,让用户把注意力集中在对话框上。"
: variant === "blur"
? "模糊背景会柔和地虚化背后内容,同时保留一定的环境上下文。"
: "透明背景会完整保留背后内容,适合重要性较低的确认场景。"}
取消
确认
))}
);
}
```
### 尺寸
```tsx
"use client";
import {Rocket} from "@gravity-ui/icons";
import {AlertDialog, Button} from "@heroui/react";
const SIZE_LABELS = {
cover: "通栏",
lg: "大",
md: "中",
sm: "小",
xs: "超小",
} as const;
export function Sizes() {
const sizes = ["xs", "sm", "md", "lg", "cover"] as const;
return (
{sizes.map((size) => (
{SIZE_LABELS[size]}
尺寸:{SIZE_LABELS[size]}
{size === "cover" ? (
<>
此警告框使用 cover 尺寸:在移动端与桌面端保留边距(移动端约
16px、桌面端约
40px)铺满可视区域,仍保持圆角与标准内边距,适合需要最大宽度又保留对话框气质的关键确认。
>
) : (
<>
此警告框使用 {size}{" "}
尺寸。在移动端各尺寸都会接近全宽以便阅读;在桌面端则对应不同的最大宽度,以适配不同信息量。
>
)}
取消
确认
))}
);
}
```
### 自定义图标
```tsx
"use client";
import {LockOpen} from "@gravity-ui/icons";
import {AlertDialog, Button} from "@heroui/react";
export function CustomIcon() {
return (
重置密码
要重置密码吗?
我们会向你的邮箱发送重置链接。你需要设置新密码以恢复账户访问。
取消
发送重置链接
);
}
```
### 自定义背景
```tsx
"use client";
import {TriangleExclamation} from "@gravity-ui/icons";
import {AlertDialog, Button} from "@heroui/react";
export function CustomBackdrop() {
return (
删除账户
要永久删除账户吗?
此操作无法撤销。你的数据、设置与内容将从服务器永久清除。醒目的红色背景用于强调该决定的严重性与不可逆性。
保留账户
永久删除
);
}
```
### 关闭行为
```tsx
"use client";
import {CircleInfo} from "@gravity-ui/icons";
import {AlertDialog, Button} from "@heroui/react";
export function DismissBehavior() {
return (
isDismissable
控制是否允许通过点击遮罩关闭对话框。警告框通常需要明确操作,因此默认为 false
。对重要性较低的确认,可设为 true。
打开警告对话框
isDismissable = false
点击遮罩不会关闭此对话框
尝试点击遮罩区域——对话框不会关闭,必须通过底部操作按钮关闭。
取消
确认
isKeyboardDismissDisabled
控制是否允许通过 ESC 关闭。警告框通常需要明确操作,因此默认为 true(禁用
ESC)。设为 false 时将允许 ESC 关闭。
打开警告对话框
isKeyboardDismissDisabled = true
已禁用 ESC 关闭
按下 ESC 不会有任何反应,必须通过操作按钮关闭此对话框。
取消
确认
);
}
```
### 关闭方式
```tsx
"use client";
import {AlertDialog, Button} from "@heroui/react";
export function CloseMethods() {
return (
使用 slot="close"
最简单的关闭方式:在对话框内的任意 Button 上添加{" "}
slot="close",点击后会自动关闭对话框。
打开对话框
使用 slot="close"
点击下方任一按钮——它们都带有 slot="close"
,点击后会自动关闭对话框。
取消
确认
使用 Dialog 的 render props
通过 Dialog 的 render props 获取 close{" "}
方法,从而完全控制关闭时机与方式,便于在关闭前加入校验等自定义逻辑。
打开对话框
{(renderProps) => (
<>
使用 Dialog render props
下方按钮使用 render props 提供的 close 方法。你可以在调用{" "}
renderProps.close() 之前加入校验或其他逻辑。
renderProps.close()}>
取消
renderProps.close()}>确认
>
)}
);
}
```
### 受控状态
```tsx
"use client";
import {AlertDialog, Button, useOverlayState} from "@heroui/react";
import React from "react";
export function Controlled() {
const [isOpen, setIsOpen] = React.useState(false);
const state = useOverlayState();
return (
配合 React.useState()
使用 React 的 useState{" "}
管理对话框开关,适合简单场景。
状态:{" "}
{isOpen ? "打开" : "关闭"}
setIsOpen(true)}>
打开对话框
setIsOpen(!isOpen)}>
切换
由 useState() 控制
该警告对话框由 React 的 useState 控制。将 isOpen 与{" "}
onOpenChange 传入即可在外部管理状态。
取消
确认
配合 useOverlayState()
使用 useOverlayState 获得更简洁的 API,内置{" "}
open()、close()、toggle() 等方法。
状态:{" "}
{state.isOpen ? "打开" : "关闭"}
打开对话框
切换
由 useOverlayState() 控制
useOverlayState 为常见操作提供专用方法,无需手写回调,直接使用{" "}
state.open()、state.close() 或{" "}
state.toggle() 即可。
取消
确认
);
}
```
### 自定义触发器
```tsx
"use client";
import {TrashBin} from "@gravity-ui/icons";
import {AlertDialog, Button} from "@heroui/react";
export function CustomTrigger() {
return (
要删除此条目吗?
使用 AlertDialog.Trigger{" "}
可在标准按钮之外自定义触发区域。此示例展示带图标与说明文字的卡片式触发器。
取消
删除条目
);
}
```
### 自定义动画
```tsx
"use client";
import {ArrowUpFromLine, Sparkles} from "@gravity-ui/icons";
import {AlertDialog, Button} from "@heroui/react";
import React from "react";
const iconMap: Record> = {
"gravity-ui:arrow-up-from-line": ArrowUpFromLine,
"gravity-ui:sparkles": Sparkles,
};
export function CustomAnimations() {
const animations = [
{
classNames: {
backdrop: [
"data-[entering]:duration-400",
"data-[entering]:ease-[cubic-bezier(0.16,1,0.3,1)]",
"data-[exiting]:duration-200",
"data-[exiting]:ease-[cubic-bezier(0.7,0,0.84,0)]",
].join(" "),
container: [
"data-[entering]:animate-in",
"data-[entering]:fade-in-0",
"data-[entering]:zoom-in-95",
"data-[entering]:duration-400",
"data-[entering]:ease-[cubic-bezier(0.16,1,0.3,1)]",
"data-[exiting]:animate-out",
"data-[exiting]:fade-out-0",
"data-[exiting]:zoom-out-95",
"data-[exiting]:duration-200",
"data-[exiting]:ease-[cubic-bezier(0.7,0,0.84,0)]",
].join(" "),
},
description:
"基于物理感的弹性缩放,模拟高阻尼弹簧:瞬态响应快、回落时间长,适合警告框与模态框。",
icon: "gravity-ui:sparkles",
name: "运动学缩放",
},
{
classNames: {
backdrop: [
"data-[entering]:duration-500",
"data-[entering]:ease-[cubic-bezier(0.25,1,0.5,1)]",
"data-[exiting]:duration-200",
"data-[exiting]:ease-[cubic-bezier(0.5,0,0.75,0)]",
].join(" "),
container: [
"data-[entering]:animate-in",
"data-[entering]:fade-in-0",
"data-[entering]:slide-in-from-bottom-4",
"data-[entering]:duration-500",
"data-[entering]:ease-[cubic-bezier(0.25,1,0.5,1)]",
"data-[exiting]:animate-out",
"data-[exiting]:fade-out-0",
"data-[exiting]:slide-out-to-bottom-2",
"data-[exiting]:duration-200",
"data-[exiting]:ease-[cubic-bezier(0.5,0,0.75,0)]",
].join(" "),
},
description:
"模拟在介质中运动并受流体阻力影响,避免机械式线性,更自然、更贴地,适合底部抽屉或 Toast。",
icon: "gravity-ui:arrow-up-from-line",
name: "流体滑入",
},
];
return (
{animations.map(({classNames, description, icon, name}) => {
const IconComponent = iconMap[icon];
return (
{name}
{!!IconComponent && }
{name} 动画
{description}
关闭
再试一次
);
})}
);
}
```
### 自定义 Portal
```tsx
"use client";
import {AlertDialog, Button} from "@heroui/react";
import {useCallback, useRef, useState} from "react";
export function CustomPortal() {
const portalRef = useRef(null);
const [portalContainer, setPortalContainer] = useState(null);
const setPortalRef = useCallback((node: HTMLDivElement | null) => {
portalRef.current = node;
setPortalContainer(node);
}, []);
return (
将警告对话框渲染到自定义容器,而不是 document.body
为容器应用 transform: translateZ(0){" "}
可创建新的层叠上下文。
{!!portalContainer && (
打开警告对话框
自定义传送门
此段为示例占位文案,用于演示在自定义容器内渲染对话框时的滚动与排版效果。实际项目中请替换为真实说明内容。
通过将浮层挂载到局部容器,可以配合裁剪、缩放或卡片布局,避免遮挡整个页面,同时仍保持焦点管理与无障碍行为。
若容器存在 transform 或 filter{" "}
等属性,请注意浏览器会为其创建新的包含块,从而影响定位与层级关系。
取消
确认
)}
);
}
```
## Related Components
* **Button**: Allows a user to perform an action
* **CloseButton**: Button for dismissing overlays
## 样式
### 传入 Tailwind CSS 类
```tsx
import {AlertDialog, Button} from "@heroui/react";
function CustomAlertDialog() {
return (
Delete
Custom Styled Alert
This alert dialog has custom styling applied via Tailwind classes
Cancel
Delete
);
}
```
### 自定义组件类
要自定义 AlertDialog 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.alert-dialog__backdrop {
@apply bg-gradient-to-br from-black/60 to-black/80;
}
.alert-dialog__dialog {
@apply rounded-2xl border border-red-500/20 shadow-2xl;
}
.alert-dialog__header {
@apply gap-4;
}
.alert-dialog__icon {
@apply size-16;
}
.alert-dialog__close-trigger {
@apply rounded-full bg-white/10 hover:bg-white/20;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
AlertDialog 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/alert-dialog.css)):
#### 基础类
* `.alert-dialog__trigger` - 打开对话框的触发元素
* `.alert-dialog__backdrop` - 对话框背后的遮罩层
* `.alert-dialog__container` - 支持位置配置的包裹层
* `.alert-dialog__dialog` - 对话框内容容器
* `.alert-dialog__header` - 图标与标题区域
* `.alert-dialog__heading` - 标题文本样式
* `.alert-dialog__body` - 主内容区域
* `.alert-dialog__footer` - 操作按钮区域
* `.alert-dialog__icon` - 带状态色的图标容器
* `.alert-dialog__close-trigger` - 关闭按钮元素
#### 背景变体
* `.alert-dialog__backdrop--opaque` - 不透明有色背景(默认)
* `.alert-dialog__backdrop--blur` - 带玻璃效果的模糊背景
* `.alert-dialog__backdrop--transparent` - 透明背景(无遮罩)
#### 状态变体(图标)
* `.alert-dialog__icon--default` - 默认灰色状态
* `.alert-dialog__icon--accent` - 强调蓝色状态
* `.alert-dialog__icon--success` - 成功绿色状态
* `.alert-dialog__icon--warning` - 警告橙色状态
* `.alert-dialog__icon--danger` - 危险红色状态
### 交互状态
组件支持以下交互状态:
* **聚焦**:`:focus-visible` 或 `[data-focus-visible="true"]` — 应用于触发器、对话框与关闭按钮
* **悬停**:`:hover` 或 `[data-hovered="true"]` — 应用于关闭按钮悬停时
* **激活**:`:active` 或 `[data-pressed="true"]` — 应用于关闭按钮按下时
* **进入**:`[data-entering]` — 应用于对话框打开动画期间
* **离开**:`[data-exiting]` — 应用于对话框关闭动画期间
* **位置**:`[data-placement="*"]` — 根据对话框位置应用(auto、top、center、bottom)
## API 参考
### AlertDialog
| Prop | 类型 | 默认值 | 描述 |
| ---------- | ----------- | --- | -------- |
| `children` | `ReactNode` | - | 触发器与容器元素 |
### AlertDialog.Trigger
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | -------- |
| `children` | `ReactNode` | - | 自定义触发器内容 |
| `className` | `string` | - | CSS 类 |
### AlertDialog.Backdrop
| Prop | 类型 | 默认值 | 描述 |
| --------------------------- | ------------------------------------- | ---------- | ------------- |
| `variant` | `"opaque" \| "blur" \| "transparent"` | `"opaque"` | 背景遮罩样式 |
| `isDismissable` | `boolean` | `false` | 点击背景是否关闭 |
| `isKeyboardDismissDisabled` | `boolean` | `true` | 是否禁用 ESC 关闭 |
| `isOpen` | `boolean` | - | 受控的打开状态 |
| `onOpenChange` | `(isOpen: boolean) => void` | - | 打开状态变化的事件处理函数 |
| `className` | `string \| (values) => string` | - | 背景的 CSS 类 |
| `UNSTABLE_portalContainer` | `HTMLElement` | - | 自定义 portal 容器 |
### AlertDialog.Container
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------------------------------- | -------- | ---------------- |
| `placement` | `"auto" \| "center" \| "top" \| "bottom"` | `"auto"` | 对话框在屏幕上的位置 |
| `size` | `"xs" \| "sm" \| "md" \| "lg" \| "cover"` | `"md"` | AlertDialog 尺寸变体 |
| `className` | `string \| (values) => string` | - | 容器的 CSS 类 |
### AlertDialog.Dialog
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | ------------------------------------- | --------------- | --------- |
| `children` | `ReactNode \| ({close}) => ReactNode` | - | 内容或渲染函数 |
| `className` | `string` | - | CSS 类 |
| `role` | `string` | `"alertdialog"` | ARIA role |
| `aria-label` | `string` | - | 无障碍标签 |
| `aria-labelledby` | `string` | - | 标签元素的 id |
| `aria-describedby` | `string` | - | 描述元素的 id |
### AlertDialog.Header
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------------ |
| `children` | `ReactNode` | - | 头部内容(通常为 Icon 与 Heading) |
| `className` | `string` | - | CSS 类 |
### AlertDialog.Heading
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ----- |
| `children` | `ReactNode` | - | 标题文本 |
| `className` | `string` | - | CSS 类 |
### AlertDialog.Body
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ----- |
| `children` | `ReactNode` | - | 正文内容 |
| `className` | `string` | - | CSS 类 |
### AlertDialog.Footer
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------- |
| `children` | `ReactNode` | - | 底部内容(通常为操作按钮) |
| `className` | `string` | - | CSS 类 |
### AlertDialog.Icon
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------- | ---------- | ------- |
| `children` | `ReactNode` | - | 自定义图标元素 |
| `status` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"danger"` | 状态颜色变体 |
| `className` | `string` | - | CSS 类 |
### AlertDialog.CloseTrigger
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------ | --- | ------- |
| `children` | `ReactNode` | - | 自定义关闭按钮 |
| `className` | `string \| (values) => string` | - | CSS 类 |
### useOverlayState Hook
```tsx
import {useOverlayState} from "@heroui/react";
const state = useOverlayState({
defaultOpen: false,
onOpenChange: (isOpen) => console.log(isOpen),
});
state.isOpen; // Current state
state.open(); // Open dialog
state.close(); // Close dialog
state.toggle(); // Toggle state
state.setOpen(); // Set state directly
```
## 无障碍
实现 [WAI-ARIA AlertDialog 模式](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/):
* **焦点陷阱**:焦点限制在 AlertDialog 内
* **键盘**:`ESC` 关闭(若启用)、`Tab` 在可聚焦元素间循环
* **屏幕阅读器**:`role="alertdialog"` 等合适的 ARIA 属性
* **滚动锁定**:打开时禁用 body 滚动
* **需要明确操作**:默认需要用户明确操作(不通过点击背景/ESC 轻易关闭)
# Drawer 抽屉
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/drawer
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(overlays)/drawer.mdx
> 用于补充内容与操作的侧滑面板。
## 引入
```tsx
import { Drawer, Button } from "@heroui/react";
```
### 用法
```tsx
import {Button, Drawer} from "@heroui/react";
export function Basic() {
return (
打开抽屉
抽屉标题
这是一个基于 React Aria Modal 组件构建的抽屉。它会从屏幕边缘滑入,并通过流畅的 CSS
过渡呈现动画效果。
取消
确认
);
}
```
### 组件结构
```tsx
import { Drawer, Button } from "@heroui/react";
export default () => (
Open Drawer
{/* Optional: Drag handle */}
{/* Optional: Close button */}
);
```
### 位置
```tsx
import {Button, Drawer} from "@heroui/react";
const PLACEMENT_LABELS = {
bottom: "底部",
left: "左侧",
right: "右侧",
top: "顶部",
} as const;
export function Placements() {
const placements = ["bottom", "top", "left", "right"] as const;
return (
{placements.map((placement) => (
{PLACEMENT_LABELS[placement]}
{placement === "bottom" && }
{PLACEMENT_LABELS[placement]}抽屉
此抽屉从屏幕{PLACEMENT_LABELS[placement]} 边缘滑入。
取消
完成
{placement === "top" && }
))}
);
}
```
### 遮罩变体
```tsx
import {Button, Drawer} from "@heroui/react";
const VARIANT_LABELS = {
blur: "模糊",
opaque: "不透明",
transparent: "透明",
} as const;
export function BackdropVariants() {
const variants = ["opaque", "blur", "transparent"] as const;
return (
{variants.map((variant) => (
{VARIANT_LABELS[variant]}
背景:{VARIANT_LABELS[variant]}
此抽屉使用 {variant} 背景变体。
关闭
))}
);
}
```
### 不可关闭
在 `Drawer.Backdrop` 上设置 `isDismissable={false}`,可阻止通过点击外部或拖动关闭。用户必须与抽屉内的操作按钮交互才能关闭。
```tsx
import {Button, Drawer} from "@heroui/react";
export function NonDismissable() {
return (
重要操作
确认操作
此抽屉无法通过点击外部或拖拽关闭。你必须使用下方按钮之一来完成操作。
取消
确认
);
}
```
### 可滚动内容
`Drawer.Body` 会使用原生滚动处理溢出。为避免与滚动冲突,拖拽关闭不会在 body 区域生效。
```tsx
import {Button, Drawer} from "@heroui/react";
export function ScrollableContent() {
return (
条款与条件
条款与条件
{Array.from({length: 20}).map((_, i) => (
段落 {i + 1}:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
pulvinar risus non risus hendrerit venenatis. Pellentesque sit amet hendrerit
risus, sed porttitor quam.
))}
拒绝
接受
);
}
```
### 受控状态
```tsx
"use client";
import {Button, Drawer, useOverlayState} from "@heroui/react";
import React from "react";
export function Controlled() {
const [isOpen, setIsOpen] = React.useState(false);
const state = useOverlayState();
return (
配合 React.useState()
使用 React 的 useState{" "}
管理抽屉开关,适合简单场景。
状态:{" "}
{isOpen ? "打开" : "关闭"}
setIsOpen(true)}>
打开抽屉
setIsOpen(!isOpen)}>
切换
由 useState() 控制
该抽屉由 React 的 useState 控制。将 isOpen 与{" "}
onOpenChange 传入即可在外部管理状态。
关闭
配合 useOverlayState()
使用 useOverlayState 获得更简洁的 API,内置{" "}
open()、close()、toggle() 等方法。
状态:{" "}
{state.isOpen ? "打开" : "关闭"}
打开抽屉
切换
由 useOverlayState() 控制
useOverlayState 为常见操作提供专用方法,无需手写回调,直接使用{" "}
state.open()、state.close() 或{" "}
state.toggle() 即可。
关闭
);
}
```
### 带表单
```tsx
import {Button, Drawer, Input, Label, TextField} from "@heroui/react";
export function WithForm() {
return (
编辑资料
编辑资料
姓名
邮箱
简介
取消
保存更改
);
}
```
### 导航抽屉
```tsx
import type {ComponentType, SVGProps} from "react";
import {Bars, Bell, Envelope, Gear, House, Magnifier, Person} from "@gravity-ui/icons";
import {Button, Drawer} from "@heroui/react";
export function Navigation() {
const navItems: {icon: ComponentType>; label: string}[] = [
{icon: House, label: "首页"},
{icon: Magnifier, label: "搜索"},
{icon: Bell, label: "通知"},
{icon: Envelope, label: "消息"},
{icon: Person, label: "个人资料"},
{icon: Gear, label: "设置"},
];
return (
菜单
导航
{navItems.map((item) => (
{item.label}
))}
);
}
```
## Related Components
* **Modal**: Displays content in a modal overlay
* **Button**: Allows a user to perform an action
* **CloseButton**: Button for dismissing overlays
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Drawer, Button } from "@heroui/react";
function CustomDrawer() {
return (
Open Drawer
Custom Styled Drawer
This drawer has custom styling applied via Tailwind classes.
Close
);
}
```
### 自定义组件类
若要自定义 Drawer 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.drawer__backdrop {
@apply bg-gradient-to-br from-black/50 to-black/70;
}
.drawer__dialog {
@apply rounded-2xl border border-white/10 shadow-2xl;
}
.drawer__header {
@apply text-center;
}
.drawer__close-trigger {
@apply rounded-full bg-white/10 hover:bg-white/20;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Drawer 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/drawer.css)):
#### 基础类
* `.drawer__trigger` - 打开 Drawer 的触发元素
* `.drawer__backdrop` - Drawer 背后的遮罩
* `.drawer__content` - Drawer 面板的定位包裹层
* `.drawer__dialog` - Drawer 面板本体
* `.drawer__header` - 表头区域
* `.drawer__heading` - 主标题文本
* `.drawer__body` - 可滚动主体内容区域
* `.drawer__footer` - 表底操作区域
* `.drawer__handle` - 视觉拖拽把手
* `.drawer__close-trigger` - 关闭按钮元素
#### 遮罩变体
* `.drawer__backdrop--opaque` - 不透明有色遮罩(默认)
* `.drawer__backdrop--blur` - 带玻璃效果的模糊遮罩
* `.drawer__backdrop--transparent` - 透明遮罩(无叠加层)
#### 位置变体
* `.drawer__content--bottom` - 自底边上滑(默认)
* `.drawer__content--top` - 自顶边下滑
* `.drawer__content--left` - 自左侧滑入
* `.drawer__content--right` - 自右侧滑入
#### 对话框变体
* `.drawer__dialog--top` - 自顶边下滑
* `.drawer__dialog--bottom` - 自底边上滑
* `.drawer__dialog--left` - 自左侧滑入
* `.drawer__dialog--right` - 自右侧滑入
### 交互状态
该组件支持以下交互状态:
* **聚焦**:`:focus-visible` 或 `[data-focus-visible="true"]` — 应用于触发器与关闭按钮
* **悬停**:`:hover` 或 `[data-hovered="true"]` — 关闭按钮悬停时应用
* **激活**:`:active` 或 `[data-pressed="true"]` — 触发器与关闭按钮被按压时应用
* **进入**:`[data-entering]` — Drawer 打开动画期间应用
* **离开**:`[data-exiting]` — Drawer 关闭动画期间应用
* **位置**:`[data-placement="*"]` — 根据 Drawer 位置应用(top、bottom、left、right)
## API 参考
### Drawer
| Prop | Type | 默认值 | 描述 |
| ---------- | ----------------------- | --- | ---------- |
| `children` | `ReactNode` | - | 触发器与遮罩子元素。 |
| `state` | `UseOverlayStateReturn` | - | 受控的叠加层状态。 |
### Drawer.Trigger
| Prop | Type | 默认值 | 描述 |
| ----------- | ----------- | --- | -------- |
| `children` | `ReactNode` | - | 自定义触发内容。 |
| `className` | `string` | - | CSS 类。 |
### Drawer.Backdrop
| Prop | Type | 默认值 | 描述 |
| --------------------------- | ------------------------------------- | ---------- | --------------- |
| `variant` | `"opaque" \| "blur" \| "transparent"` | `"opaque"` | 遮罩叠加样式。 |
| `isDismissable` | `boolean` | `true` | 点击遮罩是否关闭。 |
| `isKeyboardDismissDisabled` | `boolean` | `false` | 是否禁用 ESC 关闭。 |
| `isOpen` | `boolean` | - | 受控打开状态。 |
| `onOpenChange` | `(isOpen: boolean) => void` | - | 打开状态变化时的事件处理函数。 |
| `className` | `string \| (values) => string` | - | 遮罩 CSS 类。 |
### Drawer.Content
| Prop | Type | 默认值 | 描述 |
| ----------- | ---------------------------------------- | ---------- | -------------- |
| `placement` | `"top" \| "bottom" \| "left" \| "right"` | `"bottom"` | Drawer 从哪一侧滑入。 |
| `className` | `string \| (values) => string` | - | Content CSS 类。 |
### Drawer.Dialog
| Prop | Type | 默认值 | 描述 |
| ----------------- | ----------- | ---------- | ---------- |
| `children` | `ReactNode` | - | 对话框内容。 |
| `className` | `string` | - | CSS 类。 |
| `role` | `string` | `"dialog"` | ARIA role。 |
| `aria-label` | `string` | - | 无障碍标签。 |
| `aria-labelledby` | `string` | - | 标签元素 ID。 |
### Drawer.Header
| Prop | Type | 默认值 | 描述 |
| ----------- | ----------- | --- | ------ |
| `children` | `ReactNode` | - | 表头内容。 |
| `className` | `string` | - | CSS 类。 |
### Drawer.Heading
| Prop | Type | 默认值 | 描述 |
| ----------- | ----------- | --- | ------ |
| `children` | `ReactNode` | - | 标题文本。 |
| `className` | `string` | - | CSS 类。 |
### Drawer.Body
| Prop | Type | 默认值 | 描述 |
| ----------- | ----------- | --- | ------ |
| `children` | `ReactNode` | - | 主体内容。 |
| `className` | `string` | - | CSS 类。 |
### Drawer.Footer
| Prop | Type | 默认值 | 描述 |
| ----------- | ----------- | --- | ------ |
| `children` | `ReactNode` | - | 表底内容。 |
| `className` | `string` | - | CSS 类。 |
### Drawer.Handle
| Prop | Type | 默认值 | 描述 |
| ----------- | -------- | --- | ------ |
| `className` | `string` | - | CSS 类。 |
### Drawer.CloseTrigger
| Prop | Type | 默认值 | 描述 |
| ----------- | ------------------------------ | --- | -------- |
| `children` | `ReactNode` | - | 自定义关闭按钮。 |
| `className` | `string \| (values) => string` | - | CSS 类。 |
### useOverlayState Hook
```tsx
import { useOverlayState } from "@heroui/react";
const state = useOverlayState({
defaultOpen: false,
onOpenChange: (isOpen) => console.log(isOpen),
});
state.isOpen; // Current state
state.open(); // Open drawer
state.close(); // Close drawer
state.toggle(); // Toggle state
state.setOpen(); // Set state directly
```
## 无障碍
实现 [WAI-ARIA Dialog 模式](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/):
* **焦点陷阱**:打开时将焦点锁定在 Drawer 内
* **键盘**:可关闭时 `ESC` 关闭,`Tab` 在可聚焦元素间循环
* **屏幕阅读器**:通过 React Aria 提供合适的 ARIA 属性
* **滚动锁定**:打开时禁用 body 滚动
* **拖拽关闭**:支持在把手、表头与表底等区域的指针拖拽手势
# Modal 模态框
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/modal
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(overlays)/modal.mdx
> 用于聚焦用户交互与重要内容的对话框遮罩层。
## 引入
```tsx
import { Modal } from "@heroui/react";
```
### 用法
```tsx
"use client";
import {Rocket} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
export function Default() {
return (
打开模态框
欢迎使用 HeroUI
一套美观、快速、现代的 React UI 库,可轻松构建无障碍且高度可定制的 Web 应用。
继续
);
}
```
### 组件结构
导入 Modal 组件后,可通过点语法访问所有子部分。
```tsx
import {Modal, Button} from "@heroui/react";
export default () => (
Open Modal
{/* Optional: Close button */}
{/* Optional: Icon */}
);
```
### 位置
```tsx
"use client";
import {Rocket} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
const PLACEMENT_LABELS = {
auto: "自动",
bottom: "底部",
center: "居中",
top: "顶部",
} as const;
export function Placements() {
const placements = ["auto", "top", "center", "bottom"] as const;
return (
{placements.map((placement) => (
{PLACEMENT_LABELS[placement]}
{placement === "auto" ? "自动定位" : `${PLACEMENT_LABELS[placement]}位置`}
{placement === "auto"
? "在移动端默认靠近底部,在桌面端居中,以获得更合适的阅读与操作体验。"
: `模态框将锚定在视口的「${PLACEMENT_LABELS[placement]}」区域。可尝试不同 placement 查看屏幕上的定位效果。`}
继续
))}
);
}
```
### 遮罩变体
```tsx
"use client";
import {Rocket} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
const VARIANT_LABELS = {
blur: "模糊",
opaque: "不透明",
transparent: "透明",
} as const;
export function BackdropVariants() {
const variants = ["opaque", "blur", "transparent"] as const;
return (
{variants.map((variant) => (
{VARIANT_LABELS[variant]}
背景:{VARIANT_LABELS[variant]}
{variant === "opaque"
? "不透明背景会完全遮挡背后内容,让用户把注意力集中在模态框上。"
: variant === "blur"
? "模糊背景会柔和地虚化背后内容,同时保留一定的环境上下文。"
: "透明背景会完整保留背后内容,适合重要性较低的交互场景。"}
继续
))}
);
}
```
### 尺寸
```tsx
"use client";
import {Rocket} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
const SIZE_LABELS = {
cover: "通栏",
full: "全屏",
lg: "大",
md: "中",
sm: "小",
xs: "超小",
} as const;
export function Sizes() {
const sizes = ["xs", "sm", "md", "lg", "cover", "full"] as const;
return (
{sizes.map((size) => (
{SIZE_LABELS[size]}
尺寸:{SIZE_LABELS[size]}
{size === "cover" ? (
<>
此模态框使用 cover 尺寸:在移动端与桌面端保留边距(移动端约
16px、桌面端约
40px)铺满可视区域,仍保持圆角与标准内边距,适合需要最大宽度又保留模态框气质的内容展示。
>
) : size === "full" ? (
<>
此模态框使用 full{" "}
尺寸,占满整个视口,无边距、圆角或阴影,提供真正的全屏体验,适合沉浸式内容或全页交互。
>
) : (
<>
此模态框使用 {size}{" "}
尺寸。在移动端各尺寸都会接近全宽以便阅读;在桌面端则对应不同的最大宽度,以适配不同信息量。
>
)}
取消
确认
))}
);
}
```
### 自定义遮罩
```tsx
"use client";
import {Sparkles} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
export function CustomBackdrop() {
return (
自定义背景
高级背景
此背景采用从底部深色过渡到顶部完全透明的精致渐变,并配合柔和的模糊效果。渐变会在浅色与深色模式下自动调整强度,以获得最佳对比度。
Amazing!
关闭
);
}
```
### 关闭行为
```tsx
"use client";
import {CircleInfo} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
export function DismissBehavior() {
return (
isDismissable
控制是否允许通过点击遮罩关闭模态框。默认为 true。设为 false{" "}
时需通过明确操作关闭。
打开模态框
isDismissable = false
点击遮罩不会关闭此模态框
尝试点击遮罩区域——模态框不会关闭,必须使用关闭按钮或按 ESC 键关闭。
关闭
isKeyboardDismissDisabled
控制是否允许通过 ESC 关闭模态框。设为 true 时将禁用
ESC,用户须通过明确操作关闭。
打开模态框
isKeyboardDismissDisabled = true
已禁用 ESC 键
按 ESC 无反应。必须使用关闭按钮或点击遮罩才能关闭此模态框。
关闭
);
}
```
### 关闭方式
```tsx
"use client";
import {CircleCheck, CircleInfo} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
export function CloseMethods() {
return (
使用 slot="close"
关闭模态框的最简方式:为模态框内任意 Button 添加 slot="close"
,点击即可自动关闭。
打开模态框
使用 slot="close"
点击下方任一按钮——它们都带有 slot="close",会自动关闭模态框。
取消
确认
使用 Dialog 渲染属性
通过 Dialog 的渲染属性访问 close{" "}
方法,可完全控制关闭时机与方式,并在关闭前加入自定义逻辑。
打开模态框
{(renderProps) => (
<>
使用 Dialog 渲染属性
下方按钮使用渲染属性中的 close 方法。可在调用{" "}
renderProps.close() 前进行校验或其他逻辑。
renderProps.close()}>
取消
renderProps.close()}>确认
>
)}
);
}
```
### 滚动行为
```tsx
"use client";
import {Button, Modal, Radio, RadioGroup} from "@heroui/react";
import {useState} from "react";
export function ScrollComparison() {
const [scroll, setScroll] = useState<"inside" | "outside">("inside");
return (
setScroll(value as "inside" | "outside")}
>
内部
外部
打开模态框({scroll.charAt(0).toUpperCase() + scroll.slice(1)})
{scroll === "inside" ? "滚动:内部" : "滚动:外部"}
对比滚动行为——内部在模态框内滚动内容,外部允许页面滚动
{Array.from({length: 30}).map((_, i) => (
段落 {i + 1}: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
pulvinar risus non risus hendrerit venenatis. Pellentesque sit amet hendrerit
risus, sed porttitor quam.
))}
取消
确认
);
}
```
### 受控状态
```tsx
"use client";
import {CircleCheck} from "@gravity-ui/icons";
import {Button, Modal, useOverlayState} from "@heroui/react";
import React from "react";
export function Controlled() {
const [isOpen, setIsOpen] = React.useState(false);
const state = useOverlayState();
return (
配合 React.useState()
使用 React 的 useState{" "}
管理模态框开关,适合简单场景。
状态:{" "}
{isOpen ? "打开" : "关闭"}
setIsOpen(true)}>
打开模态框
setIsOpen(!isOpen)}>
切换
由 useState() 控制
该模态框由 React 的 useState 控制。将 isOpen 与{" "}
onOpenChange 传入即可在外部管理状态。
取消
确认
配合 useOverlayState()
使用 useOverlayState 获得更简洁的 API,内置{" "}
open()、close()、toggle() 等方法。
状态:{" "}
{state.isOpen ? "打开" : "关闭"}
打开模态框
切换
由 useOverlayState() 控制
useOverlayState 为常见操作提供专用方法,无需手写回调,直接使用{" "}
state.open()、state.close() 或{" "}
state.toggle() 即可。
取消
确认
);
}
```
### 带表单
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {Button, Input, Label, Modal, Surface, TextField} from "@heroui/react";
export function WithForm() {
return (
打开联系表单
联系我们
填写下方表单,我们会尽快回复。在移动端弹出键盘时,模态框会自动适配。
姓名
邮箱
电话
公司
留言
取消
发送消息
);
}
```
### 自定义触发器
```tsx
"use client";
import {Gear} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
export function CustomTrigger() {
return (
设置
使用 Modal.Trigger{" "}
可在标准按钮之外创建自定义触发器。此示例展示带图标与说明文字的卡片式触发器。
取消
保存
);
}
```
### 自定义动画
```tsx
"use client";
import {ArrowUpFromLine, Sparkles} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
import React from "react";
const iconMap: Record> = {
"gravity-ui:arrow-up-from-line": ArrowUpFromLine,
"gravity-ui:sparkles": Sparkles,
};
export function CustomAnimations() {
const animations = [
{
classNames: {
backdrop: [
"data-[entering]:duration-400",
"data-[entering]:ease-[cubic-bezier(0.16,1,0.3,1)]",
"data-[exiting]:duration-200",
"data-[exiting]:ease-[cubic-bezier(0.7,0,0.84,0)]",
].join(" "),
container: [
"data-[entering]:animate-in",
"data-[entering]:fade-in-0",
"data-[entering]:zoom-in-95",
"data-[entering]:duration-400",
"data-[entering]:ease-[cubic-bezier(0.16,1,0.3,1)]",
"data-[exiting]:animate-out",
"data-[exiting]:fade-out-0",
"data-[exiting]:zoom-out-95",
"data-[exiting]:duration-200",
"data-[exiting]:ease-[cubic-bezier(0.7,0,0.84,0)]",
].join(" "),
},
description:
"基于物理的弹性缩放,模拟高阻尼弹簧系统:快速瞬态响应与较长 settling 时间。适用于模态框与弹出层。",
icon: "gravity-ui:sparkles",
name: "运动缩放",
},
{
classNames: {
backdrop: [
"data-[entering]:duration-500",
"data-[entering]:ease-[cubic-bezier(0.25,1,0.5,1)]",
"data-[exiting]:duration-200",
"data-[exiting]:ease-[cubic-bezier(0.5,0,0.75,0)]",
].join(" "),
container: [
"data-[entering]:animate-in",
"data-[entering]:fade-in-0",
"data-[entering]:slide-in-from-bottom-4",
"data-[entering]:duration-500",
"data-[entering]:ease-[cubic-bezier(0.25,1,0.5,1)]",
"data-[exiting]:animate-out",
"data-[exiting]:fade-out-0",
"data-[exiting]:slide-out-to-bottom-2",
"data-[exiting]:duration-200",
"data-[exiting]:ease-[cubic-bezier(0.5,0,0.75,0)]",
].join(" "),
},
description:
"模拟流体阻力中的运动,摆脱机械式线性动画,呈现更自然、沉稳的质感。适用于底部抽屉或 Toast。",
icon: "gravity-ui:arrow-up-from-line",
name: "流体滑入",
},
];
return (
{animations.map(({classNames, description, icon, name}) => {
const IconComponent = iconMap[icon];
return (
{name}
{!!IconComponent && }
{name} 动画
{description}
关闭
再试一次
);
})}
);
}
```
### 自定义 Portal
```tsx
"use client";
import {Button, Modal} from "@heroui/react";
import {useCallback, useRef, useState} from "react";
export function CustomPortal() {
const portalRef = useRef(null);
const [portalContainer, setPortalContainer] = useState(null);
const setPortalRef = useCallback((node: HTMLDivElement | null) => {
portalRef.current = node;
setPortalContainer(node);
}, []);
return (
在自定义容器内渲染模态框,而非 document.body
为容器应用 transform: translateZ(0){" "}
以创建新的层叠上下文。
{!!portalContainer && (
打开模态框
自定义 Portal
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
关闭
)}
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import {Modal, Button} from "@heroui/react";
function CustomModal() {
return (
Open Modal
Custom Styled Modal
This modal has custom styling applied via Tailwind classes
Close
);
}
```
### 自定义组件类
要自定义 Modal 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.modal__backdrop {
@apply bg-gradient-to-br from-black/50 to-black/70;
}
.modal__dialog {
@apply rounded-2xl border border-white/10 shadow-2xl;
}
.modal__header {
@apply text-center;
}
.modal__close-trigger {
@apply rounded-full bg-white/10 hover:bg-white/20;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Modal 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/modal.css)):
#### 基础类
* `.modal__trigger` — 打开 Modal 的触发元素
* `.modal__backdrop` — Modal 背后的遮罩层
* `.modal__container` — 支持 placement 的定位包裹层
* `.modal__dialog` — Modal 内容容器
* `.modal__header` — 标题与图标区域
* `.modal__body` — 主内容区域
* `.modal__footer` — 操作区域
* `.modal__close-trigger` — 关闭按钮元素
#### 遮罩变体
* `.modal__backdrop--opaque` — 不透明有色遮罩(默认)
* `.modal__backdrop--blur` — 带玻璃效果的模糊遮罩
* `.modal__backdrop--transparent` — 透明遮罩(无叠加层)
#### 滚动变体
* `.modal__container--scroll-outside` — 允许整个 Modal 滚动
* `.modal__dialog--scroll-inside` — 限制 Modal 高度,由 body 区域滚动
* `.modal__body--scroll-inside` — 仅 body 区域可滚动
* `.modal__body--scroll-outside` — 允许整页滚动
### 交互状态
组件支持以下交互状态:
* **焦点**:`:focus-visible` 或 `[data-focus-visible="true"]` — 应用于触发器、对话框与关闭按钮
* **悬停**:`:hover` 或 `[data-hovered="true"]` — 应用于关闭按钮悬停
* **按下**:`:active` 或 `[data-pressed="true"]` — 应用于关闭按钮按下
* **进入**:`[data-entering]` — Modal 打开动画期间
* **离开**:`[data-exiting]` — Modal 关闭动画期间
* **位置**:`[data-placement="*"]` — 基于 Modal 位置(auto、top、center、bottom)
## API 参考
### Modal
| Prop | 类型 | 默认值 | 描述 |
| ---------- | ----------- | --- | -------- |
| `children` | `ReactNode` | - | 触发器与容器元素 |
### Modal.Trigger
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------- |
| `children` | `ReactNode` | - | 自定义触发内容 |
| `className` | `string` | - | CSS 类 |
### Modal.Backdrop
| Prop | 类型 | 默认值 | 描述 |
| --------------------------- | ------------------------------------- | ---------- | ------------- |
| `variant` | `"opaque" \| "blur" \| "transparent"` | `"opaque"` | 遮罩叠加样式 |
| `isDismissable` | `boolean` | `true` | 点击遮罩是否关闭 |
| `isKeyboardDismissDisabled` | `boolean` | `false` | 是否禁用 ESC 关闭 |
| `isOpen` | `boolean` | - | 受控打开状态 |
| `onOpenChange` | `(isOpen: boolean) => void` | - | 打开状态变化处理函数 |
| `className` | `string \| (values) => string` | - | 遮罩 CSS 类 |
| `UNSTABLE_portalContainer` | `HTMLElement` | - | 自定义 portal 容器 |
### Modal.Container
| Prop | 类型 | 默认值 | 描述 |
| ----------- | --------------------------------------------------- | ---------- | ------------- |
| `placement` | `"auto" \| "center" \| "top" \| "bottom"` | `"auto"` | Modal 在屏幕上的位置 |
| `scroll` | `"inside" \| "outside"` | `"inside"` | 滚动行为 |
| `size` | `"xs" \| "sm" \| "md" \| "lg" \| "cover" \| "full"` | `"md"` | Modal 尺寸变体 |
| `className` | `string \| (values) => string` | - | 容器 CSS 类 |
### Modal.Dialog
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | ------------------------------------- | ---------- | --------- |
| `children` | `ReactNode \| ({close}) => ReactNode` | - | 内容或渲染函数 |
| `className` | `string \| (values) => string` | - | CSS 类 |
| `role` | `string` | `"dialog"` | ARIA role |
| `aria-label` | `string` | - | 无障碍标签 |
| `aria-labelledby` | `string` | - | 标签元素的 id |
| `aria-describedby` | `string` | - | 描述元素的 id |
### Modal.Header
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ----- |
| `children` | `ReactNode` | - | 头部内容 |
| `className` | `string` | - | CSS 类 |
### Modal.Body
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ----- |
| `children` | `ReactNode` | - | 正文内容 |
| `className` | `string` | - | CSS 类 |
### Modal.Footer
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ----- |
| `children` | `ReactNode` | - | 底部内容 |
| `className` | `string` | - | CSS 类 |
### Modal.CloseTrigger
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------ | --- | ------- |
| `children` | `ReactNode` | - | 自定义关闭按钮 |
| `className` | `string \| (values) => string` | - | CSS 类 |
### useOverlayState Hook
```tsx
import {useOverlayState} from "@heroui/react";
const state = useOverlayState({
defaultOpen: false,
onOpenChange: (isOpen) => console.log(isOpen),
});
state.isOpen; // 当前状态
state.open(); // 打开 modal
state.close(); // 关闭 modal
state.toggle(); // 切换状态
state.setOpen(); // 直接设置状态
```
## 无障碍
实现 [WAI-ARIA Dialog 模式](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/):
* **焦点陷阱**:焦点锁定在 Modal 内
* **键盘**:`ESC` 关闭(启用时)、`Tab` 在元素间循环
* **屏幕阅读器**:正确的 ARIA 属性
* **滚动锁定**:打开时禁用 body 滚动
# Popover 弹出框
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/popover
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(overlays)/popover.mdx
> 在由按钮或任意自定义元素触发后,于 portal 中展示丰富内容。
## 引入
```tsx
import { Popover } from '@heroui/react';
```
### 用法
```tsx
import {Button, Popover} from "@heroui/react";
export function PopoverBasic() {
return (
点击我
弹出层标题
这是弹出层内容,你可以在这里放置任何内容。
);
}
```
### 组件结构
引入 Popover 后,可通过点语法访问各个部分。
```tsx
import { Popover } from '@heroui/react';
export default () => (
{/* content goes here */}
)
```
### 带箭头
```tsx
import {Ellipsis} from "@gravity-ui/icons";
import {Button, Popover} from "@heroui/react";
export function PopoverWithArrow() {
return (
带箭头
带箭头的弹出层
箭头指向触发弹出层的元素。
带箭头的弹出层
箭头指向触发弹出层的元素。
);
}
```
### 位置
```tsx
import {Button, Popover} from "@heroui/react";
export function PopoverPlacement() {
return (
Top
顶部位置
Left
左侧位置
点击按钮
Right
右侧位置
Bottom
底部位置
);
}
```
### 可交互内容
```tsx
"use client";
import {Avatar, Button, Popover} from "@heroui/react";
import {useState} from "react";
export function PopoverInteractive() {
const [isFollowing, setIsFollowing] = useState(false);
return (
setIsFollowing(!isFollowing)}
>
{isFollowing ? "已关注" : "关注"}
产品设计师兼创意总监,打造有意义的美好体验。
);
}
```
## Related Components
* **Button**: Allows a user to perform an action
* **Tooltip**: Contextual information on hover or focus
* **Select**: Dropdown select control
### 自定义渲染函数
```tsx
"use client";
import {Button, Popover} from "@heroui/react";
export function CustomRenderFunction() {
return (
点击我
}
>
弹出层标题
这是弹出层内容,你可以在这里放置任何内容。
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Popover, Button } from '@heroui/react';
function CustomPopover() {
return (
Open
Custom Styled
This popover has custom styling
);
}
```
### 自定义组件类
若要自定义 Popover 的组件类名,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.popover {
@apply rounded-xl shadow-2xl;
}
.popover__dialog {
@apply p-4;
}
.popover__heading {
@apply text-lg font-bold;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于定制。
### CSS 类
Popover 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/popover.css)):
#### 基础类
* `.popover` - Popover 根容器样式
* `.popover__dialog` - 对话框内容包裹层
* `.popover__heading` - 标题文本样式
* `.popover__trigger` - 触发元素样式
### 交互状态
组件支持以下动画相关状态:
* **进入**:`[data-entering]` — Popover 出现过程中应用
* **离开**:`[data-exiting]` — Popover 消失过程中应用
* **位置**:`[data-placement="*"]` — 根据 Popover 位置应用
* **焦点**:`:focus-visible` 或 `[data-focus-visible="true"]`
## API 参考
### Popover Props
| Prop | 类型 | 默认值 | 描述 |
| -------------- | --------------------------- | ------- | ------------------- |
| `children` | `React.ReactNode` | - | 触发器与内容元素 |
| `isOpen` | `boolean` | - | 控制 Popover 是否可见(受控) |
| `defaultOpen` | `boolean` | `false` | 初始打开状态(非受控) |
| `onOpenChange` | `(isOpen: boolean) => void` | - | 打开状态变化时调用 |
### Popover.Content Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | -------------------------------------------------------------------------- | ---------- | ---------------------- |
| `children` | `React.ReactNode` | - | 在 Popover 中展示的内容 |
| `placement` | `"top" \| "bottom" \| "left" \| "right"` (及变体) | `"bottom"` | Popover 的位置 |
| `offset` | `number` | `8` | 与触发元素的距离 |
| `shouldFlip` | `boolean` | `true` | 是否允许 Popover 改变方向以适配空间 |
| `className` | `string` | - | 额外的 CSS 类名 |
| `render` | `DOMRenderFunction` | - | 通过自定义渲染函数覆盖默认的 DOM 元素。 |
### Popover.Dialog Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | ---------- |
| `children` | `React.ReactNode` | - | 对话框内容 |
| `className` | `string` | - | 额外的 CSS 类名 |
### Popover.Trigger Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | -------------- |
| `children` | `React.ReactNode` | - | 触发 Popover 的元素 |
| `className` | `string` | - | 额外的 CSS 类名 |
### Popover.Arrow Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------------- | --- | ---------------------- |
| `children` | `React.ReactNode` | - | 自定义箭头元素 |
| `className` | `string` | - | 额外的 CSS 类名 |
| `render` | `DOMRenderFunction` | - | 通过自定义渲染函数覆盖默认的 DOM 元素。 |
# Toast 轻提示
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/toast
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(overlays)/toast.mdx
> 向用户展示临时通知与消息,支持自动消失与可定制的放置位置。
## 引入
```tsx
import { Toast, toast } from '@heroui/react';
```
## 设置
在应用根部渲染 Provider。
```tsx
import { Toast, Button, toast } from '@heroui/react';
function App() {
return (
toast("Simple message")}>
Show toast
);
}
```
### 用法
```tsx
"use client";
import {Persons} from "@gravity-ui/icons";
import {Button, toast} from "@heroui/react";
export function Default() {
return (
{
toast("您已被邀请加入团队", {
actionProps: {
children: "忽略",
onPress: () => toast.clear(),
variant: "tertiary",
},
description: "Bob 邀请您加入 HeroUI 团队",
indicator: ,
variant: "default",
});
}}
>
显示 Toast
);
}
```
### 简单 Toast
```tsx
"use client";
import {Button, toast} from "@heroui/react";
export function Simple() {
return (
toast("简单消息")}>
默认
toast.success("操作已完成")}>
成功
toast.info("有新更新可用")}>
信息
toast.warning("请检查您的设置")}>
警告
toast.danger("出了点问题")}>
错误
);
}
```
### 变体
```tsx
"use client";
import {HardDrive, Persons} from "@gravity-ui/icons";
import {Button, toast} from "@heroui/react";
const noop = () => {};
export function Variants() {
return (
{
toast("您已被邀请加入团队", {
actionProps: {
children: "忽略",
onPress: () => toast.clear(),
variant: "tertiary",
},
description: "Bob 邀请您加入 HeroUI 团队",
indicator: ,
variant: "default",
});
}}
>
默认 Toast
toast.info("您还剩 2 个积分", {
actionProps: {children: "升级", onPress: noop},
description: "升级付费方案以获取更多积分",
})
}
>
强调 Toast
toast.success("您已升级方案", {
actionProps: {
children: "账单",
className: "bg-success text-success-foreground",
onPress: noop,
},
description: "您可以继续使用 HeroUI Chat",
})
}
>
成功 Toast
toast.warning("您的积分已用完", {
actionProps: {
children: "升级",
className: "bg-warning text-warning-foreground",
onPress: noop,
},
description: "升级付费方案以继续使用",
})
}
>
警告 Toast
toast.danger("存储空间已满", {
actionProps: {children: "删除", onPress: noop, variant: "danger"},
description: "删除文件以释放空间。此处增加更多文字以演示较长内容的显示效果",
indicator: ,
})
}
>
危险 Toast
);
}
```
### 自定义指示器
```tsx
"use client";
import {Star} from "@gravity-ui/icons";
import {Button, toast} from "@heroui/react";
export function CustomIndicator() {
return (
toast("自定义图标指示器", {
indicator: ,
})
}
>
自定义指示器
);
}
```
### Promise 与加载中
```tsx
"use client";
import {Button, toast} from "@heroui/react";
const uploadFile = (): Promise<{filename: string; size: number}> => {
return new Promise<{filename: string; size: number}>((resolve) => {
setTimeout(() => resolve({filename: "document.pdf", size: 1024}), 2000);
});
};
const createEvent = (): Promise => {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error("网络错误,请重试。")), 2000);
});
};
const saveData = (): Promise<{count: number}> => {
return new Promise<{count: number}>((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve({count: 42});
} else {
reject(new Error("保存数据失败"));
}
}, 2000);
});
};
const fetchUser = (): Promise<{name: string; email: string}> => {
return new Promise<{name: string; email: string}>((resolve) => {
setTimeout(() => resolve({email: "john@example.com", name: "John Doe"}), 2000);
});
};
export function PromiseDemo() {
return (
{/* Promise API Section */}
使用 toast.promise()
自动处理加载、成功和错误状态
{
toast.promise(uploadFile(), {
error: "上传文件失败",
loading: "正在上传文件…",
success: (data) => `文件 ${data.filename} 已上传(${data.size}KB)`,
});
}}
>
上传文件
{
toast.promise(createEvent(), {
error: (err) => err.message,
loading: "正在创建活动…",
success: "活动已创建",
});
}}
>
创建活动(错误)
{
toast.promise(saveData(), {
error: (err) => err.message,
loading: "正在保存更改…",
success: (data) => `已保存 ${data.count} 项`,
});
}}
>
保存数据(随机)
{
toast.promise(fetchUser(), {
error: "获取用户失败",
loading: "正在加载用户…",
success: (data) => `欢迎回来,${data.name}!`,
});
}}
>
获取用户
{/* Manual Loading Section */}
手动加载状态
使用 isLoading 属性手动控制加载状态
{
const loadingId = toast("正在上传文件…", {
description: "请稍候,正在上传您的文件",
isLoading: true,
timeout: 0,
});
setTimeout(() => {
toast.close(loadingId);
toast.success("文件已上传", {
description: "您的文件已成功上传",
});
}, 3000);
}}
>
上传(含加载)
{
const loadingId = toast("正在处理付款…", {
isLoading: true,
timeout: 0,
});
setTimeout(() => {
toast.close(loadingId);
toast.success("付款已处理", {
description: "您的付款已成功处理",
});
}, 2500);
}}
>
付款处理
{
const loadingId = toast("正在保存更改…", {
isLoading: true,
timeout: 0,
});
setTimeout(() => {
toast.close(loadingId);
toast.danger("保存失败", {
description: "请重试",
});
}, 2000);
}}
>
加载后显示错误
);
}
```
### 回调
```tsx
"use client";
import {Button, toast} from "@heroui/react";
import React from "react";
export function Callbacks() {
const [closedHistory, setClosedHistory] = React.useState>(
[],
);
const addToHistory = (message: string) => {
const time = new Date().toLocaleTimeString();
setClosedHistory((prev) => [{message, time}, ...prev].slice(0, 5));
};
return (
{/* Toast Buttons */}
toast("文件已保存", {
onClose: () => {
addToHistory("文件已保存(3 秒后关闭)");
},
timeout: 3000,
})
}
>
自定义超时(3 秒)
toast("更改已保存", {
onClose: () => {
addToHistory("更改已保存(10 秒后关闭)");
},
timeout: 10000,
})
}
>
自定义超时(10 秒)
toast.success("活动已创建", {
onClose: () => {
addToHistory("活动已创建(默认超时后关闭)");
},
})
}
>
使用 onClose 回调
toast("重要通知", {
description: "此 Toast 将保持显示直至关闭",
onClose: () => {
addToHistory("重要通知(手动关闭)");
},
timeout: 0,
})
}
>
持久显示 Toast
{/* 关闭历史 Panel */}
关闭历史
{closedHistory.length > 0 && (
setClosedHistory([])}
>
清空
)}
{closedHistory.length === 0 ? (
尚无已关闭的 Toast。请尝试关闭上方的 Toast!
) : (
closedHistory.map((item, index) => (
{item.message}
({item.time})
))
)}
);
}
```
### 放置位置
```tsx
"use client";
import type {ToastVariants} from "@heroui/react";
import {Button, Toast, ToastQueue} from "@heroui/react";
type Placement = NonNullable;
const placements = ["top start", "top", "top end", "bottom start", "bottom", "bottom end"] as const;
// Create a separate queue for each placement
const placementQueues = Object.fromEntries(
placements.map((p) => [p, new ToastQueue({maxVisibleToasts: 3})]),
) as Record;
export function Placements() {
const showToast = (placement: Placement) => {
placementQueues[placement].add({
description: "活动已创建",
title: "活动已创建",
variant: "default",
});
};
return (
{/* Render a ToastProvider for each placement */}
{placements.map((p) => (
))}
{placements.map((p) => (
showToast(p)}>
{p}
))}
);
}
```
### 自定义 Toast 渲染
```tsx
"use client";
import type {ToastContentValue} from "@heroui/react";
import {
Button,
Toast,
ToastContent,
ToastDescription,
ToastIndicator,
ToastQueue,
ToastTitle,
} from "@heroui/react";
export function CustomToast() {
const customQueue = new ToastQueue();
return (
{({toast: toastItem}) => {
const content = toastItem.content as ToastContentValue;
return (
{content.title ? (
{content.title}
) : null}
{content.description ? (
{content.description}
) : null}
);
}}
{
customQueue.add({
description: "使用自定义渲染函数",
title: "自定义布局 Toast",
variant: "default",
});
}}
>
自定义 Toast
);
}
```
### 自定义队列
```tsx
"use client";
import {Button, Toast, ToastQueue} from "@heroui/react";
export function CustomQueue() {
const notificationQueue = new ToastQueue({maxVisibleToasts: 2});
const errorQueue = new ToastQueue({maxVisibleToasts: 3});
const successQueue = new ToastQueue({maxVisibleToasts: 1});
return (
{/* Notification Queue */}
{
notificationQueue.add({
description: "您有一条新消息",
title: "新通知",
variant: "default",
});
}}
>
添加通知(最多 2 条)
{/* Error Queue */}
{
errorQueue.add({
description: "保存更改失败",
title: "发生错误",
variant: "danger",
});
}}
>
添加错误(最多 3 条)
{/* Success Queue */}
{
successQueue.add({
description: `操作 ${Date.now()}`,
title: "成功!",
variant: "success",
});
}}
>
添加成功(最多 1 条)
);
}
```
### 组件结构
```tsx
```
## Related Components
* **Button**: Allows a user to perform an action
* **Alert**: Display important messages and notifications
* **CloseButton**: Button for dismissing overlays
## 样式
### 传入 Tailwind CSS 类
```tsx
```
### 自定义组件类
要自定义 Toast 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.toast {
@apply rounded-xl shadow-lg;
}
.toast__content {
@apply gap-2;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Toast 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/toast.css)):
#### 基础类
* `.toast` — Toast 根容器
* `.toast__region` — Toast 区域容器
* `.toast__content` — 包裹标题与说明的内容容器
* `.toast__indicator` — 图标/指示器容器
* `.toast__title` — Toast 标题文本
* `.toast__description` — Toast 说明文本
* `.toast__action` — 操作按钮容器
* `.toast__close` — 关闭按钮容器
#### 变体类
* `.toast--default` — 默认灰色变体
* `.toast--accent` — 强调蓝色变体
* `.toast--success` — 成功绿色变体
* `.toast--warning` — 警告黄/橙色变体
* `.toast--danger` — 危险红色变体
### 交互状态
组件支持多种状态:
* **最前**:`[data-frontmost]` — 应用于堆叠中最上层可见的 Toast
* **索引**:`[data-index]` — 基于 Toast 在堆叠中的位置
* **放置**:`[data-placement="*"]` — 基于 Toast 区域的放置位置
## API 参考
### Toast.Provider Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | --------------------------------------------------------------------------------- | ---------- | ------------------- |
| `placement` | `"top start" \| "top" \| "top end" \| "bottom start" \| "bottom" \| "bottom end"` | `"bottom"` | Toast 区域的放置位置 |
| `gap` | `number` | `12` | Toast 之间的间距(像素) |
| `maxVisibleToasts` | `number` | `3` | 同时最多显示的 Toast 数量 |
| `scaleFactor` | `number` | `0.05` | 堆叠 Toast 的缩放系数(0–1) |
| `width` | `number \| string` | `460` | Toast 宽度(像素或 CSS 值) |
| `queue` | `ToastQueue` | - | 自定义 Toast 队列实例 |
| `children` | `ReactNode \| ((props: {toast: QueuedToast}) => ReactNode)` | - | 自定义渲染函数或子节点 |
| `className` | `string` | - | 附加的 CSS 类 |
### Toast Props
| Prop | 类型 | 默认值 | 描述 |
| ------------- | ------------------------------------------------------------- | ----------- | --------------------------------------- |
| `toast` | `QueuedToast` | - | 来自队列的 Toast 数据(必填) |
| `variant` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"default"` | Toast 的视觉变体 |
| `placement` | `ToastVariants["placement"]` | - | 放置位置(继承自 Provider) |
| `scaleFactor` | `number` | - | 缩放系数(继承自 Provider) |
| `className` | `string` | - | 附加的 CSS 类 |
| `children` | `ReactNode` | - | Toast 内容(ToastContent、ToastIndicator 等) |
### Toast.Content Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------------------------- |
| `children` | `ReactNode` | - | 内容(通常为 ToastTitle 与 ToastDescription) |
| `className` | `string` | - | 附加的 CSS 类 |
### Toast.Indicator Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------------------------- | --- | ----------------- |
| `variant` | `ToastVariants["variant"]` | - | 默认图标的变体 |
| `children` | `ReactNode` | - | 自定义指示图标(默认使用变体图标) |
| `className` | `string` | - | 附加的 CSS 类 |
### Toast.Title Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | --------- |
| `children` | `ReactNode` | - | 标题文本 |
| `className` | `string` | - | 附加的 CSS 类 |
### Toast.Description Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | --------- |
| `children` | `ReactNode` | - | 说明文本 |
| `className` | `string` | - | 附加的 CSS 类 |
### Toast.ActionButton Props
| Prop | 类型 | 默认值 | 描述 |
| ------------------ | ----------- | --- | --------------------- |
| `children` | `ReactNode` | - | 操作按钮内容 |
| `className` | `string` | - | 附加的 CSS 类 |
| All `Button` props | - | - | 接受 Button 组件的全部 props |
### Toast.CloseButton Props
| Prop | 类型 | 默认值 | 描述 |
| ----------------------- | -------- | --- | -------------------------- |
| `className` | `string` | - | 附加的 CSS 类 |
| All `CloseButton` props | - | - | 接受 CloseButton 组件的全部 props |
### ToastQueue
`ToastQueue` 用于管理 `` 的状态。状态存放在 React 之外,因此你可以在应用的任意位置触发 Toast。
#### 构造选项
| Option | 类型 | 默认值 | 描述 |
| ------------------ | -------------------------- | --- | -------------------------------- |
| `maxVisibleToasts` | `number` | `3` | 同时最多显示的 Toast 数量(仅视觉) |
| `wrapUpdate` | `(fn: () => void) => void` | - | 包裹状态更新的函数(例如用于 view transitions) |
#### 方法
| Method | 参数 | 返回值 | 描述 |
| ----------- | -------------------------------------- | ------------ | ------------------------- |
| `add` | `(content: T, options?: ToastOptions)` | `string` | 将 Toast 加入队列,返回 Toast key |
| `close` | `(key: string)` | `void` | 按 key 关闭 Toast |
| `pauseAll` | `()` | `void` | 暂停所有 Toast 计时器 |
| `resumeAll` | `()` | `void` | 恢复所有 Toast 计时器 |
| `clear` | `()` | `void` | 关闭所有 Toast |
| `subscribe` | `(fn: () => void)` | `() => void` | 订阅队列变化,返回取消订阅函数 |
### toast 函数
默认 `toast` 函数提供便捷方法用于显示 Toast:
```tsx
import { toast } from '@heroui/react';
// 基础 toast(默认约 4 秒后自动消失)
toast("Event has been created");
// 变体方法(默认同样约 4 秒后自动消失)
toast.success("File saved");
toast.info("New update available");
toast.warning("Please check your settings");
toast.danger("Something went wrong");
// 传入 options
toast("Event has been created", {
description: "Your event has been scheduled for tomorrow",
variant: "default",
timeout: 5000, // 自定义超时:5 秒
onClose: () => console.log("Closed"),
actionProps: {
children: "View",
onPress: () => {},
},
indicator: ,
});
// Promise 支持(自动显示加载指示)
toast.promise(
uploadFile(),
{
loading: "Uploading file...",
success: (data) => `File ${data.filename} uploaded`,
error: "Failed to upload file",
}
);
// 手动加载状态(持久 toast:不自动消失)
const loadingId = toast("Creating event...", {
isLoading: true,
timeout: 0, // 持久 toast:不自动消失
});
// 随后关闭并展示结果
toast.close(loadingId);
toast.success("Event created");
// 队列方法
toast.close(key);
toast.clear();
toast.pauseAll();
toast.resumeAll();
```
#### toast Options
| Option | 类型 | 默认值 | 描述 |
| ------------- | ------------------------------------------------------------- | ----------- | ------------------------------------------------- |
| `title` | `ReactNode` | - | Toast 标题(变体方法的第一个参数) |
| `description` | `ReactNode` | - | 可选说明文本 |
| `variant` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"default"` | 视觉变体 |
| `indicator` | `ReactNode` | - | 自定义指示图标(`null` 可隐藏) |
| `actionProps` | `ButtonProps` | - | 操作按钮 props |
| `isLoading` | `boolean` | `false` | 使用加载指示替代指示器 |
| `timeout` | `number` | `4000` | 自动消失超时(毫秒)。默认 4000ms(4 秒)。设为 `0` 表示持久 Toast,不自动消失 |
| `onClose` | `() => void` | - | Toast 关闭时的回调 |
#### toast.promise Options
| Option | 类型 | 默认值 | 描述 |
| --------- | -------------------------------------------- | --- | ---------------------- |
| `loading` | `ReactNode` | - | Promise pending 时显示的消息 |
| `success` | `ReactNode \| ((data: T) => ReactNode)` | - | 成功时显示的消息(可为函数) |
| `error` | `ReactNode \| ((error: Error) => ReactNode)` | - | 失败时显示的消息(可为函数) |
# Tooltip 工具提示
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/tooltip
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(overlays)/tooltip.mdx
> 当用户悬停或聚焦某个元素时,展示提示性文本。
## 引入
```tsx
import { Tooltip } from '@heroui/react';
```
### 用法
```tsx
import {CircleInfo} from "@gravity-ui/icons";
import {Button, Tooltip} from "@heroui/react";
export function TooltipBasic() {
return (
);
}
```
### 组件结构
引入 Tooltip 后,可通过点语法访问各个部分。
```tsx
import { Tooltip, Button } from '@heroui/react';
export default () => (
Hover for tooltip
Helpful information about this element
)
```
### 带箭头
```tsx
import {Button, Tooltip} from "@heroui/react";
export function TooltipWithArrow() {
return (
带箭头
带箭头指示器的工具提示
自定义偏移
与触发器的自定义偏移
);
}
```
### 位置
```tsx
import {Button, Tooltip} from "@heroui/react";
export function TooltipPlacement() {
return (
Top
顶部位置
Left
左侧位置
悬停按钮
Right
右侧位置
Bottom
底部位置
);
}
```
### 自定义触发
```tsx
import {CircleCheckFill, CircleQuestion} from "@gravity-ui/icons";
import {Avatar, Chip, Tooltip} from "@heroui/react";
export function TooltipCustomTrigger() {
return (
JD
Jane Doe
jane@example.com
活跃
Jane 当前在线
帮助信息
这是包含有关此功能更详细信息的实用工具提示。
);
}
```
## Related Components
* **Button**: Allows a user to perform an action
* **Popover**: Displays content in context with a trigger
### 自定义渲染函数
```tsx
"use client";
import {CircleInfo} from "@gravity-ui/icons";
import {Button, Tooltip} from "@heroui/react";
export function CustomRenderFunction() {
return (
);
}
```
## 样式
### 全局延迟配置
你可以通过定义 CSS 变量,为应用中所有 Tooltip 设置默认的显示与隐藏延迟:
```css
/* 在你的全局 CSS 文件中 */
:root {
--tooltip-delay: 1500ms;
--tooltip-close-delay: 500ms;
}
/* 也可以为浅色/深色主题设置不同的值 */
.light, [data-theme="light"] {
--tooltip-delay: 1200ms;
}
.dark, [data-theme="dark"] {
--tooltip-close-delay: 300ms;
}
```
值支持 `ms`、`s` 等 CSS 时间单位。在单个 Tooltip 上指定 `delay` 或 `closeDelay` 时,会覆盖这些全局设置。
### 传入 Tailwind CSS 类
```tsx
import { Tooltip, Button } from '@heroui/react';
function CustomTooltip() {
return (
Hover me
Custom styled tooltip
);
}
```
### 自定义组件类
若要自定义 Tooltip 的组件类名,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.tooltip {
@apply rounded-xl shadow-lg;
}
.tooltip__trigger {
@apply cursor-help;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于定制。
### CSS 类
Tooltip 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/tooltip.css)):
#### 基础类
* `.tooltip` - 带动画的基础 Tooltip 样式
* `.tooltip__trigger` - 触发元素样式
### 交互状态
组件支持以下动画相关状态:
* **进入**:`[data-entering]` — Tooltip 出现过程中应用
* **离开**:`[data-exiting]` — Tooltip 消失过程中应用
* **位置**:`[data-placement="*"]` — 根据 Tooltip 位置应用
## API 参考
### Tooltip Props
| Prop | 类型 | 默认值 | 描述 |
| ------------ | -------------------- | --------------- | ---------------------------------------------------------- |
| `children` | `React.ReactNode` | - | 触发元素与内容 |
| `delay` | `number` | `1500` 或 CSS 变量 | 显示 Tooltip 前的延迟(毫秒);可通过 `--tooltip-delay` CSS 变量全局配置 |
| `closeDelay` | `number` | `500` 或 CSS 变量 | 隐藏 Tooltip 前的延迟(毫秒);可通过 `--tooltip-close-delay` CSS 变量全局配置 |
| `trigger` | `"hover" \| "focus"` | `"hover"` | Tooltip 的触发方式 |
| `isDisabled` | `boolean` | `false` | 是否禁用 Tooltip |
### Tooltip.Content Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | -------------------------------------------------------------------------- | ------------ | ---------------------- |
| `children` | `React.ReactNode` | - | 在 Tooltip 中展示的内容 |
| `showArrow` | `boolean` | `false` | 是否显示箭头指示器 |
| `offset` | `number` | `3`(带箭头时为 7) | 与触发元素的距离 |
| `placement` | `"top" \| "bottom" \| "left" \| "right"` (及变体) | `"top"` | Tooltip 的位置 |
| `className` | `string` | - | 额外的 CSS 类名 |
| `render` | `DOMRenderFunction` | - | 通过自定义渲染函数覆盖默认的 DOM 元素。 |
### Tooltip.Trigger Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | -------------- |
| `children` | `React.ReactNode` | - | 触发 Tooltip 的元素 |
| `className` | `string` | - | 额外的 CSS 类名 |
### Tooltip.Arrow Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------------- | --- | ---------------------- |
| `children` | `React.ReactNode` | - | 自定义箭头元素 |
| `className` | `string` | - | 额外的 CSS 类名 |
| `render` | `DOMRenderFunction` | - | 通过自定义渲染函数覆盖默认的 DOM 元素。 |
# Autocomplete 自动完成
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/autocomplete
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(pickers)/autocomplete.mdx
> 自动完成将选择与过滤结合,让用户可以搜索并从选项列表中选择。
## 引入
```tsx
import { Autocomplete, useFilter } from "@heroui/react";
```
### 用法
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
EmptyState,
Label,
ListBox,
SearchField,
Tag,
TagGroup,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export default function Default() {
const {contains} = useFilter({sensitivity: "base"});
const [selectedKeys, setSelectedKeys] = useState([]);
const items = [
{id: "florida", name: "Florida"},
{id: "delaware", name: "Delaware"},
{id: "california", name: "California"},
{id: "texas", name: "Texas"},
{id: "new-york", name: "New York"},
{id: "washington", name: "Washington"},
];
const onRemoveTags = (keys: Set) => {
setSelectedKeys((prev) => prev.filter((key) => !keys.has(key)));
};
return (
setSelectedKeys(keys as Key[])}
>
计划前往的州
{({defaultChildren, isPlaceholder, state}: any) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItemsKeys = state.selectedItems.map((item: any) => item.key);
return (
{selectedItemsKeys.map((selectedItemKey: Key) => {
const item = items.find((s) => s.id === selectedItemKey);
if (!item) return null;
return (
{item.name}
);
})}
);
}}
未找到结果 }>
{items.map((item) => (
{item.name}
))}
);
}
```
### 组件结构
导入 Autocomplete 组件后,可通过点语法访问各个子部分。
```tsx
import {Autocomplete, Label, Description, SearchField, ListBox} from "@heroui/react";
export default () => (
);
```
### 带描述
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
Description,
EmptyState,
Label,
ListBox,
SearchField,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export function WithDescription() {
const [selectedKey, setSelectedKey] = useState(null);
const {contains} = useFilter({sensitivity: "base"});
const items = [
{id: "florida", name: "Florida"},
{id: "delaware", name: "Delaware"},
{id: "california", name: "California"},
{id: "texas", name: "Texas"},
{id: "new-york", name: "New York"},
{id: "washington", name: "Washington"},
];
return (
州
未找到结果 }>
{items.map((item) => (
{item.name}
))}
请选择你的居住州
);
}
```
### 多选
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
EmptyState,
Label,
ListBox,
SearchField,
Tag,
TagGroup,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export function MultipleSelect() {
const [selectedKeys, setSelectedKeys] = useState([]);
const {contains} = useFilter({sensitivity: "base"});
const items = [
{id: "california", name: "California"},
{id: "texas", name: "Texas"},
{id: "florida", name: "Florida"},
{id: "new-york", name: "New York"},
{id: "illinois", name: "Illinois"},
{id: "pennsylvania", name: "Pennsylvania"},
];
const onRemoveTags = (keys: Set) => {
setSelectedKeys((prev) => prev.filter((key) => !keys.has(key)));
};
return (
setSelectedKeys(keys as Key[])}
>
州
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItemsKeys = state.selectedItems.map((item) => item.key);
return (
{selectedItemsKeys.map((selectedItemKey) => {
const item = items.find((s) => s.id === selectedItemKey);
if (!item) return null;
return (
{item.name}
);
})}
);
}}
未找到结果 }>
{items.map((item) => (
{item.name}
))}
);
}
```
### 分组
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
EmptyState,
Header,
Label,
ListBox,
SearchField,
Separator,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export function WithSections() {
const [selectedKey, setSelectedKey] = useState(null);
const {contains} = useFilter({sensitivity: "base"});
return (
国家
未找到结果 }>
美国
加拿大
墨西哥
英国
法国
德国
西班牙
意大利
日本
中国
印度
韩国
);
}
```
### 含禁用选项
```tsx
"use client";
import type {Key} from "@heroui/react";
import {Autocomplete, EmptyState, Label, ListBox, SearchField, useFilter} from "@heroui/react";
import {useState} from "react";
export function WithDisabledOptions() {
const [selectedKey, setSelectedKey] = useState(null);
const {contains} = useFilter({sensitivity: "base"});
return (
动物
未找到结果 }>
狗
猫
鸟
袋鼠
象
老虎
);
}
```
### 允许空集合
`allowsEmptyCollection` prop 让自动完成在集合中没有任何条目时仍可使用。适用于列表初始可能为空,或过滤后没有结果等场景。
```tsx
"use client";
import {Autocomplete, EmptyState, Label, ListBox, SearchField, useFilter} from "@heroui/react";
export function AllowsEmptyCollection() {
const {contains} = useFilter({sensitivity: "base"});
return (
州
未找到结果 } />
);
}
```
### 自定义指示器
```tsx
"use client";
import type {Key} from "@heroui/react";
import {Autocomplete, EmptyState, Label, ListBox, SearchField, useFilter} from "@heroui/react";
import {Icon} from "@iconify/react";
import {useState} from "react";
export function CustomIndicator() {
const [selectedKey, setSelectedKey] = useState(null);
const {contains} = useFilter({sensitivity: "base"});
const items = [
{id: "florida", name: "Florida"},
{id: "delaware", name: "Delaware"},
{id: "california", name: "California"},
{id: "texas", name: "Texas"},
{id: "new-york", name: "New York"},
{id: "washington", name: "Washington"},
];
return (
州
未找到结果 }>
{items.map((item) => (
{item.name}
))}
);
}
```
### 必填
```tsx
"use client";
import {
Autocomplete,
Button,
EmptyState,
FieldError,
Form,
Label,
ListBox,
SearchField,
useFilter,
} from "@heroui/react";
export function Required() {
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: Record = {};
// Convert FormData to plain object
formData.forEach((value, key) => {
data[key] = value.toString();
});
alert("表单提交成功!");
};
const {contains} = useFilter({sensitivity: "base"});
const states = [
{id: "florida", name: "Florida"},
{id: "delaware", name: "Delaware"},
{id: "california", name: "California"},
{id: "texas", name: "Texas"},
{id: "new-york", name: "New York"},
{id: "washington", name: "Washington"},
];
const countries = [
{id: "usa", name: "United States"},
{id: "canada", name: "Canada"},
{id: "mexico", name: "Mexico"},
{id: "uk", name: "United Kingdom"},
{id: "france", name: "France"},
{id: "germany", name: "Germany"},
];
return (
州
未找到结果 }>
{states.map((state) => (
{state.name}
))}
国家
未找到结果 }>
{countries.map((country) => (
{country.name}
))}
提交
);
}
```
### 全宽
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
EmptyState,
Label,
ListBox,
SearchField,
Surface,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export function FullWidth() {
const [selectedKey, setSelectedKey] = useState(null);
const {contains} = useFilter({sensitivity: "base"});
const items = [
{id: "florida", name: "Florida"},
{id: "delaware", name: "Delaware"},
{id: "california", name: "California"},
{id: "texas", name: "Texas"},
{id: "new-york", name: "New York"},
{id: "washington", name: "Washington"},
];
return (
州
未找到结果 }>
{items.map((item) => (
{item.name}
))}
);
}
```
### 变体
Autocomplete 支持两种视觉变体:
* **`primary`**(默认)— 带阴影的标准样式,适用于大多数场景
* **`secondary`** — 低强调、无阴影,适合用于 Surface 组件
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
EmptyState,
Label,
ListBox,
SearchField,
Tag,
TagGroup,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export function Variants() {
const [selectedKey1, setSelectedKey1] = useState(null);
const [selectedKey2, setSelectedKey2] = useState(null);
const [selectedKeys1, setSelectedKeys1] = useState([]);
const [selectedKeys2, setSelectedKeys2] = useState([]);
const {contains} = useFilter({sensitivity: "base"});
const items = [
{id: "option1", name: "选项 1"},
{id: "option2", name: "选项 2"},
{id: "option3", name: "选项 3"},
{id: "option4", name: "选项 4"},
];
const onRemoveTags1 = (keys: Set) => {
setSelectedKeys1((prev) => prev.filter((key) => !keys.has(key)));
};
const onRemoveTags2 = (keys: Set) => {
setSelectedKeys2((prev) => prev.filter((key) => !keys.has(key)));
};
return (
单选变体
主色(primary)变体
未找到结果 }>
{items.map((item) => (
{item.name}
))}
次色(secondary)变体
未找到结果 }>
{items.map((item) => (
{item.name}
))}
多选变体
setSelectedKeys1(keys as Key[])}
>
主色(primary)变体
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItemsKeys = state.selectedItems.map((item) => item.key);
return (
{selectedItemsKeys.map((selectedItemKey) => {
const item = items.find((s) => s.id === selectedItemKey);
if (!item) return null;
return (
{item.name}
);
})}
);
}}
未找到结果 }>
{items.map((item) => (
{item.name}
))}
setSelectedKeys2(keys as Key[])}
>
次色(secondary)变体
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItemsKeys = state.selectedItems.map((item) => item.key);
return (
{selectedItemsKeys.map((selectedItemKey) => {
const item = items.find((s) => s.id === selectedItemKey);
if (!item) return null;
return (
{item.name}
);
})}
);
}}
未找到结果 }>
{items.map((item) => (
{item.name}
))}
);
}
```
### 在 Surface 中
在 [Surface](/docs/components/surface) 内使用时,请使用 `variant="secondary"`,以应用适合表面背景的低强调变体。
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
EmptyState,
Label,
ListBox,
SearchField,
Surface,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export function FullWidth() {
const [selectedKey, setSelectedKey] = useState(null);
const {contains} = useFilter({sensitivity: "base"});
const items = [
{id: "florida", name: "Florida"},
{id: "delaware", name: "Delaware"},
{id: "california", name: "California"},
{id: "texas", name: "Texas"},
{id: "new-york", name: "New York"},
{id: "washington", name: "Washington"},
];
return (
州
未找到结果 }>
{items.map((item) => (
{item.name}
))}
);
}
```
### 自定义值
你可以使用渲染 prop 自定义展示的值:
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
Avatar,
AvatarFallback,
AvatarImage,
Description,
EmptyState,
Label,
ListBox,
SearchField,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export function UserSelection() {
const users = [
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg",
email: "bob@heroui.com",
fallback: "B",
id: "1",
name: "Bob",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg",
email: "fred@heroui.com",
fallback: "F",
id: "2",
name: "Fred",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg",
email: "martha@heroui.com",
fallback: "M",
id: "3",
name: "Martha",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg",
email: "john@heroui.com",
fallback: "J",
id: "4",
name: "John",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg",
email: "jane@heroui.com",
fallback: "J",
id: "5",
name: "Jane",
},
];
const [selectedKey, setSelectedKey] = useState(null);
const {contains} = useFilter({sensitivity: "base"});
return (
用户
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItems = state.selectedItems;
if (selectedItems.length > 1) {
return `已选择 ${selectedItems.length} 位用户`;
}
const selectedItem = users.find((user) => user.id === selectedItems[0]?.key);
if (!selectedItem) {
return defaultChildren;
}
return (
{selectedItem.fallback}
{selectedItem.name}
);
}}
未找到结果 }>
{users.map((user) => (
{user.fallback}
{user.name}
{user.email}
))}
);
}
```
### 受控
```tsx
"use client";
import type {Key} from "@heroui/react";
import {Autocomplete, EmptyState, Label, ListBox, SearchField, useFilter} from "@heroui/react";
import {useState} from "react";
export function Controlled() {
const states = [
{id: "california", name: "California"},
{id: "texas", name: "Texas"},
{id: "florida", name: "Florida"},
{id: "new-york", name: "New York"},
{id: "illinois", name: "Illinois"},
{id: "pennsylvania", name: "Pennsylvania"},
];
const [state, setState] = useState("california");
const {contains} = useFilter({sensitivity: "base"});
const selectedState = states.find((s) => s.id === state);
return (
州(受控)
未找到结果 }>
{states.map((state) => (
{state.name}
))}
已选:{selectedState?.name || "无"}
);
}
```
### 受控多选
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
EmptyState,
Label,
ListBox,
SearchField,
Tag,
TagGroup,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export function MultipleSelect() {
const [selectedKeys, setSelectedKeys] = useState([]);
const {contains} = useFilter({sensitivity: "base"});
const items = [
{id: "california", name: "California"},
{id: "texas", name: "Texas"},
{id: "florida", name: "Florida"},
{id: "new-york", name: "New York"},
{id: "illinois", name: "Illinois"},
{id: "pennsylvania", name: "Pennsylvania"},
];
const onRemoveTags = (keys: Set) => {
setSelectedKeys((prev) => prev.filter((key) => !keys.has(key)));
};
return (
setSelectedKeys(keys as Key[])}
>
州
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItemsKeys = state.selectedItems.map((item) => item.key);
return (
{selectedItemsKeys.map((selectedItemKey) => {
const item = items.find((s) => s.id === selectedItemKey);
if (!item) return null;
return (
{item.name}
);
})}
);
}}
未找到结果 }>
{items.map((item) => (
{item.name}
))}
);
}
```
### 受控展开状态
```tsx
"use client";
import {
Autocomplete,
Button,
EmptyState,
Label,
ListBox,
SearchField,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export function ControlledOpenState() {
const [isOpen, setIsOpen] = useState(false);
const {contains} = useFilter({sensitivity: "base"});
const items = [
{id: "florida", name: "Florida"},
{id: "delaware", name: "Delaware"},
{id: "california", name: "California"},
{id: "texas", name: "Texas"},
{id: "new-york", name: "New York"},
{id: "washington", name: "Washington"},
];
return (
州
未找到结果 }>
{items.map((item) => (
{item.name}
))}
setIsOpen(!isOpen)}>{isOpen ? "关闭" : "打开"} 自动完成
自动完成处于{isOpen ? "打开" : "关闭"}状态
);
}
```
### 异步过滤
```tsx
"use client";
import {Autocomplete, EmptyState, Label, ListBox, SearchField, Spinner} from "@heroui/react";
import {useAsyncList} from "@react-stately/data";
import {cn} from "tailwind-variants";
interface Character {
name: string;
}
export function AsynchronousFiltering() {
const list = useAsyncList({
async load({filterText, signal}) {
const res = await fetch(`https://swapi.py4e.com/api/people/?search=${filterText}`, {
signal,
});
const json = await res.json();
return {
items: json.results,
};
},
});
return (
搜索《星球大战》角色
未找到结果 }
>
{(item: Character) => (
{item.name}
)}
);
}
```
### 虚拟化
Autocomplete 通过 [Virtualizer](https://react-aria.adobe.com/Virtualizer) 支持虚拟化,仅渲染视口内可见的行,从而高效展示大数据集。
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
Description,
EmptyState,
Label,
ListBox,
ListLayout,
SearchField,
Virtualizer,
useFilter,
} from "@heroui/react";
import {useMemo, useState} from "react";
interface User {
email: string;
id: number;
name: string;
}
function generateUsers(n: number): User[] {
const firstNames = [
"Emma",
"Liam",
"Olivia",
"Noah",
"Ava",
"James",
"Sophia",
"Oliver",
"Isabella",
"Lucas",
"Mia",
"Ethan",
"Charlotte",
"Mason",
"Amelia",
"Logan",
"Harper",
"Alexander",
"Ella",
"Benjamin",
];
const lastNames = [
"Smith",
"Johnson",
"Williams",
"Brown",
"Jones",
"Garcia",
"Miller",
"Davis",
"Rodriguez",
"Martinez",
"Anderson",
"Taylor",
"Thomas",
"Jackson",
"White",
"Harris",
"Clark",
"Lewis",
"Robinson",
"Walker",
];
const users: User[] = [];
for (let i = 0; i < n; i++) {
const firstName = firstNames[i % firstNames.length]!;
const lastName = lastNames[Math.floor(i / firstNames.length) % lastNames.length]!;
const name = `${firstName} ${lastName}`;
users.push({
email: `${firstName.toLowerCase()}.${lastName.toLowerCase()}@acme.com`,
id: i + 1,
name,
});
}
return users;
}
export function Virtualization() {
const [selectedKey, setSelectedKey] = useState(null);
const [searchQuery, setSearchQuery] = useState("");
const {contains} = useFilter({sensitivity: "base"});
const allUsers = useMemo(() => generateUsers(1000), []);
const filteredUsers = useMemo(() => {
if (!searchQuery) return allUsers;
return allUsers.filter(
(user) => contains(user.name, searchQuery) || contains(user.email, searchQuery),
);
}, [allUsers, contains, searchQuery]);
return (
用户
未找到结果 }
>
{(user) => (
{user.name}
{user.email}
)}
);
}
```
### 禁用
```tsx
"use client";
import {Autocomplete, EmptyState, Label, ListBox, SearchField, useFilter} from "@heroui/react";
export function Disabled() {
const {contains} = useFilter({sensitivity: "base"});
const items = [
{id: "florida", name: "Florida"},
{id: "delaware", name: "Delaware"},
{id: "california", name: "California"},
{id: "texas", name: "Texas"},
{id: "new-york", name: "New York"},
{id: "washington", name: "Washington"},
];
const countries = [
{id: "argentina", name: "Argentina"},
{id: "venezuela", name: "Venezuela"},
{id: "japan", name: "Japan"},
{id: "france", name: "France"},
{id: "italy", name: "Italy"},
{id: "spain", name: "Spain"},
];
return (
州
未找到结果 }>
{items.map((item) => (
{item.name}
))}
计划前往的国家
未找到结果 }>
{countries.map((country) => (
{country.name}
))}
);
}
```
### 进阶示例
#### 用户选择
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
Avatar,
AvatarFallback,
AvatarImage,
Description,
EmptyState,
Label,
ListBox,
SearchField,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export function UserSelection() {
const users = [
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg",
email: "bob@heroui.com",
fallback: "B",
id: "1",
name: "Bob",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg",
email: "fred@heroui.com",
fallback: "F",
id: "2",
name: "Fred",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg",
email: "martha@heroui.com",
fallback: "M",
id: "3",
name: "Martha",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg",
email: "john@heroui.com",
fallback: "J",
id: "4",
name: "John",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg",
email: "jane@heroui.com",
fallback: "J",
id: "5",
name: "Jane",
},
];
const [selectedKey, setSelectedKey] = useState(null);
const {contains} = useFilter({sensitivity: "base"});
return (
用户
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItems = state.selectedItems;
if (selectedItems.length > 1) {
return `已选择 ${selectedItems.length} 位用户`;
}
const selectedItem = users.find((user) => user.id === selectedItems[0]?.key);
if (!selectedItem) {
return defaultChildren;
}
return (
{selectedItem.fallback}
{selectedItem.name}
);
}}
未找到结果 }>
{users.map((user) => (
{user.fallback}
{user.name}
{user.email}
))}
);
}
```
#### 用户多选
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
Avatar,
AvatarFallback,
AvatarImage,
Description,
EmptyState,
Label,
ListBox,
SearchField,
Tag,
TagGroup,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export function UserSelectionMultiple() {
const users = [
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg",
email: "bob@heroui.com",
fallback: "B",
id: "1",
name: "Bob",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg",
email: "fred@heroui.com",
fallback: "F",
id: "2",
name: "Fred",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg",
email: "martha@heroui.com",
fallback: "M",
id: "3",
name: "Martha",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg",
email: "john@heroui.com",
fallback: "J",
id: "4",
name: "John",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg",
email: "jane@heroui.com",
fallback: "J",
id: "5",
name: "Jane",
},
];
const [selectedKeys, setSelectedKeys] = useState([]);
const {contains} = useFilter({sensitivity: "base"});
const onRemoveTags = (keys: Set) => {
setSelectedKeys((prev) => prev.filter((key) => !keys.has(key)));
};
return (
setSelectedKeys(keys as Key[])}
>
用户
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItemsKeys = state.selectedItems.map((item) => item.key);
return (
{selectedItemsKeys.map((selectedItemKey) => {
const selectedItem = users.find((user) => user.id === selectedItemKey);
if (!selectedItem) {
return null;
}
return (
{selectedItem.fallback}
{selectedItem.name}
);
})}
);
}}
未找到结果 }>
{users.map((user) => (
{user.fallback}
{user.name}
{user.email}
))}
);
}
```
#### 地点搜索
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
Description,
EmptyState,
Label,
ListBox,
SearchField,
useFilter,
} from "@heroui/react";
import {useState} from "react";
interface City {
name: string;
country: string;
}
export function LocationSearch() {
const allCities: City[] = [
{country: "美国", name: "纽约"},
{country: "美国", name: "洛杉矶"},
{country: "美国", name: "芝加哥"},
{country: "英国", name: "伦敦"},
{country: "法国", name: "巴黎"},
{country: "日本", name: "东京"},
{country: "澳大利亚", name: "悉尼"},
{country: "加拿大", name: "多伦多"},
{country: "德国", name: "柏林"},
{country: "西班牙", name: "马德里"},
];
const [selectedKey, setSelectedKey] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const {contains} = useFilter({sensitivity: "base"});
// Simulate async filtering
const customFilter = (text: string, inputValue: string) => {
if (!inputValue) return true;
setIsLoading(true);
setTimeout(() => setIsLoading(false), 300);
return contains(text, inputValue);
};
return (
城市
{isLoading ? "搜索中…" : "未找到城市"} }
>
{allCities.map((city) => (
{city.name}
{city.country}
))}
);
}
```
#### Tag Group 选择
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
EmptyState,
Label,
ListBox,
SearchField,
Tag,
TagGroup,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export function TagGroupSelection() {
const tags = [
{id: "react", name: "React"},
{id: "typescript", name: "TypeScript"},
{id: "javascript", name: "JavaScript"},
{id: "nodejs", name: "Node.js"},
{id: "python", name: "Python"},
{id: "vue", name: "Vue"},
{id: "angular", name: "Angular"},
{id: "nextjs", name: "Next.js"},
];
const [selectedKeys, setSelectedKeys] = useState([]);
const {contains} = useFilter({sensitivity: "base"});
const onRemoveTags = (keys: Set) => {
setSelectedKeys((prev) => prev.filter((key) => !keys.has(key)));
};
return (
setSelectedKeys(keys as Key[])}
>
标签
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItemsKeys = state.selectedItems.map((item) => item.key);
return (
{selectedItemsKeys.map((selectedItemKey) => {
const tag = tags.find((t) => t.id === selectedItemKey);
if (!tag) return null;
return (
{tag.name}
);
})}
);
}}
未找到标签 }>
{tags.map((tag) => (
{tag.name}
))}
);
}
```
#### 邮件收件人
```tsx
"use client";
import type {Key} from "@heroui/react";
import {
Autocomplete,
Description,
EmptyState,
Label,
ListBox,
SearchField,
Tag,
TagGroup,
useFilter,
} from "@heroui/react";
import {useState} from "react";
export function EmailRecipients() {
const emails = [
{email: "alice@example.com", id: "alice@example.com", name: "Alice Johnson"},
{email: "bob@example.com", id: "bob@example.com", name: "Bob Smith"},
{email: "charlie@example.com", id: "charlie@example.com", name: "Charlie Brown"},
{email: "diana@example.com", id: "diana@example.com", name: "Diana Prince"},
{email: "eve@example.com", id: "eve@example.com", name: "Eve Wilson"},
];
const [selectedKeys, setSelectedKeys] = useState([]);
const {contains} = useFilter({sensitivity: "base"});
const onRemoveTags = (keys: Set) => {
setSelectedKeys((prev) => prev.filter((key) => !keys.has(key)));
};
return (
setSelectedKeys(keys as Key[])}
>
收件人
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItemsKeys = state.selectedItems.map((item) => item.key);
return (
{selectedItemsKeys.map((selectedItemKey) => {
const email = emails.find((e) => e.id === selectedItemKey);
if (!email) return null;
return (
{email.email}
);
})}
);
}}
未找到收件人 }>
{emails.map((email) => (
{email.name}
{email.email}
))}
);
}
```
## Related Components
* **Listbox**: Scrollable list of selectable items
* **Popover**: Displays content in context with a trigger
* **Input**: Single-line text input built on React Aria
## 样式
### 传入 Tailwind CSS 类
```tsx
import {Autocomplete, SearchField, ListBox} from "@heroui/react";
function CustomAutocomplete() {
return (
State
Item 1
);
}
```
### 自定义组件类
要自定义 Autocomplete 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.autocomplete {
@apply flex flex-col gap-1;
}
.autocomplete__trigger {
@apply rounded-lg border border-border bg-surface p-2;
}
.autocomplete__value {
@apply text-current;
}
.autocomplete__clear-button {
@apply text-muted hover:text-foreground;
}
.autocomplete__indicator {
@apply text-muted;
}
.autocomplete__popover {
@apply rounded-lg border border-border bg-surface p-2;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Autocomplete 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/autocomplete.css)):
#### 基础类
* `.autocomplete` - 自动完成根容器
* `.autocomplete__trigger` - 触发自动完成的按钮
* `.autocomplete__value` - 显示的值或占位符
* `.autocomplete__clear-button` - 清除已选值的按钮
* `.autocomplete__indicator` - 下拉指示图标
* `.autocomplete__popover` - 弹出层容器
* `.autocomplete__filter` - 过滤区域包裹层
#### 变体类
* `.autocomplete--primary` - 主变体,带阴影(默认)
* `.autocomplete--secondary` - 次变体,无阴影,适合用于 Surface
#### 状态类
* `.autocomplete[data-invalid="true"]` - 无效状态
* `.autocomplete__trigger[data-focus-visible="true"]` - 触发器聚焦状态
* `.autocomplete__trigger[data-disabled="true"]` - 触发器禁用状态
* `.autocomplete__value[data-placeholder="true"]` - 占位符状态
* `.autocomplete__clear-button[data-empty="true"]` - 无选中时隐藏清除按钮
* `.autocomplete__indicator[data-open="true"]` - 展开时的指示器状态
### 交互状态
组件同时支持 CSS 伪类与 data 属性,便于灵活编写样式:
* **悬停**:触发器上 `:hover` 或 `[data-hovered="true"]`
* **聚焦**:触发器上 `:focus-visible` 或 `[data-focus-visible="true"]`
* **禁用**:自动完成上 `:disabled` 或 `[data-disabled="true"]`
* **展开**:指示器上 `[data-open="true"]`
## API 参考
### Autocomplete Props
| Prop | 类型 | 默认值 | 描述 |
| ----------------------- | --------------------------------------- | ------------------ | ---------------------------------------------------------- |
| `placeholder` | `string` | `'Select an item'` | 自动完成为空时显示的占位文本 |
| `selectionMode` | `"single" \| "multiple"` | `"single"` | 启用单选或多选 |
| `allowsEmptyCollection` | `boolean` | `false` | 是否允许空集合。为 `true` 时,即使没有任何条目也可使用自动完成。 |
| `isOpen` | `boolean` | - | 设置弹出层的打开状态(受控) |
| `defaultOpen` | `boolean` | - | 设置弹出层的默认打开状态(非受控) |
| `onOpenChange` | `(isOpen: boolean) => void` | - | 打开状态变化时触发的事件处理函数 |
| `disabledKeys` | `Iterable` | - | 禁用条目的 key |
| `isDisabled` | `boolean` | - | 是否禁用自动完成 |
| `value` | `Key \| Key[] \| null` | - | 当前值(受控) |
| `defaultValue` | `Key \| Key[] \| null` | - | 默认值(非受控) |
| `onChange` | `(value: Key \| Key[] \| null) => void` | - | 值变化时触发的事件处理函数 |
| `isRequired` | `boolean` | - | 是否要求用户输入 |
| `isInvalid` | `boolean` | - | 自动完成的值是否无效 |
| `name` | `string` | - | 输入的 name,用于提交 HTML 表单 |
| `fullWidth` | `boolean` | `false` | 自动完成是否占满容器宽度 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 视觉变体。`primary` 为默认带阴影样式;`secondary` 为低强调、无阴影,适合用于 Surface。 |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode \| RenderFunction` | - | 自动完成内容或渲染函数 |
### Autocomplete.Trigger Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------------------- | --- | ---------- |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode \| RenderFunction` | - | 触发器内容或渲染函数 |
### Autocomplete.Value Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------------------- | --- | ---------- |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode \| RenderFunction` | - | 值区域内容或渲染函数 |
### Autocomplete.Indicator Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | --------- |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode` | - | 自定义指示器内容 |
### Autocomplete.ClearButton Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------ | --- | -------------- |
| `className` | `string` | - | 额外的 CSS 类 |
| `onClick` | `(e: MouseEvent) => void` | - | 点击按钮时触发的事件处理函数 |
| `ref` | `RefObject` | - | 清除按钮元素的 ref |
### Autocomplete.Popover Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ----------- |
| `placement` | `"bottom" \| "bottom left" \| "bottom right" \| "bottom start" \| "bottom end" \| "top" \| "top left" \| "top right" \| "top start" \| "top end" \| "left" \| "left top" \| "left bottom" \| "start" \| "start top" \| "start bottom" \| "right" \| "right top" \| "right bottom" \| "end" \| "end top" \| "end bottom"` | `"bottom"` | 弹出层相对触发器的位置 |
| `className` | `string` | - | 额外的 CSS 类 |
| `children` | `ReactNode` | - | 子内容 |
### Autocomplete.Filter Props
| Prop | 类型 | 默认值 | 描述 |
| --------------- | ------------------------------------------ | --- | --------------------------- |
| `filter` | `(text: string, input: string) => boolean` | - | 自定义过滤函数 |
| `inputValue` | `string` | - | 受控的输入值 |
| `onInputChange` | `(value: string) => void` | - | 输入值变化时触发的事件处理函数 |
| `children` | `ReactNode` | - | 过滤内容(SearchField 与 ListBox) |
### useFilter Hook
React Aria 的 `useFilter` hook 为自动完成提供过滤函数。
```tsx
import {useFilter} from "@heroui/react";
const {contains} = useFilter({sensitivity: "base"});
...
...
```
**选项:**
| 选项 | 类型 | 默认值 | 描述 |
| ------------- | ------------------------------------------- | -------- | --------- |
| `sensitivity` | `"base" \| "accent" \| "case" \| "variant"` | `"base"` | 匹配的本地化敏感度 |
**返回值:**
| 函数 | 类型 | 描述 |
| ------------ | ------------------------------------------------ | -------------- |
| `contains` | `(string: string, substring: string) => boolean` | 判断字符串是否包含给定子串 |
| `startsWith` | `(string: string, substring: string) => boolean` | 判断字符串是否以给定子串开头 |
| `endsWith` | `(string: string, substring: string) => boolean` | 判断字符串是否以给定子串结尾 |
### RenderProps
对 `Autocomplete.Value` 使用渲染函数时,会提供以下值:
| Prop | 类型 | 描述 |
| ----------------- | ------------- | ------- |
| `defaultChildren` | `ReactNode` | 默认渲染的值 |
| `isPlaceholder` | `boolean` | 值是否为占位符 |
| `state` | `SelectState` | 自动完成的状态 |
| `selectedItems` | `Node[]` | 当前选中的条目 |
## 无障碍
Autocomplete 实现带过滤的 ARIA 选择模式,并提供:
* 完整键盘导航支持
* 选择变化时的屏幕阅读器播报
* 合理的焦点管理
* 禁用状态支持
* 可搜索与过滤
* HTML 表单集成
更多信息见 [React Aria Select 文档](https://react-spectrum.adobe.com/react-aria/Select.html)。
# ComboBox 组合框
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/combo-box
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(pickers)/combo-box.mdx
> 将文本输入与 ListBox 结合,用户可通过输入查询把选项列表过滤为匹配项。
## 引入
```tsx
import { ComboBox } from '@heroui/react';
```
### 用法
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function Default() {
return (
最喜欢的动物
土豚
猫
狗
袋鼠
熊猫
蛇
);
}
```
### 组件结构
引入 ComboBox 组件并通过点语法访问所有子部分。
```tsx
import { ComboBox, Input, Label, Description, Header, ListBox, Separator } from '@heroui/react';
export default () => (
)
```
### 带描述
```tsx
"use client";
import {ComboBox, Description, Input, Label, ListBox} from "@heroui/react";
export function WithDescription() {
return (
最喜欢的动物
土豚
猫
狗
袋鼠
熊猫
蛇
搜索并选择你最喜欢的动物
);
}
```
### 带分组
```tsx
"use client";
import {ComboBox, Header, Input, Label, ListBox, Separator} from "@heroui/react";
export function WithSections() {
return (
国家
美国
加拿大
墨西哥
英国
法国
德国
西班牙
意大利
日本
中国
印度
韩国
);
}
```
### 带禁用选项
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function WithDisabledOptions() {
return (
动物
狗
猫
鸟
袋鼠
象
老虎
);
}
```
### 自定义指示器
```tsx
"use client";
import {ChevronsExpandVertical} from "@gravity-ui/icons";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function CustomIndicator() {
return (
最喜欢的动物
土豚
猫
狗
袋鼠
熊猫
蛇
);
}
```
### 必填
```tsx
"use client";
import {Button, ComboBox, FieldError, Form, Input, Label, ListBox} from "@heroui/react";
export function Required() {
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: Record = {};
formData.forEach((value, key) => {
data[key] = value.toString();
});
alert("表单提交成功!");
};
return (
最喜欢的动物
土豚
猫
狗
袋鼠
熊猫
蛇
提交
);
}
```
### 自定义值
```tsx
"use client";
import {
Avatar,
AvatarFallback,
AvatarImage,
ComboBox,
Description,
Input,
Label,
ListBox,
} from "@heroui/react";
export function CustomValue() {
const users = [
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg",
email: "bob@heroui.com",
fallback: "B",
id: "1",
name: "Bob",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg",
email: "fred@heroui.com",
fallback: "F",
id: "2",
name: "Fred",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg",
email: "martha@heroui.com",
fallback: "M",
id: "3",
name: "Martha",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg",
email: "john@heroui.com",
fallback: "J",
id: "4",
name: "John",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg",
email: "jane@heroui.com",
fallback: "J",
id: "5",
name: "Jane",
},
];
return (
用户
{users.map((user) => (
{user.fallback}
{user.name}
{user.email}
))}
);
}
```
### 受控
```tsx
"use client";
import type {Key} from "@heroui/react";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
import {useState} from "react";
export function Controlled() {
const animals = [
{
id: "cat",
name: "猫",
},
{
id: "dog",
name: "狗",
},
{
id: "bird",
name: "鸟",
},
{
id: "fish",
name: "鱼",
},
{
id: "hamster",
name: "仓鼠",
},
];
const [selectedKey, setSelectedKey] = useState("cat");
const selectedAnimal = animals.find((a) => a.id === selectedKey);
return (
);
}
```
### 受控输入值
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
import {useState} from "react";
export function ControlledInputValue() {
const [inputValue, setInputValue] = useState("");
return (
);
}
```
### 异步加载
```tsx
"use client";
import {
Collection,
ComboBox,
EmptyState,
Input,
Label,
ListBox,
ListBoxLoadMoreItem,
Spinner,
} from "@heroui/react";
import {useAsyncList} from "@react-stately/data";
interface Character {
name: string;
}
export function AsynchronousLoading() {
const list = useAsyncList({
async load({cursor, filterText, signal}) {
if (cursor) {
cursor = cursor.replace(/^http:\/\//i, "https://");
}
const res = await fetch(cursor || `https://swapi.py4e.com/api/people/?search=${filterText}`, {
signal,
});
const json = await res.json();
return {
cursor: json.next,
items: json.results,
};
},
});
return (
选择角色
}>
{(item) => (
{item.name}
)}
加载更多…
);
}
```
### 自定义过滤
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function CustomFiltering() {
const animals = [
{id: "cat", name: "猫"},
{id: "dog", name: "狗"},
{id: "bird", name: "鸟"},
{id: "fish", name: "鱼"},
{id: "hamster", name: "仓鼠"},
];
return (
{
if (!inputValue) return true;
return text.toLowerCase().includes(inputValue.toLowerCase());
}}
>
动物(自定义筛选)
{animals.map((animal) => (
{animal.name}
))}
);
}
```
### 允许自定义值
```tsx
"use client";
import {ComboBox, Description, Input, Label, ListBox} from "@heroui/react";
export function AllowsCustomValue() {
return (
最喜欢的动物
土豚
猫
狗
袋鼠
熊猫
蛇
可输入任意动物名称,即使不在列表中
);
}
```
### 禁用
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function Disabled() {
return (
最喜欢的动物
土豚
猫
狗
袋鼠
熊猫
蛇
);
}
```
### 默认选中项
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function DefaultSelectedKey() {
return (
最喜欢的动物
土豚
猫
狗
袋鼠
熊猫
蛇
);
}
```
### 全宽
```tsx
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function FullWidth() {
return (
最喜欢的动物
土豚
猫
狗
);
}
```
### 在 Surface 内
在 [Surface](/docs/components/surface) 内使用时,请使用 `variant="secondary"`,以应用适合表面背景的弱强调变体。
```tsx
"use client";
import {Button, ComboBox, FieldError, Form, Input, Label, ListBox, Surface} from "@heroui/react";
export function OnSurface() {
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: Record = {};
formData.forEach((value, key) => {
data[key] = value.toString();
});
alert("表单提交成功!");
};
return (
最喜欢的动物
土豚
猫
狗
袋鼠
熊猫
蛇
提交
);
}
```
### 菜单触发
使用 `menuTrigger` prop 控制 Popover 何时打开:
* `focus`(默认):输入框获得焦点时打开 Popover
* `input`:用户编辑输入文本时打开 Popover
* `manual`:仅当用户按下触发按钮或使用方向键时打开 Popover
```tsx
"use client";
import {ComboBox, Description, Input, Label, ListBox} from "@heroui/react";
export function MenuTrigger() {
return (
);
}
```
### 自定义渲染函数
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function CustomRenderFunction() {
return (
}>
最喜欢的动物
土豚
猫
狗
袋鼠
熊猫
蛇
);
}
```
## Related Components
* **Listbox**: Scrollable list of selectable items
* **Popover**: Displays content in context with a trigger
* **Input**: Single-line text input built on React Aria
## 样式
### 传入 Tailwind CSS 类
```tsx
import { ComboBox, Input } from '@heroui/react';
function CustomComboBox() {
return (
Favorite Animal
Item 1
);
}
```
### 自定义组件类
若要自定义 ComboBox 组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.combo-box {
@apply flex flex-col gap-1;
}
.combo-box__input-group {
@apply relative inline-flex items-center;
}
.combo-box__trigger {
@apply absolute right-0 text-muted;
}
.combo-box__popover {
@apply rounded-lg border border-border bg-surface p-2;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
ComboBox 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/combo-box.css)):
#### 基础类
* `.combo-box` - ComboBox 根容器
* `.combo-box__input-group` - 输入框与触发按钮的容器
* `.combo-box__trigger` - 打开 Popover 的按钮
* `.combo-box__popover` - Popover 容器
#### 状态类
* `.combo-box[data-invalid="true"]` - 无效状态
* `.combo-box[data-disabled="true"]` - 禁用状态
* `.combo-box__trigger[data-focus-visible="true"]` - 触发器聚焦
* `.combo-box__trigger[data-disabled="true"]` - 触发器禁用
* `.combo-box__trigger[data-open="true"]` - 展开状态
### 交互状态
组件同时支持伪类与 data 属性:
* **悬停**:触发器上 `:hover` 或 `[data-hovered="true"]`
* **聚焦**:触发器上 `:focus-visible` 或 `[data-focus-visible="true"]`
* **禁用**:ComboBox 上 `:disabled` 或 `[data-disabled="true"]`
* **打开**:触发器上 `[data-open="true"]`
## API 参考
### ComboBox Props
| Prop | 类型 | 默认值 | 描述 |
| ----------------------- | ---------------------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------- |
| `inputValue` | `string` | - | 当前输入值(受控)。 |
| `defaultInputValue` | `string` | - | 默认输入值(非受控)。 |
| `onInputChange` | `(value: string) => void` | - | 输入值变化时调用的事件处理函数。 |
| `selectedKey` | `Key \| null` | - | 当前选中的 key(受控)。 |
| `defaultSelectedKey` | `Key \| null` | - | 默认选中的 key(非受控)。 |
| `onSelectionChange` | `(key: Key \| null) => void` | - | 选中变化时调用的事件处理函数。 |
| `isOpen` | `boolean` | - | Popover 是否打开(受控)。 |
| `defaultOpen` | `boolean` | - | Popover 默认是否打开(非受控)。 |
| `onOpenChange` | `(isOpen: boolean) => void` | - | Popover 打开状态变化时调用的事件处理函数。 |
| `items` | `Iterable` | - | 在 ListBox 中展示的 items。 |
| `disabledKeys` | `Iterable` | - | 禁用项的 key。 |
| `defaultFilter` | `(text: string, inputValue: string) => boolean` | - | 用于过滤 items 的自定义过滤函数。 |
| `isDisabled` | `boolean` | - | 是否禁用 ComboBox。 |
| `isReadOnly` | `boolean` | - | 输入是否可选中但不可由用户更改。 |
| `isRequired` | `boolean` | - | 是否必填。 |
| `isInvalid` | `boolean` | - | ComboBox 的值是否无效。 |
| `validate` | `(value: ComboBoxValidationValue) => ValidationError \| true \| null \| undefined` | - | 若给定值无效则返回错误信息的函数。当 `validationBehavior="native"` 时,提交表单会向用户展示校验错误;实时校验请改用 `isInvalid` prop。 |
| `validationBehavior` | `"native" \| "aria"` | `"native"` | 使用原生 HTML 表单校验在值缺失或无效时阻止提交,还是通过 ARIA 将字段标记为必填或无效。 |
| `name` | `string` | - | 提交 HTML 表单时 input 的 name。 |
| `form` | `string` | - | 要关联的 `` 元素 id。 |
| `formValue` | `"text" \| "key"` | `"key"` | 在 HTML 表单提交时提交选中项的文本还是 key。当 `allowsCustomValue` 为 `true` 时该选项不适用,始终提交文本。 |
| `autoComplete` | `string` | - | 自动完成行为类型。 |
| `autoFocus` | `boolean` | - | 是否在挂载时自动聚焦。 |
| `allowsCustomValue` | `boolean` | - | 是否允许不在列表中的自定义值。 |
| `allowsEmptyCollection` | `boolean` | - | 是否允许空集合。 |
| `menuTrigger` | `"focus" \| "input" \| "manual"` | `"focus"` | 展示 ComboBox 菜单所需的交互。 |
| `shouldFocusWrap` | `boolean` | - | 键盘导航是否循环。 |
| `fullWidth` | `boolean` | `false` | ComboBox 是否占满容器宽度。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | ComboBox 内容或渲染函数。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### ComboBox.InputGroup Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | InputGroup 内容。 |
### ComboBox.Trigger Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ------------------- |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 自定义触发器内容。 |
### ComboBox.Popover Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ------------------- |
| `placement` | `"bottom" \| "bottom left" \| "bottom right" \| "bottom start" \| "bottom end" \| "top" \| "top left" \| "top right" \| "top start" \| "top end" \| "left" \| "left top" \| "left bottom" \| "start" \| "start top" \| "start bottom" \| "right" \| "right top" \| "right bottom" \| "end" \| "end top" \| "end bottom"` | `"bottom"` | 相对于触发器的 Popover 位置。 |
| `className` | `string` | - | 额外的 Tailwind CSS 类。 |
| `children` | `ReactNode` | - | 子内容。 |
### RenderProps
对 ComboBox 使用渲染函数时,会传入以下值:
| Prop | 类型 | 描述 |
| -------------- | --------------- | ------------ |
| `state` | `ComboBoxState` | ComboBox 状态。 |
| `inputValue` | `string` | 当前输入值。 |
| `selectedKey` | `Key \| null` | 当前选中的 key。 |
| `selectedItem` | `Node \| null` | 当前选中的 item。 |
## 示例
### 基础用法
```tsx
import { ComboBox, Input, Label, ListBox } from '@heroui/react';
Favorite Animal
Cat
Dog
```
### 带分组
```tsx
import { ComboBox, Input, Label, ListBox, Header, Separator } from '@heroui/react';
Country
United States
United Kingdom
```
### 受控选中
```tsx
import type { Key } from '@heroui/react';
import { ComboBox, Input, Label, ListBox } from '@heroui/react';
import { useState } from 'react';
function ControlledComboBox() {
const [selectedKey, setSelectedKey] = useState('cat');
return (
Animal
Cat
Dog
);
}
```
### 受控输入值
```tsx
import { ComboBox, Input, Label, ListBox } from '@heroui/react';
import { useState } from 'react';
function ControlledInputComboBox() {
const [inputValue, setInputValue] = useState('');
return (
Search
Cat
Dog
);
}
```
### 异步加载
```tsx
import { Collection, ComboBox, EmptyState, Input, Label, ListBox, ListBoxLoadMoreItem, Spinner } from '@heroui/react';
import { useAsyncList } from '@react-stately/data';
interface Character {
name: string;
}
function AsyncComboBox() {
const list = useAsyncList({
async load({cursor, filterText, signal}) {
const res = await fetch(
cursor || `https://swapi.py4e.com/api/people/?search=${filterText}`,
{ signal }
);
const json = await res.json();
return {
items: json.results,
cursor: json.next,
};
},
});
return (
Pick a Character
}>
{(item) => (
{item.name}
)}
Loading more...
);
}
```
### 自定义过滤
```tsx
import { ComboBox, Input, Label, ListBox } from '@heroui/react';
{
if (!inputValue) return true;
return text.toLowerCase().includes(inputValue.toLowerCase());
}}
>
Animal
Cat
Dog
```
### 菜单触发
使用 `menuTrigger` prop 控制 Popover 何时打开:
```tsx
import { ComboBox, Description, Input, Label, ListBox } from '@heroui/react';
// 在聚焦时打开(默认)
Favorite Animal
Cat
Popover opens when the input is focused
// 在输入时打开
Favorite Animal
Cat
Popover opens when the user edits the input text
// 仅手动打开
Favorite Animal
Cat
Popover only opens when the trigger button is pressed or arrow keys are used
```
### 表单值
使用 `formValue` prop 控制提交表单时提交选中项的 key 还是文本:
```tsx
import { Button, ComboBox, FieldError, Form, Input, Label, ListBox } from '@heroui/react';
function FormValueExample() {
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
console.log('Submitted value:', formData.get('animal')); // Will be "cat" (the key)
};
return (
{/* Submits the key (default) */}
Animal
Cat
Dog
{/* Submits the text */}
Animal (text)
Cat
Dog
Submit
);
}
```
### 校验行为
使用 `validationBehavior` prop 控制校验信息的展示方式:
```tsx
import { Button, ComboBox, FieldError, Form, Input, Label, ListBox } from '@heroui/react';
function ValidationExample() {
return (
{/* Native validation (default) - blocks form submission */}
Animal (native validation)
Cat
Submit
{/* ARIA validation - shows errors in realtime, doesn't block submission */}
Animal (ARIA validation)
Cat
Submit
);
}
```
### 自定义校验
使用 `validate` prop 添加自定义校验逻辑:
```tsx
import { ComboBox, FieldError, Input, Label, ListBox } from '@heroui/react';
function CustomValidationExample() {
return (
{
if (!value || value.selectedKey === null) {
return 'Please select an animal';
}
if (value.selectedKey === 'snake') {
return 'Snakes are not allowed';
}
return true;
}}
>
Favorite Animal
Cat
Dog
Snake
);
}
```
### 只读
使用 `isReadOnly` 将 ComboBox 设为只读:
```tsx
import { ComboBox, Input, Label, ListBox } from '@heroui/react';
Favorite Animal
Cat
Dog
```
## 无障碍
ComboBox 实现 ARIA ComboBox 模式,并提供:
* 完整键盘导航
* 选择与输入变化时的屏幕阅读器播报
* 合理的焦点管理
* 禁用状态支持
* 输入过滤(typeahead)式搜索
* 与 HTML 表单的集成
* 自定义值支持
更多信息见 [React Aria ComboBox 文档](https://react-spectrum.adobe.com/react-aria/ComboBox.html)。
# Select 选择器
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/select
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(pickers)/select.mdx
> Select 展示可折叠的选项列表,并允许用户从中选择一项。
## 引入
```tsx
import { Select } from "@heroui/react";
```
### 用法
```tsx
import {Label, ListBox, Select} from "@heroui/react";
export function Default() {
return (
州
佛罗里达
特拉华
加利福尼亚
德克萨斯
纽约
华盛顿
);
}
```
### 组件结构
引入 Select 组件,并通过点语法访问各部分。
```tsx
import {Select, Label, Description, Header, ListBox, Separator} from "@heroui/react";
export default () => (
);
```
### 带描述
```tsx
import {Description, Label, ListBox, Select} from "@heroui/react";
export function WithDescription() {
return (
州
佛罗里达
特拉华
加利福尼亚
德克萨斯
纽约
华盛顿
请选择居住州
);
}
```
### 多选
```tsx
import {Label, ListBox, Select} from "@heroui/react";
export function MultipleSelect() {
return (
拟访问国家
阿根廷
委内瑞拉
日本
法国
意大利
西班牙
泰国
新西兰
冰岛
);
}
```
### 分区
```tsx
import {Header, Label, ListBox, Select, Separator} from "@heroui/react";
export function WithSections() {
return (
国家
美国
加拿大
墨西哥
英国
法国
德国
西班牙
意大利
日本
中国
印度
韩国
);
}
```
### 含禁用项
```tsx
import {Label, ListBox, Select} from "@heroui/react";
export function WithDisabledOptions() {
return (
动物
狗
猫
鸟
袋鼠
大象
老虎
);
}
```
### 自定义指示器
```tsx
import {ChevronsExpandVertical} from "@gravity-ui/icons";
import {Label, ListBox, Select} from "@heroui/react";
export function CustomIndicator() {
return (
州
佛罗里达
特拉华
加利福尼亚
德克萨斯
纽约
华盛顿
);
}
```
### 必填
```tsx
"use client";
import {Button, FieldError, Form, Label, ListBox, Select} from "@heroui/react";
export function Required() {
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: Record = {};
// Convert FormData to plain object
formData.forEach((value, key) => {
data[key] = value.toString();
});
alert("表单提交成功!");
};
return (
州
佛罗里达
特拉华
加利福尼亚
德克萨斯
纽约
华盛顿
国家
美国
加拿大
墨西哥
英国
法国
德国
提交
);
}
```
### 全宽
```tsx
import {Label, ListBox, Select} from "@heroui/react";
export function FullWidth() {
return (
喜爱的动物
猫
狗
鸟
);
}
```
### 变体
Select 组件支持两种视觉变体:
* **`primary`**(默认)— 带阴影的标准样式,适用于大多数场景
* **`secondary`** — 低强调、无阴影,适合在 Surface 等表面背景上使用
```tsx
import {Label, ListBox, Select} from "@heroui/react";
export function Variants() {
return (
主要变体
Option 1
Option 2
次要变体
Option 1
Option 2
);
}
```
### 在 Surface 内
在 [Surface](/docs/components/surface) 内使用时,请使用 `variant="secondary"`,以应用适合表面背景的低强调变体。
```tsx
"use client";
import {Button, FieldError, Form, Label, ListBox, Select, Surface} from "@heroui/react";
export function OnSurface() {
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data: Record = {};
// Convert FormData to plain object
formData.forEach((value, key) => {
data[key] = value.toString();
});
alert("表单提交成功!");
};
return (
州
佛罗里达
特拉华
加利福尼亚
德克萨斯
纽约
华盛顿
国家
美国
加拿大
墨西哥
英国
法国
德国
提交
);
}
```
### 自定义展示值
```tsx
"use client";
import {
Avatar,
AvatarFallback,
AvatarImage,
Description,
Label,
ListBox,
Select,
} from "@heroui/react";
export function CustomValue() {
const users = [
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg",
email: "bob@heroui.com",
fallback: "B",
id: "1",
name: "Bob",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg",
email: "fred@heroui.com",
fallback: "F",
id: "2",
name: "Fred",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg",
email: "martha@heroui.com",
fallback: "M",
id: "3",
name: "Martha",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg",
email: "john@heroui.com",
fallback: "J",
id: "4",
name: "John",
},
{
avatarUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg",
email: "jane@heroui.com",
fallback: "J",
id: "5",
name: "Jane",
},
];
return (
用户
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItems = state.selectedItems;
if (selectedItems.length > 1) {
return `${selectedItems.length} 位用户已选`;
}
const selectedItem = users.find((user) => user.id === selectedItems[0]?.key);
if (!selectedItem) {
return defaultChildren;
}
return (
{selectedItem.fallback}
{selectedItem.name}
);
}}
{users.map((user) => (
{user.fallback}
{user.name}
{user.email}
))}
);
}
```
### 受控
```tsx
"use client";
import type {Key} from "@heroui/react";
import {Label, ListBox, Select} from "@heroui/react";
import {useState} from "react";
export function Controlled() {
const states = [
{
id: "california",
name: "加利福尼亚",
},
{
id: "texas",
name: "德克萨斯",
},
{
id: "florida",
name: "佛罗里达",
},
{
id: "new-york",
name: "纽约",
},
{
id: "illinois",
name: "伊利诺伊",
},
{
id: "pennsylvania",
name: "宾夕法尼亚",
},
];
const [state, setState] = useState("california");
const selectedState = states.find((s) => s.id === state);
return (
setState(value)}
>
州(受控)
{states.map((state) => (
{state.name}
))}
已选:{selectedState?.name || "无"}
);
}
```
### 受控多选
```tsx
"use client";
import type {Key} from "@heroui/react";
import {Label, ListBox, Select} from "@heroui/react";
import React from "react";
export function ControlledMultiple() {
const [selected, setSelected] = React.useState(["california", "texas"]);
return (
setSelected(keys as Key[])}
>
州(受控多选)
加利福尼亚
德克萨斯
佛罗里达
纽约
伊利诺伊
宾夕法尼亚
已选:{selected.length > 0 ? selected.join(", ") : "无"}
);
}
```
### 受控展开状态
```tsx
"use client";
import {Button, Label, ListBox, Select} from "@heroui/react";
import {useState} from "react";
export function ControlledOpenState() {
const [isOpen, setIsOpen] = useState(false);
return (
州
佛罗里达
特拉华
加利福尼亚
德克萨斯
纽约
华盛顿
setIsOpen(!isOpen)}>{isOpen ? "关闭" : "打开"}选择框
选择框{isOpen ? "已打开" : "已关闭"}
);
}
```
### 异步加载
```tsx
"use client";
import {Label, ListBox, Select, Spinner} from "@heroui/react";
import {useAsyncList} from "@react-stately/data";
import {Collection, ListBoxLoadMoreItem} from "react-aria-components";
interface Pokemon {
name: string;
}
export function AsynchronousLoading() {
const list = useAsyncList({
async load({cursor, signal}) {
const res = await fetch(cursor || `https://pokeapi.co/api/v2/pokemon`, {signal});
const json = await res.json();
return {
cursor: json.next,
items: json.results,
};
},
});
return (
选择宝可梦
{(item: Pokemon) => (
{item.name}
)}
加载更多…
);
}
```
### 禁用
```tsx
import {Label, ListBox, Select} from "@heroui/react";
export function Disabled() {
return (
州
佛罗里达
特拉华
加利福尼亚
德克萨斯
纽约
华盛顿
拟访问国家
阿根廷
委内瑞拉
日本
法国
意大利
西班牙
);
}
```
## Related Components
* **Listbox**: Scrollable list of selectable items
* **Popover**: Displays content in context with a trigger
* **Label**: Accessible label for form controls
### 自定义渲染函数
```tsx
"use client";
import {Label, ListBox, Select} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
州
佛罗里达
特拉华
加利福尼亚
德克萨斯
纽约
华盛顿
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import {Select} from "@heroui/react";
function CustomSelect() {
return (
State
Item 1
);
}
```
### 自定义组件类
若要自定义 Select 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.select {
@apply flex flex-col gap-1;
}
.select__trigger {
@apply rounded-lg border border-border bg-surface p-2;
}
.select__value {
@apply text-current;
}
.select__indicator {
@apply text-muted;
}
.select__popover {
@apply rounded-lg border border-border bg-surface p-2;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Select 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/select.css)):
#### 基础类
* `.select` - Select 根容器
* `.select__trigger` - 打开下拉的触发按钮
* `.select__value` - 当前显示的值或占位符
* `.select__indicator` - 下拉指示图标
* `.select__popover` - 弹出层容器
#### 变体类
* `.select--primary` - Primary 变体,带阴影(默认)
* `.select--secondary` - Secondary 变体,无阴影,适合在 Surface 上使用
#### 状态类
* `.select[data-invalid="true"]` - 无效状态
* `.select__trigger[data-focus-visible="true"]` - 触发器聚焦状态
* `.select__trigger[data-disabled="true"]` - 触发器禁用状态
* `.select__value[data-placeholder="true"]` - 占位符状态
* `.select__indicator[data-open="true"]` - 展开时的指示器状态
### 交互状态
该组件同时支持 CSS 伪类与 data 属性,以提供更灵活的状态控制:
* **悬停**:触发器上的 `:hover` 或 `[data-hovered="true"]`
* **聚焦**:触发器上的 `:focus-visible` 或 `[data-focus-visible="true"]`
* **禁用**:Select 上的 `:disabled` 或 `[data-disabled="true"]`
* **展开**:指示器上的 `[data-open="true"]`
## API 参考
### Select Props
| Prop | 类型 | 默认值 | 描述 |
| --------------- | ------------------------------------------------------------------------- | ------------------ | --------------------------------------------------------------- |
| `placeholder` | `string` | `'Select an item'` | Select 为空时显示的占位符文本。 |
| `selectionMode` | `"single" \| "multiple"` | `"single"` | 启用单选或多选。 |
| `isOpen` | `boolean` | - | 设置菜单是否打开(受控)。 |
| `defaultOpen` | `boolean` | - | 设置菜单默认是否打开(非受控)。 |
| `onOpenChange` | `(isOpen: boolean) => void` | - | 展开状态变化时的事件处理函数。 |
| `disabledKeys` | `Iterable` | - | 禁用条目的 key。 |
| `isDisabled` | `boolean` | - | Select 是否禁用。 |
| `value` | `Key \| Key[] \| null` | - | 当前值(受控)。 |
| `defaultValue` | `Key \| Key[] \| null` | - | 默认值(非受控)。 |
| `onChange` | `(value: Key \| Key[] \| null) => void` | - | 值变化时的事件处理函数。 |
| `isRequired` | `boolean` | - | 用户输入是否必填。 |
| `isInvalid` | `boolean` | - | Select 的值是否无效。 |
| `name` | `string` | - | 输入框名称,用于提交 HTML 表单。 |
| `autoComplete` | `string` | - | 描述自动完成行为类型。 |
| `fullWidth` | `boolean` | `false` | Select 是否占满容器宽度。 |
| `variant` | `"primary" \| "secondary"` | `"primary"` | 视觉变体。`primary` 为默认带阴影样式。`secondary` 为低强调、无阴影变体,适合在 Surface 上使用。 |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | Select 内容或渲染函数。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Select.Trigger Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------------------- | --- | ----------- |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | 触发器内容或渲染函数。 |
### Select.Value Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------------ | --- | --------------------- |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `ReactNode \| RenderFunction` | - | 值区域内容或渲染函数。 |
| `render` | `DOMRenderFunction` | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
### Select.Indicator Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------- | --- | ---------- |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `ReactNode` | - | 自定义指示器内容。 |
### Select.Popover Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ------------ |
| `placement` | `"bottom" \| "bottom left" \| "bottom right" \| "bottom start" \| "bottom end" \| "top" \| "top left" \| "top right" \| "top start" \| "top end" \| "left" \| "left top" \| "left bottom" \| "start" \| "start top" \| "start bottom" \| "right" \| "right top" \| "right bottom" \| "end" \| "end top" \| "end bottom"` | `"bottom"` | 弹出层相对触发器的位置。 |
| `className` | `string` | - | 额外的 CSS 类。 |
| `children` | `ReactNode` | - | 子内容。 |
### RenderProps
对 `Select.Value` 使用渲染函数时,会提供以下值:
| Prop | 类型 | 描述 |
| ----------------- | ------------- | ----------- |
| `defaultChildren` | `ReactNode` | 默认渲染的值。 |
| `isPlaceholder` | `boolean` | 是否为占位符状态。 |
| `state` | `SelectState` | Select 的状态。 |
| `selectedItems` | `Node[]` | 当前已选中的条目。 |
## 无障碍
Select 组件实现 ARIA 列表框模式,并提供:
* 完整的键盘导航支持
* 选择变化时的屏幕阅读器播报
* 合理的焦点管理
* 禁用状态支持
* 输入首字母快速定位(typeahead)
* 与 HTML 表单的集成
更多信息见 [React Aria Select 文档](https://react-spectrum.adobe.com/react-aria/Select.html)。
# Kbd 键盘按键
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/kbd
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(typography)/kbd.mdx
> 用于展示键盘快捷键与组合键。
## 引入
```tsx
import { Kbd } from "@heroui/react";
```
### 用法
```tsx
import {Kbd} from "@heroui/react";
export function Basic() {
return (
K
P
C
D
);
}
```
### 组件结构
导入 Kbd 组件后,可通过点语法访问所有子部分。
```tsx
import { Kbd } from "@heroui/react";
export default () => (
⌘
K
);
```
### 导航键
```tsx
import {Kbd} from "@heroui/react";
export function NavigationKeys() {
return (
);
}
```
### 行内用法
```tsx
import {Kbd} from "@heroui/react";
export function InlineUsage() {
return (
按{" "}
Esc
{" "}
关闭对话框。
使用{" "}
K
{" "}
打开命令面板。
使用{" "}
{" "}
和{" "}
{" "}
方向键进行导航。
使用{" "}
S
{" "}
定期保存你的工作。
);
}
```
### 说明性文本
```tsx
import {Kbd} from "@heroui/react";
export function InstructionalText() {
return (
快捷操作
• 打开搜索:{" "}
K
• 切换侧边栏:{" "}
B
• 新建文件:{" "}
N
• 快速保存:{" "}
S
);
}
```
### 特殊键
```tsx
import {Kbd} from "@heroui/react";
export function SpecialKeys() {
return (
按{" "}
{" "}
确认,或按{" "}
{" "}
取消。
使用{" "}
{" "}
在表单字段间切换,使用{" "}
{" "}
返回上一项。
按住{" "}
{" "}
可临时启用平移模式。
);
}
```
### 变体
```tsx
import {Kbd} from "@heroui/react";
export function Variants() {
return (
复制:
C
C
粘贴:
V
V
剪切:
X
X
撤销:
Z
Z
重做:
Z
Z
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import { Kbd } from "@heroui/react";
function CustomKbd() {
return (
K
);
}
```
### 自定义组件类
要自定义 Kbd 的组件类,可使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes)。
```css
@layer components {
.kbd {
@apply bg-gray-100 dark:bg-gray-800 border-gray-300;
}
.kbd__abbr {
@apply font-bold;
}
.kbd__content {
@apply text-sm;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
Kbd 使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/kbd.css)):
#### 基础类
* `.kbd` — 按键基础样式(背景、边框与间距)
* `.kbd__abbr` — 修饰键的缩写元素
* `.kbd__content` — 按键文字的包裹层
## API 参考
### Kbd Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ---------------------- | --------- | --------- |
| `children` | `React.ReactNode` | - | 按键内容 |
| `variant` | `"default" \| "light"` | `default` | 键盘按键的视觉变体 |
| `className` | `string` | - | 自定义 CSS 类 |
### Kbd.Abbr Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | ------------------------------ |
| `title` | `string` | - | 无障碍 `title`(例如 ⌘ 对应 “Command”) |
| `children` | `React.ReactNode` | - | 显示的符号或文本(例如 ⌘、⌥、⇧) |
| `className` | `string` | - | 自定义 CSS 类 |
### Kbd.Key Props
| Prop | 类型 | 默认值 | 描述 |
| ----------- | ----------------- | --- | --------- |
| `children` | `React.ReactNode` | - | 按键上的文本 |
| `className` | `string` | - | 自定义 CSS 类 |
### Kbd.Content Type
`keyValue` 属性可用的按键取值:
| Modifier Keys | Special Keys | Navigation Keys | Function Keys |
| ------------- | ------------ | --------------- | ------------- |
| `command` | `enter` | `up` | `fn` |
| `shift` | `delete` | `down` | |
| `ctrl` | `escape` | `left` | |
| `option` | `tab` | `right` | |
| `alt` | `space` | `pageup` | |
| `win` | `capslock` | `pagedown` | |
| | `help` | `home` | |
| | | `end` | |
# Typography 排版
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/typography
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(typography)/typography.mdx
> 面向标题、正文与行内代码的语义化排版原语,基于 React Aria Components 的 Text 构建。
## 引入
```tsx
import {Typography} from "@heroui/react";
```
## 用法
```tsx
import {Typography} from "@heroui/react";
const scale = [
{
label: "h1",
meta: "36px / 600 / 1.11 / tight",
sample: "打造更出色的界面",
type: "h1" as const,
},
{
label: "h2",
meta: "30px / 600 / 1.17 / tight",
sample: "为智能时代而生",
type: "h2" as const,
},
{
label: "h3",
meta: "24px / 600 / 1.25 / tight",
sample: "按您的条件定价",
type: "h3" as const,
},
{
label: "h4",
meta: "20px / 600 / 1.33 / tight",
sample: "申请创业计划",
type: "h4" as const,
},
{
label: "h5",
meta: "18px / 600 / 1.39 / tight",
sample: "卡片标题",
type: "h5" as const,
},
{
label: "h6",
meta: "16px / 600 / 1.50 / tight",
sample: "较小的功能标题",
type: "h6" as const,
},
{
label: "body",
meta: "16px / 400 / 1.75",
sample: "用于文档、营销文案与描述的主要正文。",
type: "body" as const,
},
{
label: "body-sm",
meta: "14px / 400 / 1.50",
sample: "次要正文、表格单元格、导航与侧边栏项。",
type: "body-sm" as const,
},
{
label: "body-xs",
meta: "12px / 400 / 1.25",
sample: "说明文字、徽章、辅助文本与细则。",
type: "body-xs" as const,
},
{
label: "code",
meta: "14px / mono",
sample: "pnpm add @heroui/react",
type: "code" as const,
},
] as const;
export const TypographyScale = () => {
return (
{scale.map((row) => (
{row.label}
{row.meta}
{row.sample}
))}
);
};
```
默认情况下,`Typography` 会将视觉上的 `type` 映射到对应的语义化元素。
## 子组件
```tsx
import {Typography} from "@heroui/react";
export const Primitives = () => {
return (
仪表盘
便捷原语是 Typography 的薄封装,可在不学习第二套样式系统的情况下选择显式组合。
Paragraph 支持 base、sm 和 xs 尺寸。
Typography.Code
);
};
```
* `Typography.Heading` 将 `level={1..6}` 映射为 `type="h1"` 至 `type="h6"`。
* `Typography.Paragraph` 将 `size="base" | "sm" | "xs"` 映射为正文样式。
* `Typography.Code` 映射为行内代码样式。
* `Typography.Prose` 为以常规 HTML 子节点传入的富文本内容提供排版样式。
## Prose
```tsx
import {Typography} from "@heroui/react";
export const Prose = () => {
return (
正文标题
Prose 适用于标记已是语义化、由 HeroUI 应用默认排版节奏的写作型内容。
章节标题
行内代码如 render 与 Typography 原语获得相同的代码样式处理。
);
};
```
## Render Prop
```tsx
"use client";
import {Typography} from "@heroui/react";
export const RenderProps = () => {
return (
{children} } type="h1">
H1 视觉样式,h2 语义元素
{children} }>
render prop 可更换底层元素,同时保留 HeroUI 的 props 与样式。
);
};
```
需要自定义实际渲染的元素时,可使用 React Aria Components 风格的 `render` prop。
## CSS 类名
### 基础类
* `.typography` - 排版基础原语
* `.typography-prose` - 富文本文章体容器
### 类型类
* `.typography--h1` 至 `.typography--h6`
* `.typography--body`、`.typography--body-sm`、`.typography--body-xs`
* `.typography--code`
### 修饰类
* `.typography--align-start`、`.typography--align-center`、`.typography--align-end`、`.typography--align-justify`
* `.typography--color-default`、`.typography--color-muted`
* `.typography--truncate`
* `.typography--weight-normal`、`.typography--weight-medium`、`.typography--weight-semibold`、`.typography--weight-bold`
## API 参考
### Typography 属性
| 属性 | 类型 | 默认值 | 说明 |
| ---------- | -------------------------------------------------------------------------------------------- | ----------- | ----------------------- |
| `type` | `'h1' \| 'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6' \| 'body' \| 'body-sm' \| 'body-xs' \| 'code'` | `'body'` | 语义化排版样式。 |
| `align` | `'start' \| 'center' \| 'end' \| 'justify'` | `'start'` | 文本对齐。 |
| `color` | `'default' \| 'muted'` | `'default'` | 文本颜色。 |
| `weight` | `'normal' \| 'medium' \| 'semibold' \| 'bold'` | - | 字重覆盖。 |
| `truncate` | `boolean` | - | 将文本截断为单行并显示省略号。 |
| `render` | `DOMRenderFunction` | - | 来自 React Aria 的自定义渲染函数。 |
| `children` | `ReactNode` | - | 文本内容。 |
# ScrollShadow 滚动阴影
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/components/scroll-shadow
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/components/(utilities)/scroll-shadow.mdx
> 通过阴影提示可滚动溢出内容,并根据滚动位置自动检测显示或隐藏。
## 引入
```tsx
import { ScrollShadow } from "@heroui/react";
```
## 用法
```tsx
import {ScrollShadow} from "@heroui/react";
export default function Default() {
return (
{Array.from({length: 10}).map((_, idx) => (
段落 {idx + 1}:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
pulvinar risus non risus hendrerit venenatis. Pellentesque sit amet hendrerit risus,
sed porttitor quam. Morbi accumsan cursus enim, sed ultricies sapien.
))}
);
}
```
## 方向
```tsx
import {Card, ScrollShadow} from "@heroui/react";
const images = [
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/robot1.jpeg",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/avocado.jpeg",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/oranges.jpeg",
];
export default function Orientation() {
const getRandomImage = (idx: number) => {
return images[idx % images.length];
};
return (
垂直
{Array.from({length: 10}).map((_, idx) => (
段落 {idx + 1}:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
pulvinar risus non risus hendrerit venenatis. Pellentesque sit amet hendrerit
risus, sed porttitor quam. Morbi accumsan cursus enim, sed ultricies sapien.
))}
水平
{Array.from({length: 10}).map((_, idx) => (
连接未来
今天 18:30
))}
);
}
```
## 隐藏滚动条
```tsx
import {ScrollShadow} from "@heroui/react";
export default function HideScrollBar() {
return (
{Array.from({length: 10}).map((_, idx) => (
段落 {idx + 1}:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
pulvinar risus non risus hendrerit venenatis. Pellentesque sit amet hendrerit risus,
sed porttitor quam. Morbi accumsan cursus enim, sed ultricies sapien.
))}
);
}
```
## 自定义阴影尺寸
```tsx
import {ScrollShadow} from "@heroui/react";
export default function CustomSize() {
return (
{Array.from({length: 10}).map((_, idx) => (
段落 {idx + 1}:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
pulvinar risus non risus hendrerit venenatis. Pellentesque sit amet hendrerit risus,
sed porttitor quam. Morbi accumsan cursus enim, sed ultricies sapien.
))}
);
}
```
## 可见性变化
```tsx
"use client";
import type {ScrollShadowVisibility} from "@heroui/react";
import {Card, ScrollShadow} from "@heroui/react";
import {useState} from "react";
const images = [
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/robot1.jpeg",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/avocado.jpeg",
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/oranges.jpeg",
];
const VISIBILITY_LABELS: Record = {
auto: "自动",
both: "两侧",
bottom: "底部",
left: "左侧",
none: "无",
right: "右侧",
top: "顶部",
};
export default function VisibilityChange() {
const [verticalState, setVerticalState] = useState("none");
const [horizontalState, setHorizontalState] = useState("none");
const getRandomImage = (idx: number) => {
return images[idx % images.length];
};
return (
垂直阴影状态:{VISIBILITY_LABELS[verticalState]}
setVerticalState(visibility)}
>
{Array.from({length: 10}).map((_, idx) => (
段落 {idx + 1}:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
pulvinar risus non risus hendrerit venenatis. Pellentesque sit amet hendrerit
risus, sed porttitor quam. Morbi accumsan cursus enim, sed ultricies sapien.
))}
水平阴影状态:{VISIBILITY_LABELS[horizontalState]}
setHorizontalState(visibility)}
>
{Array.from({length: 10}).map((_, idx) => (
连接未来
今天 18:30
))}
);
}
```
## 与 Card 组合
```tsx
import {Button, Card, ScrollShadow} from "@heroui/react";
export default function WithCard() {
return (
条款与条件
继续前请先阅读
{Array.from({length: 10}).map((_, idx) => (
段落 {idx + 1}:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
pulvinar risus non risus hendrerit venenatis. Pellentesque sit amet hendrerit risus,
sed porttitor quam. Morbi accumsan cursus enim, sed ultricies sapien.
))}
Cancel
接受
);
}
```
## 样式
### 传入 Tailwind CSS 类
```tsx
import {ScrollShadow, Card} from "@heroui/react";
function CustomScrollShadow() {
return (
{Array.from({length: 10}).map((_, idx) => (
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar risus non
risus hendrerit venenatis.
))}
);
}
```
### 自定义组件类
若要自定义 ScrollShadow 组件类,可以使用 `@layer components` 指令。
[了解更多](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.scroll-shadow {
@apply rounded-xl border border-default-200;
}
.scroll-shadow--vertical {
@apply pr-2; /* Add padding for custom scrollbar styling */
}
.scroll-shadow--horizontal {
@apply pb-2;
}
}
```
HeroUI 遵循 [BEM](https://getbem.com/) 方法论,确保组件变体与状态可复用且易于自定义。
### CSS 类
ScrollShadow 组件使用以下 CSS 类([查看源码样式](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/scroll-shadow.css)):
#### 基础类
* `.scroll-shadow` - 根容器元素
#### 方向变体
* `.scroll-shadow--vertical` - 纵向滚动(默认)
* `.scroll-shadow--horizontal` - 横向滚动
#### 状态修饰符
* `.scroll-shadow--hide-scrollbar` - 隐藏原生滚动条
### CSS 变量
ScrollShadow 组件使用 CSS 变量设置渐变遮罩尺寸,并为可见的原生滚动条保留空间:
| 变量 | 默认值 | 描述 |
| -------------------------------- | -------------------------------- | ---------------------------------------------- |
| `--scroll-shadow-size` | `40px` | 控制渐变阴影尺寸。该值由 `size` prop 设置。 |
| `--scroll-shadow-scrollbar-size` | `10px`(`hideScrollBar` 时为 `0px`) | 为原生滚动条保留一段实色遮罩区域,避免渐变覆盖滚动条。使用更宽的自定义滚动条时可以覆盖该值。 |
### Data 属性
组件使用 data 属性控制阴影可见性:
* **滚动状态**:`[data-top-scroll]`、`[data-bottom-scroll]`、`[data-left-scroll]`、`[data-right-scroll]` — 当内容可向对应方向滚动时应用
* **组合状态**:`[data-top-bottom-scroll]`、`[data-left-right-scroll]` — 当内容可向两个方向滚动时应用
* **方向**:`[data-orientation="vertical"]` 或 `[data-orientation="horizontal"]` — 表示滚动方向
* **尺寸**:`[data-scroll-shadow-size]` — 阴影渐变尺寸数值
## API 参考
### ScrollShadow
| Prop | 类型 | 默认值 | 描述 |
| -------------------- | ---------------------------------------------------------------------------------- | ------------ | ----------------- |
| `orientation` | `"vertical"` \| `"horizontal"` | `"vertical"` | 滚动方向 |
| `variant` | `"fade"` | `"fade"` | 阴影视觉效果样式 |
| `size` | `number` | `40` | 阴影渐变尺寸(像素) |
| `offset` | `number` | `0` | 开始显示阴影前的滚动偏移量(像素) |
| `hideScrollBar` | `boolean` | `false` | 是否隐藏原生滚动条 |
| `isEnabled` | `boolean` | `true` | 是否启用滚动阴影检测 |
| `visibility` | `"auto"` \| `"both"` \| `"top"` \| `"bottom"` \| `"left"` \| `"right"` \| `"none"` | `"auto"` | 受控的阴影可见性 |
| `onVisibilityChange` | `(visibility: ScrollShadowVisibility) => void` | - | 阴影可见性变化时调用的回调 |
| `className` | `string` | - | 应用到根元素上的额外 CSS 类 |
| `children` | `ReactNode` | - | 可滚动的子内容 |
# 动画
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started/animation
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/(handbook)/animation.mdx
> 为 HeroUI v3 组件添加流畅的动画和过渡
HeroUI 组件支持多种动画方法:内置 CSS 过渡、自定义 CSS 动画以及 Framer Motion 等 JavaScript 库。
## 内置动画
HeroUI 组件使用数据属性来公开其动画状态:
```css
/* Popover entrance/exit */
.popover[data-entering] {
@apply animate-in zoom-in-90 fade-in-0 duration-200;
}
.popover[data-exiting] {
@apply animate-out zoom-out-95 fade-out duration-150;
}
/* Button press effect */
.button:active,
.button[data-pressed="true"] {
transform: scale(0.97);
}
/* Accordion expansion */
.accordion__panel[aria-hidden="false"] {
@apply h-[var(--panel-height)] opacity-100;
}
```
**状态样式属性:**
* `[data-hovered="true"]`- 悬停状态
* `[data-pressed="true"]`- 活动/按下状态
* `[data-focus-visible="true"]`- 键盘焦点
* `[data-disabled="true"]`- 禁用状态
* `[data-entering]` / `[data-exiting]`- 过渡状态
* `[aria-expanded="true"]`- 展开状态
## CSS 动画
**使用 Tailwind 实用程序:**
```tsx
// Pulse on hover
Hover me
// Fade in entrance
Welcome message
// Staggered list
Item 1
Item 2
```
**自定义过渡:**
```css
/* Slower accordion */
.accordion__panel {
@apply transition-all duration-500;
}
/* Bouncy button */
.button:active {
animation: bounce 0.3s;
}
@keyframes bounce {
50% { transform: scale(0.95); }
}
```
## Framer Motion
HeroUI 组件与 Framer Motion 无缝协作,实现高级动画。
**基本用法:**
```tsx
import { motion } from 'framer-motion';
import { Button } from '@heroui/react';
const MotionButton = motion(Button);
Animated Button
```
**入口动画:**
```tsx
Welcome!
```
**布局动画:**
```tsx
import { AnimatePresence, motion } from 'framer-motion';
function Tabs({ items, selected }) {
return (
{items.map((item, i) => (
setSelected(i)}>
{item}
{selected === i && (
)}
))}
);
}
```
## 渲染属性
根据组件状态应用动态动画:
```tsx
{({ isPressed, isHovered }) => (
Interactive Button
)}
```
## 无障碍
**尊重动态偏好:** HeroUI 使用 Tailwind 的 motion-reduce 工具自动尊重用户动态效果偏好。当用户在系统设置中启用“减少动态效果”时,所有内置的过渡和动画效果都将被禁用。
HeroUI 扩展了 Tailwind 的 motion-reduce: 变体,以同时支持原生 `prefers-reduced-motion` 媒体查询和 `data-reduce-motion` 属性。
```css
/* HeroUI pattern - uses Tailwind's motion-reduce: */
.button {
@apply transition-colors motion-reduce:transition-none;
}
/* Expands to support both approaches: */
@media (prefers-reduced-motion: reduce) {
.button {
transition: none;
}
}
[data-reduce-motion="true"] .button {
transition: none;
}
```
使用Framer Motion:
```tsx
import { useReducedMotion } from 'framer-motion';
function AnimatedCard() {
const shouldReduceMotion = useReducedMotion();
return (
Content
);
}
```
**全局禁用动画:** 添加`data-reduce-motion="true"`到``或者``标签:
```html
```
HeroUI自动检测用户的`prefers-reduced-motion: reduce`相应地设置并禁用动画。
## 性能技巧
**使用 GPU 加速属性**: 首选`transform`和`opacity`对于流畅的动画:
```css
/* Good - GPU accelerated */
.slide-in {
transform: translateX(-100%);
transition: transform 0.3s;
}
/* Avoid - Triggers layout */
.slide-in {
left: -100%;
transition: left 0.3s;
}
```
**`will-change`优化**: 使用`will-change`优化动画,但在不设置动画时将其删除:
```css
.button {
will-change: transform;
}
.button:not(:hover) {
will-change: auto;
}
```
## 下一步
* 了解[样式](/docs/handbook/styling)方法
* 探索[组件](/docs/react/components)示例
* 查看[主题](/docs/handbook/theming)文档
# 颜色
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started/colors
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/(handbook)/colors.mdx
> HeroUI v3 的调色板与主题系统
import {ColorSectionSideBySide, ColorSectionStacked, ColorSectionFormField, ColorSectionPrimitive} from "@/components/color-section";
HeroUI 的颜色体系围绕语义意图构建,而非堆砌视觉色板。系统不会暴露庞大的原始色表,而是定义一小套有意义的色彩角色,覆盖绝大多数界面需求。
系统中的多数颜色会由少量基础值自动派生。这样 HeroUI 能在保持对比度、层级与主题行为一致的同时,让整套体系易于理解与修改。
颜色应首先传达用途与状态;视觉变化来自尺度、强调与上下文。
**想要创建你自己的主题?** 试试 [主题构建器](/themes),以可视化方式自定义颜色、圆角、字体等,然后导出 CSS 用于你的项目。
## 强调色
强调色代表品牌或产品的主识别色,用于吸引对关键操作、高亮与重点时刻的注意。
强调色应有意识地节制使用。滥用会削弱其冲击力,并破坏视觉层级。多数情况下,组件会从基础强调色自动派生悬停、柔和背景与聚焦等相关取值。
## 默认(中性色)
默认色构成系统的中性骨架,用于大多数非强调的界面元素。
## 成功
成功色传达积极结果、确认与完成状态,常见于反馈组件、状态指示与校验通过等场景。
## 警告
警告色表示需谨慎、存在风险,或需要留意但非破坏性的操作,常用于提示、消息以及用户应暂停或复核信息的过渡状态。
## 危险
危险色表示破坏性、不可逆或关键的操作与状态,应一眼可辨,并稳定用于错误、危险按钮与严重告警。
## 前景色
前景色用于正文级内容,如文字与图标。这些颜色针对可读性与无障碍优化,并会随背景与表面上下文自动适配。请勿在组件内硬编码前景色。
## 背景色
背景色定义界面的基底画布,在保持视觉克制的前提下建立整体对比与氛围。
## 表面色
表面色叠在背景之上,用于卡片、面板、模态与下拉等容器。表面通过抬升、对比与分层形成区隔与层级,而非依赖强烈的色相跳跃。
## 表单字段
表单字段色是面向输入、控件与可交互字段的专用令牌,覆盖默认、聚焦与悬停等多种状态。将其独立出来,可让表单元素在视觉上与按钮及界面其余部分保持清晰区分。
## 分隔线
分隔线色用于分割线、描边与轻量边界,用来组织内容、引导视线而不增加噪点。分隔线色应保持低对比、不抢眼。
## 其他
其他颜色在界面中承担特定工具性角色,用于组织内容、引导视线而不增加噪点。
## 基础色
基础色是与模式无关的底层取值,作为语义色令牌的根基,在明暗主题之间不会改变。
## 如何使用颜色
**在组件中:**
```jsx
点击我
```
**在 CSS 文件中:**
```css title="global.css"
/* 直接使用 CSS 变量 */
.my-component {
background: var(--accent);
color: var(--accent-foreground);
border: 1px solid var(--border);
}
/* 配合 @apply 与 @layer */
@layer components {
.button {
@apply bg-accent text-accent-foreground;
&:hover,
&[data-hovered="true"] {
@apply bg-accent-hover;
}
&:active,
&[data-pressed="true"] {
@apply bg-accent-hover;
transform: scale(0.97);
}
}
}
```
## 默认主题
完整主题定义见仓库中的 [variables.css](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/themes/default/variables.css)。该主题会根据 `class="dark"` 或 `data-theme="dark"` 属性在明暗模式间自动切换。
```css
@layer base {
/* HeroUI 默认主题 */
:root {
color-scheme: light;
/* == 通用变量 == */
/* 基础色(在明暗模式下勿改动) */
--white: oklch(100% 0 0);
--black: oklch(0% 0 0);
--snow: oklch(0.9911 0 0);
--eclipse: oklch(0.2103 0.0059 285.89);
/* 间距刻度 */
--spacing: 0.25rem;
/* 边框 */
--border-width: 1px;
--field-border-width: 0px;
--disabled-opacity: 0.5;
/* 聚焦环偏移,用于聚焦环 */
--ring-offset-width: 2px;
/* 光标 */
--cursor-interactive: pointer;
--cursor-disabled: not-allowed;
/* 圆角 */
--radius: 0.5rem;
--field-radius: calc(var(--radius) * 1.5);
/* == 浅色主题变量 == */
/* 基础颜色 */
--background: oklch(0.9702 0 0);
--foreground: var(--eclipse);
/* 表面:用于非浮层组件(卡片、手风琴、折叠组等) */
--surface: var(--white);
--surface-foreground: var(--foreground);
/* 遮罩层:用于悬浮/浮层组件(工具提示、气泡、模态框、菜单) */
--overlay: var(--white);
--overlay-foreground: var(--foreground);
--muted: oklch(0.5517 0.0138 285.94);
--scrollbar: oklch(87.1% 0.006 286.286);
--default: oklch(94% 0.001 286.375);
--default-foreground: var(--eclipse);
--accent: oklch(0.6204 0.195 253.83);
--accent-foreground: var(--snow);
/* 表单字段默认值 - 颜色 */
--field-background: var(--white);
--field-foreground: oklch(0.2103 0.0059 285.89);
--field-placeholder: var(--muted);
--field-border: transparent; /* 表单字段默认无边框 */
/* 状态色 */
--success: oklch(0.7329 0.1935 150.81);
--success-foreground: var(--eclipse);
--warning: oklch(0.7819 0.1585 72.33);
--warning-foreground: var(--eclipse);
--danger: oklch(0.6532 0.2328 25.74);
--danger-foreground: var(--snow);
/* 组件颜色 */
--segment: var(--white);
--segment-foreground: var(--eclipse);
/* 杂项颜色 */
--border: oklch(92% 0.004 286.32);
--separator: oklch(92% 0.004 286.32);
--focus: var(--accent);
--link: var(--foreground);
/* 背衬 */
--backdrop: rgba(0, 0, 0, 0.5);
/* 阴影 */
--surface-shadow:
0 2px 4px 0 rgba(0, 0, 0, 0.04), 0 1px 2px 0 rgba(0, 0, 0, 0.06),
0 0 1px 0 rgba(0, 0, 0, 0.06);
/* 遮罩层阴影 */
--overlay-shadow: 0 4px 16px 0 rgba(24, 24, 27, 0.08), 0 8px 24px 0 rgba(24, 24, 27, 0.09);
--field-shadow:
0 2px 4px 0 rgba(0, 0, 0, 0.04), 0 1px 2px 0 rgba(0, 0, 0, 0.06),
0 0 1px 0 rgba(0, 0, 0, 0.06);
/* 骨架屏全局默认动画 */
--skeleton-animation: shimmer; /* shimmer, pulse, none */
/* Tooltip 默认延迟 */
--tooltip-delay: 1500ms;
--tooltip-close-delay: 500ms;
}
.dark,
[data-theme="dark"] {
color-scheme: dark;
/* == 深色主题变量 == */
/* 基础颜色 */
--background: oklch(12% 0.005 285.823);
--foreground: var(--snow);
/* 表面:用于非浮层组件(卡片、手风琴、折叠组等) */
--surface: oklch(0.2103 0.0059 285.89);
--surface-foreground: var(--foreground);
/* 遮罩层:用于悬浮/浮层组件(工具提示、气泡、模态框、菜单)——略浅于表面以提高对比度 */
--overlay: oklch(0.22 0.0059 285.89); /* 比表面色略浅,便于在深色模式下辨识 */
--overlay-foreground: var(--foreground);
--muted: oklch(70.5% 0.015 286.067);
--scrollbar: oklch(70.5% 0.015 286.067);
--default: oklch(27.4% 0.006 286.033);
--default-foreground: var(--snow);
/* 表单字段默认值 - 颜色(仅列出与浅色主题不同的项) */
--field-background: var(--default);
--field-foreground: var(--foreground);
/* 状态色 */
--warning: oklch(0.8203 0.1388 76.34);
--warning-foreground: var(--eclipse);
--danger: oklch(0.594 0.1967 24.63);
--danger-foreground: var(--snow);
/* 组件颜色 */
--segment: oklch(0.3964 0.01 285.93);
--segment-foreground: var(--foreground);
/* 杂项颜色 */
--border: oklch(22% 0.006 286.033);
--separator: oklch(22% 0.006 286.033);
--focus: var(--accent);
--link: var(--foreground);
/* 背衬 */
--backdrop: rgba(0, 0, 0, 0.6);
/* 阴影 */
--surface-shadow: 0 0 0 0 transparent inset; /* 深色模式下无阴影 */
--overlay-shadow: 0 0 0 0 transparent inset; /* 深色模式下无阴影 */
--field-shadow: 0 0 0 0 transparent inset; /* 透明阴影,以便环形工具类生效 */
}
}
```
## 自定义颜色
**覆盖已有颜色:**
```css
:root {
/* 覆盖默认颜色 */
--accent: oklch(0.7 0.15 250);
--success: oklch(0.65 0.15 155);
}
[data-theme="dark"] {
/* 覆盖深色主题颜色 */
--accent: oklch(0.8 0.12 250);
--success: oklch(0.75 0.12 155);
}
```
**提示:** 可在 [oklch.com](https://oklch.com) 转换颜色。
**添加自定义颜色:**
```css
:root,
[data-theme="light"] {
--info: oklch(0.6 0.15 210);
--info-foreground: oklch(0.98 0 0);
}
.dark,
[data-theme="dark"] {
--info: oklch(0.7 0.12 210);
--info-foreground: oklch(0.15 0 0);
}
/* 将颜色暴露给 Tailwind */
@theme inline {
--color-info: var(--info);
--color-info-foreground: var(--info-foreground);
}
```
随后即可使用:
```tsx
提示信息
```
> **注意:** 若要进一步了解主题变量及其在 Tailwind CSS v4 中的行为,请参阅 [Tailwind CSS 主题文档](https://tailwindcss.com/docs/theme)。
# 组合
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started/composition
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/(handbook)/composition.mdx
> 使用组件组合模式构建灵活的 UI
HeroUI 使用组合模式来创建灵活、可定制的组件。你可以更换渲染的元素、把组件组合在一起,并完全掌控最终的标记结构。
## 与框架无关的样式
HeroUI 的变体函数位于 `@heroui/styles` 包中,可以独立于 React 使用。这使得 Vue、Svelte 等其他框架也能使用 HeroUI 的设计系统:
```tsx
// Import directly from @heroui/styles (framework-agnostic)
import { buttonVariants } from '@heroui/styles';
// Or import from @heroui/react (re-exports the same functions)
import { buttonVariants } from '@heroui/react';
```
两种导入方式的工作原理完全相同。在为非 React 框架构建,或希望避免引入 React 依赖时,请使用 `@heroui/styles`。
## 多态样式
使用变体函数或 BEM 类,将 HeroUI 样式应用到任何元素上。可以把组件样式扩展到框架组件、原生 HTML 元素或自定义组件,并保持完整的类型安全。
**示例:将 Link 设置为按钮样式**
你可以使用 `buttonVariants` 为 Link 组件应用按钮样式:
```tsx
import { buttonVariants } from '@heroui/styles';
import Link from 'next/link';
// Style a Next.js Link as a primary button
About
// Style a native anchor as a secondary button with custom size
External Link
```
**直接使用 BEM 类:**
```tsx
import Link from 'next/link';
// Apply button styles directly using BEM classes
About
```
**配合复合组件使用**
当使用自定义根元素而非 HeroUI 的 Root 组件时,子组件无法访问 context slot。你可以使用变体函数或 BEM 类,手动将 `className` 传递给子组件:
```tsx
import { Link } from '@heroui/react';
import { linkVariants } from '@heroui/styles';
import NextLink from 'next/link';
// With custom root - pass className manually
const slots = linkVariants();
About Page
About Page
```
这种方法之所以可行,是因为 HeroUI 的变体函数和 BEM 类可以应用到任何元素上,让你能够灵活地用 HeroUI 的设计系统为框架组件、原生元素或自定义组件设置样式。
## 直接应用类名
为链接或其他元素设置样式最简单的方法,就是直接使用 HeroUI 的 [BEM](https://getbem.com/) 类。这种方法简单直接,适用于任何框架或纯 HTML。
**配合 Next.js Link 使用:**
```tsx
import Link from 'next/link';
Return Home
```
**配合原生 anchor 使用:**
```tsx
Go to Dashboard
```
**可用的按钮类名:**
* `.button` — 基础按钮样式
* `.button--primary`、`.button--secondary`、`.button--tertiary`、`.button--danger`、`.button--ghost` — 变体
* `.button--sm`、`.button--md`、`.button--lg` — 尺寸
* `.button--icon-only` — 仅图标按钮
这种方法之所以可行,是因为 HeroUI 使用了 [BEM](https://getbem.com/) 类,可以应用到任何元素上。当你不需要组件的交互功能(例如 `onPress` 事件处理器)、只想要视觉样式时,这种方式非常合适。
## 使用变体函数
如需更多控制和类型安全,可以使用变体函数将 HeroUI 样式应用到特定框架的组件或自定义元素上。`@heroui/styles`(与框架无关)和 `@heroui/react`(重新导出)都提供了变体函数。
**配合 Next.js Link 使用:**
```tsx
import { Link } from '@heroui/react';
import { linkVariants } from '@heroui/styles';
import NextLink from 'next/link';
const slots = linkVariants();
About Page
```
**配合 Button 样式:**
```tsx
import { buttonVariants } from '@heroui/styles';
import Link from 'next/link';
Dashboard
```
**可用的变体函数:** 每个组件都从 `@heroui/styles` 导出其变体函数(`buttonVariants`、`chipVariants`、`linkVariants`、`spinnerVariants` 等)。使用它们可以在保持类型安全的同时,将 HeroUI 的设计系统应用到任何元素上。
## 复合组件
HeroUI 组件以复合组件的方式构建 —— 它们会导出多个协同工作的子部件。你可以通过三种灵活的方式来使用它们:
**选项 1:复合模式(推荐)** — 直接使用主组件,无需 `.Root` 后缀:
```tsx
import { Alert } from '@heroui/react';
Success
Your changes have been saved.
```
**选项 2:使用 .Root 的复合模式** — 如果你喜欢显式命名,可以添加 `.Root` 后缀:
```tsx
import { Alert } from '@heroui/react';
Success
Your changes have been saved.
```
**选项 3:命名导出** — 单独导入每个部分:
```tsx
import {
AlertRoot,
AlertIcon,
AlertContent,
AlertTitle,
AlertDescription,
AlertClose
} from '@heroui/react';
Success
Your changes have been saved.
```
**混合语法:** 在同一组件中混合复合和命名导出:
```tsx
import { Alert, AlertTitle, AlertDescription } from '@heroui/react';
Success
Your changes have been saved.
```
**简单组件:** 像 `Button` 这样的简单组件以同样的方式工作 —— 无需 `.Root`:
```tsx
import { Button } from '@heroui/react';
// Recommended - no .Root needed
Click me
// Or with .Root
Click me
// Or named export
import { ButtonRoot } from '@heroui/react';
Click me
```
**优点:** 这三种模式都能提供灵活性、可定制性、可控性和一致性。选择最适合你代码库的那一种即可。
## 混合使用变体函数
你可以组合来自不同组件的变体函数,以创建独特的样式:
```tsx
import { Link } from '@heroui/react';
import { linkVariants, buttonVariants } from '@heroui/styles';
// Link styled with button variants
const buttonStyles = buttonVariants({ variant: "tertiary", size: "md" });
HeroUI
```
## 自定义组件
通过组合 HeroUI 原语,创建你自己的组件:
```tsx
import { Button, Tooltip } from '@heroui/react';
import { buttonVariants } from '@heroui/styles';
// Link button component using variant functions
function LinkButton({ href, children, variant = "primary", ...props }) {
return (
{children}
);
}
// Icon button with tooltip
function IconButton({ icon, label, ...props }) {
return (
{label}
);
}
```
## 自定义变体
通过扩展组件的变体函数来创建自定义变体:
```tsx
import type { ButtonRootProps } from "@heroui/react";
import type { VariantProps } from "tailwind-variants";
import { Button } from "@heroui/react";
import { buttonVariants, tv } from "@heroui/styles";
const myButtonVariants = tv({
extend: buttonVariants,
base: "text-md text-shadow-lg font-semibold shadow-md data-[pending=true]:opacity-40",
variants: {
radius: {
lg: "rounded-lg",
md: "rounded-md",
sm: "rounded-sm",
full: "rounded-full",
},
size: {
sm: "h-10 px-4",
md: "h-11 px-6",
lg: "h-12 px-8",
xl: "h-13 px-10",
},
variant: {
primary: "text-white dark:bg-white/10 dark:text-white dark:hover:bg-white/15",
},
},
defaultVariants: {
radius: "full",
variant: "primary",
},
});
type MyButtonVariants = VariantProps;
export type MyButtonProps = Omit &
MyButtonVariants & { className?: string };
function CustomButton({ className, radius, variant, ...props }: MyButtonProps) {
return ;
}
export function CustomVariants() {
return Custom Button ;
}
```
**类型引用:** 在使用组件类型时,可以使用命名类型导入或对象样式语法。
**推荐 — 命名类型导入:**
```tsx
import type { ButtonRootProps, AvatarRootProps } from "@heroui/react";
type MyButtonProps = ButtonRootProps;
type MyAvatarProps = AvatarRootProps;
```
**替代方案 — 对象样式语法:**
```tsx
import { Button, Avatar } from "@heroui/react";
type MyButtonProps = Button["RootProps"];
type MyAvatarProps = Avatar["RootProps"];
```
**注意:** 不再支持 `Button.RootProps` 这种命名空间语法。请使用 `Button["RootProps"]` 或命名导入。
## 自定义 DOM 元素
在以下组件上使用 `render` prop,可以渲染自定义组件来代替默认的 DOM 元素。
例如,你可以渲染一个 [Motion](https://motion.dev/) 按钮,并利用其状态来驱动动画。
```tsx
import {Button} from '@heroui/react';
import {motion} from 'motion/react';
(
)}>
Press me
```
`render` prop 对于从客户端路由库渲染链接组件,或复用已有的展示型组件也很有用。
```tsx
import {Link} from '@heroui/react';
import NextLink from 'next/link';
(
} href="/privacy-policy" />
)}
>
Privacy Policy
```
请遵循以下规则,以免破坏组件的行为和无障碍能力:
* 始终渲染期望的元素类型(例如,如果期望的是 ``,就不要渲染 ``)。如果检测到不匹配,开发期间你会看到警告。
* 只渲染单个根 DOM 元素(不要使用 fragment)。
* 始终将传入的 props 传递给底层 DOM 元素,并根据需要通过 `mergeProps` 与你自己的 props 合并。
## 框架集成
**配合 Next.js 使用:**
使用变体函数获得类型安全的样式:
```tsx
import { buttonVariants } from '@heroui/styles';
import Link from 'next/link';
Dashboard
```
或者直接应用 BEM 类(最简单):
```tsx
import Link from 'next/link';
Dashboard
```
**配合 React Router 使用:**
使用变体函数:
```tsx
import { buttonVariants } from '@heroui/styles';
import { Link } from 'react-router-dom';
Dashboard
```
或者直接应用 BEM 类(最简单):
```tsx
import { Link } from 'react-router-dom';
Dashboard
```
**配合 Vue、Svelte 或其他框架使用:**
由于 `@heroui/styles` 没有 React 依赖,你可以直接在任何框架中使用它:
```vue
Click me
```
## 下一步
* 了解组件的 [样式](/docs/handbook/styling)
* 探索 [动画](/docs/handbook/animation) 选项
* 浏览 [组件](/docs/react/components) 获取更多示例
# 深色模式
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started/dark-mode
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/(handbook)/dark-mode.mdx
> 在 HeroUI v3 中添加浅色、深色以及跟随系统的主题切换
HeroUI 的深色模式由 CSS 驱动。组件会从根元素读取主题变量,因此你不需要使用 HeroUI Provider。只需在 `` 上添加 `dark` 类或 `data-theme="dark"`,HeroUI 就会应用深色主题。
```html
```
请保持你的应用根元素上有 `bg-background` 与 `text-foreground` 类,这样页面画布才会随着当前主题变化。
HeroUI 内置的浅色和深色主题同时响应 `.light` / `.dark` 类以及 `data-theme="light"` / `data-theme="dark"` 属性。如果你手动同时设置两者,请确保它们的值保持一致。
## 在 Next.js 中使用 next-themes
当你在 Next.js 应用中需要主题持久化、系统偏好支持,并希望在水合(hydration)前不出现闪烁时,请使用 [next-themes](https://github.com/pacocoursey/next-themes)。
### 安装 next-themes
```bash
npm i next-themes
```
```bash
pnpm add next-themes
```
```bash
yarn add next-themes
```
```bash
bun add next-themes
```
### App Router
为 `next-themes` 创建一个客户端 Provider。
```tsx
// app/providers.tsx
"use client";
import {ThemeProvider as NextThemesProvider} from "next-themes";
export function Providers({children}: {children: React.ReactNode}) {
return (
{children}
);
}
```
在根布局中用该 Provider 包裹你的应用。请在 `` 上添加 `suppressHydrationWarning`,因为 `next-themes` 会在水合之前更新该元素。
```tsx
// app/layout.tsx
import "./globals.css";
import {Providers} from "./providers";
export default function RootLayout({children}: {children: React.ReactNode}) {
return (
{children}
);
}
```
### 主题切换器
在客户端组件中使用 `next-themes` 提供的 `useTheme`。请等到组件挂载后再渲染,因为在 SSR 期间无法得知当前激活的主题。
```tsx
// app/components/theme-switcher.tsx
"use client";
import {Button} from "@heroui/react";
import {useTheme} from "next-themes";
import {useEffect, useState} from "react";
export function ThemeSwitcher() {
const [mounted, setMounted] = useState(false);
const {resolvedTheme, setTheme, theme} = useTheme();
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) return null;
const activeTheme = theme === "system" ? resolvedTheme : theme;
return (
setTheme("light")}
>
Light
setTheme("dark")}
>
Dark
setTheme("system")}>
System
);
}
```
### Pages Router
如果使用 `pages/`,请在 `pages/_app.tsx` 中包裹你的应用。
```tsx
// pages/_app.tsx
import "@/styles/globals.css";
import type {AppProps} from "next/app";
import {ThemeProvider as NextThemesProvider} from "next-themes";
export default function App({Component, pageProps}: AppProps) {
return (
);
}
```
## 使用自定义主题名称
`attribute="class"` 这套配置非常适合内置的 `light` 和 `dark` 主题。如果你的自定义主题 CSS 是基于 `data-theme` 选择器编写的,请改为让 `next-themes` 写入 `data-theme`。
```tsx
{children}
```
当你传入自定义的 `themes` 列表时,如果仍然希望保留内置主题,请将 `"light"` 和 `"dark"` 一并包含进去。
## 在 React 中使用 useTheme
当你正在构建一个普通的 React 应用(例如 Vite 或 Create React App),并且不需要 `next-themes` 时,可以使用 HeroUI 提供的 `useTheme` 钩子。
该钩子从 `@heroui/react` 中导出。它会将当前选择的主题保存到 `localStorage`,根据用户的操作系统偏好解析 `"system"`,并同时把对应的类与 `data-theme` 属性应用到 `` 上。
```tsx
// src/components/theme-switcher.tsx
import {Button, useTheme} from "@heroui/react";
export function ThemeSwitcher() {
const {resolvedTheme, setTheme, theme} = useTheme("system");
return (
setTheme("light")}
>
Light
setTheme("dark")}
>
Dark
setTheme("system")}>
System
);
}
```
每个应用只应使用一个主题控制器。在 Next.js 中,推荐使用 `next-themes` 及其 `useTheme` 钩子;在普通的 React 应用中,请使用 `@heroui/react` 提供的 `useTheme`。
## 同时为两种主题设置样式
主题相关的工具类会自动生效,因为它们读取的是 CSS 变量:
```tsx
```
对于仅在深色模式下生效的一次性样式调整,请使用 `dark:` 变体:
```tsx
Custom dark-mode adjustment
```
# 样式
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started/styling
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/(handbook)/styling.mdx
> 使用 CSS、Tailwind 或 CSS-in-JS 为 HeroUI 组件设置样式
HeroUI 组件提供灵活的样式方案:Tailwind CSS 工具类、配合 [BEM](https://getbem.com/) 类名或数据属性的 CSS、CSS-in-JS 库,以及用于动态样式的渲染属性。
## 基础样式
**使用 className:** 所有 HeroUI 组件都接受 `className` 属性:
```tsx
Custom Button
{/* content */}
```
**使用 style:** 组件也接受内联样式:
```tsx
Styled Button
```
## 滚动条
HeroUI 的滚动插槽在组件 CSS 中使用 `@apply scrollbar`。对于你自己的溢出容器,可使用 `@heroui/styles` 提供的工具类:
```tsx
{/* 长内容 */}
```
| 工具类 | 效果 |
| ------------------- | -------------------------------------- |
| `scrollbar` | HeroUI 滚动条滑块(读取主题的 `--scrollbar-*` 变量) |
| `scrollbar-thin` | HeroUI 主题化纤细滚动条 |
| `scrollbar-default` | 操作系统 / 浏览器默认滚动条 |
| `scrollbar-none` | 隐藏滚动条 |
全局及子树级别的控制可通过在祖先元素上使用 `data-scrollbar` 实现。有关令牌(tokens)和模式的更多信息,请参阅 [主题](/docs/handbook/theming#scrollbars)。
## 基于状态的样式
HeroUI 组件通过数据属性公开其状态,类似于 CSS 伪类:
```css
/* Target different states */
.button[data-hovered="true"], .button:hover {
background: var(--accent-hover);
}
.button[data-pressed="true"], .button:active {
transform: scale(0.97);
}
.button[data-focus-visible="true"], .button:focus-visible {
outline: 2px solid var(--focus);
}
```
## 渲染属性
根据组件状态动态应用样式:
```tsx
// Dynamic classes
isPressed ? 'bg-blue-600' : 'bg-blue-500'
}
>
Press me
// Dynamic content
{({ isHovered, isPressed }) => (
<>
Like
>
)}
```
## BEM 类名
HeroUI 使用 [BEM 方法论](https://getbem.com/) 来保持类名命名的一致性:
```css
/* Block */
.button { }
.accordion { }
/* Element */
.accordion__trigger { }
.accordion__panel { }
/* Modifier */
.button--primary { }
.button--lg { }
.accordion--outline { }
```
**全局自定义组件:**
```css
/* global.css */
@layer components {
/* Override button styles */
.button {
@apply font-semibold uppercase;
}
.button--primary {
@apply bg-indigo-600 hover:bg-indigo-700;
}
/* Add custom variant */
.button--gradient {
@apply bg-gradient-to-r from-purple-500 to-pink-500;
}
}
```
## 创建包装组件
使用 [tailwind-variants](https://tailwind-variants.org/) 创建可复用的自定义组件 —— 它是 Tailwind CSS 的一等公民变体 API:
```tsx
import { Button as HeroButton, type ButtonProps } from '@heroui/react';
import { buttonVariants, tv, type VariantProps } from '@heroui/styles';
const customButtonVariants = tv({
extend: buttonVariants,
base: 'font-medium transition-all',
variants: {
intent: {
primary: 'bg-blue-500 hover:bg-blue-600 text-white',
secondary: 'bg-gray-200 hover:bg-gray-300',
danger: 'bg-red-500 hover:bg-red-600 text-white',
},
size: {
small: 'text-sm px-2 py-1',
medium: 'text-base px-4 py-2',
large: 'text-lg px-6 py-3',
},
},
defaultVariants: {
intent: 'primary',
size: 'medium',
},
});
type CustomButtonVariants = VariantProps;
interface CustomButtonProps
extends Omit,
CustomButtonVariants {
className?: string;
}
export function CustomButton({ intent, size, className, ...props }: CustomButtonProps) {
return (
);
}
```
## CSS-in-JS 集成
**Styled Components:**
```tsx
import styled from 'styled-components';
import { Button } from '@heroui/react';
const StyledButton = styled(Button)`
background: linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%);
border-radius: 8px;
color: white;
padding: 12px 24px;
&:hover {
box-shadow: 0 3px 10px rgba(255, 105, 135, 0.3);
}
`;
```
**Emotion:**
```tsx
import { css } from '@emotion/css';
import { Button } from '@heroui/react';
const buttonStyles = css`
background: linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%);
border-radius: 8px;
color: white;
padding: 12px 24px;
&:hover {
box-shadow: 0 3px 10px rgba(255, 105, 135, 0.3);
}
`;
Emotion Button
```
## 响应式设计
**使用 Tailwind 工具类:**
```tsx
Responsive Button
```
**或者使用 CSS:**
```css
.button {
font-size: 0.875rem;
padding: 0.5rem 1rem;
}
@media (min-width: 768px) {
.button {
font-size: 1rem;
padding: 0.75rem 1.5rem;
}
}
```
## CSS 模块
如需作用域化的样式,可以使用 CSS 模块:
```css
/* Button.module.css */
.button {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 12px 24px;
border-radius: 8px;
}
.button:hover {
transform: translateY(-2px);
}
.button--primary {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 12px 24px;
border-radius: 8px;
}
```
```tsx
import styles from './Button.module.css';
import { Button } from '@heroui/react';
Scoped Button
```
## 组件类名参考
**Button:** `.button`、`.button--{variant}`、`.button--{size}`、`.button--icon-only`
**Accordion:** `.accordion`、`.accordion__item`、`.accordion__trigger`、`.accordion__panel`、`.accordion--outline`
> **注意:** 完整的类名参考请查看各组件文档:[Button](/docs/components/button#css-classes)、[Accordion](/docs/components/accordion#css-classes)
所有组件的类名请查看 [@heroui/styles/components](https://github.com/heroui-inc/heroui/tree/main/packages/styles/components)。
## 下一步
* 了解 [动画](/docs/handbook/animation) 相关技巧
* 探索 [主题](/docs/handbook/theming) 系统
* 浏览 [组件](/docs/react/components) 示例
# 主题
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started/theming
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/(handbook)/theming.mdx
> 使用 CSS 变量和全局样式自定义 HeroUI 的设计系统
HeroUI 使用 CSS 变量和 [BEM](https://getbem.com/) 类来实现主题化。你可以使用标准 CSS 自定义从颜色到组件样式的所有内容。
**想要创建你自己的主题?** 试试 [主题构建器](/themes),以可视化方式自定义颜色、圆角、字体等,然后导出 CSS 用于你的项目。
## 工作原理
HeroUI 的主题系统构建于 [Tailwind CSS v4](https://tailwindcss.com/docs/theme) 的主题之上。当你导入 `@heroui/styles` 时,它会使用 Tailwind 的内置调色板,将其映射到语义化变量,自动在浅色和深色主题之间切换,并使用 CSS 层和 `@theme` 指令进行组织。
**命名规则:**
* 不带后缀的颜色用作背景(例如 `--accent`)
* 带 `-foreground` 后缀的颜色用于该背景上的文本(例如 `--accent-foreground`)
## 快速开始
**应用主题:** 将主题类添加到 HTML 并将颜色应用到 body 上:
```html
```
**切换主题:**
```html
```
**使用 [next-themes](https://github.com/pacocoursey/next-themes) 以编程方式切换主题(适用于 Next.js):**
首先,用 `ThemeProvider` 包装你的应用:
```tsx
// app/providers.tsx
"use client";
import { ThemeProvider } from "next-themes";
export function Providers({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
```tsx
// app/layout.tsx
import { Providers } from "./providers";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
然后使用 `useTheme` 在主题之间切换:
```tsx
"use client";
import { useTheme } from "next-themes";
export function ThemeSwitch() {
const { theme, setTheme } = useTheme();
return (
setTheme(theme === "dark" ? "light" : "dark")}>
Toggle {theme === "dark" ? "Light" : "Dark"} Mode
);
}
```
**覆盖颜色:**
```css
/* app/globals.css */
@import "tailwindcss";
@import "@heroui/styles";
:root {
/* Override any color variable */
--accent: oklch(0.7 0.25 260);
--success: oklch(0.65 0.15 155);
}
```
> **注意**:完整的调色板和视觉参考请参见 [颜色](/docs/handbook/colors)。
> **深色模式**:如需基于 `next-themes` 与 HeroUI `useTheme` 钩子的完整配置指南,请参见 [深色模式](/docs/handbook/dark-mode)。
**创建你自己的主题:**
```css
/* src/themes/ocean.css */
@layer base {
/* Ocean Light */
[data-theme="ocean"] {
color-scheme: light;
/* Primitive Colors (Do not change between light and dark) */
--white: oklch(100% 0 0);
--black: oklch(0% 0 0);
--snow: oklch(0.9911 0 0);
--eclipse: oklch(0.2103 0.0059 285.89);
/* Spacing & Layout */
--spacing: 0.25rem;
--border-width: 1px;
--field-border-width: 0px;
--disabled-opacity: 0.5;
--ring-offset-width: 2px;
--cursor-interactive: pointer;
--cursor-disabled: not-allowed;
/* Radius */
--radius: 0.75rem;
--field-radius: calc(var(--radius) * 1.5);
/* Base Colors */
--background: oklch(0.985 0.015 225);
--foreground: var(--eclipse);
/* Surface: Used for non-overlay components */
--surface: var(--white);
--surface-foreground: var(--foreground);
/* Overlay: Used for floating/overlay components */
--overlay: var(--white);
--overlay-foreground: var(--foreground);
--muted: oklch(0.5517 0.0138 285.94);
--scrollbar-thumb: color-mix(in oklch, var(--foreground) 15%, transparent);
--scrollbar-track: transparent;
--scrollbar-gutter: auto;
--scrollbar-width: thin;
--scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
--scrollbar: var(--scrollbar-thumb);
--default: oklch(94% 0.001 286.375);
--default-foreground: var(--eclipse);
/* Ocean accent */
--accent: oklch(0.450 0.150 230);
--accent-foreground: var(--snow);
/* Form Field Defaults */
--field-background: var(--white);
--field-foreground: oklch(0.2103 0.0059 285.89);
--field-placeholder: var(--muted);
--field-border: transparent;
/* Status (kept compatible) */
--success: oklch(0.7329 0.1935 150.81);
--success-foreground: var(--eclipse);
--warning: oklch(0.7819 0.1585 72.33);
--warning-foreground: var(--eclipse);
--danger: oklch(0.6532 0.2328 25.74);
--danger-foreground: var(--snow);
/* Component Colors */
--segment: var(--white);
--segment-foreground: var(--foreground);
/* Misc */
--border: oklch(0.50 0.060 230 / 22%);
--separator: oklch(92% 0.004 286.32);
--focus: var(--accent);
--link: var(--accent);
/* Shadows */
--surface-shadow:
0 2px 4px 0 rgba(0, 0, 0, 0.04), 0 1px 2px 0 rgba(0, 0, 0, 0.06),
0 0 1px 0 rgba(0, 0, 0, 0.06);
--overlay-shadow: 0 4px 16px 0 rgba(24, 24, 27, 0.08), 0 8px 24px 0 rgba(24, 24, 27, 0.09);
--field-shadow:
0 2px 4px 0 rgba(0, 0, 0, 0.04), 0 1px 2px 0 rgba(0, 0, 0, 0.06),
0 0 1px 0 rgba(0, 0, 0, 0.06);
/* Skeleton Default Global Animation */
--skeleton-animation: shimmer; /* Possible values: shimmer, pulse, none */
/* Tooltip Default Delays */
--tooltip-delay: 1500ms;
--tooltip-close-delay: 500ms;
}
/* Ocean Dark */
[data-theme="ocean-dark"] {
color-scheme: dark;
/* Base Colors */
--background: oklch(0.140 0.020 230);
--foreground: var(--snow);
/* Surface: Used for non-overlay components */
--surface: oklch(0.2103 0.0059 285.89);
--surface-foreground: var(--foreground);
/* Overlay: Used for floating/overlay components */
--overlay: oklch(0.22 0.0059 285.89);
--overlay-foreground: var(--foreground);
--muted: oklch(70.5% 0.015 286.067);
--scrollbar-thumb: color-mix(in oklch, var(--foreground) 15%, transparent);
--scrollbar-track: transparent;
--scrollbar-gutter: auto;
--scrollbar-width: thin;
--scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
--scrollbar: var(--scrollbar-thumb);
--default: oklch(27.4% 0.006 286.033);
--default-foreground: var(--snow);
/* Form Field Defaults */
--field-background: var(--default);
--field-foreground: var(--foreground);
/* Ocean accent */
--accent: oklch(0.860 0.080 230);
--accent-foreground: var(--eclipse);
/* Status */
--success: oklch(0.7329 0.1935 150.81);
--success-foreground: var(--eclipse);
--warning: oklch(0.8203 0.1388 76.34);
--warning-foreground: var(--eclipse);
--danger: oklch(0.594 0.1967 24.63);
--danger-foreground: var(--snow);
/* Component Colors */
--segment: oklch(0.3964 0.01 285.93);
--segment-foreground: var(--foreground);
/* Misc */
--border: oklch(22% 0.006 286.033);
--separator: oklch(22% 0.006 286.033);
--focus: var(--accent);
--link: var(--accent);
/* Shadows */
--surface-shadow: 0 0 0 0 transparent inset;
--overlay-shadow: 0 0 0 0 transparent inset;
--field-shadow: 0 0 0 0 transparent inset;
}
}
```
使用你的主题:
```css
/* app/globals.css */
@layer theme, base, components, utilities;
@import "tailwindcss";
@import "@heroui/styles";
@import "./src/themes/ocean.css" layer(theme); /* [!code highlight]*/
```
应用你的主题:
```html
```
## 自定义组件
**全局组件样式:** 使用 BEM 类覆盖任何组件:
```css
@layer components {
/* Customize buttons */
.button {
@apply font-semibold tracking-wide;
}
.button--primary {
@apply bg-blue-600 hover:bg-blue-700;
}
/* Customize accordions */
.accordion__trigger {
@apply text-lg font-bold;
}
}
```
> **注意**:完整的样式参考请参见 [样式](/docs/handbook/styling)。
**查找组件类名:** 每个组件文档页面都会列出所有可用的类名(基类、修饰符、元素、状态)。示例:[Button 类名](/docs/components/button#css-classes)
## 导入策略
**完整导入(推荐):** 两行代码即可获得全部内容:
```css
@import "tailwindcss";
@import "@heroui/styles";
```
**按需导入:** 只导入你需要的内容:
```css
/* Define layers */
@layer theme, base, components, utilities;
/* Base requirements */
@import "tailwindcss";
@import "@heroui/styles/base" layer(base);
/* OR specific base file */
@import "@heroui/styles/base/base.css" layer(base);
/* Theme variables */
@import "@heroui/styles/themes/shared/theme.css" layer(theme);
@import "@heroui/styles/themes/default" layer(theme);
/* OR specific theme files */
@import "@heroui/styles/themes/default/index.css" layer(theme);
@import "@heroui/styles/themes/default/variables.css" layer(theme);
/* Components (all components) */
@import "@heroui/styles/components" layer(components);
/* OR specific component files */
@import "@heroui/styles/components/index.css" layer(components);
@import "@heroui/styles/components/button.css" layer(components);
@import "@heroui/styles/components/accordion.css" layer(components);
/* Utilities (optional) */
@import "@heroui/styles/utilities" layer(utilities);
/* Variants (optional) */
@import "@heroui/styles/variants" layer(utilities);
```
> **注意**:目录导入(例如 `@heroui/styles/components`)会自动解析为其对应的 `index.css` 文件。使用显式文件路径(例如 `@heroui/styles/components/button.css`)来导入单个组件的样式。
**Headless 模式:** 从头开始构建你自己的样式:
```css
@import "tailwindcss";
@import "@heroui/styles/base/base.css";
/* Your custom styles */
.button {
/* Your button styles */
}
```
## 添加自定义颜色
将你自己的语义化颜色添加到主题中:
```css
/* Define in both light and dark themes */
:root,
[data-theme="light"] {
--info: oklch(0.6 0.15 210);
--info-foreground: oklch(0.98 0 0);
}
.dark,
[data-theme="dark"] {
--info: oklch(0.7 0.12 210);
--info-foreground: oklch(0.15 0 0);
}
/* Make the color available to Tailwind */
@theme inline {
--color-info: var(--info);
--color-info-foreground: var(--info-foreground);
}
```
现在你可以在组件中使用它:
```tsx
Info message
```
## 变量参考
HeroUI 在 [`variables.css`](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/themes/default/variables.css) 中定义了三种类型的变量:
1. **基础变量(Base Variables)** — 不会变化的值,例如 `--white`、`--black`、间距以及排版
2. **主题变量(Theme Variables)** — 在浅色 / 深色主题之间切换的颜色,以及滚动条令牌(`--scrollbar-thumb`、`--scrollbar-width` 等)
3. **计算变量(Calculated Variables)** — 悬停状态、柔和(soft)变体以及边框 / 分隔线层级(每个浅色 / 深色主题中的 **Calculated Colors** 区块,使用 `color-mix()` 计算)
如需完整参考,请查阅:[颜色文档](/docs/handbook/colors)、[默认主题变量](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/themes/default/variables.css)、[共享主题工具](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/themes/shared/theme.css)
**Tailwind 主题桥接(`@theme inline`):**
[`themes/shared/theme.css`](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/themes/shared/theme.css) 将语义化变量映射为 Tailwind 令牌(`--color-*`、`--radius-*`、`--ease-*`)。计算颜色引用的是 `variables.css` 中的底层变量(例如 `--surface-hover`、`--accent-soft`)—— 它们并不会在该文件中通过 `color-mix()` 内联展开:
```css
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-surface: var(--surface);
--color-surface-foreground: var(--surface-foreground);
--color-surface-hover: var(--surface-hover);
--color-surface-secondary: var(--surface-secondary);
--color-surface-secondary-foreground: var(--surface-secondary-foreground);
--color-surface-tertiary: var(--surface-tertiary);
--color-surface-tertiary-foreground: var(--surface-tertiary-foreground);
--color-overlay: var(--overlay);
--color-overlay-foreground: var(--overlay-foreground);
--color-muted: var(--muted);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-segment: var(--segment);
--color-segment-foreground: var(--segment-foreground);
--color-border: var(--border);
--color-separator: var(--separator);
--color-focus: var(--focus);
--color-link: var(--link);
--color-default: var(--default);
--color-default-foreground: var(--default-foreground);
--color-success: var(--success);
--color-success-foreground: var(--success-foreground);
--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);
--color-danger: var(--danger);
--color-danger-foreground: var(--danger-foreground);
--color-backdrop: var(--backdrop);
--shadow-surface: var(--surface-shadow);
--shadow-overlay: var(--overlay-shadow);
--shadow-field: var(--field-shadow);
/* 表单字段令牌 */
--color-field: var(--field-background, var(--default));
--color-field-hover: var(--field-hover);
--color-field-foreground: var(--field-foreground, var(--foreground));
--color-field-placeholder: var(--field-placeholder, var(--muted));
--color-field-border: var(--field-border, var(--border));
--radius-field: var(--field-radius, calc(var(--radius) * 1.5));
--border-width-field: var(--field-border-width, var(--border-width));
/* 颜色令牌 */
--color-background-secondary: var(--background-secondary);
--color-background-tertiary: var(--background-tertiary);
--color-background-inverse: var(--background-inverse);
--color-default-hover: var(--default-hover);
--color-accent-hover: var(--accent-hover);
--color-success-hover: var(--success-hover);
--color-warning-hover: var(--warning-hover);
--color-danger-hover: var(--danger-hover);
/* 表单字段颜色 */
--color-field-focus: var(--field-focus);
--color-field-border-hover: var(--field-border-hover);
--color-field-border-focus: var(--field-border-focus);
/* 柔和(Soft)颜色 */
--color-default-soft: var(--default-soft);
--color-default-soft-foreground: var(--default-soft-foreground);
--color-default-soft-hover: var(--default-soft-hover);
--color-accent-soft: var(--accent-soft);
--color-accent-soft-foreground: var(--accent-soft-foreground);
--color-accent-soft-hover: var(--accent-soft-hover);
--color-danger-soft: var(--danger-soft);
--color-danger-soft-foreground: var(--danger-soft-foreground);
--color-danger-soft-hover: var(--danger-soft-hover);
--color-warning-soft: var(--warning-soft);
--color-warning-soft-foreground: var(--warning-soft-foreground);
--color-warning-soft-hover: var(--warning-soft-hover);
--color-success-soft: var(--success-soft);
--color-success-soft-foreground: var(--success-soft-foreground);
--color-success-soft-hover: var(--success-soft-hover);
/* 分隔线颜色 - 层级 */
--color-separator-secondary: var(--separator-secondary);
--color-separator-tertiary: var(--separator-tertiary);
/* 边框颜色 - 层级 */
--color-border-secondary: var(--border-secondary);
--color-border-tertiary: var(--border-tertiary);
/* 圆角与默认尺寸 - 只需更改 --radius 即可调整默认值 */
--radius-xs: calc(var(--radius) * 0.25); /* 0.125rem (2px) */
--radius-sm: calc(var(--radius) * 0.5); /* 0.25rem (4px) */
--radius-md: calc(var(--radius) * 0.75); /* 0.375rem (6px) */
--radius-lg: calc(var(--radius) * 1); /* 0.5rem (8px) */
--radius-xl: calc(var(--radius) * 1.5); /* 0.75rem (12px) */
--radius-2xl: calc(var(--radius) * 2); /* 1rem (16px) */
--radius-3xl: calc(var(--radius) * 3); /* 1.5rem (24px) */
--radius-4xl: calc(var(--radius) * 4); /* 2rem (32px) */
/* 过渡缓动函数 */
--ease-smooth: ease; /* 等同于 transition: ease; */
/* 这些自定义曲线由 https://twitter.com/bdc 制作 */
/* 由平缓到快速 */
--ease-in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53);
--ease-in-cubic: cubic-bezier(0.55, 0.055, 0.675, 0.19);
--ease-in-quart: cubic-bezier(0.895, 0.03, 0.685, 0.22);
--ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06);
--ease-in-expo: cubic-bezier(0.95, 0.05, 0.795, 0.035);
--ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.335);
/* 由慢到快 */
--ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
--ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);
--ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1);
--ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
--ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);
--ease-out-circ: cubic-bezier(0.075, 0.82, 0.165, 1);
/* 自定义的平滑收尾曲线:快速启动、平滑结束 —— Apple 风格 */
--ease-out-fluid: cubic-bezier(0.32, 0.72, 0, 1);
/* 由慢到快 */
--ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955);
--ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1);
--ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1);
--ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1);
--ease-in-out-expo: cubic-bezier(1, 0, 0, 1);
--ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
/* 线性 */
--ease-linear: linear;
/* 动画 */
--animate-spin-fast: spin 0.75s linear infinite;
--animate-skeleton: skeleton 2s linear infinite;
--animate-caret-blink: caret-blink 1.2s ease-out infinite;
@keyframes skeleton {
100% {
transform: translateX(200%);
}
}
@keyframes caret-blink {
0%,
70%,
100% {
opacity: 1;
}
20%,
50% {
opacity: 0;
}
}
}
```
表单控件依赖 `--field-*` 主题变量。悬停、聚焦及边框变体定义于 `variables.css` 的 **Calculated Colors** 区块中,并在 `theme.css` 中映射到 Tailwind 令牌(例如 `--color-field-hover: var(--field-hover)`)。在你的主题中覆盖 `--field-background`、`--field-hover` 及相关令牌,即可重新设计输入框、复选框、单选框和 OTP 输入槽的样式,而不会影响按钮或卡片等表面(surface)。
## 滚动条
HeroUI 为组件中的滚动区域(表格、弹出框、抽屉等)应用统一的滚动条样式。滚动条使用标准 CSS 属性(`scrollbar-width`、`scrollbar-color`、`scrollbar-gutter`),不再依赖 `::-webkit-scrollbar` 覆盖。
**模式** — 在 ``、组件根元素或滚动插槽上设置 `data-scrollbar`:
| 模式 | `data-scrollbar` | 行为 |
| ---------- | ---------------- | ------------------------------- |
| HeroUI 纤细 | *(未设置)* 或 `thin` | 使用主题令牌呈现的纤细滑块 |
| 操作系统 / 浏览器 | `default` | 原生滚动条(`auto`) |
| 隐藏 | `none` | 不显示滚动条(`scrollbar-width: none`) |
```html
...
...
```
**主题变量** — 在 [`variables.css`](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/themes/default/variables.css) 的浅色与深色主题区块中定义:
| 变量 | 描述 |
| -------------------- | ---------------------------------------------- |
| `--scrollbar-thumb` | 滑块颜色(默认通过 `color-mix` 混入 15% 的 `--foreground`) |
| `--scrollbar-track` | 轨道颜色(默认 `transparent`) |
| `--scrollbar-gutter` | 滚动条间隙(默认 `auto`) |
| `--scrollbar-width` | `scrollbar-width` 属性(默认 `thin`) |
| `--scrollbar-color` | `scrollbar-color` 属性(默认为滑块颜色 + 轨道颜色) |
| `--scrollbar` | `--scrollbar-thumb` 的旧版别名 |
**全局自定义:**
```css
/* app/globals.css */
:root {
--scrollbar-thumb: color-mix(in oklch, var(--accent) 30%, transparent);
--scrollbar-gutter: auto;
}
```
**按滚动插槽** — 在组件上传入 `data-scrollbar`,或在外层包裹元素上覆盖相关令牌:
```tsx
```
**自定义溢出区域** — 在自己的元素上使用 `@heroui/styles` 提供的 `scrollbar`、`scrollbar-thin`、`scrollbar-default` 或 `scrollbar-none` 工具类。基于类的覆盖方式详见 [样式](/docs/handbook/styling)。
> **注意**:部分组件默认隐藏滚动条(日期选择器弹出框、颜色选择器、次级标签页)。嵌套的滚动插槽(例如日期选择器内部的日历年份选择器)会保留 HeroUI 滚动条,因为 `scrollbar-none` 只作用于其所在的元素本身,不会影响使用 `@apply scrollbar` 的后代元素。
## 资源
* [颜色文档](/docs/handbook/colors)
* [样式指南](/docs/handbook/styling)
* [Tailwind CSS v4 主题](https://tailwindcss.com/docs/theme)
* [BEM 方法论](https://getbem.com/)
* [OKLCH 颜色工具](https://oklch.com)
# 命令行
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started/cli
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/(overview)/cli.mdx
> 使用命令行管理 HeroUI 依赖并初始化项目。
`heroui-cli`是官方命令行工具,提供了一整套命令,用于初始化、管理和优化你的 HeroUI 项目。你可以 `install`、`uninstall` 或 `upgrade` 单个组件,检查项目健康状况,下载用于 AI 编程助手的文档,等等。
## 安装
环境要求:
* [Node.js 22.22.0 或更高版本](https://nodejs.org/en/)
### 全局安装
要全局安装 `heroui-cli`,请在终端中执行以下命令之一:
npm
pnpm
yarn
bun
```bash
npm install heroui-cli@latest -g
```
```bash
pnpm add heroui-cli@latest -g
```
```bash
yarn global add heroui-cli@latest
```
```bash
bun add heroui-cli@latest --global
```
### 无需安装直接使用
或者,你也可以在不进行全局安装的情况下使用 `heroui-cli`,运行以下命令之一即可:
```bash
pnpm dlx heroui-cli@latest
```
```bash
npx heroui-cli@latest
```
```bash
yarn dlx heroui-cli@latest
```
```bash
bunx heroui-cli@latest
```
## 快速开始
安装 `heroui-cli` 后,运行以下命令以查看可用命令:
```bash
heroui
```
将会输出如下帮助信息:
```bash
Usage: heroui [command]
Options:
-v, --version Output the current version
--no-cache Disable cache, by default data will be cached for 30m after the first request
-d, --debug Debug mode will not install dependencies
-h --help Display help information for commands
Commands:
init [options] [projectName] Initializes a new project
install [options] Installs @heroui/react and @heroui/styles to your project
upgrade [options] Upgrades @heroui/react and @heroui/styles to the latest versions
uninstall [options] Uninstall @heroui/react and @heroui/styles from the project
list [options] Lists installed HeroUI packages (@heroui/react, @heroui/styles)
env [options] Displays debugging information for the local environment
doctor [options] Checks for issues in the project
agents-md [options] Downloads HeroUI documentation for AI coding agents
help [command] Display help for command
```
### init
使用 `init` 命令初始化一个新的 HeroUI 项目。该命令会为你的项目完成必要的配置。
```bash
heroui init [options]
```
**选项:**
* `-t --template [string]` 新项目使用的模板,例如 app、pages、vite
* `-p --package [string]` 新项目使用的包管理器
输出:
```bash
HeroUI CLI
┌ Create a new project
│
◇ Select a template (Enter to select)
│ ● App (A Next.js 16 with app directory template pre-configured with HeroUI (v3) and Tailwind CSS.)
│ ○ Pages (A Next.js 16 with pages directory template pre-configured with HeroUI (v3) and Tailwind CSS.)
│ ○ Vite (A Vite template pre-configured with HeroUI (v3) and Tailwind CSS.)
│
◇ New project name (Enter to skip with default name)
│ my-heroui-app
│
◇ Select a package manager (Enter to select)
│ ● npm
│ ○ yarn
│ ○ pnpm
│ ○ bun
│
◇ Template created successfully!
│
◇ Next steps ───────╮
│ │
│ cd my-heroui-app │
│ npm install │
│ │
├────────────────────╯
│
└ 🚀 Get started with npm run dev
```
安装依赖以启动本地服务器:
```bash
cd my-heroui-app && npm install
```
```bash
cd my-heroui-app && pnpm install
```
```bash
cd my-heroui-app && yarn install
```
```bash
cd my-heroui-app && bun install
```
启动本地服务器:
npm
pnpm
yarn
bun
```bash
npm run dev
```
```bash
pnpm run dev
```
```bash
yarn dev
```
```bash
bun run dev
```
### Install
将 `@heroui/react` 和 `@heroui/styles` 及其对等依赖安装到你的项目中。若它们已安装,则该命令不会执行任何操作。
```bash
heroui install [options]
```
**选项:**
* `-p --packagePath` \[string] package.json 文件的路径
**输出:**
```bash
HeroUI CLI
📦 Packages to be installed:
╭─────────────────────────────────────────────────────────────────────────────╮
│ Package │ Version │ Status │ Docs │
│─────────────────────────────────────────────────────────────────────────────│
│ @heroui/react │ 3.0.0 │ stable │ https://heroui.com │
│ @heroui/styles │ 3.0.0 │ stable │ https://heroui.com │
╰─────────────────────────────────────────────────────────────────────────────╯
╭─────────────── PeerDependencies ────────────────╮
│ react@18.3.1 latest │
│ react-dom@18.3.1 latest │
│ tailwindcss@4.2.2 latest │
╰─────────────────────────────────────────────────╯
? Proceed with installation? › - Use arrow-keys. Return to submit.
❯ Yes
No
✅ @heroui/react and @heroui/styles installed successfully
```
### upgrade
将 `@heroui/react` 和 `@heroui/styles` 及其对等依赖升级到最新版本。
```bash
heroui upgrade [options]
```
**选项:**
* `-p --packagePath` \[string] package.json 文件的路径
**输出:**
```bash
HeroUI CLI
╭──────────────────────────── Upgrade ────────────────────────────╮
│ @heroui/react ^3.0.0 -> ^3.1.0 │
│ @heroui/styles ^3.0.0 -> ^3.1.0 │
╰─────────────────────────────────────────────────────────────────╯
? Would you like to proceed with the upgrade? › - Use arrow-keys. Return to submit.
❯ Yes
No
✅ Upgrade complete. All packages are up to date.
```
### uninstall
从你的项目中卸载 `@heroui/react` 和 `@heroui/styles`。对等依赖不会被卸载。
```bash
heroui uninstall [options]
```
**选项:**
* `-p --packagePath` \[string] package.json 文件的路径
**输出:**
```bash
HeroUI CLI
❗️ Packages slated for uninstallation:
╭──────────────────────────────────────────────────────────────────────────────────────╮
│ Package │ Version │ Status │ Docs │
│──────────────────────────────────────────────────────────────────────────────────────│
│ @heroui/react │ 3.0.0 │ stable │ https://heroui.com │
│ @heroui/styles │ 3.0.0 │ stable │ https://heroui.com │
╰──────────────────────────────────────────────────────────────────────────────────────╯
? Confirm uninstallation of these packages: › - Use arrow-keys. Return to submit.
❯ Yes
No
✅ Successfully uninstalled: @heroui/react, @heroui/styles
```
### list
列出已安装的 HeroUI 包(`@heroui/react`、`@heroui/styles`)。
```bash
heroui list [options]
```
**选项:**
* `-p --packagePath` \[string] package.json 文件的路径
**输出:**
```bash
HeroUI CLI
Current installed packages:
╭──────────────────────────────────────────────────────────────────────────────────────╮
│ Package │ Version │ Status │ Docs │
│──────────────────────────────────────────────────────────────────────────────────────│
│ @heroui/react │ 3.0.0 🚀latest │ stable │ https://heroui.com │
│ @heroui/styles │ 3.0.0 🚀latest │ stable │ https://heroui.com │
╰──────────────────────────────────────────────────────────────────────────────────────╯
```
### doctor
检查项目中存在的问题。
* 检查 `@heroui/react` 和 `@heroui/styles` 是否已安装
* 检查项目中是否安装了所需的 `对等依赖` 并满足最低版本要求
```bash
heroui doctor [options]
```
**选项:**
* `-p --packagePath` \[string] package.json 文件的路径
**输出:**
如果项目中存在问题,`doctor` 命令将显示问题信息。
```bash
HeroUI CLI
HeroUI CLI: ❌ Your project has 1 issue that require attention
❗️Issue 1: missingHeroUIPackages
The following HeroUI packages are not installed:
- @heroui/styles
Run `heroui install` to install them.
```
否则,`doctor` 命令将显示以下消息。
```bash
HeroUI CLI
✅ Your project has no detected issues.
```
### env
显示本地环境的调试信息。
```bash
heroui env [options]
```
**选项:**
* `-p --packagePath` \[string] package.json 文件的路径
**输出:**
```bash
HeroUI CLI
Current installed packages:
╭──────────────────────────────────────────────────────────────────────────────────────╮
│ Package │ Version │ Status │ Docs │
│──────────────────────────────────────────────────────────────────────────────────────│
│ @heroui/react │ 3.0.0 🚀latest │ stable │ https://heroui.com │
│ @heroui/styles │ 3.0.0 🚀latest │ stable │ https://heroui.com │
╰──────────────────────────────────────────────────────────────────────────────────────╯
Environment Info:
System:
OS: darwin
CPU: arm64
Binaries:
Node: v25.8.1
```
### agents-md
下载用于 AI 编程助手(Claude、Cursor 等)的 HeroUI 文档。该命令会从 HeroUI 仓库克隆最新文档,并将一份精简索引注入到 `AGENTS.md` 或 `CLAUDE.md` 中,方便助手参考你项目中的 HeroUI 配置。
```bash
heroui agents-md [options]
```
**选项:**
* `--react` \[boolean] 仅包含 React 文档(一次只能选择一个文档库)
* `--native` \[boolean] 仅包含 Native 文档
* `--migration` \[boolean] 仅包含 HeroUI v2 到 v3 的迁移文档
* `--output ` \[string] 目标文件路径(例如 `AGENTS.md`、`CLAUDE.md`)
* `--ssh` \[boolean] 使用 SSH 而非 HTTPS 进行 git clone
**示例:**
不带任何标志运行以进入交互模式:
```bash
heroui agents-md
```
将 React 文档下载到指定文件:
```bash
heroui agents-md --react --output AGENTS.md
```
下载 Native 或迁移文档:
```bash
heroui agents-md --native --output CLAUDE.md
heroui agents-md --migration --output AGENTS.md
```
**工作原理:**
1. 使用 git sparse-checkout 从 `v3` 分支克隆文档
2. 生成文档和示例文件的精简索引
3. 将该索引注入到你的 markdown 文件中,置于标记之间(`` / ``,Native 和 Migration 也有类似的标记)
4. 将 `.heroui-docs/` 添加到 `.gitignore`
`--react`、`--native` 和 `--migration` 一次只能选择其中一个。
更多详情请参阅 [AGENTS.md](/docs/react/getting-started/agents-md)。
`agents-md` 命令会收集匿名使用数据(所选项、输出文件名、耗时、成功或错误)。设置 `HEROUI_ANALYTICS_DISABLED=1` 可以选择不参与。
## 问题反馈
如果你发现了 bug,请在 [heroui-cli Issues](https://github.com/heroui-inc/heroui-cli/issues) 中报告。
# 设计原则
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started/design-principles
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/(overview)/design-principles.mdx
> 指导 HeroUI v3 设计和开发的核心原则
HeroUI v3 遵循 10 项核心原则,注重清晰度、无障碍、可定制性以及开发者体验。
## 核心原则
### 1. 语义意图优于视觉风格
使用语义命名(primary、secondary、tertiary),而非视觉描述(solid、flat、bordered)。灵感来自 [Uber 的 Base 设计系统](https://base.uber.com/6d2425e9f/p/756216-button),变体遵循清晰的层次结构:
```tsx
// ✅ Semantic variants communicate hierarchy
Save
Edit
Cancel
```
| 变体 | 用途 | 使用方式 |
| ------------- | ----------- | -------- |
| **Primary** | 推进流程的主要操作 | 每种场景 1 个 |
| **Secondary** | 备选操作 | 可使用多个 |
| **Tertiary** | 次要操作(取消、跳过) | 谨慎使用 |
| **Danger** | 破坏性操作 | 需要时使用 |
### 2. 无障碍是基础
基于 [React Aria Components](https://react-spectrum.adobe.com/react-aria/) 构建,符合 WCAG 2.1 AA 标准。内置自动 ARIA 属性、键盘导航与屏幕阅读器支持。
```tsx
import { Tabs, TabList, Tab, TabPanel } from '@heroui/react';
Profile
Security
Content
Content
```
### 3. 组合优于配置
复合组件允许你按需重新组合、自定义或省略其中的各个部分。可以使用点号语法、命名导出,或两者混合使用。
```tsx
// Compose parts to build exactly what you need
import {
Accordion,
AccordionItem,
AccordionHeading,
AccordionTrigger,
AccordionIndicator,
AccordionPanel,
AccordionBody
} from '@heroui/react';
Question Text
Answer content
```
### 4. 渐进式呈现
从简单开始,仅在需要时才增加复杂度。组件只需最少的 props 即可工作,并能随需求增长而扩展。
```tsx
// Level 1: Minimal
Click me
// Level 2: Enhanced
Submit
// Level 3: Advanced
{isLoading ? <> Loading...> : 'Submit'}
```
### 5. 可预测的行为
所有组件遵循一致的模式:尺寸(`sm`、`md`、`lg`)、变体、`className` 支持以及 data 属性。相同的 API,相同的行为。
```tsx
// All components follow the same patterns
// Compound components support both named exports and dot notation
import { Alert, AlertIcon, CardHeader, AccordionTrigger } from '@heroui/react';
// Named exports
// Dot notation
```
### 6. 类型安全优先
完整的 TypeScript 支持,包括 IntelliSense、自动补全与编译时错误检测。可为自定义组件扩展类型。
```tsx
import type { ButtonProps } from '@heroui/react';
// Type-safe props and event handlers
{ // e is properly typed as PressEvent
console.log(e.target);
}}
/>
// Extend types for custom components
interface CustomButtonProps extends Omit {
intent: 'save' | 'cancel' | 'delete';
}
```
### 7. 样式与逻辑分离
样式(`@heroui/styles`)与逻辑(`@heroui/react`)相互分离,可与任何框架或纯 HTML 一同使用。参见 [Tailwind Play 示例](https://play.tailwindcss.com/vMYXzKPyUx)。
```html
Click me
```
或与 React 一同使用:
```tsx
// Apply styles to any component
import { buttonVariants } from '@heroui/styles';
Home
```
### 8. 卓越的开发者体验
清晰的 API、富含信息的错误提示、IntelliSense、对 AI 友好的 Markdown 文档,以及用于可视化测试的 Storybook。
### 9. 完全可定制
开箱即用的精美默认样式。使用 CSS 变量或 [BEM](https://getbem.com/) 类即可彻底改变整体外观。每一个插槽都可定制。
```css
/* Theme-wide changes with variables */
:root {
--accent: oklch(0.7 0.25 260);
--radius: 0.375rem;
--spacing: 0.5rem;
}
/* Component-specific customization */
@layer components {
.button {
@apply uppercase tracking-wider;
}
.button--primary {
@apply bg-gradient-to-r from-purple-500 to-pink-500;
}
}
```
### 10. 开放且可扩展
可以包装、扩展或自定义组件以满足你的需求。可以使用变体函数、直接应用 BEM 类,或创建自定义的包装组件。
**使用变体函数应用样式:**
```tsx
import { Link } from '@heroui/react';
import { linkVariants } from '@heroui/styles';
import NextLink from 'next/link';
// Use variant functions to style framework-specific components
const slots = linkVariants({ underline: "hover" });
About Page
```
**直接应用 BEM 类:**
```tsx
import Link from 'next/link';
// Apply HeroUI's BEM classes directly to any element
Dashboard
```
**创建自定义包装组件:**
```tsx
// Custom wrapper component
const CTAButton = ({
intent = 'primary-cta',
children,
ref,
...props
}: CTAButtonProps) => {
const variantMap = {
'primary-cta': 'primary',
'secondary-cta': 'secondary',
'minimal': 'ghost'
};
return (
{children}
);
};
```
**使用 Tailwind Variants 扩展:**
```tsx
import { Button } from '@heroui/react';
import { buttonVariants, tv } from '@heroui/styles';
// Extend button styles with custom variants
const myButtonVariants = tv({
extend: buttonVariants,
variants: {
variant: {
'primary-cta': 'bg-gradient-to-r from-blue-500 to-purple-600 text-white shadow-lg',
'secondary-cta': 'border-2 border-blue-500 text-blue-500 hover:bg-blue-50',
}
}
});
// Use the custom variants
function CustomButton({ variant, className, ...props }) {
return ;
}
// Usage
Get Started
Learn More
```
## 与 HeroUI v2 的对比
| 维度 | HeroUI v2 | HeroUI v3 |
| --------- | ------------------------- | -------------------------------- |
| **动画** | Framer Motion | CSS + GPU 加速 |
| **组件模式** | 带有大量 props 的单一组件 | 复合组件 |
| **变体** | 基于视觉(solid、bordered、flat) | 基于语义(primary、secondary、tertiary) |
| **样式** | 部分支持 Tailwind v4 | 完整支持 Tailwind v4 |
| **无障碍** | 优秀(基于 React Aria) | 优秀(基于 React Aria) |
| **打包体积** | 较大(整体打包) | 较小(支持 tree-shaking) |
| **自定义难度** | 中等(基于 props) | 简单(复合组件 + 原生 CSS) |
# 框架集成
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started/frameworks
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/(overview)/frameworks.mdx
> 将 HeroUI 集成到你的框架中
## Next.js
### 1. 创建 Next.js 项目
```bash
npx heroui-cli@latest init
```
当出现提示时,选择 **App** 或 **Pages** 模板。然后进入新创建的文件夹并安装依赖(例如 `pnpm install`)。
### 2. 使用你的第一个 HeroUI 组件
示例:`app/page.tsx`
```tsx
import {Button} from "@heroui/react";
export default function HomePage() {
return (
Hello HeroUI
);
}
```
示例:`pages/index.tsx`
```tsx
import {Button} from "@heroui/react";
export default function HomePage() {
return (
Hello HeroUI
);
}
```
HeroUI v3 无需 Provider。安装并导入样式后,组件即可直接使用。
### 3. 区域设置(可选)
为了与 Next.js 集成,请确保服务端的区域设置与客户端一致。
在根布局中,确定用户的首选语言,并在 `` 元素上设置 `lang` 和 `dir` 属性。
```tsx
// app/layout.tsx
import {headers} from 'next/headers';
import {isRTL} from '@heroui/react';
import {ClientProviders} from './provider';
export default async function RootLayout({children}) {
// Get the user's preferred language from the Accept-Language header.
// You could also get this from a database, URL param, etc.
const acceptLanguage = (await headers()).get('accept-language');
const lang = acceptLanguage?.split(/[,;]/)[0] || 'en-US';
return (
{children}
);
}
```
创建 `app/provider.tsx`,其中应渲染一个 `I18nProvider`,用于设置 React Aria 所使用的区域设置。
```tsx
// app/provider.tsx
"use client";
import {I18nProvider} from '@heroui/react';
export function ClientProviders({lang, children}) {
return (
{children}
);
}
```
如果你使用了带 nonce 的 [内容安全策略](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP)(CSP),请在文档的 head 中添加 ` ` 标签,并将其 content 属性设置为生成的 nonce 值。React Aria 会自动从该标签读取 nonce。
## Vite
### 1. 创建 Vite 项目
```bash
npx heroui-cli@latest init
```
当出现提示时,选择 **Vite** 模板。然后进入新创建的文件夹并安装依赖(例如 `pnpm install`)。
### 2. 使用你的第一个 HeroUI 组件
示例:`src/App.tsx`
```tsx
import {Button} from "@heroui/react";
function App() {
return (
Hello HeroUI
);
}
export default App;
```
HeroUI v3 无需 Provider。安装并导入样式后,组件即可直接使用。
## 下一步
* [快速入门](/docs/react/getting-started/quick-start) — 最快上手的方式
* [主题](/docs/react/getting-started/theming) — 自定义颜色和设计令牌
* [组件](/docs/react/components) — 探索所有可用的组件
# 快速入门
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started/quick-start
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/(overview)/quick-start.mdx
> 只需几分钟即可开始使用 HeroUI v3
## 环境要求
* [React 19+](https://reactjs.org/)
* [Tailwind CSS v4](https://tailwindcss.com/docs/installation/framework-guides)
## 快速安装
\` 作为最小冒烟测试,确认样式已正确生效。
完成后,请总结你做出的变更,并告诉我如何启动开发服务器。`}
>
**想让 AI 助手代劳?** 在你的编辑器中安装 [HeroUI MCP Server](/docs/react/getting-started/mcp-server),然后把上面的提示词粘贴给 AI 助手——它会分析你的项目并自动完成全部配置。
安装 HeroUI 及其所需依赖:
```bash
npm i @heroui/styles @heroui/react
```
```bash
pnpm add @heroui/styles @heroui/react
```
```bash
yarn add @heroui/styles @heroui/react
```
```bash
bun add @heroui/styles @heroui/react
```
## 导入样式
将以下内容添加到你的主 CSS 文件 `globals.css`:
```css
@import "tailwindcss";
@import "@heroui/styles"; /* [!code highlight]*/
```
导入顺序很重要。请务必先导入 `tailwindcss`。
## 使用组件
```tsx
import { Button } from '@heroui/react';
function App() {
return (
My Button
);
}
```
## 下一步
* [主题](/themes) — 创建并分享你自己的主题
* [浏览组件](/docs/react/components) — 查看所有可用的组件
* [学习样式](/docs/handbook/styling) — 使用 Tailwind CSS 进行自定义
* [探索组合模式](/docs/handbook/composition) — 掌握复合组件
# Agent Skills
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started/agent-skills
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/(ui-for-agents)/agent-skills.mdx
> 让 AI 助手能够使用 HeroUI v3 组件来构建 UI
HeroUI Skills 为你的 AI 助手提供关于 HeroUI v3 组件、模式与最佳实践的全面知识。
### 安装
```bash
curl -fsSL https://heroui.com/install | bash -s heroui-react
```
或者使用 skills 包:
```bash
npx skills add heroui-inc/heroui
```
支持 Claude Code、Cursor、OpenCode 等。
### 使用方法
Skills 会被你的 AI 助手 **自动发现**,你也可以通过 `/heroui-react` 命令直接调用。
只需让你的 AI 助手:
* 使用 HeroUI v3 构建组件
* 使用 HeroUI 组件创建页面
* 自定义主题和样式
* 查阅组件文档
对于更复杂的使用场景,请使用 [MCP 服务器](/docs/react/getting-started/mcp-server),它提供对组件文档与源代码的实时访问。
### 包含的内容
* HeroUI v3 安装指南
* 所有 HeroUI v3 组件,包含 props、示例与使用模式
* 主题与样式指南
* 设计原则与组合模式
### 结构
```
skills/heroui-react/
├── SKILL.md # Main skill documentation
├── LICENSE.txt # Apache License 2.0
└── scripts/ # Utility scripts
├── list_components.mjs
├── get_component_docs.mjs
├── get_source.mjs
├── get_styles.mjs
├── get_theme.mjs
└── get_docs.mjs
```
### 相关文档
* [Agent Skills 规范](https://agentskills.io/home) — 了解 Agent Skills 的格式
* [Claude Agent Skills](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview) — Claude 的 Skills 文档
* [Cursor Skills](https://cursor.com/docs/context/skills) — 在 Cursor 中使用 Skills
* [OpenCode Skills](https://opencode.ai/docs/skills) — 在 OpenCode 中使用 Skills
# AGENTS.md
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started/agents-md
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/(ui-for-agents)/agents-md.mdx
> 为 AI 编码代理下载 HeroUI v3 React 文档
将 HeroUI v3 React 文档直接下载到你的项目中,供 AI 助手参考。
**注意:** `agents-md` 命令专门用于 HeroUI React v3。其他 CLI 命令(如 `add`、`init`、`upgrade` 等)目前仍用于 HeroUI v2。
### 用法
```bash
npx heroui-cli@latest agents-md --react
```
或者指定输出文件:
```bash
npx heroui-cli@latest agents-md --react --output AGENTS.md
```
### 功能说明
* 将最新的 HeroUI v3 React 文档下载到 `.heroui-docs/react/`
* 在 `AGENTS.md` 或 `CLAUDE.md` 中生成索引
* 包含用于代码示例的 demo 文件
* 自动将 `.heroui-docs/` 添加到 `.gitignore`
### 选项
* `--react` — 仅下载 React 文档
* `--output ` — 目标文件(例如 `AGENTS.md`,或 `AGENTS.md CLAUDE.md`)
* `--ssh` — 使用 SSH 进行 git clone
### 环境要求
* Tailwind CSS >= v4
* React >= 19.0.0
* `@heroui/react >= 3.0.0` 或 `@latest`
### 相关文档
* [AGENTS.md](https://agents.md/) — 了解面向编码代理的 AGENTS.md 格式
* [CLAUDE.md](https://code.claude.com/docs/en/best-practices#write-an-effective-claude-md) — Claude 对应版本的 AGENTS.md
* [AGENTS.md vs Skills](https://vercel.com/blog/agents-md-outperforms-skills-in-our-agent-evals) — AGENTS.md 的性能表现
# LLMs.txt
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started/llms-txt
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/(ui-for-agents)/llms-txt.mdx
> 让 Claude、Cursor、Windsurf 等 AI 助手理解 HeroUI v3
我们提供 [LLMs.txt](https://llmstxt.org/) 文件,让 AI 编码助手可以访问 HeroUI v3 文档。
## 可用文件
**核心文档:**
* [/react/llms.txt](/react/llms.txt) — React 文档的快速参考索引
* [/react/llms-full.txt](/react/llms-full.txt) — 完整的 HeroUI React 文档
**适用于上下文窗口有限的场景:**
* [/react/llms-components.txt](/react/llms-components.txt) — 仅包含组件文档
* [/react/llms-patterns.txt](/react/llms-patterns.txt) — 常见模式与代码片段
**全平台:**
* [/llms.txt](/llms.txt) — 快速参考索引(React + Native)
* [/llms-full.txt](/llms-full.txt) — 完整文档(React + Native)
* [/llms-components.txt](/llms-components.txt) — 全部组件文档
* [/llms-patterns.txt](/llms-patterns.txt) — 全部模式与代码片段
## 集成
**Claude Code:** 让 Claude 参考文档:
```
Use HeroUI React documentation from https://heroui.com/react/llms.txt
```
或者添加到你项目中的 `.claude` 文件以自动加载。
**Cursor:** 使用 `@Docs` 功能:
```
@Docs https://heroui.com/react/llms-full.txt
```
[了解更多](https://docs.cursor.com/context/@-symbols/@-docs)
**Windsurf:** 添加到你的 `.windsurfrules` 文件:
```
#docs https://heroui.com/react/llms-full.txt
```
[了解更多](https://docs.codeium.com/windsurf/memories#memories-and-rules)
**其他 AI 工具:** 大多数 AI 助手都可以通过 URL 引用文档。只需提供:
```
https://heroui.com/react/llms.txt
```
**针对特定组件的文档:**
```
https://heroui.com/react/llms-components.txt
```
**针对模式与最佳实践:**
```
https://heroui.com/react/llms-patterns.txt
```
## 参与贡献
发现 AI 生成的代码有问题?欢迎在 [GitHub](https://github.com/heroui-inc/heroui) 上帮助我们改进 LLMs.txt 文件。
# MCP 服务器
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/getting-started/mcp-server
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/getting-started/(ui-for-agents)/mcp-server.mdx
> 直接在你的 AI 助手中访问 HeroUI v3 文档
HeroUI MCP 服务器让 AI 助手可以直接访问 HeroUI v3 组件文档,从而更轻松地在 AI 驱动的开发环境中使用 HeroUI 进行构建。
MCP 服务器目前仅支持 **@heroui/react v3**,并使用 [stdio 传输](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#stdio)。已在 npm 上以 `@heroui/react-mcp` 发布。源代码请查看 [GitHub](https://github.com/heroui-inc/heroui-mcp)。
随着我们向 HeroUI v3 添加更多组件,它们也将在 MCP 服务器中可用。
## 快速设置
### Cursor
或者手动添加到 **Cursor Settings** → **Tools** → **MCP Servers**:
```json title=".cursor/mcp.json"
{
"mcpServers": {
"heroui-react": {
"command": "npx",
"args": ["-y", "@heroui/react-mcp@latest"]
}
}
}
```
或者,将以下内容添加到你的 `~/.cursor/mcp.json` 文件。更多信息请参阅 [Cursor 文档](https://cursor.com/docs/context/mcp)。
### Claude Code
在终端中运行此命令:
```bash
claude mcp add heroui-react -- npx -y @heroui/react-mcp@latest
```
或者手动添加到你项目的 `.mcp.json` 文件:
```json title=".mcp.json"
{
"mcpServers": {
"heroui-react": {
"command": "npx",
"args": ["-y", "@heroui/react-mcp@latest"]
}
}
}
```
添加配置后,重启 Claude Code 并运行 `/mcp`,即可在列表中看到 HeroUI MCP 服务器。如果你看到 **Connected**,就可以开始使用了。
更多详情请参阅 [Claude Code MCP 文档](https://docs.claude.com/en/docs/claude-code/mcp)。
### Windsurf
将 HeroUI 服务器添加到你项目的 `.windsurf/mcp.json` 配置文件:
```json title=".windsurf/mcp.json"
{
"mcpServers": {
"heroui-react": {
"command": "npx",
"args": ["-y", "@heroui/react-mcp@latest"]
}
}
}
```
添加配置后,重新启动 Windsurf 以激活 MCP 服务器。
更多详情请参阅 [Windsurf MCP 文档](https://docs.windsurf.com/windsurf/cascade/mcp)。
### Zed
将 HeroUI 服务器添加到你的 `settings.json` 配置文件。通过命令面板打开设置(`zed: open settings`),或使用 `Cmd-,`(Mac)/ `Ctrl-,`(Linux):
```json title="settings.json"
{
"context_servers": {
"heroui-react": {
"command": "npx",
"args": ["-y", "@heroui/react-mcp@latest"],
"env": {}
}
}
}
```
添加配置后,重新启动 Zed 并打开 Agent Panel 设置视图。检查 heroui 服务器旁边的指示点是否为绿色,且 tooltip 显示为 "Server is active"。
更多详情请参阅 [Zed MCP 文档](https://zed.dev/docs/ai/mcp)。
### VS Code
要在 VS Code 中通过 GitHub Copilot 配置 MCP,请将 HeroUI 服务器添加到项目的 `.vscode/mcp.json` 配置文件:
```json title=".vscode/mcp.json"
{
"servers": {
"heroui-react": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@heroui/react-mcp@latest"]
}
}
}
```
添加配置后,打开 `.vscode/mcp.json`,然后点击 heroui-react 服务器旁边的 **Start**。
更多详情请参阅 [VS Code MCP 文档](https://code.visualstudio.com/docs/copilot/customization/mcp-servers)。
### Codex
将 HeroUI 服务器添加到你的 `~/.codex/config.toml`(或项目级的 `.codex/config.toml`):
```toml title="config.toml"
[mcp_servers.heroui-react]
command = "npx"
args = ["-y", "@heroui/react-mcp@latest"]
```
添加配置后,重启 Codex 并在 TUI 中运行 `/mcp`,以验证服务器是否处于活动状态。
更多详情请参阅 [Codex MCP 文档](https://developers.openai.com/codex/mcp)。
### OpenCode
将 HeroUI 服务器添加到你项目的 `opencode.json` 配置文件:
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"heroui-react": {
"type": "local",
"command": ["npx", "-y", "@heroui/react-mcp@latest"]
}
}
}
```
添加配置后,重新启动 OpenCode 以激活 MCP 服务器。
更多详情请参阅 [OpenCode MCP 文档](https://open-code.ai/docs/en/mcp-servers)。
## 用法
配置完成后,可以这样询问你的 AI 助手:
* “帮我在 Next.js / Vite / Astro 应用中安装 HeroUI v3”
* “显示所有 HeroUI 组件”
* “Button 组件有哪些 props?”
* “给我一个使用 Card 组件的示例”
* “获取 Button 组件的源代码”
* “显示 Card 组件的 CSS 样式”
* “深色模式下有哪些主题变量?”
### 自动更新
MCP 服务器可以帮助你升级到最新的 HeroUI 版本:
```bash
"Hey Cursor, update HeroUI to the latest version"
```
你的 AI 助手会自动完成以下事项:
* 将你当前的版本与最新发布的版本进行比较
* 查看变更日志,了解破坏性变更
* 将必要的代码更新应用到你的项目中
无论是更新到最新的稳定版还是预发布版,这一流程都适用于任何版本升级。
## 可用工具
MCP 服务器为 AI 助手提供以下工具:
| 工具 | 描述 |
| ----------------------------- | ------------------------------------------------------------------------------------ |
| `list_components` | 列出所有可用的 HeroUI v3 组件 |
| `get_component_docs` | 获取一个或多个组件的完整文档,包括组件结构、props、示例和使用模式 |
| `get_component_source_code` | 获取组件的 React/TypeScript 源代码(.tsx 文件) |
| `get_component_source_styles` | 查看组件的 CSS 样式(.css 文件) |
| `get_theme_variables` | 获取颜色、排版、间距相关的主题变量,支持浅色/深色模式 |
| `get_docs` | 浏览完整的 HeroUI v3 文档,包括指南和设计原则(如需安装说明,请使用路径 `/docs/react/getting-started/quick-start`) |
## 故障排查
**环境要求:** Node.js 22 或更高版本。使用 `npx` 时会自动下载该包。
**需要帮助?** [GitHub Issues](https://github.com/heroui-inc/heroui-mcp/issues) | [Discord 社区](https://discord.gg/heroui)
## 链接
* [npm 包](https://www.npmjs.com/package/@heroui/react-mcp)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-mcp)
* [贡献指南](https://github.com/heroui-inc/heroui-mcp/blob/main/CONTRIBUTING.md)
# Agent Skills
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/agent-skills
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(migration-for-agents)/agent-skills.mdx
> 让 AI 助手协助你将 HeroUI v2 迁移到 v3。
HeroUI Migration Skills 为你的 AI 助手提供关于 HeroUI v2 到 v3 迁移流程、组件指南与最佳实践的完整知识。
### 安装
```bash
curl -fsSL https://heroui.com/install | bash -s heroui-migration
```
或者通过 skills 包安装:
```bash
npx skills add heroui-inc/heroui
```
支持 Claude Code、Cursor、OpenCode 等。
### 使用方法
Skills 会被你的 AI 助手**自动发现**,你也可以直接通过 `/heroui-migration` 命令调用它。
只需让你的 AI 助手做这些事:
* 将组件从 HeroUI v2 迁移到 v3
* 获取完整迁移或渐进式迁移的流程
* 获取面向特定组件的迁移指南
* 查阅 Hooks 与样式相关的迁移文档
### 包含的内容
* 迁移流程(完整与渐进式)
* 面向特定组件的迁移指南(39 个组件)
* Hooks 迁移指南(如 `useDisclosure` → `useOverlayState` 等)
* 样式迁移指南(工具类、颜色 token、CSS 变量)
* 关键 API 变更与复合组件模式
### 结构
```
skills/heroui-migration/
├── SKILL.md # Main skill documentation
├── LICENSE.txt # Apache License 2.0
└── scripts/ # Utility scripts
├── list_migration_guides.mjs
├── get_migration_guide.mjs
├── get_component_migration_guides.mjs
├── get_styling_migration_guide.mjs
└── get_hooks_migration_guide.mjs
```
## 链接
* [GitHub 仓库](https://github.com/heroui-inc/heroui)
* [Discord 社区](https://discord.gg/9b6yyZKmH4)
* [Agent Skills](https://agentskills.io/) —— 了解 Agent Skills 的格式
# AGENTS.md
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/agents-md
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(migration-for-agents)/agents-md.mdx
> 下载 AI 编码代理的 HeroUI v2 到 v3 迁移文档
将 HeroUI v2 到 v3 的迁移文档直接下载到您的项目中,供 AI 助手参考。
**注:** `agents-md --migration` 仅下载迁移文档;使用 `--react` 下载组件文档。一次只能选择一个库选项。
### 用法
```bash
npx heroui-cli@latest agents-md --migration
```
或者指定输出文件:
```bash
npx heroui-cli@latest agents-md --migration --output AGENTS.md
```
### 它的作用
* 将迁移文档下载到`.heroui-docs/migration/`
* 生成索引 `AGENTS.md` 或 `CLAUDE.md`,包含迁移专用内容
* 包括工作流程指南(完整和增量)、组件迁移指南、挂钩和样式
* 添加`.heroui-docs/`到`.gitignore`自动地
### 选项
* `--migration`- 仅下载迁移文档(v2 到 v3)
* `--output ` — 目标文件(例如 `AGENTS.md` 或 `AGENTS.md CLAUDE.md`)
* `--ssh`- 使用 SSH 进行 git 克隆
### 要求
迁移文档对于从 HeroUI v2 迁移到 v3 的任何项目都很有用。目标项目需要:
* Tailwind CSS >= v4
* React >= 19.0.0(对于 v3)
* `@heroui/react`或者`@heroui/styles`(迁移后)
## 链接
* [GitHub 存储库](https://github.com/heroui-inc/heroui)
* [Discord社区](https://discord.gg/9b6yyZKmH4)
* [代理.md](https://agents.md/)- 了解 AGENTS.md 格式
* [Claude.md](https://code.claude.com/docs/en/best-practices#write-an-effective-claude-md) — Claude 侧的 AGENTS.md 等价物
* [AGENTS.md 与技能](https://vercel.com/blog/agents-md-outperforms-skills-in-our-agent-evals)- AGENTS.md 性能
# MCP服务器
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/mcp-server
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(migration-for-agents)/mcp-server.mdx
> 在 AI 助手中访问 HeroUI v2 到 v3 的迁移指南
HeroUI 迁移 MCP 服务器使 AI 助手可以直接访问 HeroUI v2 到 v3 的迁移文档。
迁移 MCP 服务器使用 **Streamable HTTP 传输**(基于 URL)并部署在`https://migration-mcp.heroui.com`。查看源代码[GitHub](https://github.com/heroui-inc/heroui-mcp/tree/main/apps/migration-mcp).
迁移 MCP 仅用于**迁移**。完成迁移后,切换到[HeroUI React MCP 服务器](/docs/react/getting-started/mcp-server)用于 v3 组件开发。
## 快速设置
### Cursor
添加到 **Cursor 设置** → **工具** → **MCP 服务器**,或项目的 `.cursor/mcp.json`:
```json title=".cursor/mcp.json"
{
"mcpServers": {
"heroui-migration": {
"url": "https://migration-mcp.heroui.com"
}
}
}
```
要了解更多信息,请参阅 [Cursor 文档](https://cursor.com/docs/context/mcp)。
### Claude Code
添加到项目的 `.mcp.json`:
```json title=".mcp.json"
{
"mcpServers": {
"heroui-migration": {
"type": "http",
"url": "https://migration-mcp.heroui.com"
}
}
}
```
添加配置后,重启 Claude Code 并运行 `/mcp`,在列表中查看 HeroUI 迁移 MCP。若显示 **已连接**,即可开始使用。
请参阅 [Claude Code MCP 文档](https://docs.claude.com/en/docs/claude-code/mcp)了解更多详情。
### Windsurf
将迁移 MCP 服务器添加到您的项目`.windsurf/mcp.json`配置文件:
```json title=".windsurf/mcp.json"
{
"mcpServers": {
"heroui-migration": {
"url": "https://migration-mcp.heroui.com"
}
}
}
```
添加配置后,重新启动Windsurf以激活MCP服务器。
请参阅[Windsurf MCP 文档](https://docs.windsurf.com/windsurf/cascade/mcp)了解更多详情。
### Zed
将迁移 MCP 添加到你的 `settings.json`。通过命令面板打开设置(`zed: open settings`)或使用 `Cmd-,`(macOS)/ `Ctrl-,`(Linux):
```json title="settings.json"
{
"context_servers": {
"heroui-migration": {
"url": "https://migration-mcp.heroui.com"
}
}
}
```
请参阅[Zed MCP 文档](https://zed.dev/docs/ai/mcp)了解更多详情。
### VS Code
若要在 VS Code 中通过 GitHub Copilot 配置 MCP,请将迁移 MCP 添加到项目的 `.vscode/mcp.json`:
```json title=".vscode/mcp.json"
{
"servers": {
"heroui-migration": {
"type": "http",
"url": "https://migration-mcp.heroui.com"
}
}
}
```
添加配置后,打开`.vscode/mcp.json`然后单击herui-migration 服务器旁边的**开始**。
请参阅[VS Code MCP 文档](https://code.visualstudio.com/docs/copilot/customization/mcp-servers)了解更多详情。
### Codex
将迁移 MCP 添加到 `~/.codex/config.toml`(或项目内的 `.codex/config.toml`):
```toml title="config.toml"
[mcp_servers.heroui-migration]
url = "https://migration-mcp.heroui.com"
```
添加配置后,重启 Codex 并在 TUI 中运行 `/mcp` 以确认服务器已激活。
请参阅 [Codex MCP 文档](https://developers.openai.com/codex/mcp)了解更多详情。
### OpenCode
将迁移 MCP 添加到项目的 `opencode.json`:
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"heroui-migration": {
"type": "remote",
"url": "https://migration-mcp.heroui.com"
}
}
}
```
添加配置后,重新启动OpenCode以激活MCP服务器。
请参阅[OpenCode MCP 文档](https://open-code.ai/docs/en/mcp-servers)了解更多详情。
## 用法
配置完成后,向您的 AI 助手询问以下问题:
* “帮我将 Button 从 HeroUI v2 迁移到 v3”
* “获取我的项目的完整迁移工作流程”
* “复选框迁移有何变化?”
* “显示 Card 和 Modal 的迁移指南”
* “获取样式迁移指南”
* “我需要迁移哪些钩子?”
## 可用工具
Migration MCP 服务器为 AI 助手提供以下工具:
| 工具 | 描述 |
| --------------------------------- | -------------------------------------------------------------------- |
| `get_migration_workflow` | 获取全面的迁移指南。使用`migrationType: "full"`(默认)或`"incremental"`完整迁移方法与增量迁移方法 |
| `list_component_migration_guides` | 列出所有 39 个可用组件迁移指南 |
| `get_component_migration_guides` | 获取指定组件的迁移指南。传入 `components: ["button", "card"]`(kebab-case) |
| `get_styling_migration_guide` | 获取样式迁移指南(实用程序类、颜色标记、CSS 变量) |
| `get_hooks_migration_guide` | 获取 hooks 迁移指南(useDisclosure → useOverlayState 等) |
## 可用提示
迁移 MCP 服务器提供引导提示,可在代理聊天中与斜杠命令一起使用:
| 提示词 | 描述 |
| --------------------- | --------------------------------------------------------------------------------------------------- |
| `analyze-and-plan` | 分析项目以识别所有 HeroUI v2 组件并创建迁移计划。建议作为迁移第一步。可选:`migrationType: "full"`(默认)或 `"incremental"` |
| `implement-migration` | 分阶段实施组件迁移,并设置检查点供用户确认。完成组件迁移与依赖切换后,继续指导 hooks 与样式迁移。可选:`migrationType: "full"`(默认)或 `"incremental"` |
**工作流程:** 运行`analyze-and-plan`首先创建您的迁移计划,然后使用`implement-migration`一步步执行它。
## 链接
* [GitHub 存储库](https://github.com/heroui-inc/heroui-mcp/tree/main/apps/migration-mcp)
* [Discord 社区](https://discord.gg/9b6yyZKmH4)
* [Model Context Protocol](https://modelcontextprotocol.io/) — 了解 MCP
# Accordion
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/accordion
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/accordion.mdx
> Accordion 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Accordion 文档](/docs/react/components/accordion)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`AccordionItem` 是一个独立组件,通过 props 接收标题、副标题、内容等元素:
```tsx
import { Accordion, AccordionItem } from "@heroui/react";
export default function App() {
return (
Content here
Content here
);
}
```
在 v3 中,Accordion 采用复合组件模式,并使用显式子组件进行组合:
```tsx
import { Accordion } from "@heroui/react";
export default function App() {
return (
Accordion 1
Content here
Accordion 2
Content here
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 单个带 props 的 `AccordionItem` 组件\
**v3:** 复合组件:`Accordion.Item`、`Accordion.Heading`、`Accordion.Trigger`、`Accordion.Panel`、`Accordion.Body`、`Accordion.Indicator`
### 2. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ------------------------------------------------------------ | ----------- | ------------------------------------------------------- |
| `selectedKeys` | `Accordion` | 已重命名为 `expandedKeys`;类型为 `Iterable` |
| `defaultSelectedKeys` | `Accordion` | 已重命名为 `defaultExpandedKeys` |
| `onSelectionChange` | `Accordion` | 已重命名为 `onExpandedChange`;签名为 `(keys: Set) => void` |
| `selectionMode="multiple"` | `Accordion` | 使用 `allowsMultipleExpanded`(布尔值) |
| `isCompact` | — | 已移除(请使用 Tailwind CSS,例如 `text-sm`、`py-2`) |
| `hideIndicator` | — | 不渲染 ` ` 即可隐藏 |
| `disableAnimation`、`disableIndicatorAnimation`、`motionProps` | — | 已移除(不支持动画 / 请使用 CSS) |
| `showDivider`、`dividerProps` | — | 已移除(请手动添加分隔线或使用 `Divider`) |
| `keepContentMounted` | — | 已移除(v3 中内容始终挂载) |
| `selectionBehavior`、`disallowEmptySelection` | — | 已移除(不适用) |
| `itemClasses` | — | 在各项上使用 `className` |
| `startContent` | — | 将内容放在 `` 内 |
| `title`、`subtitle` | — | 将内容放在 `` 内 |
### 3. 变体
**v2 变体:** `light`、`shadow`、`bordered`、`splitted`\
**v3 变体:** `default`、`surface`
v3 的变体更精简。若要接近 v2 的视觉效果:
* **v2 `light`** → v3 `default`
* **v2 `shadow`** → v3 `surface`
* **v2 `bordered`** → v3 `default` + 添加边框类
* **v2 `splitted`** → v3 `default` + 为各项添加背景色与间距 / 外边距
### 4. 条目标识
**v2:** React 的 `key` 同时用于列表调和与展开状态。\
**v3:** 在 `Accordion.Item` 上使用 `id` 标识展开状态;列表中仍可为各项保留 React 的 `key`。
## 迁移示例
### 受控状态
```tsx
import { useState } from "react";
import { Accordion, AccordionItem } from "@heroui/react";
const [selectedKeys, setSelectedKeys] = useState(new Set(["1"]));
Content 1
Content 2
```
```tsx
import { useState } from "react";
import { Accordion } from "@heroui/react";
import type { Key } from "@heroui/react";
const [expandedKeys, setExpandedKeys] = useState>(new Set(["1"]));
Item 1
Content 1
Item 2
Content 2
```
### 副标题与前置内容
```tsx
import { Icon } from "@iconify/react";
}
>
Content here
```
```tsx
import { Icon } from "@iconify/react";
Accordion 1
Press to expand
Content here
```
### 自定义指示器
```tsx
import { Icon } from "@iconify/react";
(
props.isOpen ? :
)}
>
Content
```
```tsx
import { Icon } from "@iconify/react";
import { useState } from "react";
import { Accordion } from "@heroui/react";
import type { Key } from "@heroui/react";
const [expandedKeys, setExpandedKeys] = useState>(new Set());
Item 1
{expandedKeys.has("1") ? (
) : (
)}
Content
```
### 禁用项与默认展开键
```tsx
Content 1
Content 2
```
```tsx
Item 1
Content 1
Item 2
Content 2
```
## 样式变化
### v2:`classNames` prop
```tsx
```
### v3:在各子组件上使用 `className`
```tsx
Title
Content
```
## 组件剖析
v3 Accordion 的结构如下:
```
Accordion (Root)
└── Accordion.Item
├── Accordion.Heading
│ └── Accordion.Trigger
│ ├── [Your content: title, subtitle, icons, etc.]
│ └── Accordion.Indicator (optional)
└── Accordion.Panel
└── Accordion.Body
└── [Your content]
```
## 总结
1. **组件结构**:必须使用复合组件,而不是仅靠 props 拼装。
2. **状态 props**:`selectedKeys` → `expandedKeys`,`onSelectionChange` → `onExpandedChange`。
3. **多段展开**:`selectionMode="multiple"` → `allowsMultipleExpanded={true}`。
4. **条目标识**:展开状态用 `id`;列表调和仍使用 React 的 `key`。
5. **变体**:由 4 种缩减为 2 种。
6. **已移除的 props**:大量便捷 props 已移除;请改用 Tailwind CSS 类。
7. **内容结构**:标题、副标题与前置内容需手动放入 `Trigger`。
8. **指示器**:必须显式渲染;不再自动生成指示器。
# Alert
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/alert
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/alert.mdx
> Alert 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Alert 文档](/docs/react/components/alert)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`Alert` 是单个组件,通过 prop 接收标题、描述、图标等内容:
```tsx
import { Alert } from "@heroui/react";
export default function App() {
return (
);
}
```
在 v3 中,Alert 改为复合组件模式,子节点显式声明:
```tsx
import { Alert } from "@heroui/react";
export default function App() {
return (
This is an alert
Thanks for subscribing to our newsletter!
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 单一 `Alert` 组件,通过 prop 配置\
**v3:** 复合组件:`Alert`、`Alert.Indicator`、`Alert.Content`、`Alert.Title`、`Alert.Description`
### 2. Color / Status prop
| v2 颜色 | v3 状态 | 说明 |
| ----------- | --------- | --------------- |
| `default` | `default` | 相同 |
| `primary` | `accent` | 已重命名 |
| `secondary` | `default` | 改用 `default` 状态 |
| `success` | `success` | 相同 |
| `warning` | `warning` | 相同 |
| `danger` | `danger` | 相同 |
### 3. 移除变体
**v2 变体:** `solid`、`bordered`、`flat`、`faded`\
**v3:** 不再提供 `variant` prop——请使用 Tailwind CSS 类来实现类似效果
要在 v3 中复刻 v2 的变体外观:
* **v2 `solid`** → v3 `status` + 添加背景颜色类
* **v2 `bordered`** → v3 `status` + 添加 `border` 类
* **v2 `flat`** → v3 默认效果(无需额外类)
* **v2 `faded`** → v3 `status` + 添加透明度 / 背景类
### 4. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ------------------------------------------------ | ------------------- | ---------------------------------------------- |
| `variant` | - | 已移除(请改用 Tailwind CSS) |
| `radius` | - | 已移除(请改用 Tailwind,例如 `rounded-lg`) |
| `startContent` | - | 将内容放在 ` ` 之前 |
| `endContent` | - | 将内容放在 ` ` 之后 |
| `hideIcon` | - | 省略 ` ` |
| `hideIconWrapper` | - | 已移除(v3 不再有图标包装层) |
| `icon` | `Alert.Indicator` | 将图标作为 ` ` 的子节点 |
| `isVisible`、`isDefaultVisible`、`onVisibleChange` | - | 已移除(请通过条件渲染控制) |
| `isClosable`、`onClose`、`closeButtonProps` | - | 已移除(请手动添加 `CloseButton`) |
| `title` | `Alert.Title` | 在 `` 内部使用 `` |
| `description` | `Alert.Description` | 在 `` 内部使用 `` |
### 5. 图标处理
**v2:** 通过 `icon` prop 设置或 `hideIcon` 隐藏\
**v3:** 使用 ` `,通过子节点自定义图标,或完全省略它
## 迁移示例
### 图标处理
```tsx
import { Icon } from '@iconify/react';
{/* With custom icon */}
}
title="Custom Icon Alert"
/>
{/* Without icon */}
```
```tsx
import { Icon } from '@iconify/react';
{/* With custom icon */}
Custom Icon Alert
{/* Without icon */}
No Icon Alert
```
### 带操作按钮(end content)
```tsx
import { Alert, Button } from "@heroui/react";
Upgrade
}
/>
```
```tsx
import { Alert, Button } from "@heroui/react";
You have no credits left
Upgrade to a paid plan to continue
Upgrade
```
### 可关闭的 Alert
```tsx
console.log("Closed")}
/>
```
```tsx
import { Alert, CloseButton } from "@heroui/react";
import { useState } from "react";
const [isVisible, setIsVisible] = useState(true);
{isVisible && (
Closable Alert
setIsVisible(false)}
/>
)}
```
## 样式变更
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className` prop
```tsx
Title
Description
```
## 组件结构
v3 Alert 遵循以下结构:
```
Alert (Root)
├── Alert.Indicator (optional)
├── Alert.Content (optional)
│ ├── Alert.Title (optional)
│ └── Alert.Description (optional)
└── [Additional content like buttons, close button, etc.] (optional)
```
## 总结
1. **组件结构**:必须使用复合组件,而不是 prop
2. **Color → Status**:`color` prop 已重命名为 `status`,`primary` → `accent`,`secondary` → `default`
3. **移除 variant**:不再提供 `variant` prop;请使用 Tailwind CSS 类
4. **移除 radius**:不再提供 `radius` prop;请使用 Tailwind CSS 类
5. **不再内置关闭按钮**:必须手动添加 `CloseButton`
6. **不再内置可见性控制**:请通过条件渲染处理可见性
7. **不再有 startContent / endContent prop**:直接将内容放入组件树中
8. **图标处理**:使用 ` ` 配合子节点,或省略它
# Autocomplete
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/autocomplete
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/autocomplete.mdx
> Autocomplete 从 HeroUI v2 到 v3 的迁移指南。
在 v3 中,v2 的 `Autocomplete` 可以迁移为 [v3 Autocomplete](/docs/react/components/autocomplete) 或 [v3 ComboBox](/docs/react/components/combo-box),取决于你的使用场景。请参阅下文「何时使用 Autocomplete 与 ComboBox」。本指南只关注从 HeroUI v2 的迁移。
## 何时使用 Autocomplete 与 ComboBox
v3 提供两个组件来替代 v2 的 Autocomplete:
| 组件 | 适用场景 | 输入方式 | 底层实现 |
| ---------------- | -------------------------- | ------------------------- | ------------------------------------ |
| **Autocomplete** | 选择器式交互,Popover 内带搜索 / 筛选字段 | 按钮触发器展示已选值;Popover 内为搜索输入 | React Aria `Select` + `Autocomplete` |
| **ComboBox** | 在可见输入框中输入以筛选下拉列表 | 内联文本输入与下拉触发器 | React Aria `ComboBox` |
**使用 Autocomplete**:用户从预定义列表中选择,且搜索字段应出现在下拉内(类似可搜索的选择器)。
**使用 ComboBox**:用户在可见输入框中直接输入以筛选或搜索选项(行为最接近 v2)。
## 结构变化
在 v2 中,Autocomplete 是内部包装 Input 的单一组件:
```tsx
import { Autocomplete, AutocompleteItem } from "@heroui/react";
export default function App() {
return (
Cat
Dog
);
}
```
### 迁移到 v3 ComboBox(最接近 v2)
ComboBox 提供与 v2 Autocomplete 最接近的体验:内联文本输入并随输入筛选选项。
```tsx
import { ComboBox, Input, Label, ListBox } from "@heroui/react";
export default function App() {
return (
Select an animal
Cat
Dog
);
}
```
### 迁移到 v3 Autocomplete(可搜索的选择器)
Autocomplete 提供选择器式触发器,并在 Popover 内提供搜索 / 筛选字段:
```tsx
import { Autocomplete, Label, SearchField, ListBox, useFilter } from "@heroui/react";
export default function App() {
const { contains } = useFilter({ sensitivity: "base" });
return (
Select an animal
Cat
Dog
);
}
```
## 关键变化
### 1. 两个替代组件
**v2:** `Autocomplete`(单一组件)\
**v3:** `ComboBox`(内联输入 + 下拉)或 `Autocomplete`(选择器式 + Popover 内搜索)
### 2. 组件结构
**v2:** 单一组件,内部包含 Input\
**v3 ComboBox:** 复合组件(`ComboBox.InputGroup`、`ComboBox.Trigger`、`ComboBox.Popover`)\
**v3 Autocomplete:** 复合组件(`Autocomplete.Trigger`、`Autocomplete.Value`、`Autocomplete.ClearButton`、`Autocomplete.Indicator`、`Autocomplete.Popover`、`Autocomplete.Filter`)
### 3. 菜单项组件
**v2:** `AutocompleteItem`、`AutocompleteSection`\
**v3:** `ListBox.Item`、`ListBox.Section`(来自 ListBox)
### 4. 菜单项标识
**v2:** React 的 `key` 同时用于列表协调与选择时的项标识。\
**v3:** 在 `ListBox.Item` 上使用 `id` 与 `textValue`(状态与无障碍);列表中的项仍保留 React 的 `key`。
### 5. Autocomplete 专用子组件(v3)
v3 Autocomplete 引入 v2 中不存在的子组件:
| 子组件 | 用途 |
| -------------------------- | ------------------------------------------------------------------------------ |
| `Autocomplete.Trigger` | 打开 Popover 的按钮组 |
| `Autocomplete.Value` | 显示当前选中值或占位符 |
| `Autocomplete.ClearButton` | 清除当前选择 |
| `Autocomplete.Indicator` | 下拉箭头图标(打开时旋转) |
| `Autocomplete.Popover` | 下拉 Popover 容器 |
| `Autocomplete.Filter` | 包裹 `SearchField` 与 `ListBox` 以启用筛选;接受 `filter` 函数、`inputValue`、`onInputChange` |
### 6. SearchField 集成
v3 Autocomplete 在 `Autocomplete.Filter` 内使用 `SearchField`,作为 Popover 中的搜索输入:
```tsx
...
```
`SearchField` 还有子组件:`SearchField.Group`、`SearchField.Input`、`SearchField.SearchIcon`、`SearchField.ClearButton`。
### 7. useFilter Hook
v3 Autocomplete 使用 `useFilter` hook(来自 React Aria,由 `@heroui/react` 再导出)提供带区域设置感知的筛选函数:
```tsx
import { useFilter } from "@heroui/react";
const { contains } = useFilter({ sensitivity: "base" });
// 传给 Autocomplete.Filter
...
```
该 hook 返回 `contains`、`startsWith`、`endsWith`。`sensitivity` 控制区域设置感知匹配(`"base"`、`"accent"`、`"case"`、`"variant"`)。
### 8. Prop 变更(ComboBox)
| v2 prop | v3 位置 | 说明 |
| ---------------------------------------------------------------------- | ------------------------------- | ------------------------------------ |
| — | `id`(在 `ListBox.Item` 上) | 状态用的项标识 |
| — | `textValue`(在 `ListBox.Item` 上) | 无障碍(提前输入) |
| `label` | `Label` | 使用 `Label` 组件 |
| `description` | `Description` | 使用 `Description` 组件 |
| `placeholder` | `Input` | 写在 `Input` 的 `placeholder` |
| `selectedKey`、`onSelectionChange`、`inputValue`、`onInputChange` | `ComboBox` | 与 v2 相同 |
| `allowsCustomValue`、`allowsEmptyCollection`、`defaultFilter` | `ComboBox` | 与 v2 相同 |
| `disabledKeys`、`isDisabled`、`isRequired`、`isInvalid`、`name` | `ComboBox` | 与 v2 相同 |
| `menuTrigger` | `ComboBox` | `"focus"`(默认)、`"input"` 或 `"manual"` |
| `variant`、`color`、`size`、`radius` | — | 已移除(请使用 Tailwind CSS) |
| `labelPlacement` | — | 已移除(请自行摆放 Label) |
| `startContent`、`endContent` | — | 加到 `Input` 或 InputGroup |
| `selectorIcon`、`clearIcon` | — | 自定义 `ComboBox.Trigger` 或手动实现 |
| `isClearable` | — | 手动实现 |
| `showScrollIndicators` | — | 已移除 |
| `classNames` | — | 在各部件上使用 `className` |
| `popoverProps`、`listboxProps`、`inputProps` | — | 直接在对应子组件上配置 |
| `scrollShadowProps`、`scrollRef` | — | 已移除 |
| `selectorButtonProps`、`clearButtonProps`、`disableSelectorIconRotation` | — | 已移除 |
| `isReadOnly` | `ComboBox` | `ComboBox` 上的 `isReadOnly` prop |
| `fullWidth` | `ComboBox` | `ComboBox` 上的 `fullWidth` prop |
| `isVirtualized`、`maxListboxHeight`、`itemHeight` | — | 已移除 |
| `onClose`、`onClear` | — | 改用其他事件处理函数 |
| `validationBehavior`、`validate` | `ComboBox` | 写在 `ComboBox` 上的 prop |
### 9. Prop 变更(Autocomplete)
| v2 prop | v3 位置 | 说明 |
| ----------------------------------------------------------- | ------------------------------- | -------------------------------------------------------------- |
| — | `id`(在 `ListBox.Item` 上) | 状态用的项标识 |
| — | `textValue`(在 `ListBox.Item` 上) | 无障碍(提前输入) |
| `label` | `Label` | 使用 `Label` 组件 |
| `description` | `Description` | 使用 `Description` 组件 |
| `placeholder` | `Autocomplete` | 根组件上的 `placeholder` prop |
| `selectedKey` / `onSelectionChange` | `value` / `onChange` | Autocomplete 上已重命名 |
| `selectionMode` | `Autocomplete` | `"single"`(默认)或 `"multiple"` |
| `inputValue`、`onInputChange` | `Autocomplete.Filter` | 写在 `Filter` 上的 `inputValue` 与 `onInputChange` |
| `disabledKeys`、`isDisabled`、`isRequired`、`isInvalid`、`name` | `Autocomplete` | 与 v2 相同 |
| `variant`、`color`、`size`、`radius` | — | 已移除(请使用 Tailwind CSS);`variant` 支持 `"primary"` / `"secondary"` |
| `isClearable` | `Autocomplete.ClearButton` | 内置子组件 |
| `selectorIcon` | `Autocomplete.Indicator` | 将自定义图标作为 children 传入 |
| `onClear` | `Autocomplete` | 根组件上的 `onClear` prop |
| `fullWidth` | `Autocomplete` | 根组件上的 `fullWidth` prop |
| `classNames` | — | 在各部件上使用 `className` |
| `popoverProps`、`listboxProps`、`inputProps` | — | 直接在对应子组件上配置 |
## 迁移示例
### 受控选择(ComboBox)
```tsx
import { useState } from "react";
const [selectedKey, setSelectedKey] = useState("cat");
Cat
Dog
```
```tsx
import { useState } from "react";
import type { Key } from "@heroui/react";
const [selectedKey, setSelectedKey] = useState("cat");
Animal
Cat
Dog
```
### 受控选择(Autocomplete)
```tsx
import { useState } from "react";
const [selectedKey, setSelectedKey] = useState("cat");
Cat
Dog
```
```tsx
import { useState } from "react";
import type { Key } from "@heroui/react";
import { Autocomplete, Label, SearchField, ListBox, useFilter } from "@heroui/react";
const [value, setValue] = useState("cat");
const { contains } = useFilter({ sensitivity: "base" });
Animal
Cat
Dog
```
### 带分组
```tsx
United States
Canada
United Kingdom
```
```tsx
import { Header, Separator } from "@heroui/react";
Country
United States
Canada
United Kingdom
```
### 使用 useFilter(Autocomplete)
```tsx
{/* v2 在内部处理筛选 */}
Cat
Dog
```
```tsx
import { Autocomplete, Label, SearchField, ListBox, useFilter } from "@heroui/react";
function FilterExample() {
const { contains } = useFilter({ sensitivity: "base" });
return (
Animal
Cat
Dog
);
}
```
### 表单校验
```tsx
{/* Required field */}
Cat
{/* With error message */}
Cat
```
```tsx
import { FieldError, Form } from "@heroui/react";
{/* Required field */}
Animal
Cat
{/* With error message */}
Animal
Cat
Please select an animal
```
## 组件剖析
### ComboBox 剖析
```
ComboBox (Root)
├── Label
├── ComboBox.InputGroup
│ ├── Input
│ └── ComboBox.Trigger
├── Description (optional)
├── ComboBox.Popover
│ └── ListBox
│ ├── ListBox.Item
│ │ ├── [Content]
│ │ └── ListBox.ItemIndicator (optional)
│ └── ListBox.Section (optional)
│ ├── Header
│ └── ListBox.Item
└── FieldError (optional)
```
### Autocomplete 剖析
```
Autocomplete (Root)
├── Label
├── Autocomplete.Trigger
│ ├── Autocomplete.Value
│ ├── Autocomplete.ClearButton
│ └── Autocomplete.Indicator
├── Description (optional)
├── Autocomplete.Popover
│ └── Autocomplete.Filter
│ ├── SearchField
│ │ └── SearchField.Group
│ │ ├── SearchField.SearchIcon
│ │ └── SearchField.Input
│ └── ListBox
│ ├── ListBox.Item
│ │ ├── [Content]
│ │ └── ListBox.ItemIndicator (optional)
│ └── ListBox.Section (optional)
│ ├── Header
│ └── ListBox.Item
└── FieldError (optional)
```
## 总结
1. **两个替代组件**:v2 `Autocomplete` 可迁移为 v3 `ComboBox`(内联输入)或 v3 `Autocomplete`(Popover 内可筛选的选择器)。
2. **组件结构**:由带 prop 的单一组件变为带显式子节点的复合组件。
3. **菜单项组件**:`AutocompleteItem` → `ListBox.Item`;`AutocompleteSection` → `ListBox.Section`。
4. **菜单项标识**:在项上使用 `id` 与 `textValue`;列表协调仍用 React 的 `key`。
5. **输入**:v3 ComboBox 需要显式 `Input`;v3 Autocomplete 在 `Autocomplete.Filter` 内使用 `SearchField`。
6. **标签 / 描述**:对应 prop 拆为独立的 `Label` 与 `Description` 组件。
7. **筛选**:v3 Autocomplete 通过 `Autocomplete.Filter` 与 `useFilter` 做区域设置感知筛选;v3 ComboBox 使用 `defaultFilter` prop。
8. **清除按钮**:v3 Autocomplete 内置 `Autocomplete.ClearButton`;v3 ComboBox 需手动实现。
9. **选中值展示**:v3 Autocomplete 用 `Autocomplete.Value` 展示选中值,并支持渲染 prop。
10. **样式类 prop 已移除**:`color`、`size`、`radius` 已移除(请使用 Tailwind CSS);`variant` 现支持 `"primary"` / `"secondary"`。
11. **classNames 已移除**:在各子组件上使用 `className` prop。
12. **useFilter Hook**:v3 新增,提供 `contains`、`startsWith`、`endsWith` 用于区域设置感知的文本匹配。
# Avatar
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/avatar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/avatar.mdx
> Avatar 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Avatar 文档](/docs/react/components/avatar)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`Avatar` 是单个组件,通过 props 传入图片地址、姓名、后备内容等:
```tsx
import { Avatar } from "@heroui/react";
export default function App() {
return (
);
}
```
在 v3 中,`Avatar` 采用显式子组件的复合组件模式:
```tsx
import { Avatar } from "@heroui/react";
export default function App() {
return (
JD
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 单个带 props 的 `Avatar`\
**v3:** 复合组件:`Avatar`、`Avatar.Image`、`Avatar.Fallback`
### 2. Prop 变更
| v2 prop | v3 位置 | 说明 |
| --------------------------- | ----------------- | ----------------------------------------------- |
| `src` | `Avatar.Image` | 使用 ` ` |
| `name` | — | 请自行生成首字母缩写并传入 ` ` |
| `showFallback` | — | 已移除(图片加载失败或未提供时显示后备) |
| `fallback`, `icon` | `Avatar.Fallback` | 将内容放在 ` ` 内 |
| `color` | `Avatar` | 相同;`primary` → `accent`,`secondary` → `default` |
| `variant` | `Avatar` | v3 新增;`"default"` \| `"soft"` |
| `size` | `Avatar` | 相同 |
| `isBordered` | — | 已移除(请使用 Tailwind,例如 `ring-2 ring-background`) |
| `radius` | — | 已移除(请使用 Tailwind,例如 `rounded-full`) |
| `isDisabled`, `isFocusable` | — | 已移除(如需请用 Tailwind / `asChild`) |
| `getInitials` | — | 请手动生成首字母缩写 |
| `ImgComponent`, `imgProps` | — | 如需请在 `Avatar.Image` 上使用 `asChild` |
| `onError` | `Avatar.Image` | 在 ` ` 上使用 `onError` |
| — | `Avatar.Image` | 新增:`srcSet`、`sizes`、`loading`(响应式图片) |
| — | `Avatar.Fallback` | 新增:`delayMs` 延迟显示后备(减少闪烁) |
| `classNames` | — | 请在各部分使用 `className` |
| `AvatarGroup` | — | 无 v3 等价组件;请用 CSS 自行成组 |
### 3. `variant` prop(v3 新增)
**v2:** 无 `variant` prop,仅通过 `color` 控制样式\
**v3:** 新增 `variant` prop,可选 `"default"` 与 `"soft"`。`"soft"` 使用更浅的背景样式。
```tsx
JD
```
### 4. `Avatar.Image` 响应式属性(v3 新增)
**v2:** 仅支持 `src` 与 `onError`\
**v3:** `Avatar.Image` 现支持 `srcSet`、`sizes`、`loading`,用于响应式图片
```tsx
JD
```
### 5. `Avatar.Fallback` 的 `delayMs` prop(v3 新增)
**v2:** 立即显示后备,或由 `showFallback` 控制\
**v3:** `Avatar.Fallback` 支持 `delayMs`,可延后渲染,避免图片很快加载完成时出现后备闪烁
```tsx
JD
```
### 6. 图片与后备内容
**v2:** 使用 `src`、`name`、`showFallback`、`fallback` 等 props\
**v3:** 需显式渲染 ` ` 与 ` `
### 7. `color` 映射
| v2 color | v3 color | 说明 |
| ----------- | --------- | ------------ |
| `default` | `default` | 相同 |
| `primary` | `accent` | 已重命名 |
| `secondary` | `default` | 使用 `default` |
| `success` | `success` | 相同 |
| `warning` | `warning` | 相同 |
| `danger` | `danger` | 相同 |
### 8. `AvatarGroup` 已移除
**v2:** 提供独立的 `AvatarGroup` 组件\
**v3:** 无 `AvatarGroup` — 请用 CSS 类手动成组
## 迁移示例
### 尺寸与颜色
```tsx
```
```tsx
J
```
### 自定义后备
```tsx
import { Icon } from "@iconify/react";
}
/>
```
```tsx
import { Icon } from "@iconify/react";
```
### Avatar 组
```tsx
import { Avatar, AvatarGroup } from "@heroui/react";
```
```tsx
import { Avatar } from "@heroui/react";
```
### 带头像数量上限的组
```tsx
import { Avatar, AvatarGroup } from "@heroui/react";
```
```tsx
import { Avatar } from "@heroui/react";
const users = [
{ id: 1, src: "https://example.com/1.jpg", name: "User 1" },
{ id: 2, src: "https://example.com/2.jpg", name: "User 2" },
{ id: 3, src: "https://example.com/3.jpg", name: "User 3" },
{ id: 4, src: "https://example.com/4.jpg", name: "User 4" },
{ id: 5, src: "https://example.com/5.jpg", name: "User 5" },
];
{users.slice(0, 3).map((user) => (
{user.name.split(" ").map(n => n[0]).join("")}
))}
+{users.length - 3}
```
### 变体
```tsx
{/* v2 doesn't have variants, but uses color prop */}
```
```tsx
{/* v3 has variant prop */}
J
```
## 样式变化
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className` prop
```tsx
JD
```
## 组件组成
v3 `Avatar` 的结构如下:
```
Avatar (Root)
├── Avatar.Image (optional)
└── Avatar.Fallback (optional; shown when image fails or not provided)
```
## 生成首字母缩写的辅助函数
v3 没有 `name` prop,需要自行生成首字母缩写:
```tsx
function getInitials(name: string): string {
return name
.split(" ")
.map(n => n[0])
.join("")
.toUpperCase()
.slice(0, 2);
}
// Usage
{getInitials("John Doe")}
```
## 总结
1. **组件结构**:必须使用复合组件(`Avatar.Image`、`Avatar.Fallback`)
2. **`name` prop 已移除**:请手动生成首字母缩写
3. **`showFallback` 已移除**:图片失败时仍会显示后备
4. **新增 `variant` prop**:`"default"` | `"soft"`,用于视觉样式
5. **响应式图片**:`Avatar.Image` 支持 `srcSet`、`sizes`、`loading`
6. **后备延迟**:`Avatar.Fallback` 支持 `delayMs`,减轻后备闪烁
7. **颜色映射**:`primary` → `accent`,`secondary` → `default`
8. **`isBordered` 已移除**:请使用 Tailwind `ring-2 ring-background` 等类
9. **`radius` 已移除**:请使用 Tailwind `rounded-*` 类
10. **`isDisabled` 等已移除**:请使用 Tailwind `opacity-50` 等类
11. **`AvatarGroup` 已移除**:请用 CSS 手动成组
12. **`icon` prop 已移除**:请将图标内容放入 `Avatar.Fallback`
13. **`classNames` 已移除**:请在各子组件上使用 `className` prop
# Badge
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/badge
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/badge.mdx
> Badge 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Badge 文档](/docs/react/components/badge)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`Badge` 是一个包装组件,相对其子元素进行内容定位:
```tsx
import { Badge, Avatar } from "@heroui/react";
export default function App() {
return (
);
}
```
在 v3 中,Badge 改为复合组件模式,由 `Badge.Anchor` 负责定位:
```tsx
import { Badge, Avatar } from "@heroui/react";
export default function App() {
return (
U
5
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 单一 `Badge` 组件包裹子节点,通过 `content` prop 设置徽章文本\
**v3:** 复合组件:`Badge.Anchor`(定位包装器)、`Badge`(徽章本身)、`Badge.Label`(文本插槽,字符串子节点会自动包裹)
### 2. 变体
**v2:** `solid`、`flat`、`faded`、`shadow`\
**v3:** `primary`、`secondary`、`soft`
### 3. 颜色
**v2:** `default`、`primary`、`secondary`、`success`、`warning`、`danger`\
**v3:** `default`、`accent`、`success`、`warning`、`danger`
### 4. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ------------------ | -------------- | ------------------------------------------------------- |
| `content` | `Badge` 子节点 | 内容现在以子节点传入 |
| `children` | `Badge.Anchor` | 锚定元素放入 `Badge.Anchor` 内部 |
| `variant` | `Badge` | 取值变化(详见变体映射) |
| `color` | `Badge` | `primary` → `accent`,`secondary` 已移除 |
| `size` | `Badge` | 相同(`sm`、`md`、`lg`) |
| `placement` | `Badge` | 相同(`top-right`、`top-left`、`bottom-right`、`bottom-left`) |
| `shape` | - | 已移除(请改用 Tailwind CSS) |
| `showOutline` | - | 已移除(请改用 Tailwind CSS,例如 `border-2`) |
| `disableOutline` | - | 已移除 |
| `disableAnimation` | - | 已移除 |
| `isInvisible` | - | 已移除(请使用条件渲染) |
| `isOneChar` | - | 已移除(请改用 Tailwind CSS) |
| `isDot` | - | 省略子节点即可渲染为圆点 |
| `classNames` | - | 改在各子组件上使用 `className` |
### 5. 变体映射
| v2 变体 | v3 对应项 | 说明 |
| -------- | ----------- | -------------------------- |
| `solid` | `primary` | 实心背景 |
| `flat` | `soft` | 浅色背景 |
| `faded` | `secondary` | 边框 + 背景 |
| `shadow` | `primary` | 配合 Tailwind 的 `shadow-*` 类 |
### 6. 颜色映射
| v2 颜色 | v3 对应项 | 说明 |
| ----------- | -------------------- | ------ |
| `default` | `default` | 相同 |
| `primary` | `accent` | 已重命名 |
| `secondary` | `default` 或 `accent` | 视上下文而定 |
| `success` | `success` | 相同 |
| `warning` | `warning` | 相同 |
| `danger` | `danger` | 相同 |
## 迁移示例
### 基本徽章
```tsx
import { Badge, Avatar } from "@heroui/react";
```
```tsx
import { Badge, Avatar } from "@heroui/react";
U
5
```
### 圆点徽章
```tsx
```
```tsx
U
```
### 位置
```tsx
```
```tsx
U
5
```
### 可见性切换
```tsx
const [isInvisible, setIsInvisible] = useState(false);
```
```tsx
const [isVisible, setIsVisible] = useState(true);
U
{isVisible && 5 }
```
### 带图标内容
```tsx
import { Icon } from "@iconify/react";
} color="success">
```
```tsx
import { Icon } from "@iconify/react";
U
```
## 样式变更
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className` prop
```tsx
5
```
## 组件结构
v3 Badge 遵循以下结构:
```
Badge.Anchor (positioning wrapper)
├── [Anchored element: Avatar, Button, etc.]
└── Badge (the badge indicator)
└── Badge.Label (auto-wrapped for string/number children)
```
## 总结
1. **组件结构**:`Badge` 包裹子节点 → `Badge.Anchor` + `Badge` 作为同级
2. **移除 content prop**:`content` prop → 将内容作为 `Badge` 的子节点传入
3. **圆点徽章**:`isDot` prop → 省略子节点
4. **可见性**:`isInvisible` prop → 改用条件渲染
5. **变体收敛**:从 4 个变体减少为 3 个(`primary`、`secondary`、`soft`)
6. **颜色变化**:`primary` → `accent`,`secondary` 已移除
7. **移除的 prop**:`shape`、`showOutline`、`disableAnimation`、`isOneChar` —— 请改用 Tailwind CSS
8. **移除 classNames**:改在各复合子组件上使用 `className`
# Breadcrumbs
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/breadcrumbs
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/breadcrumbs.mdx
> Breadcrumbs 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Breadcrumbs 文档](/docs/react/components/breadcrumbs)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`Breadcrumbs` 接受 `items` prop 或子节点:
```tsx
import { Breadcrumbs, BreadcrumbItem } from "@heroui/react";
export default function App() {
return (
Home
Products
Current Page
);
}
```
在 v3 中,Breadcrumbs 改为复合组件模式,由 `Breadcrumbs` 与 `BreadcrumbsItem` 组成:
```tsx
import { Breadcrumbs, BreadcrumbsItem } from "@heroui/react";
export default function App() {
return (
Home
Products
Current Page
);
}
```
## 主要变化
### 1. 组件名称:`BreadcrumbItem` → `BreadcrumbsItem`
**v2:** 使用 `BreadcrumbItem`(单数)\
**v3:** 使用 `BreadcrumbsItem`(复数)
### 2. 自定义分隔符
**v2:** 通过 prop 自定义分隔符\
**v3:** 通过在 `Breadcrumbs` 根节点上设置 `separator` prop 自定义
### 3. 内置链接组件
**v2:** 项目可以是链接或纯文本\
**v3:** 当提供 `href` 时,项目会自动使用 `Link` 组件
### 4. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ----------------------------------------------------------- | ------------------ | --------------------------------------------------- |
| `maxItems` | - | 已移除(渲染全部项目,或自行实现折叠逻辑) |
| `itemsBeforeCollapse`、`itemsAfterCollapse`、`renderEllipsis` | - | 已移除(与 `maxItems` 折叠/省略号一并移除) |
| `items`(prop 写法) | - | 改用子节点 |
| `onAction` | - | 如有需要,可在单个项目上使用 `onPress` |
| `isDisabled` | 根节点上的 `isDisabled` | 现在根节点支持,可一次性禁用全部面包屑链接 |
| `classNames`、`itemClasses` | - | 改用 `Breadcrumbs` 与 `BreadcrumbsItem` 上的 `className` |
### 5. 根节点上的 `isDisabled` prop
**v2:** 根组件不支持 `isDisabled`\
**v3:** `Breadcrumbs` 根节点支持 `isDisabled`,可立即禁用所有面包屑链接
### 6. 自动 `aria-current="page"` 行为
**v3:** 最后一个未提供 `href` 的面包屑项目会自动带上 `aria-current="page"`,向辅助技术指明当前页面,无需手动配置。
### 7. 集成 React Aria Components
**v3:** 基于 React Aria Components 构建,提供更佳的无障碍体验与键盘导航支持。
## 迁移示例
### 自定义分隔符
```tsx
Home
Current
```
```tsx
import { Icon } from "@iconify/react";
import { Breadcrumbs, BreadcrumbsItem } from "@heroui/react";
}>
Home
Current
```
### 禁用状态
```tsx
{/* v2 did not support isDisabled on the root */}
Home
Current
```
```tsx
{/* v3 supports isDisabled on root to disable all links */}
Home
Current
```
## 总结
* 将 `BreadcrumbItem` 重命名为 `BreadcrumbsItem`
* 在根节点上设置 `separator`,可以是字符串或 React 元素(例如图标)
* 提供 `href` 的项目会自动使用内置 Link
* 根节点现在支持 `isDisabled`,可立即禁用全部面包屑链接
* 最后一个未提供 `href` 的项目会自动带上 `aria-current="page"`,提升无障碍体验
* v3 不再提供 `maxItems` / 折叠功能——请显示全部项目或自行实现;用 `className` 取代 `classNames` / `itemClasses`
* 基于 React Aria Components 构建,更易实现无障碍
# ButtonGroup
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/button-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/button-group.mdx
> ButtonGroup 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 ButtonGroup 文档](/docs/react/components/button-group)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`ButtonGroup` 用于将按钮分组:
```tsx
import { ButtonGroup, Button } from "@heroui/react";
export default function App() {
return (
First
Second
Third
);
}
```
在 v3 中,`ButtonGroup` 的基本结构相同,但加强了 prop 继承:
```tsx
import { ButtonGroup, Button } from "@heroui/react";
export default function App() {
return (
First
Second
Third
);
}
```
## 主要变化
### 1. 导入路径
**v2:** 从 `@heroui/react` 导入\
**v3:** 从 `@heroui/react` 导入(同一个包,但导入方式已统一)
### 2. Prop 继承
**v2:** ButtonGroup 会向子按钮传递 `size`、`color`、`variant`、`radius`、`isIconOnly`、`isDisabled`、`disableAnimation`、`disableRipple` 与 `fullWidth`。
**v3:** ButtonGroup 会向子按钮传递 `size`、`variant`、`isDisabled` 与 `fullWidth`。`color` 与 `radius` 等 prop 在按钮上已不存在(由 `variant` 取代),`disableAnimation` 与 `disableRipple` 已从设计系统中移除。
### 3. 隐藏分隔符
**v3:** 新增 `hideSeparator` prop,可隐藏按钮之间的视觉分隔线。
## 总结
* 在 ButtonGroup 上设置 `size`、`variant`、`isDisabled` 与 `fullWidth`,会作用到所有子按钮(v3 用单一的 `variant` 取代了 v2 的 `color` + `variant` 组合)
* 使用新增的 `hideSeparator` prop 可隐藏按钮之间的分隔线
* 结构保持不变;已被移除的 prop(`radius`、`disableAnimation`、`disableRipple`)在 v3 的 Button 上已不再可用
# Button
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/button
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/button.mdx
> Button 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Button 文档](/docs/react/components/button)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`Button` 通过 `color` 与 `variant` 组合使用:
```tsx
import { Button } from "@heroui/react";
export default function App() {
return Button ;
}
```
在 v3 中,`Button` 仅使用 `variant` prop(不再有独立的 `color` prop):
```tsx
import { Button } from "@heroui/react";
export default function App() {
return Button ;
}
```
## 主要变化
### 1. 变体与颜色
**v2:** 使用 `color` + `variant` 组合\
**v3:** 仅使用 `variant`(无独立 `color` prop)
| v2 color + variant | v3 变体 | 说明 |
| ------------------------------------ | ----------------------- | -------------------- |
| `color="primary" variant="solid"` | `variant="primary"` | 默认主按钮 |
| `color="default" variant="solid"` | `variant="primary"` | 使用 primary 变体 |
| `color="secondary" variant="solid"` | `variant="secondary"` | 相同 |
| `color="success" variant="solid"` | `variant="primary"` | 如需可配合自定义样式使用 primary |
| `color="warning" variant="solid"` | `variant="primary"` | 如需可配合自定义样式使用 primary |
| `color="danger" variant="solid"` | `variant="danger"` | 提供 danger 变体 |
| `color="primary" variant="bordered"` | `variant="secondary"` | 外观相近 |
| `color="primary" variant="light"` | `variant="tertiary"` | 外观相近 |
| `color="primary" variant="flat"` | `variant="tertiary"` | 外观相近 |
| `color="primary" variant="faded"` | `variant="secondary"` | 外观相近 |
| `color="primary" variant="ghost"` | `variant="ghost"` | 相同 |
| `color="danger" variant="flat"` | `variant="danger-soft"` | 新增柔和危险变体 |
**v2 变体:** `solid`、`bordered`、`light`、`flat`、`faded`、`shadow`、`ghost`\
**v3 变体:** `primary`、`secondary`、`tertiary`、`outline`、`ghost`、`danger`、`danger-soft`
### 2. 加载状态:`isLoading` → `isPending`
**v2:** 使用 `isLoading` prop\
**v3:** 使用 `isPending` prop
### 3. 默认宽度行为
**v2:** 按钮按尺寸带有最小宽度(sm:`min-w-16`,md:`min-w-20`,lg:`min-w-24`)\
**v3:** 按钮默认使用 `w-fit`(宽度随内容,无最小宽度)
因此当文案较短时,v3 按钮会比 v2 更窄。若要保留 v2 的最小宽度行为,请添加 Tailwind 类,请见下方 **尺寸与最小宽度** 示例小节。
### 4. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ----------------------------- | -------- | --------------------------------- |
| `isLoading` | `Button` | 已重命名为 `isPending` |
| `isIconOnly` | `Button` | v3 仍支持 — 渲染仅含图标的方形按钮 |
| `color` | — | 已移除(由变体负责样式) |
| `radius` | — | 已移除(请使用 Tailwind,例如 `rounded-lg`) |
| `startContent`, `endContent` | — | 请将图标作为 children 放置 |
| `spinner`, `spinnerPlacement` | — | 请通过渲染 prop 自行处理加载态 |
| `disableRipple` | — | 已移除(v3 已移除波纹) |
| `disableAnimation` | — | 已移除(动画由内部处理) |
| `classNames` | — | 请使用 `className` |
### 5. ButtonGroup 仍可用
**v2:** 提供独立的 `ButtonGroup` 组件\
**v3:** `ButtonGroup` 仍然存在。详见 [ButtonGroup 迁移指南](/docs/react/migration/button-group)。
## 迁移示例
### 变体
```tsx
Solid
Bordered
Ghost
```
```tsx
Primary
Secondary
Ghost
```
### danger-soft 变体
`danger-soft` 变体为 v3 新增,用于替代 v2 中 `color="danger" variant="flat"` 的用法。
```tsx
Delete
```
```tsx
Delete
```
### 仅图标(`isIconOnly`)
`isIconOnly` prop 在 v2 与 v3 中均可用,用于渲染适配单个图标的方形按钮。v3 中请用 `variant` 替代 `color` 控制样式。
```tsx
```
```tsx
import { Icon } from "@iconify/react";
```
### 加载状态
```tsx
{/* Simple loading */}
Loading
{/* Loading with conditional content */}
setIsLoading(true)}
>
Upload File
```
```tsx
import { useState } from "react";
import { Spinner } from "@heroui/react";
import { Icon } from "@iconify/react";
const [isLoading, setIsLoading] = useState(false);
{/* Simple loading */}
{({isPending}) => (
<>
{isPending && }
Loading
>
)}
{/* Loading with conditional content */}
setIsLoading(true)}
>
{({isPending}) => (
<>
{isPending ? (
) : (
)}
{isPending ? "Uploading..." : "Upload File"}
>
)}
```
### 带图标
```tsx
import { Icon } from "@iconify/react";
}
>
Take a photo
}
variant="bordered"
>
Delete user
```
```tsx
import { Icon } from "@iconify/react";
Take a photo
Delete user
```
### 仅图标按钮
```tsx
```
```tsx
import { Icon } from "@iconify/react";
```
### 按钮组
```tsx
import { Button, ButtonGroup } from "@heroui/react";
One
Two
Three
```
```tsx
import { Button, ButtonGroup } from "@heroui/react";
One
Two
Three
```
### 尺寸与最小宽度
```tsx
{/* v2 automatically applies minimum widths */}
Save
```
```tsx
{/* v3 uses w-fit by default - add min-width to match v2 */}
Save
```
## 渲染 prop 模式
v3 `Button` 支持渲染 prop 模式,可拿到状态信息:
```tsx
{({isPending, isPressed, isHovered, isFocused, isFocusVisible, isDisabled}) => (
<>
{isPending && }
{isPressed ? "Pressed!" : "Click me"}
>
)}
```
可用的渲染 prop:
* `isPending` — 是否处于加载状态
* `isPressed` — 是否正被按下
* `isHovered` — 是否悬停
* `isFocused` — 是否聚焦
* `isFocusVisible` — 是否应显示焦点环
* `isDisabled` — 是否禁用
## 总结
1. **`color` prop 已移除**:请用 `variant` 替代 `color` + `variant` 组合
2. **变体体系变更**:新变体系统(`primary`、`secondary`、`tertiary`、`outline`、`ghost`、`danger`、`danger-soft`)
3. **`danger-soft` 变体**:v3 新增,替代 v2 的 `color="danger" variant="flat"`
4. **`isLoading` → `isPending`**:加载相关 prop 已重命名
5. **`isIconOnly`**:v3 仍支持 — 用于仅图标的方形按钮
6. **默认宽度变更**:按钮默认 `w-fit`,不再内置最小宽度 — 可添加 `min-w-*` 贴近 v2
7. **图标**:`startContent` / `endContent` 已移除 — 请将图标作为 children
8. **加载指示器**:需通过渲染 prop 自行组合 Spinner 等
9. **渲染 prop**:v3 `Button` 的 children 可为函数,参数包含 `isPending`、`isPressed`、`isHovered`、`isFocused`、`isFocusVisible`、`isDisabled`
10. **`radius` 已移除**:请使用 Tailwind CSS 类
11. **波纹已移除**:v3 无波纹效果
12. **ButtonGroup**:参见 [ButtonGroup 迁移指南](/docs/react/migration/button-group)
13. **`classNames` 已移除**:请使用 `className` prop
# Calendar
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/calendar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/calendar.mdx
> Calendar 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Calendar 文档](/docs/react/components/calendar)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`Calendar` 是一个完全通过 props 配置的单一组件:
```tsx
import { Calendar } from "@heroui/react";
export default function App() {
return ;
}
```
在 v3 中,Calendar 改用带显式子组件的复合组件模式:
```tsx
import { Calendar } from "@heroui/react";
export default function App() {
return (
{(day) => {day} }
{(date) => }
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 单一 `Calendar` 组件,所有布局都在内部处理\
**v3:** 复合组件:`Calendar.Header`、`Calendar.Heading`、`Calendar.NavButton`、`Calendar.Grid`、`Calendar.GridHeader`、`Calendar.GridBody`、`Calendar.HeaderCell`、`Calendar.Cell`、`Calendar.CellIndicator`
### 2. 年份选择器
**v2:** 通过 `showMonthAndYearPickers` prop 提供内置月份 / 年份选择器\
**v3:** 使用专用复合组件:`Calendar.YearPickerTrigger`、`Calendar.YearPickerGrid`、`Calendar.YearPickerGridBody`、`Calendar.YearPickerCell`
### 3. Prop 变更
| v2 prop | v3 等效项 | 说明 |
| ------------------------- | ------------------------ | ----------------------------------------------------------- |
| `value` | `value` | 保持一致 |
| `defaultValue` | `defaultValue` | 保持一致 |
| `onChange` | `onChange` | 保持一致 |
| `focusedValue` | `focusedValue` | 保持一致 |
| `onFocusChange` | `onFocusChange` | 保持一致 |
| `minValue` | `minValue` | 保持一致 |
| `maxValue` | `maxValue` | 保持一致 |
| `isDateUnavailable` | `isDateUnavailable` | 保持一致 |
| `isDisabled` | `isDisabled` | 保持一致 |
| `isReadOnly` | `isReadOnly` | 保持一致 |
| `isInvalid` | `isInvalid` | 保持一致 |
| `visibleMonths` | `visibleDuration` | 改为 `{months: number}` 对象 |
| `showMonthAndYearPickers` | - | 使用 `Calendar.YearPickerTrigger` 和 `Calendar.YearPickerGrid` |
| `onHeaderExpandedChange` | `onYearPickerOpenChange` | 已重命名 |
| `color` | - | 已移除(请改用 Tailwind CSS) |
| `calendarWidth` | - | 已移除(请改用 `className` 或 Tailwind CSS) |
| `weekdayStyle` | - | 已移除 |
| `pageBehavior` | `pageBehavior` | 保持一致 |
| `firstDayOfWeek` | - | 改用 `I18nProvider` 的 locale |
| `hideDisabledDates` | - | 已移除 |
| `disableAnimation` | - | 已移除 |
| `topContent` | - | 将自定义内容作为 `Calendar` children 放在 `Calendar.Grid` 之前 |
| `bottomContent` | - | 将自定义内容作为 `Calendar` children 放在 `Calendar.Grid` 之后 |
| `classNames` | - | 在各个复合组件上使用 `className` |
| `errorMessage` | - | 已移除(请在外部处理校验) |
### 4. color prop 已移除
**v2:** `color` prop 支持 `default`、`primary`、`secondary`、`success`、`warning`、`danger`\
**v3:** 不再提供 `color` prop,请使用 Tailwind CSS 类设置单元格样式,或通过自定义 `calendar.css` 覆盖
## 迁移示例
### 基本日历
```tsx
import { Calendar } from "@heroui/react";
import { today, getLocalTimeZone } from "@internationalized/date";
```
```tsx
import { Calendar } from "@heroui/react";
import { today, getLocalTimeZone } from "@internationalized/date";
{(day) => {day} }
{(date) => }
```
### 受控状态
```tsx
import { useState } from "react";
import { Calendar } from "@heroui/react";
import { parseDate } from "@internationalized/date";
const [value, setValue] = useState(parseDate("2024-03-07"));
```
```tsx
import { useState } from "react";
import { Calendar } from "@heroui/react";
import { parseDate } from "@internationalized/date";
const [value, setValue] = useState(parseDate("2024-03-07"));
{(day) => {day} }
{(date) => }
```
### 月份和年份选择器
```tsx
```
```tsx
{(day) => {day} }
{(date) => }
{(year) => }
```
### 多个月份
```tsx
```
```tsx
{(day) => {day} }
{(date) => }
{(day) => {day} }
{(date) => }
```
### 不可用日期
```tsx
import { isWeekend } from "@internationalized/date";
import { useLocale } from "@react-aria/i18n";
const { locale } = useLocale();
isWeekend(date, locale)}
/>
```
```tsx
import { isWeekend } from "@internationalized/date";
import { useLocale } from "@react-aria/i18n";
const { locale } = useLocale();
isWeekend(date, locale)}
>
{(day) => {day} }
{(date) => }
```
### 顶部与底部内容
```tsx
Select a date}
bottomContent={
setValue(today(getLocalTimeZone()))}>
Today
}
/>
```
```tsx
Select a date
{(day) => {day} }
{(date) => }
setValue(today(getLocalTimeZone()))}>
Today
```
### 单元格指示器
```tsx
{/* v2 did not have a built-in cell indicator API */}
```
```tsx
{(day) => {day} }
{(date) => (
{({formattedDate}) => (
<>
{formattedDate}
{hasEvent(date) && }
>
)}
)}
```
## 样式变化
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className` prop
```tsx
{(day) => {day} }
{(date) => }
```
## 组件结构
v3 Calendar 遵循以下结构:
```
Calendar (Root)
├── [Custom top content]
├── Calendar.Header
│ ├── Calendar.Heading (or Calendar.YearPickerTrigger)
│ ├── Calendar.NavButton slot="previous"
│ └── Calendar.NavButton slot="next"
├── Calendar.Grid (one per visible month)
│ ├── Calendar.GridHeader
│ │ └── Calendar.HeaderCell (render prop)
│ └── Calendar.GridBody
│ └── Calendar.Cell (render prop)
│ └── Calendar.CellIndicator (optional)
├── Calendar.YearPickerGrid (optional)
│ └── Calendar.YearPickerGridBody
│ └── Calendar.YearPickerCell
└── [Custom bottom content]
```
## 总结
1. **组件结构**:单一组件 → 带显式布局控制的复合组件
2. **年份选择器**:`showMonthAndYearPickers` prop → 专用的 `Calendar.YearPickerTrigger` 和 `Calendar.YearPickerGrid` 组件
3. **多个月份**:`visibleMonths={n}` → `visibleDuration={{months: n}}`,并使用多个带 `offset` 的 `Calendar.Grid` 组件
4. **color 已移除**:请改用 Tailwind CSS 类
5. **顶部 / 底部内容**:prop 已移除 → 直接将内容作为 children 放在 `Calendar` 内
6. **单元格自定义**:新增 `Calendar.CellIndicator`,且 `Calendar.Cell` 支持渲染 prop
7. **样式**:`classNames` prop → 各个复合组件上的 `className`
8. **已移除 prop**:`calendarWidth`、`weekdayStyle`、`hideDisabledDates`、`disableAnimation` —— 请使用 Tailwind CSS 或省略
# Card
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/card
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/card.mdx
> Card 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Card 文档](/docs/react/components/card)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,Card 使用独立导出的子组件:`CardHeader`、`CardBody`、`CardFooter`。
```tsx
import { Card, CardHeader, CardBody, CardFooter } from "@heroui/react";
export default function App() {
return (
Header
Body
Footer
);
}
```
在 v3 中,Card 使用复合组件模式,子部件以 `Card.*` 形式显式组合:
```tsx
import { Card } from "@heroui/react";
export default function App() {
return (
Title
Description
Body content
Footer
);
}
```
## 关键变化
### 1. 组件结构
**v2:** 独立组件:`CardHeader`、`CardBody`、`CardFooter`\
**v3:** 复合组件:`Card.Header`、`Card.Title`、`Card.Description`、`Card.Content`、`Card.Footer`
### 2. 组件命名变化
| v2 组件 | v3 组件 | 说明 |
| ------------ | ------------------ | ---------- |
| `CardHeader` | `Card.Header` | 功能等价 |
| `CardBody` | `Card.Content` | 已重命名 |
| `CardFooter` | `Card.Footer` | 功能等价 |
| — | `Card.Title` | 新增:用于标题区标题 |
| — | `Card.Description` | 新增:用于标题区描述 |
### 3. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ---------------------------------- | ----- | ------------------------------------------ |
| `shadow` | — | 已移除(例如使用 Tailwind `shadow-sm`、`shadow-md`) |
| `radius` | — | 已移除(例如使用 Tailwind `rounded-lg`) |
| `fullWidth` | — | 已移除(例如使用 Tailwind `w-full`) |
| `isHoverable` | — | 已移除(请使用 Tailwind 的 hover class) |
| `isPressable` | — | 请在 Card 内使用 `button` 或 `a` 包裹可点击区域 |
| `isBlurred`、`isFooterBlurred` | — | 已移除(例如使用 Tailwind `backdrop-blur-*`) |
| `isDisabled` | — | 已移除(请使用条件渲染处理) |
| `disableAnimation`、`disableRipple` | — | 已移除 |
| `allowTextSelectionOnPress` | — | 已移除(不再适用) |
| `classNames` | — | 在各子部件上使用 `className` |
### 4. 变体
**v2:** 无 `variant` prop(主要通过 shadow / radius 控制观感)\
**v3:** 提供 `variant` prop:`transparent`、`default`、`secondary`、`tertiary`
## 迁移示例
### Card 结构
```tsx
{/* Basic structure */}
Header
Body content
Footer
{/* With title and description */}
Daily Mix
12 Tracks
Frontend Radio
Content
```
```tsx
{/* 基础结构 */}
Header
Body content
Footer
{/* 标题 + 描述 */}
Frontend Radio
Daily Mix • 12 Tracks
Content
```
### 可点击的 Card
```tsx
console.log("pressed")}
>
Clickable card
```
```tsx
console.log("pressed")}
>
Clickable card
```
### 带图片的 Card
```tsx
Frontend Radio
```
```tsx
Frontend Radio
```
### 页脚背景模糊(Blurred Footer)
```tsx
import { Card, CardFooter, Image, Button } from "@heroui/react";
Available soon.
Notify me
```
```tsx
import { Card, Button } from "@heroui/react";
Available soon.
Notify me
```
### Card 变体
```tsx
{/* v2 doesn't have variants, uses shadow/radius */}
Content
```
```tsx
Content
Content
```
## 样式变化
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className` prop
```tsx
Title
Content
Footer
```
## 组件组成
v3 的 Card 结构如下:
```
Card (Root)
├── Card.Header (optional)
│ ├── Card.Title (optional)
│ └── Card.Description (optional)
├── Card.Content (optional)
└── Card.Footer (optional)
```
## 总结
1. **组件结构**:请使用复合组件,而不是 v2 的独立导出子组件。
2. **`CardBody` → `Card.Content`**:组件已重命名。
3. **新增子组件**:`Card.Title` 与 `Card.Description`,用于结构化标题区。
4. **大量样式 prop 已移除**:请改用 Tailwind CSS class。
5. **可点击 Card**:不要用 `isPressable`,改为在 Card 内使用 `button` 或链接。
6. **模糊效果**:请使用 Tailwind `backdrop-blur-*` class,而不是相关 prop。
7. **变体**:新增用于表达语义层级 / 视觉强调度的变体体系。
8. **`classNames` 已移除**:在各子部件上使用 `className` prop。
# CheckboxGroup
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/checkbox-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/checkbox-group.mdx
> CheckboxGroup 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 CheckboxGroup 文档](/docs/react/components/checkbox-group)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`CheckboxGroup` 是单个组件,通过 `label` prop 描述分组,子项为简单的 `Checkbox`:
```tsx
import { Checkbox, CheckboxGroup } from "@heroui/react";
export default function App() {
return (
Coding
Design
Writing
);
}
```
在 v3 中,`CheckboxGroup` 仍是单个组件,但改为以 `Label` / `Description` 作为子节点,并使用全新的 Checkbox 复合结构:
```tsx
import { CheckboxGroup, Checkbox, Label, Description } from "@heroui/react";
export default function App() {
return (
Select interests
Coding
Design
Writing
);
}
```
## 主要变化
### 1. 组件名称保持一致
**v2:** `CheckboxGroup`(单一组件,通过 `label` prop 描述分组)\
**v3:** `CheckboxGroup`(单一组件,使用 `Label` 组件代替 `label` prop)
### 2. Label prop
**v2:** 在 `CheckboxGroup` 上使用 `label` prop\
**v3:** 改用 `Label` 组件作为 `CheckboxGroup` 的子节点
### 3. Checkbox 结构
**v2:** 简单的 Checkbox 组件,子节点直接作为标签\
**v3:** 复合 Checkbox 组件,由 `Checkbox.Content`、`Checkbox.Control` 与 `Checkbox.Indicator` 组成
### 4. 事件处理函数
**v2:** 使用 `onValueChange` prop\
**v3:** 改用 `onChange` prop(来自 React Aria Components)
### 5. 变体支持
**v3:** 新增 `variant` prop,用于设置分组容器的样式
## 迁移示例
### 受控复选框组
```tsx
import { useState } from "react";
import { Checkbox, CheckboxGroup } from "@heroui/react";
const [selected, setSelected] = useState(["coding"]);
Coding
Design
Writing
```
```tsx
import { useState } from "react";
import { CheckboxGroup, Checkbox, Label } from "@heroui/react";
const [selected, setSelected] = useState(["coding"]);
Select interests
Coding
Design
Writing
```
## 总结
* 继续使用 `CheckboxGroup`;用 `Label` 子组件取代 `label` prop
* 将 Checkbox 子项更新为复合组件结构(`Checkbox.Content`、`Checkbox.Control`、`Checkbox.Indicator`)
* 将 `onValueChange` 改为 `onChange`
* 添加 `name` prop 以便集成到表单中
* 基于 React Aria Components 构建,更易实现无障碍
# Checkbox
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/checkbox
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/checkbox.mdx
> Checkbox 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Checkbox 文档](/docs/react/components/checkbox)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`Checkbox` 将 `children` 作为标签文本:
```tsx
import { Checkbox } from "@heroui/react";
export default function App() {
return Option ;
}
```
在 v3 中,Checkbox 采用复合组件模式,并使用显式子组件:
```tsx
import { Checkbox, Label } from "@heroui/react";
export default function App() {
return (
Option
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 简单组件,`children` 即标签\
**v3:** 复合组件:`Checkbox.Content`、`Checkbox.Control`、`Checkbox.Indicator`
### 2. 标签处理
**v2:** 标签直接作为 `children` 传入 `Checkbox`\
**v3:** 标签放在 `Checkbox.Content`(可点击的标签)内;`Description` / `FieldError` 是 `Checkbox.Content` 的同级节点
### 3. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ------------------ | ---------- | ------------------------------------------------- |
| `onValueChange` | `Checkbox` | 请使用 `onChange` |
| `color` | — | 已移除(请在 `Checkbox.Control` 上使用 Tailwind) |
| `size` | — | 已移除(请使用 Tailwind,例如 `size-4`、`size-5`) |
| `radius` | — | 已移除(请使用 Tailwind,例如 `rounded-sm`) |
| `lineThrough` | — | 已移除(请在标签上使用 Tailwind `line-through`) |
| `icon` | — | 请在 `Checkbox.Indicator` 内使用自定义内容 |
| `classNames` | — | 请在各部分上使用 `className` |
| `disableAnimation` | — | 已移除 |
| — | `variant` | 新增 prop:`"primary"`(默认)或 `"secondary"`,用于较低强调度的样式 |
### 4. CheckboxGroup 变化
**v2:** `CheckboxGroup`(独立组件)使用 `label` prop,子节点为简单的 `Checkbox`\
**v3:** `CheckboxGroup`(独立组件)将 `Label` / `Description` 作为子节点,且每个 `Checkbox` 使用复合结构
迁移 CheckboxGroup 时请参阅 [CheckboxGroup 迁移指南](/docs/react/migration/checkbox-group)。
## 迁移示例
### 受控 Checkbox
```tsx
import { useState } from "react";
const [isSelected, setIsSelected] = useState(false);
Subscribe
```
```tsx
import { useState } from "react";
import { Label } from "@heroui/react";
const [isSelected, setIsSelected] = useState(false);
Subscribe
```
### 含描述的 Checkbox
```tsx
{/* v2 无内置描述 */}
Option
Description text
```
```tsx
import { Checkbox, Label, Description } from "@heroui/react";
Option
Description text
```
### CheckboxGroup
```tsx
import { Checkbox, CheckboxGroup } from "@heroui/react";
Buenos Aires
Sydney
San Francisco
```
```tsx
import { CheckboxGroup, Checkbox, Label, Description } from "@heroui/react";
Select cities
Choose all that apply
Buenos Aires
Sydney
San Francisco
```
### 颜色与尺寸
```tsx
{/* Colors */}
Primary
{/* Sizes */}
Medium
```
```tsx
{/* Colors - use Tailwind classes */}
Primary
{/* Sizes - use Tailwind classes */}
Medium
```
### 自定义图标 / 指示器
```tsx
}>
Option
```
```tsx
{({isSelected}) => isSelected ? : null}
Option
```
### 变体
v3 引入 `variant` prop,可选 `"primary"`(默认)与 `"secondary"`:
```tsx
{/* Primary variant (default) */}
Primary
{/* Secondary variant - lower emphasis, suitable for Surface components */}
Secondary
```
### 半选(indeterminate)状态
```tsx
Option
```
```tsx
Option
```
## 渲染 prop 模式
v3 Checkbox 支持渲染 prop,用于暴露状态信息:
```tsx
{({isSelected, isIndeterminate, isHovered, isPressed, isFocused, isDisabled}) => (
<>
{isSelected ? "Terms accepted" : "Accept terms"}
>
)}
```
`Checkbox.Indicator` 也支持渲染 prop,并提供相同的状态字段。便于根据 Checkbox 状态渲染自定义指示器:
```tsx
{({isSelected, isIndeterminate}) =>
isIndeterminate ? : isSelected ? : null
}
Custom indicator
```
可用的渲染 prop 字段:
* `isSelected`:是否选中
* `isIndeterminate`:是否为半选状态
* `isHovered`:是否悬停
* `isPressed`:是否处于按下中
* `isFocused`:是否聚焦
* `isFocusVisible`:是否应显示焦点环
* `isDisabled`:是否禁用
* `isReadOnly`:是否只读
## 样式变化
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className`
```tsx
Option
```
## 组件剖析
v3 Checkbox 的结构如下:
```
Checkbox (Root)
├── Checkbox.Content (the clickable label)
│ ├── Checkbox.Control
│ │ └── Checkbox.Indicator
│ └── Label
├── Description (optional, sibling)
└── FieldError (optional, sibling)
```
## 总结
1. **组件结构**:必须使用复合组件(`Button`、`Control`、`Indicator`)。
2. **标签处理**:标签放在 `Checkbox.Content` 内。
3. **`onValueChange` → `onChange`**:事件处理函数 prop 已重命名。
4. **颜色已移除**:请在 `Checkbox.Control` 上使用 Tailwind CSS 类。
5. **尺寸已移除**:请在 `Checkbox.Control` 上使用 Tailwind CSS 类。
6. **圆角已移除**:请在 `Checkbox.Control` 上使用 Tailwind CSS 类。
7. **`lineThrough` 已移除**:请在标签上使用 Tailwind `line-through` 类。
8. **`icon` prop 已移除**:请在 `Checkbox.Indicator` 内使用自定义内容。
9. **CheckboxGroup**:组件名不变;子节点使用 `Label` / `Description`,且 `Checkbox` 使用复合结构。
10. **`classNames` 已移除**:请在各子组件上使用 `className`。
11. **新增 `variant` prop**:支持 `"primary"`(默认)与 `"secondary"`,用于较低强调度样式。
12. **指示器渲染 prop**:`Checkbox.Indicator` 可传入渲染函数,参数包含 `isSelected`、`isIndeterminate` 等状态。
# Chip
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/chip
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/chip.mdx
> Chip 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Chip 文档](/docs/react/components/chip)。本指南只关注从 HeroUI v2 的迁移。
## 关键变化
### 1. 变体
**v2:** `solid`、`bordered`、`light`、`flat`、`faded`、`shadow`、`dot`\
**v3:** `primary`、`secondary`、`tertiary`、`soft`
### 2. 颜色
**v2:** `default`、`primary`、`secondary`、`success`、`warning`、`danger`\
**v3:** `default`、`accent`、`success`、`warning`、`danger`
### 3. 复合组件模式
v3 引入复合组件模式,并提供 `Chip.Label` 子组件:
* **`Chip.Label`**:渲染 Chip 内的标签文本。纯文本 `children` 会自动包在 `` 中,因此多数场景不必手写;需要自定义 `className` 时可显式使用。
```tsx
{/* Implicit label -- plain text is auto-wrapped in Chip.Label */}
Badge
{/* Explicit label -- useful when you need a custom className */}
Badge
```
### 4. 尺寸变体
**v2:** `sm`、`md`、`lg`(通过 `size` prop)\
**v3:** `sm`、`md`、`lg`(通过 `size` prop,API 相同)
可用尺寸与 v2 一致,但 v3 通过 BEM 风格的 CSS class(`chip--sm`、`chip--md`、`chip--lg`)应用。默认尺寸为 `md`。
```tsx
Small
Medium (default)
Large
```
### 5. 复合变体 class
v3 支持组合变体与颜色 class,以实现更精细的样式。下列复合 class 在 CSS 中带有默认样式:
**primary 变体组合:** `.chip--primary.chip--accent`、`.chip--primary.chip--success`、`.chip--primary.chip--warning`、`.chip--primary.chip--danger`
**soft 变体组合:** `.chip--accent.chip--soft`、`.chip--success.chip--soft`、`.chip--warning.chip--soft`、`.chip--danger.chip--soft`
你也可以在 CSS 中通过 `@layer components` 为其他任意组合(例如 `.chip--secondary.chip--accent`)补充样式。
### 6. Prop 变更
| v2 prop | v3 位置 | 说明 |
| --------------------------- | ----- | ---------------------------------------- |
| `radius` | — | 已移除(例如使用 Tailwind `rounded-full`) |
| `avatar` | — | 使用 `children`(例如将 ` ` 作为第一个子节点) |
| `startContent`、`endContent` | — | 直接使用 `children` |
| `onClose` | — | 手动实现关闭(例如使用 `CloseButton`) |
| `classNames` | — | 使用 `className` |
| `isDisabled` | — | 使用条件渲染或 Tailwind(例如 `opacity-50`) |
### 7. 变体映射
| v2 变体 | v3 对应 | 说明 |
| ---------- | ----------- | ---------------------------- |
| `solid` | `primary` | 实心背景 |
| `bordered` | `secondary` | 边框 + 透明背景 |
| `light` | `soft` | 浅色背景 |
| `flat` | `tertiary` | 透明背景 |
| `faded` | `secondary` | 观感相近 |
| `shadow` | `primary` | 配合 Tailwind `shadow-*` class |
| `dot` | — | 不再内置,请自行实现 |
### 8. 颜色映射
| v2 颜色 | v3 对应 | 说明 |
| ----------- | -------------------- | ----- |
| `default` | `default` | 相同 |
| `primary` | `accent` | 已重命名 |
| `secondary` | `default` 或 `accent` | 视场景选择 |
| `success` | `success` | 相同 |
| `warning` | `warning` | 相同 |
| `danger` | `danger` | 相同 |
## 结构变化
在 v2 中,`Chip` 是一个通过大量 prop 控制样式的组件:
```tsx
import { Chip } from "@heroui/react";
export default function App() {
return Chip ;
}
```
在 v3 中,`Chip` 的 API 更精简,变体更少:
```tsx
import { Chip } from "@heroui/react";
export default function App() {
return Chip ;
}
```
## 迁移示例
### 变体与颜色
```tsx
{/* Variants */}
Solid
Bordered
Light
{/* Colors */}
Primary
Success
```
```tsx
{/* Variants */}
Primary
Secondary
Soft
{/* Colors */}
Accent
Success
```
### 带图标
```tsx
import { Icon } from "@iconify/react";
{/* Start content */}
}>
Chip
{/* End content */}
}>
Chip
```
```tsx
import { Icon } from "@iconify/react";
{/* 前置图标 */}
Chip
{/* 后置图标 */}
Chip
```
### 带 Avatar
```tsx
import { Avatar } from "@heroui/react";
}
variant="flat"
>
Avatar
```
```tsx
import { Avatar, Chip } from "@heroui/react";
JW
Avatar
```
### 带关闭按钮
```tsx
console.log("close")}>
Chip
```
```tsx
import { CloseButton } from "@heroui/react";
Chip
console.log("close")}
/>
```
### 已移除的变体
```tsx
{/* Dot variant */}
Dot
{/* Shadow variant */}
Shadow
```
```tsx
import { Icon } from "@iconify/react";
{/* dot:自行实现 */}
Dot
{/* shadow:使用 Tailwind class */}
Shadow
```
## 样式变化
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className` prop
```tsx
{/* Content with custom classes */}
Chip
```
## 总结
1. **变体收敛**:由 7 种变体收敛为 4 种。
2. **颜色调整**:`primary` → `accent`,并弱化 `secondary` 的对应关系。
3. **复合组件**:新增 `Chip.Label`;纯文本 `children` 会自动包在 `Chip.Label` 中。
4. **尺寸变体**:仍可通过 `size` prop 使用 `sm`、`md`、`lg`,并以 BEM class(`chip--sm`、`chip--md`、`chip--lg`)落地。
5. **复合变体 class**:变体 + 颜色组合(例如 `.chip--primary.chip--accent`、`.chip--soft.chip--success`)具备内置样式,并可在 `@layer components` 中扩展。
6. **`radius` 已移除**:请改用 Tailwind CSS class。
7. **`avatar` prop 已移除**:请改用 `children`。
8. **`startContent` / `endContent` 已移除**:请改用 `children`。
9. **关闭能力已移除**:请使用 `CloseButton` 自行组合。
10. **`dot` 变体已移除**:请用图标等方式自行实现。
11. **`shadow` 变体已移除**:请使用 Tailwind `shadow-*` class。
12. **`isDisabled` prop 已移除**:请使用条件渲染或 CSS class。
13. **`classNames` 已移除**:请直接使用 `className` prop。
# CircularProgress
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/circular-progress
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/circular-progress.mdx
> CircularProgress 从 HeroUI v2 到 v3(现在称为 ProgressCircle)的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 ProgressCircle 文档](/docs/react/components/progress-circle)。本指南只关注从 HeroUI v2 的迁移。
正在寻找 Progress(线性)的迁移说明?请参阅 [Progress 迁移指南](/docs/react/migration/progress)。
## 组件重命名
`CircularProgress` 在 v3 中已重命名为 `ProgressCircle`。
## 结构变化
在 v2 中,`CircularProgress` 是单个组件,通过 prop 进行配置:
```tsx
import { CircularProgress } from "@heroui/react";
export default function App() {
return (
);
}
```
在 v3 中,`ProgressCircle` 改为复合组件模式:
```tsx
import { ProgressCircle } from "@heroui/react";
export default function App() {
return (
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 单一 `CircularProgress` 组件,SVG 在内部渲染\
**v3:** 复合组件:`ProgressCircle`、`ProgressCircle.Track`(SVG 元素)、`ProgressCircle.TrackCircle`(背景圆环)、`ProgressCircle.FillCircle`(进度弧)
### 2. 颜色
**v2:** `default`、`primary`、`secondary`、`success`、`warning`、`danger`\
**v3:** `default`、`accent`、`success`、`warning`、`danger`
### 3. Prop 变更
| v2 prop | v3 对应项 | 说明 |
| ------------------ | ----------------- | ------------------------------------------------------------------ |
| `value` | `value` | 相同 |
| `minValue` | `minValue` | 相同 |
| `maxValue` | `maxValue` | 相同 |
| `isIndeterminate` | `isIndeterminate` | 默认值变化:v2 为 `true`,v3 为 `false` |
| `formatOptions` | `formatOptions` | 相同 |
| `size` | `size` | 相同(`sm`、`md`、`lg`) |
| `color` | `color` | `primary` → `accent`,`secondary` 已移除 |
| `label` | - | 改用 `Label` 组件 |
| `valueLabel` | - | 改用 render prop 模式 |
| `showValueLabel` | - | 将值的内容直接放入 `ProgressCircle` 中 |
| `strokeWidth` | - | 直接设置在 `ProgressCircle.TrackCircle` 与 `ProgressCircle.FillCircle` 上 |
| `isDisabled` | - | 已移除 |
| `disableAnimation` | - | 已移除 |
| `classNames` | - | 改在各复合子组件上使用 `className` |
## 迁移示例
### 基本环形进度
```tsx
import { CircularProgress } from "@heroui/react";
```
```tsx
import { ProgressCircle } from "@heroui/react";
```
### 不确定状态
```tsx
{/* isIndeterminate defaults to true in v2 */}
```
```tsx
{/* isIndeterminate defaults to false in v3, must be explicit */}
```
### 带标签
```tsx
```
```tsx
import { ProgressCircle, Label } from "@heroui/react";
```
### 自定义 SVG prop
```tsx
```
```tsx
```
## 样式变更
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className` prop
```tsx
```
## 组件结构
```
ProgressCircle (Root)
└── ProgressCircle.Track (SVG element)
├── ProgressCircle.TrackCircle (background circle)
└── ProgressCircle.FillCircle (progress arc)
```
## 总结
1. **重命名**:`CircularProgress` → `ProgressCircle`
2. **组件结构**:单组件 → 复合组件(`ProgressCircle.Track`、`ProgressCircle.TrackCircle`、`ProgressCircle.FillCircle`)
3. **不确定状态默认值变化**:`isIndeterminate` 在 v3 中默认为 `false`(v2 中为 `true`)
4. **标签**:`label` prop → `Label` 组件
5. **SVG prop**:原本在根上的 `strokeWidth` → 直接设置在 `TrackCircle` 与 `FillCircle` 上
6. **颜色变化**:`primary` → `accent`,`secondary` 已移除
7. **移除的 prop**:`isDisabled`、`disableAnimation` → 请改用 Tailwind CSS
8. **移除 classNames**:改在各复合子组件上使用 `className`
# Code
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/code
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/code.mdx
> Code 从 HeroUI v2 到 v3 的迁移指南。
Code 组件已在 HeroUI v3 中**移除**。请改为使用原生 HTML `` 元素,并配合 Tailwind CSS class 编写样式。
## 关键变化
### 1. 组件移除
**v2:** 来自 `@heroui/react` 的 `` 组件\
**v3:** 原生 HTML `` 元素与 Tailwind CSS class
### 2. 变体映射
v2 的 Code 组件提供以下变体,需要在迁移时替换:
| v2 变体 | v3 对应 | 说明 |
| ------------------- | -------------------------------- | ----------------------------- |
| `color="default"` | `bg-default/40 text-default-700` | 使用 Tailwind 不透明度工具类 |
| `color="primary"` | `bg-accent/20 text-accent-600` | v3 中 `primary` 已重命名为 `accent` |
| `color="secondary"` | `bg-default/40 text-default-700` | 与 default 相近 |
| `color="success"` | `bg-success/20 text-success-700` | 颜色名相同 |
| `color="warning"` | `bg-warning/20 text-warning-700` | 颜色名相同 |
| `color="danger"` | `bg-danger/20 text-danger-600` | 颜色名相同 |
| `size="sm"` | `text-sm` | Tailwind 字号 |
| `size="md"` | `text-base` | Tailwind 字号 |
| `size="lg"` | `text-lg` | Tailwind 字号 |
| `radius="none"` | `rounded-none` | Tailwind 圆角 |
| `radius="sm"` | `rounded-sm` | Tailwind 圆角 |
| `radius="md"` | `rounded-md` | Tailwind 圆角 |
| `radius="lg"` | `rounded-lg` | Tailwind 圆角 |
| `radius="full"` | `rounded-full` | Tailwind 圆角 |
## 结构变化
在 v2 中,`Code` 是对原生 `` 元素的组件封装:
```tsx
import { Code } from "@heroui/react";
export default function App() {
return npm install @heroui/react;
}
```
在 v3 中,请直接使用原生 `` 元素,并为其添加 Tailwind CSS class:
```tsx
export default function App() {
return (
npm install @heroui/react
);
}
```
## 迁移示例
### 变体
```tsx
{/* Colors */}
Primary code
Success code
{/* Sizes */}
Medium code
```
```tsx
{/* 颜色 */}
Primary code
Success code
{/* 尺寸 */}
Medium code
```
### 组合变体
```tsx
npm install @heroui/react
```
```tsx
npm install @heroui/react
```
## 创建可复用的 Code 组件(可选)
如果你经常需要行内代码样式,可以封装一个简单的包装组件:
```tsx
import { Code } from "@heroui/react";
Code snippet
```
```tsx
// components/Code.tsx
import { cn } from "@/lib/utils"; // 或使用你自己的 cn 工具函数
interface CodeProps extends React.HTMLAttributes {
color?: "default" | "accent" | "success" | "warning" | "danger";
size?: "sm" | "md" | "lg";
radius?: "none" | "sm" | "md" | "lg" | "full";
}
const colorClasses = {
default: "bg-default/40 text-default-700",
accent: "bg-accent/20 text-accent-600",
success: "bg-success/20 text-success-700",
warning: "bg-warning/20 text-warning-700",
danger: "bg-danger/20 text-danger-600",
};
const sizeClasses = {
sm: "text-sm",
md: "text-base",
lg: "text-lg",
};
const radiusClasses = {
none: "rounded-none",
sm: "rounded-sm",
md: "rounded-md",
lg: "rounded-lg",
full: "rounded-full",
};
export function Code({
children,
className,
color = "default",
size = "sm",
radius = "sm",
...props
}: CodeProps) {
return (
{children}
);
}
// 用法
Code snippet
```
## 完整示例
```tsx
import { Code } from "@heroui/react";
export default function App() {
return (
Install HeroUI with npm install @heroui/react
Primary
Success
Warning
Danger
Small
Medium
Large
);
}
```
```tsx
export default function App() {
return (
Install HeroUI with{" "}
npm install @heroui/react
Primary
Success
Warning
Danger
Small
Medium
Large
);
}
```
## 基础样式参考
v2 的 Code 组件使用以下基础样式,迁移时建议一并保留:
* `px-2` — 水平内边距
* `py-1` — 垂直内边距
* `h-fit` — 高度随内容收缩
* `font-mono` — 等宽字体
* `font-normal` — 常规字重
* `inline-block` — 行内块级显示
* `whitespace-nowrap` — 禁止换行
## 总结
1. **组件已移除**:v3 中不再提供 `Code` 组件。
2. **导入调整**:移除 `import { Code } from "@heroui/react"`。
3. **使用原生元素**:改为原生 `` HTML 元素。
4. **样式**:直接编写 Tailwind CSS class。
5. **颜色映射**:在 v3 中将 `primary` 对应到 `accent` 色系 class。
## 迁移步骤
1. **移除导入**:从 `@heroui/react` 的导入中删除 `Code`。
2. **替换组件**:将所有 `` 替换为 `` 元素。
3. **添加 Tailwind class**:为元素补充等价的样式 class。
4. **更新颜色**:将 `color="primary"` 改为使用 `accent` 相关 class。
5. **可选**:若行内代码使用频繁,可封装可复用的包装组件。
# DatePicker
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/date-picker
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/date-picker.mdx
> DatePicker 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 DatePicker 文档](/docs/react/components/date-picker)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`DatePicker` 是一个自包含组件,通过 props 处理标签、描述、日历和时间输入:
```tsx
import { DatePicker } from "@heroui/react";
export default function App() {
return (
);
}
```
在 v3 中,DatePicker 改用组合优先的 API,你需要显式组合 `DateField` 与 `Calendar`:
```tsx
import { DatePicker, DateField, Calendar, Label } from "@heroui/react";
export default function App() {
return (
Birth date
{(segment) => }
{(day) => {day} }
{(date) => }
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 单一 `DatePicker` 组件,所有部分(输入、日历、标签、popover)都通过 props 在内部处理\
**v3:** 基于组合的 API,由 `DatePicker`、`DateField`、`Calendar` 和 `Label` 共同组成。每个部分都是独立组件。
### 2. 组成部分
| v3 组件 | 描述 |
| ----------------------------- | --------------------------------------------------------- |
| `DatePicker` | 根容器与状态持有者 |
| `DatePicker.Trigger` | 打开日历 popover 的按钮 |
| `DatePicker.TriggerIndicator` | 日历图标(默认)或自定义指示器 |
| `DatePicker.Popover` | 日历的 popover 包装层 |
| `DateField.Group` | 输入组包装层 |
| `DateField.Input` | 分段日期输入 |
| `DateField.Segment` | 单独的日期片段(月、日、年) |
| `DateField.Suffix` | 触发按钮的后缀 slot |
| `Calendar` | 完整的日历组件(参见 [Calendar 迁移](/docs/react/migration/calendar)) |
| `Label` | 外部 Label 组件 |
### 3. Prop 变更
| v2 prop | v3 等效项 | 说明 |
| ------------------------- | ------------------------- | ----------------------------------------------------------- |
| `value` | `value` | 保持一致 |
| `defaultValue` | `defaultValue` | 保持一致 |
| `onChange` | `onChange` | 保持一致 |
| `minValue` | `minValue` | 保持一致 |
| `maxValue` | `maxValue` | 保持一致 |
| `isDateUnavailable` | `isDateUnavailable` | 保持一致 |
| `isDisabled` | `isDisabled` | 保持一致 |
| `isReadOnly` | `isReadOnly` | 保持一致 |
| `isRequired` | `isRequired` | 保持一致 |
| `isInvalid` | `isInvalid` | 保持一致 |
| `granularity` | `granularity` | 保持一致 |
| `hourCycle` | `hourCycle` | 保持一致 |
| `hideTimeZone` | `hideTimeZone` | 保持一致 |
| `shouldForceLeadingZeros` | `shouldForceLeadingZeros` | 保持一致 |
| `pageBehavior` | - | 直接设置在 `Calendar` 上 |
| `label` | - | 使用 `Label` 组件 |
| `description` | - | 使用 `Description` 组件 |
| `errorMessage` | - | 使用 `FieldError` 组件 |
| `variant` | - | 使用 `DateField.Group` 的 `variant` prop 或 Tailwind CSS |
| `color` | - | 已移除(请改用 Tailwind CSS) |
| `size` | - | 已移除(请改用 Tailwind CSS) |
| `radius` | - | 已移除(请改用 Tailwind CSS) |
| `labelPlacement` | - | 使用 Tailwind CSS 控制布局 |
| `startContent` | - | 在 `DateField.Group` 内组合 |
| `endContent` | - | 在 `DateField.Group` 内组合 |
| `selectorIcon` | - | 将 children 传给 `DatePicker.TriggerIndicator` |
| `visibleMonths` | - | 使用 `Calendar` 的 `visibleDuration` prop |
| `showMonthAndYearPickers` | - | 使用 `Calendar.YearPickerTrigger` 和 `Calendar.YearPickerGrid` |
| `calendarProps` | - | 直接将 props 传给 `Calendar` |
| `popoverProps` | - | 直接将 props 传给 `DatePicker.Popover` |
| `selectorButtonProps` | - | 直接将 props 传给 `DatePicker.Trigger` |
| `timeInputProps` | - | 单独组合时间输入 |
| `CalendarBottomContent` | - | 将内容放在 `DatePicker.Popover` 内、`Calendar` 之后 |
| `validate` | - | 在外部处理校验 |
| `placeholderValue` | `placeholderValue` | 保持一致 |
| `autoFocus` | `autoFocus` | 保持一致 |
| `disableAnimation` | - | 已移除 |
| `classNames` | - | 在各个组件上使用 `className` |
## 迁移示例
### 基本日期选择器
```tsx
import { DatePicker } from "@heroui/react";
```
```tsx
import { DatePicker, DateField, Calendar, Label } from "@heroui/react";
Birth date
{(segment) => }
{(day) => {day} }
{(date) => }
```
### 受控状态
```tsx
import { useState } from "react";
import { DatePicker } from "@heroui/react";
import { parseDate } from "@internationalized/date";
const [value, setValue] = useState(parseDate("2024-03-07"));
```
```tsx
import { useState } from "react";
import { DatePicker, DateField, Calendar, Label } from "@heroui/react";
import { parseDate } from "@internationalized/date";
const [value, setValue] = useState(parseDate("2024-03-07"));
Date
{(segment) => }
{(day) => {day} }
{(date) => }
```
### 带描述和错误信息
```tsx
```
```tsx
import { DatePicker, DateField, Calendar, Label, Description, FieldError } from "@heroui/react";
Event date
{(segment) => }
Choose a future date
Date must be in the future
{/* Calendar compound components */}
```
### 自定义选择器图标
```tsx
import { Icon } from "@iconify/react";
}
/>
```
```tsx
import { Icon } from "@iconify/react";
Date
{(segment) => }
{/* Calendar compound components */}
```
### 带月份 / 年份选择器和日历底部内容
```tsx
setValue(today(getLocalTimeZone()))}>Today
}
/>
```
```tsx
Date
{(segment) => }
{(day) => {day} }
{(date) => }
{(year) => }
setValue(today(getLocalTimeZone()))}>Today
```
## 样式变化
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className` prop
```tsx
Date
{(segment) => }
{/* Calendar compound components */}
```
## 组件结构
v3 DatePicker 遵循以下结构:
```
DatePicker (Root)
├── Label
├── DateField.Group
│ ├── DateField.Input
│ │ └── DateField.Segment (render prop per segment)
│ └── DateField.Suffix
│ └── DatePicker.Trigger
│ └── DatePicker.TriggerIndicator
├── Description (optional)
├── FieldError (optional)
└── DatePicker.Popover
├── Calendar (see Calendar migration guide)
└── [Custom bottom content]
```
## 总结
1. **组件结构**:单一组件 → 由 `DatePicker`、`DateField`、`Calendar` 和 `Label` 组合而成
2. **标签**:`label` prop → `Label` 组件
3. **描述 / 错误信息**:props → `Description` 和 `FieldError` 组件
4. **日历**:内置日历 → 在 `DatePicker.Popover` 内组合带有自身复合组件的 `Calendar`
5. **选择器图标**:`selectorIcon` prop → 将 children 传给 `DatePicker.TriggerIndicator`
6. **年份选择器**:`showMonthAndYearPickers` prop → 使用 `Calendar.YearPickerTrigger` 和 `Calendar.YearPickerGrid`
7. **日历底部内容**:`CalendarBottomContent` prop → 将内容放在 `DatePicker.Popover` 内、`Calendar` 之后
8. **已移除样式 prop**:`variant`、`color`、`size`、`radius` → 使用 Tailwind CSS 或 `DateField.Group` 的变体
9. **已移除 classNames**:在各个复合组件上使用 `className`
10. **多个月份**:`visibleMonths` prop → 使用 `Calendar` 的 `visibleDuration` prop
# DateRangePicker
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/date-range-picker
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/date-range-picker.mdx
> DateRangePicker 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 DateRangePicker 文档](/docs/react/components/date-range-picker)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`DateRangePicker` 是单一自包含组件,通过 prop 配置标签、输入、日历与时间字段等:
```tsx
import { DateRangePicker } from "@heroui/react";
export default function App() {
return (
);
}
```
在 v3 中,DateRangePicker 采用“组合优先”的 API,需要你显式组合 `DateField` 与 `RangeCalendar`:
```tsx
import { DateField, DateRangePicker, Label, RangeCalendar } from "@heroui/react";
export default function App() {
return (
Trip dates
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 单一 `DateRangePicker`,两个输入、分隔符、日历、标签、popover 等主要由内部 prop 处理。\
**v3:** 组合式 API:将 `DateRangePicker`、`DateField`、`RangeCalendar` 与 `Label` 等组合在一起;两个 `DateField.Input` 分别使用 `slot="start"` 与 `slot="end"` 表示范围输入。
### 2. 组合结构说明
| v3 组件 | 说明 |
| ---------------------------------- | ------------------------------------------------------------------- |
| `DateRangePicker` | 根容器与状态持有者 |
| `DateRangePicker.Trigger` | 打开日历 popover 的按钮 |
| `DateRangePicker.TriggerIndicator` | 日历图标(默认)或自定义指示器 |
| `DateRangePicker.RangeSeparator` | 开始/结束日期输入之间的分隔符(默认 `" - "`) |
| `DateRangePicker.Popover` | 包裹范围日历的 popover |
| `DateField.Group` | 输入组外层 |
| `DateField.InputContainer` | 开始/结束输入与分隔符容器 |
| `DateField.Input slot="start"` | 开始日期的分段输入 |
| `DateField.Input slot="end"` | 结束日期的分段输入 |
| `DateField.Segment` | 单个日期片段(月/日/年等) |
| `DateField.Suffix` | 触发按钮的后缀插槽 |
| `RangeCalendar` | 完整范围日历(参见 [RangeCalendar 迁移](/docs/react/migration/range-calendar)) |
| `Label` | 外部标签组件 |
### 3. Prop 变更
| v2 prop | v3 对应 | 说明 |
| --------------------------- | ------------------------- | --------------------------------------------------------------------- |
| `value` | `value` | 相同 |
| `defaultValue` | `defaultValue` | 相同 |
| `onChange` | `onChange` | 相同 |
| `minValue` | `minValue` | 相同 |
| `maxValue` | `maxValue` | 相同 |
| `isDisabled` | `isDisabled` | 相同 |
| `isReadOnly` | `isReadOnly` | 相同 |
| `isRequired` | `isRequired` | 相同 |
| `isInvalid` | `isInvalid` | 相同 |
| `isOpen` | `isOpen` | 相同 |
| `defaultOpen` | `defaultOpen` | 相同 |
| `onOpenChange` | `onOpenChange` | 相同 |
| `granularity` | `granularity` | 相同 |
| `hourCycle` | `hourCycle` | 相同 |
| `hideTimeZone` | `hideTimeZone` | 相同 |
| `shouldForceLeadingZeros` | `shouldForceLeadingZeros` | 相同 |
| `allowsNonContiguousRanges` | — | 直接在 `RangeCalendar` 上设置 |
| `pageBehavior` | — | 直接在 `RangeCalendar` 上设置 |
| `label` | — | 使用 `Label` 组件 |
| `description` | — | 使用 `Description` 组件 |
| `errorMessage` | — | 使用 `FieldError` 组件 |
| `variant` | — | 使用 `DateField.Group` 的 `variant` prop,或 Tailwind CSS |
| `color` | — | 已移除(请用 Tailwind CSS) |
| `size` | — | 已移除(请用 Tailwind CSS) |
| `radius` | — | 已移除(请用 Tailwind CSS) |
| `labelPlacement` | — | 用 Tailwind CSS 控制布局 |
| `selectorIcon` | — | 将 children 传给 `DateRangePicker.TriggerIndicator` |
| `selectorButtonPlacement` | — | 通过 `DateField.Prefix` 或 `DateField.Suffix` 组合触发器位置 |
| `visibleMonths` | — | 使用 `RangeCalendar` 的 `visibleDuration` prop |
| `showMonthAndYearPickers` | — | 使用 `RangeCalendar.YearPickerTrigger` 与 `RangeCalendar.YearPickerGrid` |
| `calendarProps` | — | 直接将 prop 传给 `RangeCalendar` |
| `popoverProps` | — | 直接将 prop 传给 `DateRangePicker.Popover` |
| `selectorButtonProps` | — | 直接将 prop 传给 `DateRangePicker.Trigger` |
| `timeInputProps` | — | 时间输入需单独组合实现 |
| `calendarWidth` | — | 已移除(请用 Tailwind CSS) |
| `validate` | — | 在外部处理校验 |
| `placeholderValue` | `placeholderValue` | 相同 |
| `autoFocus` | `autoFocus` | 相同 |
| `disableAnimation` | — | 已移除 |
| `classNames` | — | 在各子组件上使用 `className` |
## 迁移示例
### 基础 DateRangePicker
```tsx
import { DateRangePicker } from "@heroui/react";
```
```tsx
import { DateField, DateRangePicker, Label, RangeCalendar } from "@heroui/react";
Trip dates
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
```
### 受控状态
```tsx
import { useState } from "react";
import { DateRangePicker } from "@heroui/react";
import { parseDate } from "@internationalized/date";
const [value, setValue] = useState({
start: parseDate("2024-03-01"),
end: parseDate("2024-03-14"),
});
```
```tsx
import { useState } from "react";
import { DateField, DateRangePicker, Label, RangeCalendar } from "@heroui/react";
import { parseDate } from "@internationalized/date";
const [value, setValue] = useState({
start: parseDate("2024-03-01"),
end: parseDate("2024-03-14"),
});
Trip dates
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
```
### 描述与错误信息
```tsx
```
```tsx
import { DateField, DateRangePicker, Label, Description, FieldError, RangeCalendar } from "@heroui/react";
Trip dates
{(segment) => }
{(segment) => }
Select your travel period
End date must be after start date
{/* RangeCalendar compound components */}
```
### 自定义选择器图标
```tsx
import { Icon } from "@iconify/react";
}
/>
```
```tsx
import { Icon } from "@iconify/react";
Trip dates
{(segment) => }
{(segment) => }
{/* RangeCalendar compound components */}
```
### 多月份显示与年份选择器
```tsx
```
```tsx
Trip dates
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
{(day) => {day} }
{(date) => }
{(year) => }
```
## 样式相关变化
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className`
```tsx
Trip dates
{(segment) => }
{(segment) => }
{/* RangeCalendar compound components */}
```
## 组件结构(Anatomy)
v3 的 DateRangePicker 结构如下:
```
DateRangePicker (Root)
├── Label
├── DateField.Group
│ ├── DateField.InputContainer
│ │ ├── DateField.Input slot="start"
│ │ │ └── DateField.Segment(每个 segment 的渲染 prop)
│ │ ├── DateRangePicker.RangeSeparator
│ │ └── DateField.Input slot="end"
│ │ └── DateField.Segment(每个 segment 的渲染 prop)
│ └── DateField.Suffix
│ └── DateRangePicker.Trigger
│ └── DateRangePicker.TriggerIndicator
├── Description (optional)
├── FieldError (optional)
└── DateRangePicker.Popover
└── RangeCalendar (参见 RangeCalendar 迁移指南)
```
## 总结
1. **组件结构**:由单一组件 → 组合 `DateRangePicker`、`DateField`、`RangeCalendar` 与 `Label` 等。
2. **双输入**:内置起止输入 → 两个 `DateField.Input`,分别使用 `slot="start"` 与 `slot="end"`。
3. **分隔符**:内置分隔 → `DateRangePicker.RangeSeparator`。
4. **标签**:`label` prop → `Label` 组件。
5. **描述/错误**:prop → `Description` 与 `FieldError` 组件。
6. **日历**:内置日历 → 在 `DateRangePicker.Popover` 内组合 `RangeCalendar` 及其子组件。
7. **选择器图标**:`selectorIcon` prop → 将 children 传给 `DateRangePicker.TriggerIndicator`。
8. **年份选择器**:`showMonthAndYearPickers` prop → 使用 `RangeCalendar.YearPickerTrigger` 与 `RangeCalendar.YearPickerGrid`。
9. **样式类 prop 移除**:`variant`、`color`、`size`、`radius` → 使用 Tailwind CSS,或 `DateField.Group` 的 `variant`。
10. **`classNames` 移除**:在各复合子组件上使用 `className`。
# DateInput
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/dateinput
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/dateinput.mdx
> DateInput 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 DateField 文档](/docs/react/components/date-field)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`DateInput` 是单个通过 props 配置的组件:
```tsx
import { DateInput } from "@heroui/react";
export default function App() {
return ;
}
```
在 v3 中,`DateField` 需要配合 `DateInputGroup` 使用复合组件,并通过 render prop 渲染各段位(segment):
```tsx
import { DateField, DateInputGroup, Label } from "@heroui/react";
export default function App() {
return (
Date
{(segment) => }
);
}
```
## 主要变化
### 1. 组件命名
**v2:** `DateInput`\
**v3:** `DateField`
### 2. 组件结构
**v2:** 单个带 props 的组件\
**v3:** 复合组件:`DateField`(根)+ `DateInputGroup`,其中包含 `DateInputGroup.Input`(render prop)与 `DateInputGroup.Segment`;可选 `DateInputGroup.Prefix` 与 `DateInputGroup.Suffix`
### 3. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ------------------------------------------------------------- | ------------------------------ | --------------------------------------------------- |
| `label` | — | 已移除(请使用 `Label` 组件) |
| `description` | — | 已移除(请使用 `Description` 组件) |
| `errorMessage` | — | 已移除(请使用 `FieldError` 组件) |
| `value`, `defaultValue`, `onChange` | `DateField` | 相同(React Aria) |
| `minValue`, `maxValue`, `granularity`, `placeholderValue` | `DateField` | 相同 |
| `isRequired`, `isDisabled`, `isReadOnly`, `isInvalid`, `name` | `DateField` | 相同 |
| `createCalendar`, `validationBehavior` | `DateField` | 相同 |
| `variant` | `DateInputGroup` | 仅简化为 `primary` \| `secondary` |
| `fullWidth` | `DateField` 或 `DateInputGroup` | 可在根或组上设置 |
| `color` | — | 已移除(请使用 Tailwind CSS) |
| `size` | — | 已移除(请使用 Tailwind CSS) |
| `radius` | — | 已移除(请使用 Tailwind CSS) |
| `labelPlacement` | — | 已移除(请通过布局自行处理) |
| `startContent` | `DateInputGroup.Prefix` | 使用 Prefix 子节点 |
| `endContent` | `DateInputGroup.Suffix` | 使用 Suffix 子节点 |
| `classNames` | — | 请在 `DateField` 与 `DateInputGroup` 各部分使用 `className` |
| `groupProps` | — | 已移除(请在 `DateInputGroup` 上使用 `className` 或 DOM 属性) |
| `labelProps` | — | 已移除(请在 `Label` 上使用 `className`) |
| `fieldProps` | — | 已移除(请在 `DateInputGroup` 上使用 `className`) |
| `innerWrapperProps` | — | 已移除(请在组 / 输入相关部分使用 `className`) |
| `descriptionProps` | — | 已移除(请在 `Description` 上使用 `className`) |
| `errorMessageProps` | — | 已移除(请在 `FieldError` 上使用 `className`) |
| `inputRef` | — | 已移除(ref 由 `DateField` / React Aria 处理) |
## 迁移示例
### 基础用法
```tsx
```
```tsx
Date
{(segment) => }
```
### 含描述与错误
```tsx
```
```tsx
import { Description, FieldError, Label } from "@heroui/react";
Birth date
{(segment) => }
Select your birth date
Date
{(segment) => }
Please enter a valid date
```
### 必填
```tsx
```
```tsx
Date
{(segment) => }
```
### 受控
```tsx
import { parseDate } from "@internationalized/date";
import { useState } from "react";
const [value, setValue] = useState(null);
```
```tsx
import type { DateValue } from "@internationalized/date";
import { useState } from "react";
const [value, setValue] = useState(null);
Date
{(segment) => }
```
### 最小值、最大值与粒度
```tsx
import { parseDate } from "@internationalized/date";
```
```tsx
import { parseDate } from "@internationalized/date";
Date
{(segment) => }
```
### 起始与结束内容
```tsx
}
label="Date"
name="date"
startContent={ }
/>
```
```tsx
Date
{(segment) => }
```
### 变体
```tsx
```
```tsx
Date
{(segment) => }
Date
{(segment) => }
```
## 组件组成
v3 `DateField` 的结构如下:
```
DateField (Root)
├── Label (optional)
├── DateInputGroup
│ ├── DateInputGroup.Prefix (optional)
│ ├── DateInputGroup.Input → (segment) => DateInputGroup.Segment
│ └── DateInputGroup.Suffix (optional)
├── Description (optional)
└── FieldError (optional)
```
## 总结
1. **组件已重命名**:`DateInput` → `DateField`
2. **组件结构**:必须使用复合组件:`DateField`(根)、`DateInputGroup`,以及 `DateInputGroup.Input`(render prop)与 `DateInputGroup.Segment`
3. **标签 / 描述 / 错误**:请使用独立组件(`Label`、`Description`、`FieldError`)
4. **日期相关 prop 不变**:`value`、`defaultValue`、`onChange`、`minValue`、`maxValue`、`granularity`、`placeholderValue`、`isRequired`、`isDisabled`、`isInvalid`、`name`、`createCalendar` 等仍位于 `DateField`
5. **`DateInputGroup` 的变体**:v3 仅在 `DateInputGroup` 上支持 `variant="primary"` 与 `variant="secondary"`;`color`、`size`、`radius` 已移除 — 请使用 Tailwind CSS
6. **起始 / 结束内容**:`startContent` / `endContent` → `DateInputGroup.Prefix` 与 `DateInputGroup.Suffix`
7. **`labelPlacement` 已移除**:请通过布局自行处理
8. **DOM / 类名相关 prop**:`groupProps`、`labelProps`、`fieldProps`、`classNames` 已移除 — 请在对应部分使用 `className`(及标准 DOM 属性)
# Divider
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/divider
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/divider.mdx
> Divider(已重命名为 Separator)从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Separator 文档](/docs/react/components/separator)。本指南只关注从 HeroUI v2 的迁移。
## 组件重命名
**v2:** `Divider`\
**v3:** `Separator`
## 主要变化
### 1. 组件名称
**v2:** `Divider`\
**v3:** `Separator`
### 2. 变体 prop
**v3:** Separator 新增 `variant` prop,可选 `default`、`secondary`、`tertiary`,对应不同的强调层级(v2 的 Divider 没有变体)。
### 3. Prop 对比
| 属性 | v2 | v3 | 说明 |
| ------------- | -- | -- | ----------------------------------------------- |
| `orientation` | ✅ | ✅ | 相同:`"horizontal"` \| `"vertical"` |
| `className` | ✅ | ✅ | 相同 |
| `variant` | ❌ | ✅ | 新增:`"default"` \| `"secondary"` \| `"tertiary"` |
## 结构变化
在 v2 中,组件名为 `Divider`:
```tsx
import { Divider } from "@heroui/react";
export default function App() {
return (
Content above
Content below
);
}
```
在 v3 中,组件已重命名为 `Separator`:
```tsx
import { Separator } from "@heroui/react";
export default function App() {
return (
Content above
Content below
);
}
```
## 迁移示例
### 使用变体
```tsx
{/* v2 没有 variant prop */}
```
```tsx
```
## 总结
1. **组件已重命名**:`Divider` → `Separator`
2. **导入变化**:将导入从 `Divider` 更新为 `Separator`
3. **v3 新增功能**:`variant` prop(`default`、`secondary`、`tertiary`),用于区分强调层级
## 迁移步骤
1. **更新导入**:将 `import { Divider }` 改为 `import { Separator }`
2. **替换组件**:把所有 ` ` 替换为 ` `
3. **保留相同的 prop**:`orientation` 与 `className` 行为不变
4. **可选**:使用 `variant="secondary"` 或 `variant="tertiary"` 表达不同的强调层级
# Drawer
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/drawer
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/drawer.mdx
> Drawer 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Drawer 文档](/docs/react/components/drawer)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`Drawer` 与 `Modal` 共享相同的 API,使用 `DrawerContent`、`DrawerHeader`、`DrawerBody` 和 `DrawerFooter`,并采用渲染回调模式:
```tsx
import { Drawer, DrawerContent, DrawerHeader, DrawerBody, DrawerFooter, Button, useDisclosure } from "@heroui/react";
export default function App() {
const { isOpen, onOpen, onOpenChange } = useDisclosure();
return (
<>
Open Drawer
{(onClose) => (
<>
Drawer Title
Drawer content goes here.
Close
>
)}
>
);
}
```
在 v3 中,Drawer 改用复合组件模式,提供显式子组件与内置触发器支持:
```tsx
import { Drawer, Button } from "@heroui/react";
export default function App() {
return (
Open Drawer
Drawer Title
Drawer content goes here.
Close
);
}
```
## 主要变化
### 1. 组件结构
**v2:** `Drawer` 包裹 `DrawerContent`,并使用渲染回调模式;触发器需要通过 `useDisclosure` 单独管理\
**v3:** 复合组件:`Drawer`、`Drawer.Backdrop`、`Drawer.Content`、`Drawer.Dialog`、`Drawer.Header`、`Drawer.Heading`、`Drawer.Body`、`Drawer.Footer`、`Drawer.Handle`、`Drawer.CloseTrigger`。`Drawer` 的第一个子节点会成为触发器。
### 2. 触发模式
**v2:** 使用外部触发器,并通过 `useDisclosure` 钩子与 `isOpen` / `onOpenChange` 管理状态\
**v3:** 内置触发器,`Drawer` 的第一个子节点会自动成为触发器。受控状态可通过 `useOverlayState` 钩子管理。
### 3. v3 新特性
* **拖动关闭**:在手柄、头部和底部区域内置基于指针的拖动手势
* **拖动手柄**:`Drawer.Handle` 是视觉拖动指示器组件
* **内置关闭触发器**:`Drawer.CloseTrigger` 会渲染一个关闭按钮
* **基于 slot 的关闭**:带有 `slot="close"` 的按钮会自动关闭 Drawer
### 4. Prop 变更
| v2 prop | v3 等效项 | 说明 |
| --------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `isOpen` | `Drawer.Backdrop` 的 `isOpen` | 或使用 `useOverlayState` |
| `onOpenChange` | `Drawer.Backdrop` 的 `onOpenChange` | 或使用 `useOverlayState` |
| `onClose` | - | 在按钮上使用 `onOpenChange` 或 `slot="close"` |
| `placement` | `Drawer.Content` 的 `placement` | `"right"` → `"right"`、`"left"` → `"left"`、`"top"` → `"top"`、`"bottom"` → `"bottom"`。默认值从 `"right"` 改为 `"bottom"` |
| `size` | - | 已移除(请在 `Drawer.Dialog` 上使用 Tailwind CSS) |
| `radius` | - | 已移除(请改用 Tailwind CSS) |
| `backdrop` | `Drawer.Backdrop` 的 `variant` | 值保持一致:`"opaque"`、`"blur"`、`"transparent"` |
| `isDismissable` | `Drawer.Backdrop` 的 `isDismissable` | 保持一致 |
| `isKeyboardDismissDisabled` | `Drawer.Backdrop` 的 `isKeyboardDismissDisabled` | 保持一致 |
| `shouldBlockScroll` | - | v3 中始终会阻止滚动 |
| `hideCloseButton` | - | 省略 `Drawer.CloseTrigger` 即可隐藏 |
| `closeButton` | - | 将自定义内容传给 `Drawer.CloseTrigger` |
| `motionProps` | - | 已移除(v3 使用基于 CSS 的动画) |
| `disableAnimation` | - | 已移除 |
| `portalContainer` | - | 已移除 |
| `classNames` | - | 在各个复合组件上使用 `className` |
### 5. Hook 变更
**v2:** 使用 `useDisclosure` 钩子管理打开 / 关闭状态\
**v3:** 使用 `useOverlayState` 钩子(替代 `useDisclosure`)
```tsx
// v2
const { isOpen, onOpen, onOpenChange } = useDisclosure();
// v3
const state = useOverlayState();
// state.isOpen, state.open(), state.close(), state.toggle()
```
## 迁移示例
### 基本抽屉
```tsx
import { Drawer, DrawerContent, DrawerHeader, DrawerBody, DrawerFooter, Button, useDisclosure } from "@heroui/react";
const { isOpen, onOpen, onOpenChange } = useDisclosure();
<>
Open
{(onClose) => (
<>
Title
Content
Close
>
)}
>
```
```tsx
import { Drawer, Button } from "@heroui/react";
Open
Title
Content
Close
```
### 位置
```tsx
{(onClose) => (
<>
Left Drawer
Content
>
)}
```
```tsx
Open
Left Drawer
Content
```
### 遮罩变体
```tsx
{(onClose) => (
<>
Blurred Backdrop
Content
>
)}
```
```tsx
Open
Blurred Backdrop
Content
```
### 受控状态
```tsx
import { useDisclosure } from "@heroui/react";
const { isOpen, onOpen, onOpenChange } = useDisclosure();
<>
Open
{(onClose) => (
<>
Controlled
Content
Close
>
)}
>
```
```tsx
import { useOverlayState } from "@heroui/react";
const state = useOverlayState();
<>
Open
Controlled
Content
Close
>
```
### 不可关闭
```tsx
{(onClose) => (
<>
Confirm Action
Are you sure?
Confirm
>
)}
```
```tsx
Open
Confirm Action
Are you sure?
Confirm
```
## 样式变化
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className` prop
```tsx
Open
Title
Content
Actions
```
## 组件结构
v3 Drawer 遵循以下结构:
```
Drawer (Root)
├── [Trigger element] (first child becomes trigger)
└── Drawer.Backdrop
└── Drawer.Content (placement)
└── Drawer.Dialog
├── Drawer.Handle (optional, drag indicator)
├── Drawer.CloseTrigger (optional, close button)
├── Drawer.Header
│ └── Drawer.Heading
├── Drawer.Body (scrollable)
└── Drawer.Footer
```
## 总结
1. **组件结构**:渲染回调模式 → 带显式子组件的复合组件
2. **触发模式**:外部 `useDisclosure` + `onPress` → 内置触发器(`Drawer` 的第一个子节点)
3. **状态钩子**:`useDisclosure` → `useOverlayState`,并使用 `open()`、`close()`、`toggle()` 方法
4. **位置**:`Drawer` 上的 prop → `Drawer.Content` 上的 prop。默认值从 `"right"` 改为 `"bottom"`
5. **遮罩**:`backdrop` prop → `Drawer.Backdrop` 的 `variant` prop
6. **关闭按钮**:`hideCloseButton` / `closeButton` props → 省略或自定义 `Drawer.CloseTrigger`
7. **基于 slot 的关闭**:带有 `slot="close"` 的按钮会自动关闭 Drawer
8. **新功能**:通过 `Drawer.Handle` 拖动关闭,并支持基于速度的关闭
9. **动画**:`motionProps`(Framer Motion)→ 基于 CSS 的动画
10. **已移除样式 prop**:`size`、`radius` → 使用 Tailwind CSS
11. **已移除 classNames**:在各个复合组件上使用 `className`
# Dropdown
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/dropdown
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/dropdown.mdx
> Dropdown 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Dropdown 文档](/docs/react/components/dropdown)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,Dropdown 使用相互独立的组件:`DropdownTrigger`、`DropdownMenu`、`DropdownItem`、`DropdownSection`:
```tsx
import { Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Button } from "@heroui/react";
export default function App() {
return (
Open Menu
New file
Copy link
);
}
```
在 v3 中,Dropdown 采用复合组件模式,并提供显式的子组件结构:
```tsx
import { Dropdown, Button, Label } from "@heroui/react";
export default function App() {
return (
Open Menu
New file
Copy link
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 相互独立的组件:`DropdownTrigger`、`DropdownMenu`、`DropdownItem`、`DropdownSection`\
**v3:** 复合组件:`Dropdown.Trigger`、`Dropdown.Popover`、`Dropdown.Menu`、`Dropdown.Item`、`Dropdown.Section`
### 2. 组件名称变更
| v2 组件 | v3 组件 | 说明 |
| ----------------- | ------------------ | --------------------------------- |
| `DropdownTrigger` | `Dropdown.Trigger` | 功能相同 |
| `DropdownMenu` | `Dropdown.Menu` | 需包裹在 `Dropdown.Popover` 内 |
| `DropdownItem` | `Dropdown.Item` | 使用 `id` 与 `textValue`;列表项保留 `key` |
| `DropdownSection` | `Dropdown.Section` | 功能相同 |
| — | `Dropdown.Popover` | 新增包裹组件(必填) |
### 3. 菜单项标识
**v2:** 菜单项内容通过 children 传递;React 的 `key` 同时用于列表调和与菜单项标识(选择、焦点)。\
**v3:** 菜单项的可见文本必须使用 `Label` 组件。为每个菜单项提供 `id`(状态/焦点)与 `textValue`(当内容不是纯文本时用于无障碍)。列表中的菜单项仍应保留 React 的 `key`。
### 4. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ---------------------------------------------- | --------------- | -------------------------------- |
| `variant`、`color`(在 `DropdownMenu` 上) | — | 已移除(菜单不再有 variant/color) |
| `classNames`、`itemClasses`(在 `DropdownMenu` 上) | — | 在 `Menu` 与菜单项上使用 `className` |
| `color`(在 `DropdownItem` 上) | `Dropdown.Item` | 使用 `variant="danger"` 表示危险操作 |
| `title` | — | 使用 `Label` 子组件 |
| `description` | — | 使用 `Description` 子组件 |
| `shortcut` | — | 使用 `Kbd` 子组件 |
| `startContent`、`endContent` | — | 将图标或组件作为 children 放置 |
| `selectedIcon` | — | 使用 `Dropdown.ItemIndicator` |
| `showDivider` | — | 在菜单项之间使用 `Separator` |
| `classNames`(在 `DropdownItem` 上) | — | 在菜单项上使用 `className` |
| `isSelected` | `Dropdown.Menu` | 在 `Menu` 上使用 `selectedKeys` |
| `isDisabled` | `Dropdown.Menu` | 在 `Menu` 上使用 `disabledKeys` |
| `trigger`(在 `Dropdown` 上) | `Dropdown` | 仍支持:`"press"`(默认)或 `"longPress"` |
### 5. 新增子组件
* `Dropdown.Popover`:`Dropdown.Menu` 的必填外层包裹
* `Dropdown.ItemIndicator`:用于选择指示(对勾/圆点等)
* `Dropdown.SubmenuTrigger`:子菜单触发结构
* `Dropdown.SubmenuIndicator`:子菜单 chevron 指示
## 迁移示例
### 使用 onAction
```tsx
alert(key)}>
New file
```
```tsx
alert(key)}>
New file
```
### 菜单项内容
```tsx
{/* With icon */}
}
>
New file
{/* With description */}
Edit file
{/* With shortcut */}
Copy
```
```tsx
import { Icon } from "@iconify/react";
import { Label, Description, Kbd } from "@heroui/react";
{/* With icon */}
New file
{/* With description */}
Edit file
Edit the file
{/* With shortcut */}
Copy
C
```
### 危险菜单项
```tsx
Delete file
```
```tsx
Delete file
```
### 使用分组(Section)
```tsx
New file
Edit file
Delete file
```
```tsx
import { Header, Separator } from "@heroui/react";
New file
Edit file
Delete file
```
### 选择
```tsx
import { useState } from "react";
{/* Single selection */}
const [singleSelected, setSingleSelected] = useState(new Set(["text"]));
Text
Number
{/* Multiple selection */}
const [multiSelected, setMultiSelected] = useState(new Set(["bold"]));
Bold
Italic
```
```tsx
import { useState } from "react";
{/* Single selection */}
const [singleSelected, setSingleSelected] = useState(new Set(["text"]));
Text
Number
{/* Multiple selection */}
const [multiSelected, setMultiSelected] = useState(new Set(["bold"]));
Bold
Italic
```
### 键盘快捷键
```tsx
Copy
```
```tsx
import { Label, Kbd } from "@heroui/react";
Copy
C
```
`Kbd` 的 `slot="keyboard"` prop 会把快捷键放在菜单项末尾,用以替代 v2 的 `shortcut` prop。
### 子菜单
```tsx
{/* v2 did not have built-in submenu support */}
```
```tsx
import { Label } from "@heroui/react";
Copy Link
Share
WhatsApp
Telegram
```
使用 `Dropdown.SubmenuTrigger` 包裹会打开嵌套菜单的菜单项;在菜单项内放置 `Dropdown.SubmenuIndicator` 以显示子菜单指示图标。
### 长按触发
```tsx
Long press me
Cut
Copy
```
```tsx
Long press me
Cut
Copy
```
根组件 `Dropdown` 的 `trigger` prop 接受 `"press"`(默认)或 `"longPress"`,用于控制如何打开菜单。
## 组件结构(Anatomy)
v3 的 Dropdown 结构如下:
```
Dropdown (Root) — accepts trigger="press" | "longPress"
├── Dropdown.Trigger (optional, defaults to first child)
├── Dropdown.Popover (required wrapper)
│ └── Dropdown.Menu
│ ├── Dropdown.Item
│ │ ├── Icon (optional, first child)
│ │ ├── Label (required for text)
│ │ ├── Description (optional)
│ │ ├── Kbd slot="keyboard" (optional, for shortcuts)
│ │ └── Dropdown.ItemIndicator (optional, for selection)
│ ├── Separator (for dividers)
│ ├── Dropdown.Section
│ │ ├── Header (optional)
│ │ └── Dropdown.Item
│ └── Dropdown.SubmenuTrigger
│ ├── Dropdown.Item
│ │ ├── Label
│ │ └── Dropdown.SubmenuIndicator (chevron icon)
│ └── Dropdown.Popover
│ └── Dropdown.Menu
│ └── Dropdown.Item
```
## 总结
1. **组件结构**:必须使用复合组件(`Dropdown.Trigger`、`Dropdown.Popover`、`Dropdown.Menu` 等)。
2. **`Dropdown.Popover` 为必填**:`Dropdown.Menu` 必须包裹在 `Dropdown.Popover` 内。
3. **`Label` 组件**:菜单项文本必须使用 `Label`。
4. **`Description` 组件**:用 `Description` 替代 `description` prop。
5. **`Kbd` 组件**:用带 `slot="keyboard"` 的 `Kbd` 替代 `shortcut` prop;slot 用于把快捷键放到菜单项末尾。
6. **图标作为 children**:图标作为第一个 child 放置,不再使用 `startContent` prop。
7. **`Separator` 组件**:用 `Separator` 替代 `showDivider` prop。
8. **`ItemIndicator` 组件**:用 `Dropdown.ItemIndicator` 表示选择状态。
9. **用 variant 表示危险**:用 `variant="danger"` 替代 `color="danger"`。
10. **菜单样式 prop 移除**:`Dropdown.Menu` 不再支持 `variant` 或 `color` prop。
11. **`classNames` 移除**:在各自子组件上使用 `className`。
12. **子菜单**:用 `Dropdown.SubmenuTrigger` 包裹会打开嵌套菜单的项,并在项内使用 `Dropdown.SubmenuIndicator`。
13. **`trigger` prop**:在根 `Dropdown` 上使用 `trigger="longPress"` 以长按打开菜单(默认为按压打开)。
# Form
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/form
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/form.mdx
> Form 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Form 文档](/docs/react/components/form)。本指南只关注从 HeroUI v2 的迁移。
## 主要变化
### 1. Form 组件 prop
v2 与 v3 之间,Form 组件的 prop 大体保持不变,仅新增了一个:
| 属性 | v2 | v3 | 说明 |
| -------------------- | -- | -- | --------------------------------------- |
| `validationBehavior` | ✅ | ✅ | 相同:`"native"` \| `"aria"` |
| `validationErrors` | ✅ | ✅ | 相同:`Record` |
| `onSubmit` | ✅ | ✅ | 相同 |
| `onReset` | ✅ | ✅ | 相同 |
| `action` | ✅ | ✅ | 相同 |
| `method` | ✅ | ✅ | 相同 |
| `encType` | ✅ | ✅ | 相同 |
| `target` | ✅ | ✅ | 相同 |
| `className` | ✅ | ✅ | 相同 |
| `onInvalid` | ❌ | ✅ | 新增:表单验证失败时调用的处理函数 |
## 结构变化
在 v2 中,Form 组件的用法:
```tsx
import { Form, Button } from "@heroui/react";
export default function App() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log("Form submitted");
};
return (
{/* Form content */}
Submit
);
}
```
在 v3 中,Form 组件的用法保持一致:
```tsx
import { Form, Button } from "@heroui/react";
export default function App() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log("Form submitted");
};
return (
{/* Form content */}
Submit
);
}
```
## 迁移示例
### 带 onInvalid 处理函数的表单(v3 新增)
```tsx
{/* v2 没有 onInvalid prop */}
{/* Form content */}
Submit
```
```tsx
{
e.preventDefault();
// Custom handling when form validation fails
console.log("Form validation failed");
}}
onSubmit={onSubmit}
>
{/* Form content */}
Submit
```
`onInvalid` 处理函数会在表单验证失败时被调用。默认情况下,第一个无效字段会获得焦点;通过调用 `e.preventDefault()` 可以自定义这一行为。
## 总结
1. **Form 组件**:没有破坏性变更——prop 与行为保持一致
2. **新增 prop**:`onInvalid` 可用于自定义校验失败时的处理逻辑
## 迁移步骤
1. **无需改动**:Form 组件在 v3 中的工作方式与 v2 相同
2. **可选**:如果你需要自定义校验失败时的处理逻辑,可使用新增的 `onInvalid` prop
## 关于表单字段
虽然 Form 组件本身没有变化,但其中使用的表单字段组件(例如 `Input`、`TextField` 等)在 v3 中已有更新。具体的字段迁移说明请参阅各自的迁移指南。
# Image
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/image
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/image.mdx
> Image 从 HeroUI v2 到 v3 的迁移指南。
HeroUI v3 **已移除** `Image` 组件。请改用原生 HTML `img` 元素或 Next.js 的 `Image` 组件,并结合 Tailwind CSS 类进行样式与效果处理。
## 主要变化
### 1. 组件移除
**v2:** `@heroui/react` 的 `` 组件\
**v3:** 原生 HTML `img` 元素,或 Next.js `Image` 组件
### 2. 能力对照
v2 的 `Image` 提供的能力,在 v3 中需要分别替换:
| v2 能力 | v3 对应做法 | 说明 |
| ------------------------- | ---------------------- | ------------------------------------------------------- |
| `radius` prop | Tailwind `rounded-*` 类 | 如 `rounded-sm`、`rounded-md`、`rounded-lg`、`rounded-full` |
| `shadow` prop | Tailwind `shadow-*` 类 | 如 `shadow-sm`、`shadow-md`、`shadow-lg` |
| `isBlurred` prop | 手动实现模糊 | 使用 CSS `filter: blur()` 或 Tailwind `blur-*` |
| `isZoomed` prop | 手动实现悬停缩放 | 使用 Tailwind `hover:scale-*` |
| `fallbackSrc` prop | 手动错误回退 | 使用 `onError` + 状态切换 `src` |
| `disableSkeleton` / 加载骨架图 | 手动加载态 | 使用 React state + 条件渲染 |
| `removeWrapper` prop | 直接渲染 | 无需额外 wrapper,直接渲染 `img` |
## 结构变化
在 v2 中,`Image` 是对原生 `img` 的封装组件:
```tsx
import { Image } from "@heroui/react";
export default function App() {
return (
);
}
```
在 v3 中,直接使用原生 `img`,并用 Tailwind CSS 控制样式:
```tsx
export default function App() {
return (
);
}
```
## 迁移示例
### 使用 Next.js `Image`(推荐)
若使用 Next.js,推荐使用其优化的 `Image` 组件:
```tsx
import { Image } from "@heroui/react";
```
```tsx
import Image from "next/image";
```
### 悬停放大(对应 `isZoomed`)
```tsx
```
```tsx
```
### 模糊效果(对应 `isBlurred`)
```tsx
```
```tsx
```
### 回退图片(对应 `fallbackSrc`)
```tsx
```
```tsx
import { useState } from "react";
function ImageWithFallback({ src, fallbackSrc, alt, ...props }) {
const [imgSrc, setImgSrc] = useState(src);
return (
setImgSrc(fallbackSrc)}
{...props}
/>
);
}
```
### 加载骨架(对应 skeleton)
```tsx
```
```tsx
import { useState } from "react";
function ImageWithSkeleton({ src, alt, ...props }) {
const [isLoading, setIsLoading] = useState(true);
const [hasError, setHasError] = useState(false);
return (
{isLoading && (
)}
setIsLoading(false)}
onError={() => {
setIsLoading(false);
setHasError(true);
}}
{...props}
/>
);
}
```
### 组合效果
```tsx
```
```tsx
```
## 创建可复用的图片组件(可选)
如果你经常在同类场景重复使用图片能力,可以封装一个可复用组件:
```tsx
import { Image } from "@heroui/react";
```
```tsx
import { useState } from "react";
import { cn } from "@/lib/utils"; // 或你项目里的 cn 工具
interface CustomImageProps extends React.ImgHTMLAttributes {
radius?: "none" | "sm" | "md" | "lg" | "full";
shadow?: "none" | "sm" | "md" | "lg";
isZoomed?: boolean;
isBlurred?: boolean;
fallbackSrc?: string;
}
const radiusClasses = {
none: "rounded-none",
sm: "rounded-sm",
md: "rounded-md",
lg: "rounded-lg",
full: "rounded-full",
};
const shadowClasses = {
none: "shadow-none",
sm: "shadow-sm",
md: "shadow-md",
lg: "shadow-lg",
};
export function CustomImage({
src,
alt,
className,
radius = "lg",
shadow = "none",
isZoomed = false,
isBlurred = false,
fallbackSrc,
onError,
...props
}: CustomImageProps) {
const [imgSrc, setImgSrc] = useState(src);
const [isLoading, setIsLoading] = useState(true);
const handleError = (e: React.SyntheticEvent) => {
if (fallbackSrc && imgSrc !== fallbackSrc) {
setImgSrc(fallbackSrc);
}
onError?.(e);
};
const imageElement = (
setIsLoading(false)}
onError={handleError}
{...props}
/>
);
if (isBlurred) {
return (
{imageElement}
);
}
if (isZoomed || isLoading) {
return (
{isLoading && (
)}
{imageElement}
);
}
return imageElement;
}
// 用法
```
## 完整示例
```tsx
import { Image } from "@heroui/react";
export default function App() {
return (
);
}
```
```tsx
export default function App() {
return (
);
}
```
## 总结
1. **组件已移除**:v3 不再提供 `Image` 组件。
2. **调整 import**:移除 `import { Image } from "@heroui/react"`。
3. **使用原生标签**:用原生 `img`,或 Next.js `Image`。
4. **能力补齐**:模糊、悬停放大、骨架屏与回退图需自行组合实现。
5. **样式方式**:圆角、阴影与视觉效果直接用 Tailwind CSS 类控制。
## 迁移步骤
1. **移除 import**:从 `@heroui/react` 的 import 中删除 `Image`。
2. **替换标签**:把所有 `` 替换为 `img`(或 Next.js `Image`)。
3. **补齐 Tailwind 类**:用等价类表达圆角、阴影等视觉样式。
4. **按需实现行为**:如需模糊、放大、骨架屏、回退图,请自行实现。
5. **Next.js 项目**:可优先使用 `next/image` 做图片优化。
6. **可选**:对常用模式封装可复用组件。
## Next.js 的 `Image` 组件
在 Next.js 中,`next/image` 的 `Image` 组件提供:
* 自动图片优化
* 默认懒加载
* 响应式 `srcSet`
* 占位模糊(placeholder blur)
* 内置加载体验
```tsx
import Image from "next/image";
```
# InputOTP
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/input-otp
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/input-otp.mdx
> InputOTP 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 InputOTP 文档](/docs/react/components/input-otp)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,InputOtp 根据 `length` prop 自动渲染插槽:
```tsx
import { InputOtp } from "@heroui/react";
export default function App() {
return ;
}
```
在 v3 中,InputOTP 改为复合组件,需要手动声明插槽:
```tsx
import { InputOTP } from "@heroui/react";
export default function App() {
return (
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 单个组件,自动渲染各个分段\
**v3:** 复合组件:`InputOTP.Group`、`InputOTP.Slot`、`InputOTP.Separator`
### 2. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ---------------------------- | ------------------ | ----------------------------------------------------- |
| `length` | `InputOTP` | 已重命名为 `maxLength` |
| `allowedKeys` | `InputOTP` | 已重命名为 `pattern`(正则表达式) |
| `onValueChange` | `InputOTP` | 改用 `onChange` |
| `description`、`errorMessage` | - | 通过外部 Description / FieldError 处理 |
| `variant` | `InputOTP` | 简化为仅 `primary` \| `secondary` |
| `color`、`size`、`radius` | - | 已移除(请改用 Tailwind CSS) |
| `classNames` | - | 改在各子组件上使用 `className` |
| - | `textAlign` | 新增 prop:插槽内的文本对齐(`'left'` \| `'center'` \| `'right'`) |
| - | `inputMode` | 新增 prop:移动端的虚拟键盘类型(默认 `'numeric'`) |
| - | `placeholder` | 新增 prop:空插槽的占位符文本 |
| - | `pasteTransformer` | 新增 prop:转换粘贴的文本(例如去掉连字符) |
## 迁移示例
### 受控 InputOTP
```tsx
import { useState } from "react";
const [value, setValue] = useState("");
```
```tsx
import { useState } from "react";
const [value, setValue] = useState("");
```
### 使用 allowedKeys / pattern
```tsx
```
```tsx
import { REGEXP_ONLY_CHARS } from "@heroui/react";
```
### 表单校验
```tsx
{/* With description */}
{/* With error message */}
```
```tsx
import { Description, FieldError } from "@heroui/react";
{/* With description */}
Enter the code sent to your email
{/* With error message */}
Invalid code
```
### 使用 onComplete 回调
```tsx
console.log("Complete:", value)}
/>
```
```tsx
console.log("Complete:", value)}
>
```
## 组件结构
v3 InputOTP 遵循以下结构:
```
InputOTP (Root)
├── InputOTP.Group
│ ├── InputOTP.Slot (index={0})
│ ├── InputOTP.Slot (index={1})
│ └── ...
├── InputOTP.Separator (optional)
└── InputOTP.Group (optional, for grouping)
└── InputOTP.Slot (index={...})
```
## v3 中的新增 prop
v3 引入了几个 v2 中没有的 prop:
* **`textAlign`**:控制插槽内的文本对齐(`'left'` | `'center'` | `'right'`,默认 `'left'`)
* **`inputMode`**:设置移动端虚拟键盘类型(`'numeric'` | `'text'` | `'decimal'` | `'tel'` | `'search'` | `'email'` | `'url'`,默认 `'numeric'`)
* **`placeholder`**:设置空插槽的占位符文本
* **`pasteTransformer`**:`(text: string) => string` 类型的函数,用于转换粘贴的文本(例如从粘贴的代码中移除连字符)
```tsx
text.replace(/-/g, "")}
>
```
## 导出的正则表达式模式
为方便使用,HeroUI 从 input-otp 库再导出了几个常用的正则模式:
```tsx
import { REGEXP_ONLY_DIGITS, REGEXP_ONLY_CHARS, REGEXP_ONLY_DIGITS_AND_CHARS } from "@heroui/react";
// Use with the pattern prop
{/* ... */}
```
* **`REGEXP_ONLY_DIGITS`** —— 仅限数字字符(0-9)
* **`REGEXP_ONLY_CHARS`** —— 仅限字母字符(a-z、A-Z)
* **`REGEXP_ONLY_DIGITS_AND_CHARS`** —— 字母数字字符(0-9、a-z、A-Z)
## 总结
1. **组件结构**:必须用 `InputOTP.Group` 与 `InputOTP.Slot` 手动声明插槽
2. **length → maxLength**:prop 重命名
3. **allowedKeys → pattern**:prop 重命名,改为接收正则表达式
4. **onValueChange → onChange**:事件处理函数重命名
5. **移除 description**:改用独立的 `Description` 组件
6. **移除 errorMessage**:改用独立的错误展示组件
7. **简化变体**:v3 仅支持 `variant="primary"` 与 `variant="secondary"`
8. **移除 color**:请用 Tailwind CSS 类设置样式
9. **移除 size**:请用 Tailwind CSS 类设置样式
10. **移除 radius**:请用 Tailwind CSS 类设置样式
11. **移除 classNames**:改在各子组件上使用 `className` prop
12. **新增 prop**:v3 新增了 `textAlign`、`inputMode`、`placeholder` 与 `pasteTransformer`
13. **导出的正则模式**:可使用 `REGEXP_ONLY_DIGITS`、`REGEXP_ONLY_CHARS`、`REGEXP_ONLY_DIGITS_AND_CHARS` 作为 `pattern` 的取值
# Input
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/input
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/input.mdx
> Input 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Input 文档](/docs/react/components/input)。本指南只关注从 HeroUI v2 的迁移。
## 关键变化:Input → TextField
**v2:** `Input` 是功能完备的组件,内置标签、描述、错误信息、校验、变体、颜色、尺寸等。
**v3:** `Input` 现在只是一个原语组件(仅是输入元素本身)。对于表单字段,请使用 `TextField`,它由 `Input` 与 `Label`、`Description`、`FieldError` 等子组件组成。在 v2 中并没有独立的 TextField 或 InputGroup;单一的 Input 组件就负责标签、描述、起止内容与校验等所有职责。
## 何时使用 Input vs TextField
### 使用 TextField(大多数场景)
当你需要以下能力时,请使用 `TextField`:
* 标签
* 描述
* 错误信息
* 校验
* 表单集成
### 使用 Input(仅原语)
当你需要以下能力时,请使用 `Input`:
* 仅一个基础的 input 元素
* 自定义的标签 / 错误处理
* 与自定义表单组件集成
## 结构变化
在 v2 中,`Input` 是功能完备的组件,通过 prop 接收标签、描述、占位符等:
```tsx
import { Input } from "@heroui/react";
export default function App() {
return (
);
}
```
在 v3 中,`Input` 是原语组件。对于带标签与校验的表单字段,请使用 `TextField`,将 `Input` 与 `Label`、`Description`、`FieldError` 组合在一起:
```tsx
import { TextField, Label, Input, FieldError } from "@heroui/react";
export default function App() {
return (
Email
);
}
```
## 主要变化
### 1. 组件拆分
**v2:** 单一 `Input` 组件,承担所有职责\
**v3:** 拆分为 `Input`(原语)与 `TextField`(复合组件)
### 2. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ------------------------ | --------------------- | ------------------------------------------------------------------------------------------------ |
| `label` | `Label` | 在 `TextField` 内使用 `Label` 组件 |
| `description` | `Description` | 在 `TextField` 内使用 `Description` 组件 |
| `errorMessage` | `FieldError` | 在 `TextField` 内使用 `FieldError` 组件 |
| `variant` | `Input` | 从多种变体(flat、bordered、underlined、faded)改为 `"primary"`(默认,带阴影)与 `"secondary"`(弱化、无阴影,适合 Surface 场景) |
| `color`、`size`、`radius` | - | 已移除(请改用 Tailwind CSS) |
| `fullWidth` | `Input` 或 `TextField` | `Input` 与 `TextField` 上均仍支持 `fullWidth` 布尔属性 |
| `labelPlacement` | - | 通过 `Label` 的布局自行处理 |
| `startContent` | `InputGroup.Prefix` | 在 `TextField` 内使用 `InputGroup` 与 `InputGroup.Prefix` |
| `endContent` | `InputGroup.Suffix` | 在 `TextField` 内使用 `InputGroup` 与 `InputGroup.Suffix` |
| `isClearable` | - | 请通过按钮手动实现 |
| `isRequired`、`isInvalid` | `TextField` | 请设置在 `TextField` 上 |
| `validate` | `TextField` | 在 `TextField` 上使用 `validate` |
| `classNames` | - | 改在各子组件上使用 `className` |
| `onValueChange` | `Input` | 改用 `onChange` 事件处理函数 |
## 迁移示例
### 表单校验
```tsx
{/* With description */}
{/* With error message */}
{/* Required */}
```
```tsx
import { Description } from "@heroui/react";
{/* With description */}
Email
We'll never share your email
{/* With error message */}
{
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)) {
return "Please enter a valid email";
}
return null;
}}
>
Email
{/* Required */}
Email
```
### Input 起止内容
对于带前缀或后缀内容的输入,请使用 **InputGroup** 复合组件:
```tsx
$}
type="number"
/>
```
```tsx
import { InputGroup } from "@heroui/react";
Price
$
```
**InputGroup** 为前缀 / 后缀内容提供合适的样式与布局。其中:
* `InputGroup.Prefix` 表示位于输入之前的内容(取代 `startContent`)
* `InputGroup.Suffix` 表示位于输入之后的内容(取代 `endContent`)
* `InputGroup.Input` 表示输入元素本身
### 带清除按钮的 Input
```tsx
console.log("cleared")}
type="email"
/>
```
```tsx
import { useState } from "react";
import { CloseButton } from "@heroui/react";
const [value, setValue] = useState("");
Email
setValue(e.target.value)} />
{value && (
setValue("")}
/>
)}
```
### 受控 Input
```tsx
import { useState } from "react";
const [value, setValue] = useState("");
```
```tsx
import { useState } from "react";
const [value, setValue] = useState("");
Email
setValue(e.target.value)}
value={value}
/>
```
## 总结
1. **组件拆分**:表单字段使用 `TextField`,仅原语输入使用 `Input`
2. **标签必须显式声明**:必须使用 `Label` 组件而不是 `label` prop
3. **错误展示**:必须使用 `FieldError` 组件而不是 `errorMessage` prop
4. **描述**:必须使用 `Description` 组件而不是 `description` prop
5. **校验**:将 `validate` 函数从 `Input` 移到 `TextField` 上
6. **起止内容**:在 `TextField` 内使用 `InputGroup` 与 `InputGroup.Prefix` / `InputGroup.Suffix`
7. **清除按钮**:请手动实现 `CloseButton`
8. **简化变体**:v2 提供多种变体(flat、bordered、underlined、faded);v3 提供 `"primary"`(默认)与 `"secondary"`(适合 Surface)
9. **fullWidth**:`fullWidth` prop 在 `Input` 与 `TextField` 上均可用
10. **移除 color**:请改用 Tailwind CSS 类
11. **移除 size**:请改用 Tailwind CSS 类
12. **移除 radius**:请改用 Tailwind CSS 类
13. **移除 onValueChange**:改用 `onChange` 事件处理函数
14. **移除 classNames**:改在各子组件上使用 `className` prop
# Kbd
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/kbd
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/kbd.mdx
> Kbd 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Kbd 文档](/docs/react/components/kbd)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,Kbd 通过 `keys` prop 自动渲染键盘按键:
```tsx
import { Kbd } from "@heroui/react";
export default function App() {
return K ;
}
```
在 v3 中,Kbd 改为复合组件,需要手动声明各个按键:
```tsx
import { Kbd } from "@heroui/react";
export default function App() {
return (
K
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 单组件加 `keys` prop\
**v3:** 复合组件:`Kbd.Abbr`、`Kbd.Key`、`Kbd.Content`
### 2. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ------------ | --------- | --------------------------------------- |
| `keys` | - | 已移除(改用 `Kbd.Abbr` 配合 `keyValue`) |
| `classNames` | - | 改用 `className` |
| - | `Kbd` | 新增 `variant` prop(`default` \| `light`) |
| - | `Kbd.Key` | 用于按键文本的新增子组件(`Kbd.Content` 的替代写法) |
## 迁移示例
### 变体
```tsx
K
```
```tsx
K
K
```
## Kbd.Key 与 Kbd.Content
v3 提供了两个子组件用于包装按键文本内容:
* **`Kbd.Key`**:用于按键的文本内容(例如字母或数字)。接受 `children` 与 `className` prop。
* **`Kbd.Content`**:用于包装内容文本的等价写法。接受 `children` 与 `className` prop。
两者在包装非修饰键的文本时可以互换使用:
```tsx
{/* Using Kbd.Key */}
K
{/* Using Kbd.Content */}
K
```
## 组件结构
v3 Kbd 遵循以下结构:
```
Kbd (Root)
├── Kbd.Abbr (keyValue="command")
├── Kbd.Abbr (keyValue="shift") [optional, multiple]
└── Kbd.Key or Kbd.Content [optional, for text content]
```
## 可用的键值
`keyValue` prop 支持与 v2 相同的键盘按键类型:
**修饰键:**
* `command`、`shift`、`ctrl`、`option`、`alt`、`win`
**特殊键:**
* `enter`、`delete`、`escape`、`tab`、`space`、`capslock`、`help`
**导航键:**
* `up`、`down`、`left`、`right`、`pageup`、`pagedown`、`home`、`end`
**功能键:**
* `fn`
## 总结
1. **组件结构**:按键必须通过 `Kbd.Abbr` 子组件手动声明
2. **移除 keys prop**:改用 `Kbd.Abbr` 配合 `keyValue`
3. **包装子节点**:将文本内容包裹在 `Kbd.Content` 子组件中
4. **移除 classNames**:改在各子组件上使用 `className` prop
5. **新增 variant prop**:在 `Kbd` 上新增 `variant` 样式 prop(`default` | `light`)
6. **新增 Kbd.Key 子组件**:可作为 `Kbd.Content` 的替代写法用于按键文本
# Link
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/link
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/link.mdx
> Link 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Link 文档](/docs/react/components/link)。本指南只关注从 HeroUI v2 的迁移。
## 主要变化
### 1. 组件结构
**v2:** 单个组件,通过 prop 配置图标\
**v3:** 复合组件:使用 `Link.Icon` 子组件渲染图标
### 2. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ----------------------------- | ----- | -------------------------------------------------- |
| `showAnchorIcon`、`anchorIcon` | - | 改用 `Link.Icon` 子组件配合 children |
| `isExternal` | - | 改用 `target="_blank"` 与 `rel="noopener noreferrer"` |
| `size`、`color`、`isBlock` | - | 已移除(请改用 Tailwind CSS) |
| `disableAnimation` | - | 已移除 |
| `underline` | - | 已移除(请使用 Tailwind 工具类,如 `underline` 等) |
### 3. 新增 prop
* `onPress` —— 链接被激活(通过点击或键盘)时触发的事件处理函数。接受 `(e: PressEvent) => void` 回调。
## 结构变化
在 v2 中,Link 组件的用法:
```tsx
import { Link } from "@heroui/react";
export default function App() {
return Default Link;
}
```
在 v3 中,Link 组件在基本场景下的使用方式保持不变:
```tsx
import { Link } from "@heroui/react";
export default function App() {
return Default Link;
}
```
## 迁移示例
### 带图标
```tsx
{/* Default icon */}
External Link
{/* Custom icon */}
}
href="#"
>
Custom Icon
```
```tsx
{/* Default icon */}
External Link
{/* Custom icon */}
Custom Icon
```
### 外部链接
```tsx
External Link
```
```tsx
External Link
```
### 下划线与偏移(v3:使用 Tailwind)
```tsx
Hover to underline
```
```tsx
Hover to underline
Underline with offset
```
### 配合路由库
```tsx
import NextLink from "next/link";
import { Link } from "@heroui/react";
About
```
```tsx
import NextLink from "next/link";
import { Link } from "@heroui/react";
{/* Style your router link with the same classes as Link; add an icon if needed */}
About
```
## 组件结构
v3 Link 遵循以下结构:
```
Link (Root)
├── Link content (text)
└── Link.Icon (optional)
```
## 总结
1. **图标处理**:`showAnchorIcon` 与 `anchorIcon` prop 由 `Link.Icon` 子组件取代
2. **外部链接**:`isExternal` prop 已移除——请手动设置 `target` 与 `rel`
3. **移除 size prop**:改用 Tailwind CSS 类(`text-sm`、`text-base`、`text-lg`)
4. **移除 color prop**:改用 Tailwind CSS 类(`text-primary`、`text-danger` 等)
5. **移除 isBlock prop**:改用 Tailwind CSS 类(`block`、`hover:bg-surface`)
6. **下划线**:不再作为 prop 提供;Link 默认在悬浮时显示下划线。请使用 Tailwind 类(`underline`、`no-underline`、`underline-offset-1` 等)进行自定义
7. **路由**:v3 Link 不再支持 `as` 或 `asChild`;请直接使用路由库的 Link 并配合相同的 Tailwind 类(例如 `link`、`link__icon`)保持视觉一致
8. **移除动画**:`disableAnimation` prop 已移除
9. **`onPress` 处理函数**:新增 `onPress` prop,可用于处理链接激活事件(点击或键盘)
# Listbox
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/listbox
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/listbox.mdx
> Listbox 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 ListBox 文档](/docs/react/components/listbox)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
### v2:独立组件
在 v2 中,Listbox 使用彼此独立的组件:
```tsx
import { Listbox, ListboxItem, ListboxSection } from "@heroui/react";
export default function App() {
return (
Item 1
);
}
```
### v3:复合组件
在 v3 中,ListBox 使用复合组件:
```tsx
import { ListBox, Label } from "@heroui/react";
export default function App() {
return (
Item 1
);
}
```
## 主要变化
### 1. 组件命名
**v2:** `Listbox`、`ListboxItem`、`ListboxSection`\
**v3:** `ListBox`、`ListBox.Item`、`ListBox.Section`
### 2. 条目标识
**v2:** React 的 `key` 同时用于列表调和与条目身份(选择、焦点)。\
**v3:** 使用 `id` 管理状态与焦点,使用 `textValue` 提供无障碍信息(当内容不是纯文本时);列表中仍可为各项保留 React 的 `key`。
### 3. Prop 变更
| v2 prop | v3 位置 | 说明 |
| -------------------------------- | --------------------------------------- | -------------------------------------------------- |
| `key`(用于状态) | `ListBox.Item` | 条目身份(状态)请使用 `id` |
| — | `textValue`(在 `ListBox.Item` 上) | 用于无障碍(输入预判) |
| `variant`、`color` | `ListBox` 或 `ListBox.Item` 上的 `variant` | 精简为 `"default"` \| `"danger"`(不再有单独的 `color` prop) |
| `onAction` | `ListBox` | 按下条目时触发,签名为 `(key: Key) => void` |
| `disabledKeys` | `ListBox` | 与 v2 相同——一组键,对应应为非交互的条目 |
| `startContent`、`endContent` | — | 请在条目内容中手动放置图标 |
| `description` | `Description` | 使用 `Description` 组件 |
| `title`(在 Section 上) | `Header` | 使用 `Header` 组件 |
| `topContent`、`bottomContent` | — | 已移除(请自行处理) |
| `itemClasses`、`classNames` | — | 在各部分上使用 `className` |
| `hideSelectedIcon` | — | 不渲染 `ListBox.ItemIndicator` |
| `disableAnimation` | — | 已移除 |
| `isVirtualized`、`virtualization` | React Aria `Virtualizer` | 使用 React Aria 的 `` 包装(见下方示例) |
| `selectedKeys` | `ListBox` | 与 v2 相同(使用 `Selection` 类型的 Set) |
## 迁移示例
### 选择
```tsx
import { useState } from "react";
{/* Single selection */}
const [singleSelected, setSingleSelected] = useState(new Set(["text"]));
Text
Number
{/* Multiple selection */}
const [multiSelected, setMultiSelected] = useState(new Set(["text"]));
Text
Number
```
```tsx
import { useState } from "react";
import type { Selection } from "@heroui/react";
{/* Single selection */}
const [singleSelected, setSingleSelected] = useState(new Set(["text"]));
Text
Number
{/* Multiple selection */}
const [multiSelected, setMultiSelected] = useState(new Set(["text"]));
Text
Number
```
### 含描述
```tsx
New file
```
```tsx
import { Description, Label } from "@heroui/react";
New file
Create a new file
```
### 含图标
```tsx
}
>
New file
```
```tsx
New file
```
### 含分组
```tsx
New file
Edit file
Delete
```
```tsx
import { Header, Label, Separator } from "@heroui/react";
New file
Edit file
Delete
```
### 自定义选中指示器
```tsx
}
>
Item 1
```
```tsx
Item 1
{({isSelected}) =>
isSelected ? : null
}
```
### `variant` prop
在 v3 中,`ListBox` 与 `ListBox.Item` 都接受 `variant` prop,取值为 `"default"`(默认)或 `"danger"`。在根级 `ListBox` 上设置 `variant` 会作用于所有条目;在单个 `ListBox.Item` 上设置会覆盖该条目的根级值。
```tsx
{/* Root-level variant — all items inherit "danger" styling */}
Delete
{/* Per-item variant */}
Edit
Delete
```
### `onAction` 事件处理函数
`onAction` 在条目被按下(点击或 Enter)时触发,参数为该条目的 `id`(类型为 `Key`)。
```tsx
alert(`Action on ${key}`)}>
Copy
Paste
```
### `disabledKeys`
使用 `disabledKeys` 将特定条目设为非交互:
```tsx
Copy
Paste
```
### `ListBox.Item` 的渲染 prop
`ListBox.Item` 支持渲染 prop,可读取当前交互状态。可用字段包括 `isSelected`、`isFocused`、`isDisabled`、`isPressed`:
```tsx
{({isSelected, isFocused, isDisabled, isPressed}) => (
<>
Item 1
{isSelected && }
>
)}
```
### 虚拟化
v3 仍通过 React Aria 的 `` **支持虚拟化**。用 `Virtualizer` 包裹 `ListBox` 的条目,以高效渲染长列表:
```tsx
import {Virtualizer} from "react-aria-components";
{(item) => (
{item.name}
)}
```
## 组件剖析
v3 ListBox 的结构如下:
```
ListBox (Root)
├── ListBox.Item
│ ├── Icon (optional, manual placement)
│ ├── Label (required)
│ ├── Description (optional)
│ └── ListBox.ItemIndicator (optional)
└── ListBox.Section (optional)
├── Header (optional)
└── ListBox.Item
```
## 总结
1. **组件命名**:`Listbox` → `ListBox`,`ListboxItem` → `ListBox.Item`,`ListboxSection` → `ListBox.Section`。
2. **条目结构**:必须使用 `Label`、`Description`、`ListBox.ItemIndicator` 等组件。
3. **图标**:不再使用 `startContent` / `endContent` prop,改为手动排版。
4. **分组**:使用 `Header` 组件,不再使用 Section 的 `title` prop。
5. **`variant` prop**:`variant` 与 `color` 合并为单一的 `variant`(`"default"` | `"danger"`),可设在 `ListBox` 与 `ListBox.Item` 上。
6. **`onAction`**:`ListBox` 上新增 `onAction`,用于处理条目按下。
7. **`disabledKeys`**:仍在 `ListBox` 上支持,用于禁用特定条目。
8. **渲染 prop**:`ListBox.Item` 通过渲染 prop 提供 `isSelected`、`isFocused`、`isDisabled`、`isPressed`。
9. **已移除的内容 props**:`topContent`、`bottomContent`——请自行组织布局。
10. **虚拟化**:仍通过 React Aria `` 支持(替代 `isVirtualized` prop)。
11. **选择类型**:使用 `Selection` 类型(Set),而不是数组。
# Modal
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/modal
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/modal.mdx
> Modal 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Modal 文档](/docs/react/components/modal)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,Modal 使用相互独立的组件:
```tsx
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button, useDisclosure } from "@heroui/react";
export default function App() {
const {isOpen, onOpen, onOpenChange} = useDisclosure();
return (
<>
Open Modal
Title
Content
Footer
>
);
}
```
在 v3 中,Modal 使用复合组件:
```tsx
import { Modal, Button } from "@heroui/react";
export default function App() {
return (
Open Modal
Title
Content
Footer
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 相互独立的组件(`Modal`、`ModalContent`、`ModalHeader`、`ModalBody`、`ModalFooter`)\
**v3:** 复合组件(`Modal.Backdrop`、`Modal.Container`、`Modal.Dialog`、`Modal.Header`、`Modal.Body`、`Modal.Footer`)
### 2. 内置触发:`Modal.Trigger`
**v2:** 需要 `useDisclosure`,并手动把 `onPress` 接到打开逻辑上。\
**v3:** 提供 `Modal.Trigger` 作为内置触发组件,按下即可自动打开模态框,无需自行管理状态。
```tsx
import { Modal, ModalContent, Button, useDisclosure } from "@heroui/react";
const {isOpen, onOpen, onOpenChange} = useDisclosure();
Open Modal
{/* content */}
```
```tsx
import { Modal, Button } from "@heroui/react";
{/* A Button placed as a direct child of Modal automatically becomes the trigger */}
Open Modal
{/* content */}
```
```tsx
import { Modal } from "@heroui/react";
{/* Use Modal.Trigger for custom trigger elements (cards, links, etc.) */}
Settings
Manage your preferences
{/* content */}
```
`Modal.Trigger` 会把任意内容包装成可按压元素以打开模态框。当你需要标准 `Button` 以外的自定义触发元素时使用它。
### 3. 状态管理
**v2:** 使用 `useDisclosure`。\
**v3:** 内置触发场景下可不需要状态;也可在 `Modal.Backdrop` 上用受控的 `isOpen` / `onOpenChange`,或使用 `useOverlayState` 并把 `state` 传给根组件。
#### `useOverlayState` 替代 `useDisclosure`
`useOverlayState` 可直接替代 v2 的 `useDisclosure`,支持受控与非受控:
```tsx
import { useOverlayState } from "@heroui/react";
// 非受控(内部自管状态)
const state = useOverlayState();
// 非受控且默认打开
const state = useOverlayState({ defaultOpen: true });
// 带回调
const state = useOverlayState({
onOpenChange: (isOpen) => console.log("Modal is now:", isOpen),
});
// 受控(由你管理状态)
const [isOpen, setIsOpen] = useState(false);
const state = useOverlayState({ isOpen, onOpenChange: setIsOpen });
```
**Hook API:**
| 属性 | 类型 | 说明 |
| ----------------------- | --------------------------- | -------- |
| `state.isOpen` | `boolean` | 浮层当前是否打开 |
| `state.open()` | `() => void` | 打开浮层 |
| `state.close()` | `() => void` | 关闭浮层 |
| `state.toggle()` | `() => void` | 切换打开/关闭 |
| `state.setOpen(isOpen)` | `(isOpen: boolean) => void` | 直接设置打开状态 |
将 `state` 传给 `Modal` 根组件以完成连接:
```tsx
const state = useOverlayState();
Open
{/* content */}
```
从 `useDisclosure` 迁移到 `useOverlayState` 的更多细节,请参阅 [Hooks 迁移指南](/docs/react/migration/hooks)。
### 4. Prop 变更
| v2 prop | v3 位置 | 说明 |
| --------------------------- | --------------------------------------------------- | ---------------------------------------- |
| `size` | `size`(在 `Modal.Container`) | 简化取值(xs、sm、md、lg、cover、full) |
| `radius` | — | 已移除(请用 Tailwind CSS) |
| `shadow` | — | 已移除(请用 Tailwind CSS) |
| `backdrop` | `variant`(在 **`Modal.Backdrop`**) | 已重命名;取值不变(`opaque`、`blur`、`transparent`) |
| `scrollBehavior` | `scroll`(在 `Modal.Container`) | 已重命名(`normal` → `inside`) |
| `placement` | `placement`(在 `Modal.Container`) | 移到 Container |
| `isDismissable` | `isDismissable`(在 **`Modal.Backdrop`**) | 移到 `Modal.Backdrop` |
| `isKeyboardDismissDisabled` | `isKeyboardDismissDisabled`(在 **`Modal.Backdrop`**) | 移到 `Modal.Backdrop` |
| `isOpen` | `isOpen`(在 **`Modal.Backdrop`**) | 受控状态在 `Modal.Backdrop` |
| `onOpenChange` | `onOpenChange`(在 **`Modal.Backdrop`**) | 同上 |
| `onClose` | — | 使用渲染 prop 提供的 `close` |
| `hideCloseButton` | — | 省略 `Modal.CloseTrigger` 即可 |
| `closeButton` | — | 使用 `Modal.CloseTrigger` 自定义内容 |
| `motionProps` | — | 已移除(动画机制已不同) |
| `classNames` | — | 在各子组件上使用 `className` |
| `shouldBlockScroll` | — | 已移除(由组件自动处理) |
| `portalContainer` | — | 已移除 |
## 迁移示例
### 受控 Modal
```tsx
import { useDisclosure } from "@heroui/react";
const {isOpen, onOpen, onOpenChange} = useDisclosure();
{(onClose) => (
<>
Title
Content
>
)}
```
```tsx
import { useState } from "react";
const [isOpen, setIsOpen] = useState(false);
setIsOpen(true)}>Open
{({close}) => (
<>
Title
Content
>
)}
```
```tsx
import { useOverlayState } from "@heroui/react";
const state = useOverlayState();
Open
{({close}) => (
<>
Title
Content
>
)}
```
```tsx
import { useOverlayState } from "@heroui/react";
// 将 state 直接传给 Modal 根组件,无需再手动连接 isOpen/onOpenChange
const state = useOverlayState();
Open
{({close}) => (
<>
Title
Content
>
)}
```
`useDisclosure` → `useOverlayState` 的完整迁移说明,请参阅 [Hooks 迁移指南](/docs/react/migration/hooks)。
### 遮罩与 Container 的 prop
```tsx
{/* Backdrop */}
{/* content */}
{/* Placement */}
{/* content */}
{/* Scroll behavior */}
{/* content */}
```
```tsx
{/* 遮罩:在 Modal.Backdrop 上使用 variant */}
{/* content */}
{/* 位置:在 Container 上 */}
{/* content */}
{/* 滚动:在 Container 上 */}
{/* content */}
```
### 关闭按钮
```tsx
{/* Without close button */}
{/* content */}
{/* Custom close button */}
}>
{/* content */}
```
```tsx
{/* 无关闭按钮:省略 Modal.CloseTrigger */}
Title
{/* 自定义关闭按钮 */}
{/* content */}
```
### 自定义触发器
```tsx
import { Modal, ModalContent, useDisclosure } from "@heroui/react";
const {isOpen, onOpen, onOpenChange} = useDisclosure();
{/* Any element needed manual onPress + useDisclosure */}
Settings
Manage your preferences
{/* content */}
```
```tsx
import { Modal } from "@heroui/react";
{/* Modal.Trigger handles press events and accessibility automatically */}
Settings
Manage your preferences
Settings
{/* content */}
```
### 图标与标题
```tsx
Modal Title
```
```tsx
Modal Title
```
## 组件结构(Anatomy)
v3 的 Modal 结构如下:
```
Modal (Root)
├── Trigger (e.g. Button or Modal.Trigger)
└── Modal.Backdrop (variant, isDismissable, isKeyboardDismissDisabled)
└── Modal.Container (placement, scroll, size)
└── Modal.Dialog
├── Modal.CloseTrigger (optional)
├── Modal.Header
│ ├── Modal.Icon (optional)
│ └── Modal.Heading
├── Modal.Body
└── Modal.Footer
```
## 总结
1. **组件结构**:必须使用复合组件(`Modal.Container`、`Modal.Dialog` 等)。
2. **内置触发**:自定义触发用 `Modal.Trigger`;或将 `Button` 作为 `Modal` 的直接子元素,无需再手动接线状态。
3. **状态管理**:`useDisclosure` 由 `useOverlayState` 替代;支持受控/非受控,并支持根 `Modal` 的 `state` prop。
4. **prop 迁移**:许多 prop 从 `Modal` 移到 `Modal.Backdrop` 与 `Modal.Container`。
5. **关闭回调**:`onClose` 由 `Modal.Dialog` 渲染 prop 中的 `close` 替代。
6. **关闭按钮**:`hideCloseButton` / `closeButton` 由 `Modal.CloseTrigger` 组合方式替代。
7. **尺寸**:在 `Modal.Container` 上使用 `size`(xs、sm、md、lg、cover、full);`radius` 与 `shadow` 已移除,请用 Tailwind CSS。
8. **遮罩**:`backdrop` → `Modal.Backdrop` 上的 `variant`(`opaque`、`blur`、`transparent`)。
9. **滚动 prop 重命名**:`scrollBehavior` → `scroll`(`normal` → `inside`)。
10. **动画**:`motionProps` 已移除,动画机制已变化。
11. **新增子组件**:`Modal.Trigger`、`Modal.Icon`、`Modal.Heading`、`Modal.CloseTrigger`。
# Navbar
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/navbar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/navbar.mdx
> Navbar 从 HeroUI v2 到 v3 的迁移指南。
HeroUI v3 已**移除** Navbar 组件。请使用原生 HTML 元素与 Tailwind CSS 类手动搭建导航栏。本指南介绍常见模式,并简化复杂能力的说明。
## 关键变化
### 1. 组件移除
**v2:** `` 与子组件(`NavbarBrand`、`NavbarContent`、`NavbarItem`、`NavbarMenu`、`NavbarMenuItem`、`NavbarMenuToggle`)\
**v3:** 使用原生 HTML 手动组合
### 2. 子组件映射
| v2 组件 | v3 对应 | 说明 |
| ------------------ | --------------- | ---------- |
| `Navbar` | `` 元素 | 主容器 |
| `NavbarBrand` | `` 或 `
` | Logo / 品牌区 |
| `NavbarContent` | `` 元素 | 导航列表 |
| `NavbarItem` | `` 元素 | 导航项 |
| `NavbarMenu` | 移动端菜单覆盖层 | 需自行实现 |
| `NavbarMenuItem` | 移动菜单内的 ` ` | 移动菜单项 |
| `NavbarMenuToggle` | `` | 移动菜单切换 |
### 3. 已移除的能力
* `shouldHideOnScroll` — 需自行实现滚动检测
* `isBlurred` — 请使用 Tailwind CSS 的 `backdrop-blur` 工具类
* `isBordered` — 请使用 Tailwind CSS 边框类
* `position` 变体 — 请使用 Tailwind CSS 的 `sticky`、`fixed` 等类
* `maxWidth` 变体 — 请使用 Tailwind CSS 的 `max-w-*` 类
* 移动菜单动画 — 需自行实现
* 滚动锁定 — 需自行实现(见下文「滚动锁定」)
## 迁移示例
### 基础 Navbar
```tsx
import { Navbar, NavbarBrand, NavbarContent, NavbarItem, Link, Button } from "@heroui/react";
{/* Basic */}
ACME
Features
Pricing
{/* With right-aligned content */}
Logo
Sign Up
```
```tsx
import { Link, Button } from "@heroui/react";
{/* Basic */}
{/* With right-aligned content */}
```
### 移动菜单(简化版)
```tsx
import {
Navbar,
NavbarBrand,
NavbarContent,
NavbarItem,
NavbarMenu,
NavbarMenuItem,
NavbarMenuToggle,
} from "@heroui/react";
function App() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
Logo
Features
Pricing
Features
Pricing
);
}
```
```tsx
import { useState } from "react";
import { Link, Button } from "@heroui/react";
function App() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
setIsMenuOpen(!isMenuOpen)}
aria-label="Toggle menu"
>
Menu
{isMenuOpen ? (
) : (
)}
Logo
{isMenuOpen && (
)}
);
}
```
## 完整示例
```tsx
import {
Navbar,
NavbarBrand,
NavbarContent,
NavbarItem,
NavbarMenu,
NavbarMenuItem,
NavbarMenuToggle,
Link,
Button,
} from "@heroui/react";
export default function App() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
ACME
Features
Dashboard
Pricing
Login
Sign Up
Features
Dashboard
Pricing
);
}
```
```tsx
import { useState } from "react";
import { Link, Button } from "@heroui/react";
export default function App() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
setIsMenuOpen(!isMenuOpen)}
aria-label="Toggle menu"
aria-expanded={isMenuOpen}
>
Menu
{isMenuOpen ? (
) : (
)}
Features
Dashboard
Pricing
Login
Sign Up
{isMenuOpen && (
Features
Dashboard
Pricing
Login
Sign Up
)}
);
}
```
## 创建可复用的 Navbar 组件(推荐)
导航栏在应用中很常见,下面是一个简化的可复用组件示例:
```tsx
import { useState, ReactNode } from "react";
import { Link, Button } from "@heroui/react";
import { cn } from "@/lib/utils"; // or your cn utility
interface NavbarItem {
label: string;
href: string;
isActive?: boolean;
}
interface NavbarProps {
brand: ReactNode;
items: NavbarItem[];
rightContent?: ReactNode;
className?: string;
maxWidth?: "sm" | "md" | "lg" | "xl" | "2xl" | "full";
position?: "static" | "sticky" | "fixed";
}
const maxWidthClasses = {
sm: "max-w-[640px]",
md: "max-w-[768px]",
lg: "max-w-[1024px]",
xl: "max-w-[1280px]",
"2xl": "max-w-[1536px]",
full: "max-w-full",
};
export function Navbar({
brand,
items,
rightContent,
className,
maxWidth = "lg",
position = "sticky",
}: NavbarProps) {
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
setIsMenuOpen(!isMenuOpen)}
aria-label="Toggle menu"
aria-expanded={isMenuOpen}
>
Menu
{isMenuOpen ? (
) : (
)}
{brand}
{items.map((item) => (
{item.label}
))}
{rightContent && {rightContent}
}
{isMenuOpen && (
{items.map((item) => (
{item.label}
))}
{rightContent && (
{rightContent}
)}
)}
);
}
// Usage
ACME
>
}
items={[
{ label: "Features", href: "#features" },
{ label: "Dashboard", href: "#dashboard", isActive: true },
{ label: "Pricing", href: "#pricing" },
]}
rightContent={
<>
Login
Sign Up
>
}
/>
```
## 高级能力(需自行实现)
### 滚动时隐藏
`shouldHideOnScroll` 需要自行实现滚动检测:
```tsx
import { useState, useEffect } from "react";
function useScrollDirection() {
const [isHidden, setIsHidden] = useState(false);
const [lastScrollY, setLastScrollY] = useState(0);
useEffect(() => {
const handleScroll = () => {
const currentScrollY = window.scrollY;
setIsHidden(currentScrollY > lastScrollY && currentScrollY > 64);
setLastScrollY(currentScrollY);
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, [lastScrollY]);
return isHidden;
}
function NavbarWithHideOnScroll() {
const isHidden = useScrollDirection();
return (
{/* navbar content */}
);
}
```
### 滚动锁定
在移动菜单打开时锁定页面滚动:
```tsx
useEffect(() => {
if (isMenuOpen) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "";
}
return () => {
document.body.style.overflow = "";
};
}, [isMenuOpen]);
```
## 总结
1. **组件已移除**:`Navbar`、`NavbarBrand`、`NavbarContent`、`NavbarItem`、`NavbarMenu`、`NavbarMenuItem`、`NavbarMenuToggle` 均已移除。
2. **导入调整**:从 `@heroui/react` 中移除所有 Navbar 相关导入。
3. **手动组合**:使用原生 HTML 搭建导航栏。
4. **移动菜单**:通过状态管理手动实现移动菜单。
5. **样式**:直接使用 Tailwind CSS 类。
6. **高级能力**:滚动时隐藏、动画等需自行实现。
## 迁移步骤
1. **移除导入**:删除所有与 Navbar 相关的导入。
2. **替换结构**:将 `` 替换为 ``。
3. **替换子组件**:用语义化 HTML(``、``、``)替代子组件。
4. **补充移动菜单**:手动实现菜单切换与菜单面板。
5. **应用样式**:用 Tailwind CSS 完成布局与样式。
6. **管理状态**:用 React `useState` 管理移动菜单开关。
7. **(可选)** 为应用封装可复用的 Navbar 组件。
## 常见模式
### 简单导航
```tsx
```
### 搭配下拉(使用 v3 Dropdown)
```tsx
import { Dropdown, Button, Label } from "@heroui/react";
Features
Feature 1
Feature 2
```
# NumberInput
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/numberinput
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/numberinput.mdx
> NumberInput → NumberField 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 NumberField 文档](/docs/react/components/numberfield)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,NumberInput 是单个组件,通过 prop 配置:
```tsx
import { NumberInput } from "@heroui/react";
export default function App() {
return ;
}
```
在 v3 中,NumberField 改为复合组件:
```tsx
import { NumberField, Label } from "@heroui/react";
export default function App() {
return (
Amount
);
}
```
## 主要变化
### 1. 组件命名
**v2:** `NumberInput`\
**v3:** `NumberField`
### 2. 组件结构
**v2:** 单个组件,通过 prop 配置\
**v3:** 复合组件:`NumberField.Group`、`NumberField.Input`、`NumberField.IncrementButton`、`NumberField.DecrementButton`
### 3. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ----------------- | -------------------------- | ---------------------------------------------------------------- |
| `onValueChange` | `onChange` | 事件处理函数已重命名 |
| `label` | — | 改用 `Label` 组件 |
| `description` | — | 改用 `Description` 组件 |
| `errorMessage` | — | 改用 `FieldError` 组件 |
| `variant` | `variant`(在 NumberField 上) | 简化为仅 `primary` \| `secondary` |
| `color` | — | 已移除(请改用 Tailwind CSS) |
| `size` | — | 已移除(请改用 Tailwind CSS) |
| `radius` | — | 已移除(请改用 Tailwind CSS) |
| `startContent` | — | 手动将内容放入 Group 中 |
| `endContent` | — | 手动将内容放入 Group 中 |
| `labelPlacement` | — | 通过布局类自行处理 |
| `hideStepper` | — | 省略 `NumberField.IncrementButton` 与 `NumberField.DecrementButton` |
| `isClearable` | — | 请手动实现清除功能 |
| `classNames` | — | 改在各子组件上使用 `className` prop |
| `isWheelDisabled` | — | 已移除 |
## 迁移示例
### 表单校验
```tsx
{/* With description */}
{/* With error message */}
{/* Required */}
```
```tsx
import { Description, FieldError, Label } from "@heroui/react";
{/* With description */}
Amount
Enter the amount
{/* With error message */}
Amount
Please enter a valid number
{/* Required */}
Quantity
```
### 受控
```tsx
import { useState } from "react";
const [value, setValue] = useState();
```
```tsx
import { useState } from "react";
const [value, setValue] = useState();
Amount
```
### 不带步进按钮
```tsx
```
```tsx
Amount
```
### 数值约束
```tsx
{/* Min/Max */}
{/* Step */}
{/* Format options */}
```
```tsx
{/* Min/Max */}
Quantity
{/* Step */}
Percentage
{/* Format options */}
Price
```
## 组件结构
v3 NumberField 遵循以下结构:
```
NumberField (Root)
├── Label (optional)
├── NumberField.Group
│ ├── NumberField.DecrementButton
│ ├── NumberField.Input
│ └── NumberField.IncrementButton
├── Description (optional)
└── FieldError (optional)
```
## 总结
1. **组件重命名**:`NumberInput` → `NumberField`
2. **组件结构**:必须使用复合组件(`NumberField.Group`、`NumberField.Input` 等)
3. **标签 / 描述 / 错误**:改用独立组件(`Label`、`Description`、`FieldError`)
4. **步进按钮**:必须显式包含 `NumberField.IncrementButton` 与 `NumberField.DecrementButton`
5. **事件处理函数**:`onValueChange` → `onChange`
6. **简化变体**:v3 仅支持 `variant="primary"` 与 `variant="secondary"`;`color`、`size`、`radius` 已移除——请改用 Tailwind CSS
7. **移除 content prop**:`startContent`、`endContent` —— 请手动放置
8. **移除清除按钮**:`isClearable` 已移除——请自行实现
9. **步进控件**:`hideStepper` 已移除——通过省略相应按钮实现
10. **移除 labelPlacement**:`labelPlacement` 已移除——请通过布局自行实现
# Pagination
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/pagination
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/pagination.mdx
> Pagination 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Pagination 文档](/docs/react/components/pagination)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`Pagination` 是单个组件,通过 props 在内部完成所有渲染:
```tsx
import { Pagination } from "@heroui/react";
export default function App() {
return (
);
}
```
在 v3 中,Pagination 采用复合组件模式,需要显式组合各个部分:
```tsx
import { Pagination } from "@heroui/react";
export default function App() {
return (
1
2
3
10
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 单个 `Pagination` 组件,通过 `total` prop 自动生成页码项\
**v3:** 复合组件:`Pagination`、`Pagination.Summary`、`Pagination.Content`、`Pagination.Item`、`Pagination.Link`、`Pagination.Previous`、`Pagination.PreviousIcon`、`Pagination.Next`、`Pagination.NextIcon`、`Pagination.Ellipsis`
### 2. 页码生成
**v2:** 由 `total`、`siblings`、`boundaries` 等 props 自动生成页码\
**v3:** 手动组合页码项,完全掌控布局与行为;可自行实现分页逻辑或使用分页 hook。
### 3. Prop 变更
| v2 prop | v3 对应 | 说明 |
| ------------------------ | ------------------------------------------------------------------------- | --------------------------------------------- |
| `total` | — | 已移除(请手动组合条目) |
| `page` | — | 通过在 `Pagination.Link` 上使用 `isActive` 管理当前页 |
| `initialPage` | — | 请在外部自行管理状态 |
| `onChange` | — | 请在各个 `Pagination.Link` 上使用 `onPress` |
| `siblings` | — | 已移除(请手动组合条目) |
| `boundaries` | — | 已移除(请手动组合条目) |
| `dotsJump` | — | 已移除(省略号点击请自行处理) |
| `loop` | — | 已移除(请自行实现) |
| `showControls` | — | 请组合 `Pagination.Previous` 与 `Pagination.Next` |
| `isCompact` | — | 已移除(请用 Tailwind CSS 控制样式) |
| `showShadow` | — | 已移除(请使用 Tailwind `shadow-*` 类) |
| `size` | `Pagination` 上的 `size` | 与 v2 相同(`sm`、`md`、`lg`) |
| `variant` | — | 已移除(请使用 Tailwind CSS) |
| `color` | — | 已移除(请使用 Tailwind CSS) |
| `radius` | — | 已移除(请使用 Tailwind CSS) |
| `isDisabled` | `Pagination.Link`、`Pagination.Previous`、`Pagination.Next` 上的 `isDisabled` | 按条目分别控制,而非全局 |
| `disableCursorAnimation` | — | 已移除 |
| `disableAnimation` | — | 已移除 |
| `renderItem` | — | 请直接组合子节点 |
| `getItemAriaLabel` | — | 请在各条目上设置 `aria-label` |
| `classNames` | — | 请在各复合子组件上使用 `className` |
### 4. Hook 变化
**v2:** 提供 `usePagination` hook,便于自定义实现\
**v3:** 无内置 hook——请直接组合分页项,或自行实现分页逻辑
## 迁移示例
### 基础分页
```tsx
import { Pagination } from "@heroui/react";
```
```tsx
import { useState } from "react";
import { Pagination } from "@heroui/react";
const [page, setPage] = useState(1);
const totalPages = 10;
{Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (
setPage(p)}>
{p}
))}
```
### 上一页 / 下一页控件
```tsx
```
```tsx
const [page, setPage] = useState(1);
const totalPages = 10;
setPage((p) => Math.max(1, p - 1))}
>
{Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (
setPage(p)}>
{p}
))}
setPage((p) => Math.min(totalPages, p + 1))}
>
```
### 含省略号
```tsx
```
```tsx
const [page, setPage] = useState(1);
const totalPages = 20;
setPage((p) => Math.max(1, p - 1))}
>
setPage(1)}>
1
{page > 3 && (
)}
{[page - 1, page, page + 1]
.filter((p) => p > 1 && p < totalPages)
.map((p) => (
setPage(p)}>
{p}
))}
{page < totalPages - 2 && (
)}
setPage(totalPages)}
>
{totalPages}
setPage((p) => Math.min(totalPages, p + 1))}
>
```
### 仅上一页 / 下一页
```tsx
{/* v2 无内置的仅 prev/next 模式 */}
```
```tsx
const [page, setPage] = useState(1);
const totalPages = 10;
setPage((p) => p - 1)}
>
Previous
setPage((p) => p + 1)}
>
Next
```
### 使用 `Pagination.Summary`
```tsx
{/* v2 无内置 summary 插槽 */}
```
```tsx
const [page, setPage] = useState(1);
const perPage = 10;
const total = 100;
Showing {(page - 1) * perPage + 1}-{Math.min(page * perPage, total)} of {total}
{/* Pagination items */}
```
### 自定义图标
```tsx
{/* v2 需借助 renderItem 才能自定义图标 */}
```
```tsx
import { Icon } from "@iconify/react";
setPage((p) => p - 1)}>
{/* Page links */}
setPage((p) => p + 1)}>
```
## 样式变化
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className`
```tsx
1
```
## 组件剖析
v3 Pagination 的结构如下:
```
Pagination (Root, nav element)
├── Pagination.Summary (optional, info text)
└── Pagination.Content (ul container)
└── Pagination.Item (li wrapper, repeated)
├── Pagination.Previous (with Pagination.PreviousIcon)
├── Pagination.Link (page number, isActive for current)
├── Pagination.Ellipsis
└── Pagination.Next (with Pagination.NextIcon)
```
## 总结
1. **组件结构**:由单个自动生成式组件,改为需手动组合的复合组件。
2. **页码生成**:`total` / `siblings` / `boundaries` props → 结合自定义分页逻辑手动渲染页码项。
3. **当前页**:`page` / `initialPage` props → 在各个 `Pagination.Link` 上使用 `isActive`。
4. **导航控件**:`showControls` prop → 直接组合 `Pagination.Previous` 与 `Pagination.Next`。
5. **省略号**:由自动生成 → 在需要处手动放置 `Pagination.Ellipsis`。
6. **事件**:单一 `onChange` → 各 `Pagination.Link` 上的独立 `onPress` 处理函数。
7. **新能力**:`Pagination.Summary` 用于展示结果数量;可通过 `PreviousIcon` / `NextIcon` 的 children 自定义图标。
8. **Hook 移除**:`usePagination` → 请自行实现分页逻辑。
9. **样式 props 移除**:`variant`、`color`、`radius`、`isCompact`、`showShadow` → 请使用 Tailwind CSS。
10. **`classNames` 移除**:请在各复合子组件上使用 `className`。
# Popover
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/popover
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/popover.mdx
> Popover 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Popover 文档](/docs/react/components/popover)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,Popover 由独立的多个组件组成:
```tsx
import { Popover, PopoverTrigger, PopoverContent, Button } from "@heroui/react";
export default function App() {
return (
Open
Content
);
}
```
在 v3 中,Popover 改为复合组件:
```tsx
import { Popover, Button } from "@heroui/react";
export default function App() {
return (
Open
Title
Content
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 独立的多个组件(`Popover`、`PopoverTrigger`、`PopoverContent`)\
**v3:** 复合组件(`Popover`、`Popover.Trigger`、`Popover.Content`、`Popover.Dialog`、`Popover.Heading`、`Popover.Arrow`)
### 2. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ----------------------------------------- | ------------------------- | --------------------------------------------------- |
| `placement` | `placement`(在 Content 上) | 移至 `Popover.Content` |
| `offset` | `offset`(在 Content 上) | 移至 `Popover.Content` |
| `shouldFlip` | `shouldFlip`(在 Content 上) | 移至 `Popover.Content` |
| `isOpen` / `defaultOpen` / `onOpenChange` | 同名(在根上) | 受控状态仍保留在根 `Popover` 上 |
| `showArrow` | — | 改用 `Popover.Arrow` 子组件 |
| `size` | — | 已移除(请改用 Tailwind CSS) |
| `color` | — | 已移除(请改用 Tailwind CSS) |
| `radius` | — | 已移除(请改用 Tailwind CSS) |
| `shadow` | — | 已移除(请改用 Tailwind CSS) |
| `backdrop` | — | 已移除 |
| `motionProps` | — | 已移除(动画机制已不同) |
| `classNames` | — | 改在各子组件上使用 `className` |
| `onClose` | — | 改用 `onOpenChange((open) => { if (!open) { ... } })` |
## 迁移示例
### 内容配置
```tsx
{/* With arrow */}
Open
Content
{/* With placement */}
Open
Content
{/* With offset */}
Open
Content
```
```tsx
{/* With arrow - use component */}
Open
Content
{/* With placement - moved to Content */}
Open
Content
{/* With offset - moved to Content */}
Open
Content
```
### 带标题
```tsx
```
```tsx
Title
Content
```
### 受控
```tsx
import { useState } from "react";
const [isOpen, setIsOpen] = useState(false);
Open
Content
```
```tsx
import { useState } from "react";
const [isOpen, setIsOpen] = useState(false);
Open
Content
```
### 自定义触发器
```tsx
Custom
```
```tsx
Custom
```
## 组件结构
v3 Popover 遵循以下结构:
```
Popover (Root)
├── Popover.Trigger (optional, or use Button directly)
└── Popover.Content
└── Popover.Dialog
├── Popover.Arrow (optional)
├── Popover.Heading (optional)
└── Content
```
## v3 中的新功能
### Popover.Arrow 组件
在 v2 中,箭头通过根 `Popover` 上的 `showArrow` 布尔 prop 控制。在 v3 中,`Popover.Arrow` 是一个放置在 `Popover.Content` 内部的专用组件,让你可以完全控制其渲染:
```tsx
Open
Content
```
`Popover.Arrow` 也接受 `render` prop,可完全自定义箭头的渲染。
### 受控打开状态
受控的打开状态仍保留在根 `Popover` 组件上,prop 名称与 v2 一致:
| 属性 | 类型 | 默认值 | 描述 |
| -------------- | --------------------------- | ------- | ------------------- |
| `isOpen` | `boolean` | - | 控制 Popover 的可见性(受控) |
| `defaultOpen` | `boolean` | `false` | 初始打开状态(非受控) |
| `onOpenChange` | `(isOpen: boolean) => void` | - | 打开状态变化时触发 |
### 自定义渲染函数
`Popover.Content` 与 `Popover.Arrow` 都支持 `render` prop,让你可以在高级场景下用自定义渲染函数覆盖默认 DOM 元素。
## 总结
1. **组件结构**:必须使用复合组件(`Popover.Content`、`Popover.Dialog` 等)
2. **触发器**:可以使用 `Popover.Trigger`,或直接将 Button 作为子节点
3. **内容包装**:内容必须放在 `Popover.Dialog` 中
4. **箭头**:移除 `showArrow` prop——改用 `Popover.Arrow` 子组件
5. **标题**:改用 `Popover.Heading` 组件
6. **prop 移动位置**:`placement`、`offset`、`shouldFlip` 移到 `Popover.Content` 上
7. **移除样式 prop**:`size`、`color`、`radius`、`shadow` —— 请改用 Tailwind CSS
8. **移除 backdrop**:`backdrop` prop 已移除
9. **移除 motion**:`motionProps` 已移除,动画机制已不同
10. **移除 classNames**:改在各子组件上使用 `className` prop
# Progress
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/progress
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/progress.mdx
> Progress 从 HeroUI v2 到 v3(现在称为 ProgressBar)的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 ProgressBar 文档](/docs/react/components/progress-bar)。本指南只关注从 HeroUI v2 的迁移。
正在寻找 CircularProgress 的迁移说明?请参阅 [CircularProgress 迁移指南](/docs/react/migration/circular-progress)。
## 组件重命名
`Progress` 在 v3 中已重命名为 `ProgressBar`。
## 结构变化
在 v2 中,`Progress` 是单个组件,通过 prop 进行配置:
```tsx
import { Progress } from "@heroui/react";
export default function App() {
return (
);
}
```
在 v3 中,`ProgressBar` 改为复合组件模式:
```tsx
import { ProgressBar, Label } from "@heroui/react";
export default function App() {
return (
Loading...
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 单一 `Progress` 组件,所有部分都在内部渲染\
**v3:** 复合组件:`ProgressBar`、`ProgressBar.Output`、`ProgressBar.Track`、`ProgressBar.Fill`,并配合外部的 `Label`
### 2. 颜色
**v2:** `default`、`primary`、`secondary`、`success`、`warning`、`danger`\
**v3:** `default`、`accent`、`success`、`warning`、`danger`
### 3. Prop 变更
| v2 prop | v3 对应项 | 说明 |
| ------------------ | ----------------- | ------------------------------------------- |
| `value` | `value` | 相同 |
| `minValue` | `minValue` | 相同 |
| `maxValue` | `maxValue` | 相同 |
| `isIndeterminate` | `isIndeterminate` | 相同 |
| `formatOptions` | `formatOptions` | 相同 |
| `size` | `size` | 相同(`sm`、`md`、`lg`) |
| `color` | `color` | `primary` → `accent`,`secondary` 已移除 |
| `label` | - | 改用 `Label` 组件 |
| `valueLabel` | `valueLabel` | 相同 |
| `showValueLabel` | - | 通过包含或省略 `ProgressBar.Output` 控制 |
| `radius` | - | 已移除(请改用 Tailwind CSS) |
| `isStriped` | - | 已移除(请在 `ProgressBar.Fill` 上使用 Tailwind CSS) |
| `isDisabled` | - | 已移除 |
| `disableAnimation` | - | 已移除 |
| `classNames` | - | 改在各复合子组件上使用 `className` |
## 迁移示例
### 基本进度条
```tsx
import { Progress } from "@heroui/react";
```
```tsx
import { ProgressBar, Label } from "@heroui/react";
Loading...
```
### 不确定状态
```tsx
```
```tsx
```
### 无标签(无障碍)
```tsx
```
```tsx
```
### 自定义格式
```tsx
```
```tsx
Budget
```
## 样式变更
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className` prop
```tsx
Loading
```
## 组件结构
```
ProgressBar (Root)
├── Label (optional)
├── ProgressBar.Output (optional, formatted value display)
└── ProgressBar.Track
└── ProgressBar.Fill
```
## 总结
1. **重命名**:`Progress` → `ProgressBar`
2. **组件结构**:单组件 → 复合组件(`ProgressBar.Output`、`ProgressBar.Track`、`ProgressBar.Fill`)
3. **标签**:`label` prop → `Label` 组件
4. **数值显示**:`showValueLabel` prop → 通过包含或省略 `ProgressBar.Output` 控制
5. **颜色变化**:`primary` → `accent`,`secondary` 已移除
6. **移除的 prop**:`radius`、`isStriped`、`isDisabled`、`disableAnimation` → 请改用 Tailwind CSS
7. **移除 classNames**:改在各复合子组件上使用 `className`
# RadioGroup
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/radio-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/radio-group.mdx
> RadioGroup 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 RadioGroup 文档](/docs/react/components/radio-group)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`RadioGroup` 通过 `label` prop 设置标题:
```tsx
import { RadioGroup, Radio } from "@heroui/react";
export default function App() {
return (
London
Tokyo
Paris
);
}
```
在 v3 中,`RadioGroup` 改用 `Label` 组件,并配合 Radio 的复合结构:
```tsx
import { RadioGroup, Radio, Label, Description } from "@heroui/react";
export default function App() {
return (
Select city
London
Tokyo
Capital of Japan
Paris
);
}
```
## 主要变化
### 1. label prop → Label 组件
**v2:** 在 `RadioGroup` 上使用 `label` prop\
**v3:** 改用 `Label` 组件作为 `RadioGroup` 的子节点
### 2. Radio 结构
**v2:** 简单的 Radio 组件,子节点直接作为标签\
**v3:** 复合 Radio 组件,由 `Radio.Content`、`Radio.Control` 与 `Radio.Indicator` 组成
### 3. 描述处理
**v2:** 在单个 `Radio` 组件上使用 `description` prop\
**v3:** 将 `Description` 组件作为 `Radio.Content` 的兄弟节点使用
### 4. 事件处理函数
**v2:** 使用 `onValueChange` prop\
**v3:** 改用 `onChange` prop(来自 React Aria Components)
### 5. name prop
**v3:** 集成到表单时需要 `name` prop
### 6. 变体支持
**v3:** 新增 `variant` prop:`"primary"`(默认)或 `"secondary"`,后者用于较弱的视觉强调,适合 Surface 组件场景
### 7. 只读支持
**v3:** 新增 `isReadOnly` prop,可阻止值变化但仍保持分组可聚焦
## 迁移示例
### 带描述的 RadioGroup
```tsx
Basic
Pro
```
```tsx
import { RadioGroup, Radio, Label, Description } from "@heroui/react";
Select plan
Basic
Basic features
Pro
All features
```
### 变体
```tsx
{/* v2 did not have variant support */}
Basic
Pro
```
```tsx
import { RadioGroup, Radio, Label } from "@heroui/react";
{/* Primary variant (default) */}
Select plan
Basic
{/* Secondary variant - lower emphasis, suitable for Surface components */}
Select plan
Basic
```
### 只读
```tsx
import { RadioGroup, Radio, Label } from "@heroui/react";
{/* Prevents value changes while keeping the group focusable */}
Select plan
Basic
Pro
```
### 受控 RadioGroup
```tsx
import { useState } from "react";
import { RadioGroup, Radio } from "@heroui/react";
const [value, setValue] = useState("london");
London
Tokyo
```
```tsx
import { useState } from "react";
import { RadioGroup, Radio, Label } from "@heroui/react";
const [value, setValue] = useState("london");
Select city
London
Tokyo
```
## 总结
* 用 `Label` 子组件取代 `label` prop
* 将 Radio 子项更新为复合组件结构
* 将 `Description` 组件作为 `Radio.Content` 的兄弟节点,取代 Radio 的 `description` prop
* 将 `onValueChange` 改为 `onChange`
* 添加 `name` prop 以便集成到表单中
* 新增 `variant` prop:`"primary"`(默认)或 `"secondary"`,用于较弱的视觉强调
* 新增 `isReadOnly` prop:阻止值变化但保持分组可聚焦
* 通过 React Aria Components 提供更好的无障碍体验
# Radio
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/radio
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/radio.mdx
> Radio 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Radio 文档](/docs/react/components/radio-group)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,Radio 使用较为扁平的结构,通过 prop 配置:
```tsx
import { RadioGroup, Radio } from "@heroui/react";
export default function App() {
return (
London
Tokyo
);
}
```
在 v3 中,Radio 需要使用复合组件:
```tsx
import { RadioGroup, Radio, Label, Description } from "@heroui/react";
export default function App() {
return (
Select city
London
Tokyo
Capital of Japan
);
}
```
## 关键变化
### 1. 组件结构
**v2:** 简单的 Radio,`children` 作为标签文案\
**v3:** 复合组件:`Radio.Content`、`Radio.Control`、`Radio.Indicator`
### 2. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ------------------------ | ------------ | --------------------------------------------------------------- |
| `onValueChange` | `onChange` | 事件处理函数已重命名 |
| `label`(在 RadioGroup 上) | — | 使用 `Label` 组件 |
| `description`(在 Radio 上) | — | 将 `Description` 组件作为 `Radio.Content` 的兄弟节点使用 |
| `size` | — | 已移除(请改用 Tailwind CSS) |
| `color` | — | 已移除(请改用 Tailwind CSS) |
| `classNames` | — | 在各子组件上使用 `className` prop |
| `disableAnimation` | — | 已移除(动画机制已不同) |
| — | `variant` | `RadioGroup` 上的新 prop:`"primary"`(默认)或 `"secondary"`,用于较低强调度的样式 |
| — | `isReadOnly` | `RadioGroup` 上的新 prop:在保持组合可聚焦的同时阻止更改选中值 |
## 迁移示例
### 表单校验
```tsx
{/* With description */}
London
{/* With validation */}
London
```
```tsx
import { Label, Description, FieldError } from "@heroui/react";
{/* 带描述 */}
Select city
London
Capital of England
{/* 带校验 */}
Select city
London
Please select an option
```
### 受控
```tsx
import { useState } from "react";
const [selected, setSelected] = useState("london");
London
Tokyo
```
```tsx
import { useState } from "react";
const [selected, setSelected] = useState("london");
Select city
London
Tokyo
```
### 水平方向
```tsx
London
Tokyo
```
```tsx
Select city
London
Tokyo
```
### 变体
v3 在 `RadioGroup` 上新增 `variant` prop,可选 `"primary"`(默认)与 `"secondary"`:
```tsx
{/* Primary variant (default) */}
Select city
London
{/* Secondary:视觉强调更低,适合配合 Surface 组件 */}
Select city
London
```
### 只读
v3 支持在 `RadioGroup` 上使用 `isReadOnly`:在保持组合可聚焦的同时阻止更改选中值。
```tsx
Select city
London
Tokyo
```
## 组件组成
v3 的 Radio 结构如下:
```
RadioGroup (Root)
├── Label (optional)
├── Radio
│ ├── Radio.Content (the clickable label)
│ │ ├── Radio.Control
│ │ │ └── Radio.Indicator
│ │ └── Label
│ └── Description (optional, sibling)
└── FieldError (optional)
```
## 总结
1. **组件结构**:必须使用复合组件(`Radio.Content`、`Radio.Control`、`Radio.Indicator`)。
2. **标签**:已移除 `label` prop,请使用 `Label` 组件。
3. **描述**:已移除 `description` prop,请将 `Description` 组件作为 `Radio.Content` 的兄弟节点使用。
4. **事件处理函数**:`onValueChange` → `onChange`。
5. **样式相关 prop 已移除**:`size`、`color` 等请改用 Tailwind CSS。
6. **`classNames` 已移除**:在各子组件上使用 `className` prop。
7. **错误信息**:使用 `FieldError` 组件,而不是 `errorMessage` prop。
8. **新增 `variant` prop**:`RadioGroup` 支持 `"primary"`(默认)与 `"secondary"`,用于较低强调度的样式。
9. **新增 `isReadOnly` prop**:`RadioGroup` 支持 `isReadOnly`,在保持组合可聚焦的同时阻止更改选中值。
# RangeCalendar
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/range-calendar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/range-calendar.mdx
> RangeCalendar 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 RangeCalendar 文档](/docs/react/components/range-calendar)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`RangeCalendar` 是一个完全通过 props 配置的单一组件:
```tsx
import { RangeCalendar } from "@heroui/react";
import { today, getLocalTimeZone } from "@internationalized/date";
export default function App() {
return (
);
}
```
在 v3 中,RangeCalendar 改用带显式子组件的复合组件模式:
```tsx
import { RangeCalendar } from "@heroui/react";
import { today, getLocalTimeZone } from "@internationalized/date";
export default function App() {
return (
{(day) => {day} }
{(date) => }
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 单一 `RangeCalendar` 组件,所有布局都在内部处理\
**v3:** 复合组件:`RangeCalendar.Header`、`RangeCalendar.Heading`、`RangeCalendar.NavButton`、`RangeCalendar.Grid`、`RangeCalendar.GridHeader`、`RangeCalendar.GridBody`、`RangeCalendar.HeaderCell`、`RangeCalendar.Cell`、`RangeCalendar.CellIndicator`
### 2. 年份选择器
**v2:** 通过 `showMonthAndYearPickers` prop 提供内置月份 / 年份选择器\
**v3:** 使用专用复合组件:`RangeCalendar.YearPickerTrigger`、`RangeCalendar.YearPickerGrid`、`RangeCalendar.YearPickerGridBody`、`RangeCalendar.YearPickerCell`
### 3. Prop 变更
| v2 prop | v3 等效项 | 说明 |
| --------------------------- | --------------------------- | --------------------------------------------------------------------- |
| `value` | `value` | 保持一致 |
| `defaultValue` | `defaultValue` | 保持一致 |
| `onChange` | `onChange` | 保持一致 |
| `focusedValue` | `focusedValue` | 保持一致 |
| `onFocusChange` | `onFocusChange` | 保持一致 |
| `minValue` | `minValue` | 保持一致 |
| `maxValue` | `maxValue` | 保持一致 |
| `isDateUnavailable` | `isDateUnavailable` | 保持一致 |
| `allowsNonContiguousRanges` | `allowsNonContiguousRanges` | 保持一致 |
| `isDisabled` | `isDisabled` | 保持一致 |
| `isReadOnly` | `isReadOnly` | 保持一致 |
| `isInvalid` | `isInvalid` | 保持一致 |
| `pageBehavior` | `pageBehavior` | 保持一致 |
| `visibleMonths` | `visibleDuration` | 改为 `{months: number}` 对象 |
| `showMonthAndYearPickers` | - | 使用 `RangeCalendar.YearPickerTrigger` 和 `RangeCalendar.YearPickerGrid` |
| `onHeaderExpandedChange` | `onYearPickerOpenChange` | 已重命名 |
| `color` | - | 已移除(请改用 Tailwind CSS) |
| `calendarWidth` | - | 已移除(请改用 `className` 或 Tailwind CSS) |
| `weekdayStyle` | - | 已移除 |
| `firstDayOfWeek` | - | 改用 `I18nProvider` 的 locale |
| `topContent` | - | 将自定义内容作为 `RangeCalendar` children 放在网格之前 |
| `bottomContent` | - | 将自定义内容作为 `RangeCalendar` children 放在网格之后 |
| `errorMessage` | - | 已移除(请在外部处理校验) |
| `showHelper` | - | 已移除 |
| `disableAnimation` | - | 已移除 |
| `classNames` | - | 在各个复合组件上使用 `className` |
## 迁移示例
### 基本范围选择
```tsx
import { RangeCalendar } from "@heroui/react";
import { today, getLocalTimeZone } from "@internationalized/date";
```
```tsx
import { RangeCalendar } from "@heroui/react";
import { today, getLocalTimeZone } from "@internationalized/date";
{(day) => {day} }
{(date) => }
```
### 受控状态
```tsx
import { useState } from "react";
import { RangeCalendar } from "@heroui/react";
import { parseDate } from "@internationalized/date";
const [value, setValue] = useState({
start: parseDate("2024-03-01"),
end: parseDate("2024-03-07"),
});
```
```tsx
import { useState } from "react";
import { RangeCalendar } from "@heroui/react";
import { parseDate } from "@internationalized/date";
const [value, setValue] = useState({
start: parseDate("2024-03-01"),
end: parseDate("2024-03-07"),
});
{(day) => {day} }
{(date) => }
```
### 月份和年份选择器
```tsx
```
```tsx
{(day) => {day} }
{(date) => }
{(year) => }
```
### 多个月份
```tsx
```
```tsx
{(day) => {day} }
{(date) => }
{(day) => {day} }
{(date) => }
```
### 带非连续范围的不可用日期
```tsx
import { isWeekend } from "@internationalized/date";
import { useLocale } from "@react-aria/i18n";
const { locale } = useLocale();
isWeekend(date, locale)}
allowsNonContiguousRanges
/>
```
```tsx
import { isWeekend } from "@internationalized/date";
import { useLocale } from "@react-aria/i18n";
const { locale } = useLocale();
isWeekend(date, locale)}
allowsNonContiguousRanges
>
{(day) => {day} }
{(date) => }
```
### 顶部与底部内容
```tsx
Select your travel dates}
bottomContent={
Reset
}
/>
```
```tsx
Select your travel dates
{(day) => {day} }
{(date) => }
Reset
```
## 样式变化
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className` prop
```tsx
{(day) => {day} }
{(date) => }
```
## 组件结构
v3 RangeCalendar 遵循以下结构:
```
RangeCalendar (Root)
├── [Custom top content]
├── RangeCalendar.Header
│ ├── RangeCalendar.Heading (or RangeCalendar.YearPickerTrigger)
│ ├── RangeCalendar.NavButton slot="previous"
│ └── RangeCalendar.NavButton slot="next"
├── RangeCalendar.Grid (one per visible month)
│ ├── RangeCalendar.GridHeader
│ │ └── RangeCalendar.HeaderCell (render prop)
│ └── RangeCalendar.GridBody
│ └── RangeCalendar.Cell (render prop)
│ └── RangeCalendar.CellIndicator (optional)
├── RangeCalendar.YearPickerGrid (optional)
│ └── RangeCalendar.YearPickerGridBody
│ └── RangeCalendar.YearPickerCell
└── [Custom bottom content]
```
## 总结
1. **组件结构**:单一组件 → 带显式布局控制的复合组件
2. **年份选择器**:`showMonthAndYearPickers` prop → 专用的 `RangeCalendar.YearPickerTrigger` 和 `RangeCalendar.YearPickerGrid` 组件
3. **多个月份**:`visibleMonths={n}` → `visibleDuration={{months: n}}`,并使用多个带 `offset` 的 `RangeCalendar.Grid` 组件
4. **color 已移除**:请改用 Tailwind CSS 类
5. **顶部 / 底部内容**:prop 已移除 → 直接将内容作为 children 放在 `RangeCalendar` 内
6. **单元格自定义**:新增 `RangeCalendar.CellIndicator`,且 `RangeCalendar.Cell` 支持渲染 prop
7. **样式**:`classNames` prop → 各个复合组件上的 `className`
8. **已移除 prop**:`calendarWidth`、`weekdayStyle`、`showHelper`、`errorMessage`、`disableAnimation` —— 请使用 Tailwind CSS 或在外部处理
# ScrollShadow
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/scroll-shadow
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/scroll-shadow.mdx
> ScrollShadow 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 ScrollShadow 文档](/docs/react/components/scroll-shadow)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`ScrollShadow` 是一个包装组件:
```tsx
import { ScrollShadow } from "@heroui/scroll-shadow";
export default function App() {
return (
{/* scrollable content */}
);
}
```
在 v3 中,`ScrollShadow` 的基本结构相同,但提供了更多配置项:
```tsx
import { ScrollShadow } from "@heroui/react";
export default function App() {
return (
{/* scrollable content */}
);
}
```
## 主要变化
### 1. 导入路径
**v2:** 从 `@heroui/scroll-shadow` 包导入\
**v3:** 从统一封装的 `@heroui/react` 导入
### 2. 可见性控制
**v2:** `visibility` prop 控制阴影位置\
**v3:** `visibility` prop 选项更丰富,并新增 `onVisibilityChange` 回调
### 3. 阴影尺寸控制
**v2:** 阴影尺寸固定,定制能力有限\
**v3:** `size` prop 允许自定义阴影尺寸(单位:像素)
### 4. 增强的 prop
**v3:** 新增的 prop:
* `size` —— 阴影尺寸,单位为像素(默认值:40)
* `offset` —— 触发阴影显示前的滚动偏移(默认值:0)
* `isEnabled` —— 启用/禁用阴影检测(默认值:true)
* `hideScrollBar` —— 隐藏滚动条但保留滚动能力
* `variant` —— 阴影效果类型(默认:`fade`)
* `onVisibilityChange` —— 阴影可见性变化时触发的回调
## 迁移示例
### 自定义阴影尺寸
```tsx
{/* Shadow size was fixed */}
```
```tsx
{/* Large shadow */}
```
### 增强的可见性控制
```tsx
{/* content */}
```
```tsx
import { useState } from "react";
import { ScrollShadow } from "@heroui/react";
function App() {
const [visibility, setVisibility] = useState("none");
return (
<>
Shadow state: {visibility}
{/* content */}
>
);
}
```
### 变体
```tsx
{/* v2 used theme variants (e.g. orientation, hideScrollBar) */}
```
```tsx
{/* Shadow effect controlled by variant (default: fade) */}
```
## 总结
* 将导入从 `@heroui/scroll-shadow` 更新为 `@heroui/react`
* 增强的 API,提供更多配置项
* 更好的阴影检测与可见性控制
* 同样支持横向 / 纵向方向;统一从 `@heroui/react` 导入
* 可自定义阴影尺寸与变体(例如 fade)
* 性能与无障碍均有改进
# Select
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/select
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/select.mdx
> Select 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Select 文档](/docs/react/components/select)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,Select 通过 prop 构成简单结构:
```tsx
import { Select, SelectItem } from "@heroui/react";
export default function App() {
return (
Cat
Dog
);
}
```
在 v3 中,Select 需要使用复合组件,并通过 `ListBox` 渲染菜单项:
```tsx
import { Select, Label, ListBox } from "@heroui/react";
export default function App() {
return (
Select animal
Cat
Dog
);
}
```
## 关键变化
### 1. 组件结构
**v2:** 简单的 Select,子节点为 `SelectItem`\
**v3:** 复合组件(`Select.Trigger`、`Select.Value`、`Select.Indicator`、`Select.Popover`)与用于菜单项的 `ListBox`
### 2. 菜单项组件
**v2:** `SelectItem`、`SelectSection`\
**v3:** `ListBox.Item`、`ListBox.Section`(分组标题使用 `Header`,分组之间使用 `Separator`)
### 3. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ------------------------------------------------- | --------------- | ------------------------------------------ |
| `selectedKeys` | `value` | 从 Set / 数组改为单个值或数组 |
| `onSelectionChange` | `onChange` | 已重命名的事件处理函数 |
| `defaultSelectedKeys` | `defaultValue` | 已重命名的 prop |
| `label` | — | 使用 `Label` 组件 |
| `description` | — | 使用 `Description` 组件 |
| `errorMessage` | — | 使用 `FieldError` 组件 |
| `variant` | `variant` | 仅保留 `primary` \| `secondary` |
| `color` | — | 已移除(请使用 Tailwind CSS) |
| `size` | — | 已移除(请使用 Tailwind CSS) |
| `radius` | — | 已移除(请使用 Tailwind CSS) |
| `classNames` | — | 在各子组件上使用 `className` prop |
| `startContent` | — | 直接自定义 `Select.Trigger` |
| `endContent` | — | 直接自定义 `Select.Trigger` |
| `selectorIcon` | — | 自定义 `Select.Indicator` 的 children |
| `isClearable` | — | 手动实现清除按钮 |
| `renderValue` | — | 使用 `Select.Value` 的渲染 prop |
| `labelPlacement` | — | 标签始终在外部 |
| `isRequired` | `isRequired` | 仍然可用 |
| `disabledKeys` | `disabledKeys` | 仍然可用 |
| `isOpen` | `isOpen` | 新增:受控地控制 Popover 的打开状态 |
| `defaultOpen` | `defaultOpen` | 新增:非受控的默认打开状态 |
| `onOpenChange` | `onOpenChange` | 新增:打开状态变化时触发 |
| `selectionMode` | `selectionMode` | 仍然可用 |
| `disableAnimation` | — | 已移除(动画机制已不同) |
| `popoverProps`、`listboxProps`、`scrollShadowProps` | — | 直接在 `Select.Popover`、`ListBox` 等组件上传入 prop |
## 迁移示例
### 选择
```tsx
import { useState } from "react";
{/* Single selection */}
const [singleValue, setSingleValue] = useState(new Set([]));
Cat
Dog
{/* Multiple selection */}
const [multiValue, setMultiValue] = useState(new Set([]));
Cat
Dog
```
```tsx
import { useState } from "react";
import type { Key } from "@heroui/react";
{/* Single selection */}
const [singleValue, setSingleValue] = useState(null);
Select animal
Cat
Dog
{/* Multiple selection */}
const [multiValue, setMultiValue] = useState([]);
Select animals
Cat
Dog
```
### 表单校验
```tsx
{/* With description */}
Cat
{/* With validation */}
Cat
```
```tsx
import { Label, Description, FieldError } from "@heroui/react";
{/* With description */}
Select animal
Choose your favorite
Cat
{/* With validation */}
Select animal
Cat
Please select an option
```
### 带分组
```tsx
import { Select, SelectItem, SelectSection } from "@heroui/react";
Cat
Dog
Eagle
Parrot
```
```tsx
import { Select, Label, ListBox, Header, Separator } from "@heroui/react";
Select animal
Cat
Dog
Eagle
Parrot
```
### 受控的打开状态
```tsx
{/* v2 不支持受控的打开状态 */}
Cat
Dog
```
```tsx
import { useState } from "react";
import { Select, Label, ListBox } from "@heroui/react";
const [isOpen, setIsOpen] = useState(false);
Select animal
Cat
Dog
```
### 禁用选项
```tsx
import { Select, SelectItem } from "@heroui/react";
Cat
Dog
Parrot
```
```tsx
import { Select, Label, ListBox } from "@heroui/react";
Select animal
Cat
Dog
Parrot
```
### 必填
```tsx
import { Select, SelectItem } from "@heroui/react";
Cat
Dog
```
```tsx
import { Select, Label, ListBox } from "@heroui/react";
Select animal
Cat
Dog
```
### 自定义指示器
```tsx
} label="Select animal">
Cat
```
```tsx
Select animal
Cat
```
## 组件剖析
v3 Select 的结构如下:
```
Select (Root)
├── Label (optional)
├── Select.Trigger
│ ├── Select.Value
│ └── Select.Indicator
├── Description (optional)
├── Select.Popover
│ └── ListBox
│ ├── ListBox.Item
│ │ ├── Label (optional)
│ │ ├── Description (optional)
│ │ └── ListBox.ItemIndicator
│ ├── ListBox.Section (optional)
│ │ ├── Header (section title)
│ │ └── ListBox.Item
│ └── Separator (optional, between sections)
└── FieldError (optional)
```
## 重要说明
### 菜单项标识
* **v2:** React 的 `key` 同时用于列表协调与选择时的项标识。
* **v3:** 在 `ListBox.Item` 上使用 `id`(状态 / 焦点)与 `textValue`(无障碍);列表协调仍使用 React 的 `key`。
### 选择值的类型
* **v2:** `selectedKeys` 为 `Set` 或数组
* **v3:** 单选时 `value` 为 `Key | null`,多选时为 `Key[]`
### 清除按钮
`isClearable` prop 已移除。若要实现清除按钮:
```tsx
{value && (
setValue(null)}>Clear
)}
{/* ... */}
```
## 总结
1. **组件结构**:必须使用复合组件(`Select.Trigger`、`Select.Value`、`Select.Indicator`、`Select.Popover`)。
2. **菜单项组件**:`SelectItem` → `ListBox.Item`,`SelectSection` → `ListBox.Section`(分组标题用 `Header`,分组之间用 `Separator`)。
3. **标签 / 描述 / 错误**:使用独立组件,而不是对应 prop。
4. **选择相关 prop**:`selectedKeys` / `onSelectionChange` → `value` / `onChange`。
5. **受控的打开状态**:新增 `isOpen`、`defaultOpen`、`onOpenChange` 用于控制 Popover 的打开状态。
6. **禁用选项**:仍支持 `disabledKeys` 以禁用指定菜单项。
7. **必填**:仍支持 `isRequired` 标记字段为必填。
8. **样式**:`variant` 仅保留 `primary` | `secondary`;`color`、`size`、`radius` 已移除,请用 Tailwind CSS 扩展。
9. **classNames 已移除**:在各子组件上使用 `className` prop。
10. **内容类 prop 已移除**:`startContent`、`endContent` 需通过自定义触发器实现。
11. **清除按钮**:`isClearable` 已移除,需手动实现。
12. **自定义值**:用 `Select.Value` 的渲染 prop 替代 `renderValue`。
# Skeleton
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/skeleton
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/skeleton.mdx
> Skeleton 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Skeleton 文档](/docs/react/components/skeleton)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,Skeleton 会包裹其子节点,并根据 `isLoaded` 显示或隐藏:
```tsx
import { Skeleton } from "@heroui/react";
export default function App() {
const isLoaded = false;
return (
);
}
```
在 v3 中,Skeleton 改为独立的占位符,由你自己手动控制其可见性:
```tsx
import { Skeleton } from "@heroui/react";
export default function App() {
const isLoaded = false;
return (
<>
{!isLoaded ? (
) : (
)}
>
);
}
```
## 主要变化
### 1. 组件行为
**v2:** 包裹子节点,并根据 `isLoaded` 显示/隐藏\
**v3:** 独立占位符——可见性由你自行控制
### 2. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ------------------ | --------------- | ------------------------------------------------------------------ |
| `isLoaded` | — | 通过条件渲染手动控制可见性 |
| `disableAnimation` | `animationType` | 改用 `animationType="shimmer" \| "pulse" \| "none"`(用 `"none"` 禁用动画) |
| `classNames` | — | 直接使用 `className` prop |
| `children` | — | Skeleton 不再包裹内容 |
### 3. 新增 prop
* `animationType` —— 控制动画类型:`"shimmer"`(默认)、`"pulse"` 或 `"none"`
## 迁移示例
### 配合加载状态
```tsx
import { useState } from "react";
const [isLoaded, setIsLoaded] = useState(false);
```
```tsx
import { useState } from "react";
const [isLoaded, setIsLoaded] = useState(false);
{!isLoaded ? (
) : (
)}
```
### 独立 Skeleton
```tsx
```
```tsx
```
### 动画类型
```tsx
{/* Shimmer (default) */}
{/* No animation */}
```
```tsx
{/* Shimmer (default) */}
{/* Pulse */}
{/* No animation */}
```
### 复杂示例:带内容的 Card
```tsx
import { useState } from "react";
const [isLoaded, setIsLoaded] = useState(false);
```
```tsx
import { useState } from "react";
const [isLoaded, setIsLoaded] = useState(false);
{!isLoaded ? (
<>
>
) : (
<>
>
)}
```
### 同步的微光效果
在 v3 中,你可以创建一次性扫过所有 Skeleton 的同步微光效果。在父容器上加上 `skeleton--shimmer` 类,并将每个子 Skeleton 的 `animationType` 设为 `"none"`:
```tsx
{/* The parent container drives a single shimmer across all children */}
```
这种方式适用于卡片类布局——你需要的是一次统一扫过整体的微光,而不是每个 Skeleton 各自独立地播放动画。
## 全局动画配置
在 v3 中,你可以通过 CSS 变量在全局设置默认的动画类型:
```css
:root {
--skeleton-animation: pulse; /* shimmer, pulse, or none */
}
```
各个组件上的 `animationType` prop 可以覆盖该默认值。
## 总结
1. **不再包裹子节点**:Skeleton 不再包裹其子节点——它本身就是一个独立的占位符
2. **不再有 `isLoaded` prop**:通过条件渲染手动控制可见性
3. **动画控制**:`disableAnimation` → `animationType`(`"shimmer"`、`"pulse"`、`"none"`)
4. **样式**:`classNames` → 直接使用 `className` prop
5. **简化的 API**:组件更简单,专注于占位符职责
6. **同步微光**:在父容器上使用 `skeleton--shimmer` 类,并将子 Skeleton 的 `animationType` 设为 `"none"`,即可实现统一的微光扫过效果
# Slider
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/slider
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/slider.mdx
> Slider 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Slider 文档](/docs/react/components/slider)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,Slider 是结构简单、通过 prop 配置的组件:
```tsx
import { Slider } from "@heroui/react";
export default function App() {
return (
);
}
```
在 v3 中,Slider 改为复合组件:
```tsx
import { Slider, Label } from "@heroui/react";
export default function App() {
return (
Volume
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 通过 prop 配置的简单 Slider\
**v3:** 复合组件:`Slider.Output`、`Slider.Track`、`Slider.Fill`、`Slider.Thumb`
### 2. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ------------------- | -------- | -------------------------------- |
| `label` | — | 改用 `Label` 组件 |
| `size` | — | 已移除(请改用 Tailwind CSS) |
| `color` | — | 已移除(请改用 Tailwind CSS) |
| `radius` | — | 已移除(请改用 Tailwind CSS) |
| `classNames` | — | 改在各子组件上使用 `className` prop |
| `showSteps` | — | 暂不支持 |
| `showTooltip` | — | 暂不支持 |
| `marks` | — | 暂不支持(请参阅 `Slider.Marks`) |
| `startContent` | — | 直接在 Track 包装层中自定义 |
| `endContent` | — | 直接在 Track 包装层中自定义 |
| `hideValue` | — | 省略 `Slider.Output` |
| `hideThumb` | — | 省略 `Slider.Thumb` |
| `showOutline` | — | 改用 Tailwind CSS |
| `renderThumb` | — | 使用 `Slider.Thumb` 的 render prop |
| `renderLabel` | — | 改用 `Label` 组件 |
| `renderValue` | — | 使用 `Slider.Output` 的 render prop |
| `getValue` | — | 使用 `Slider.Output` 的 render prop |
| `getTooltipValue` | — | 使用 `Slider.Output` 的 render prop |
| `fillOffset` | — | 暂不支持 |
| `disableThumbScale` | — | 改用 Tailwind CSS |
| `disableAnimation` | — | 已移除 |
| `onChangeEnd` | `Slider` | 仍支持——当用户完成拖动 thumb 时触发 |
## 迁移示例
### 受控 Slider
```tsx
import { useState } from "react";
const [value, setValue] = useState(25);
```
```tsx
import { useState } from "react";
const [value, setValue] = useState(25);
Volume
```
### 区间 Slider
```tsx
```
```tsx
Price Range
{({state}) => (
<>
{state.values.map((_, i) => (
))}
>
)}
```
### 垂直 Slider
```tsx
```
```tsx
Volume
```
### 自定义数值显示
```tsx
(
Custom: {value}
)}
label="Volume"
defaultValue={30}
/>
```
```tsx
Volume
{({state}) => `Custom: ${state.values[0]}`}
```
## 组件结构
v3 Slider 遵循以下结构:
```
Slider (Root)
├── Label (optional)
├── Slider.Output (optional)
└── Slider.Track
├── Slider.Fill
└── Slider.Thumb (or multiple for range)
```
对于区间 Slider,请使用 `Slider.Track` 的 render prop:
```tsx
{({state}) => (
<>
{state.values.map((_, i) => (
))}
>
)}
```
## 总结
1. **组件结构**:必须使用复合组件(`Slider.Output`、`Slider.Track`、`Slider.Fill`、`Slider.Thumb`)
2. **标签处理**:移除 `label` prop——改用 `Label` 组件
3. **移除样式 prop**:`size`、`color`、`radius` —— 改用 Tailwind CSS
4. **移除 classNames**:改在各子组件上使用 `className` prop
5. **移除功能性 prop**:`showSteps`、`showTooltip`、`marks`、`startContent`、`endContent` —— 暂不支持,或自行手动定制
6. **移除 render prop**:`renderThumb`、`renderLabel`、`renderValue` —— 改用各组件自身的 render prop
7. **移除可见性 prop**:`hideValue`、`hideThumb` —— 通过省略对应子组件实现
8. **区间 Slider**:必须在 `Slider.Track` 的 render prop 中遍历多个 thumb
9. **onChangeEnd**:在 `Slider` 上仍受支持——当用户结束拖动 thumb 时触发
# Snippet
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/snippet
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/snippet.mdx
> Snippet 从 HeroUI v2 到 v3 的迁移指南。
Snippet 组件在 HeroUI v3 中已**移除**。请使用原生 HTML 元素配合 Tailwind CSS,并通过 Clipboard API 自行实现复制能力。
## 主要变化
### 1. 组件移除
**v2:** 来自 `@heroui/react` 的 `` 组件\
**v3:** 原生 HTML 元素(``、``)+ 手动实现复制逻辑
### 2. 功能对照
v2 的 Snippet 具备以下能力,需要在 v3 中分别替代:
| v2 功能 | v3 替代 | 说明 |
| -------------------------------------- | ---------------------- | ------------------------------------ |
| 复制按钮 | Button + Clipboard API | 使用 `navigator.clipboard.writeText()` |
| 复制提示 | Tooltip 组件 | 使用 v3 的 Tooltip |
| 符号前缀 | 手动渲染 | 将符号作为文本内容输出 |
| 多行支持 | 数组映射 | 对字符串数组做 `map` |
| 变体(`flat`、`solid`、`bordered`、`shadow`) | Tailwind 类 | 使用背景 / 边框等工具类 |
| 颜色(`default`、`primary` 等) | Tailwind 类 | 使用颜色相关工具类 |
| 尺寸(`sm`、`md`、`lg`) | Tailwind 字号 | 使用 `text-sm`、`text-base`、`text-lg` |
| 圆角 | Tailwind 圆角 | 使用 `rounded-*` 类 |
## 结构变化
在 v2 中,`Snippet` 是带内置复制能力的包装组件:
```tsx
import { Snippet } from "@heroui/react";
export default function App() {
return (
npm install @heroui/react
);
}
```
在 v3 中,请使用原生 HTML 元素并手动实现复制:
```tsx
import { Button, Tooltip } from "@heroui/react";
import { useState } from "react";
export default function App() {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
await navigator.clipboard.writeText("npm install @heroui/react");
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
$
npm install @heroui/react
{copied ? "✓" : "📋"}
{copied ? "Copied!" : "Copy to clipboard"}
);
}
```
## 迁移示例
### 多行 Snippet
```tsx
{[
"npm install @heroui/react",
"yarn add @heroui/react",
"pnpm add @heroui/react"
]}
```
```tsx
import { Button, Tooltip } from "@heroui/react";
import { useState } from "react";
function MultiLineSnippet() {
const [copied, setCopied] = useState(false);
const lines = [
"npm install @heroui/react",
"yarn add @heroui/react",
"pnpm add @heroui/react"
];
const codeString = lines.join("\n");
const handleCopy = async () => {
await navigator.clipboard.writeText(codeString);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
{lines.map((line, index) => (
$
{line}
))}
{copied ? "✓" : "📋"}
{copied ? "Copied!" : "Copy to clipboard"}
);
}
```
### 样式选项
```tsx
{/* With variants */}
npm install @heroui/react
{/* Without symbol */}
npm install @heroui/react
{/* Without copy button */}
npm install @heroui/react
```
```tsx
{/* With variants */}
$
npm install @heroui/react
{/* Copy button */}
{/* Without symbol */}
npm install @heroui/react
{/* Copy button */}
{/* Without copy button */}
$
npm install @heroui/react
```
## 创建可复用的 Snippet 组件(推荐)
Snippet 类需求很常见,下面是一个完整的可复用组件示例:
```tsx
import { Snippet } from "@heroui/react";
npm install @heroui/react
```
```tsx
import { Button, Tooltip } from "@heroui/react";
import { useState, ReactNode } from "react";
import { cn } from "@/lib/utils"; // or your cn utility
interface SnippetProps {
children: string | string[];
symbol?: string | ReactNode;
variant?: "flat" | "solid" | "bordered" | "shadow";
color?: "default" | "primary" | "secondary" | "success" | "warning" | "danger";
size?: "sm" | "md" | "lg";
radius?: "none" | "sm" | "md" | "lg" | "full";
hideSymbol?: boolean;
hideCopyButton?: boolean;
disableCopy?: boolean;
disableTooltip?: boolean;
className?: string;
codeString?: string;
onCopy?: (value: string) => void;
}
const variantClasses = {
flat: "bg-default-100",
solid: "bg-default-200",
bordered: "border border-default-200 bg-transparent",
shadow: "bg-default-100 shadow-sm",
};
const colorClasses = {
default: "text-default-foreground",
primary: "text-accent",
secondary: "text-default-600",
success: "text-success",
warning: "text-warning",
danger: "text-danger",
};
const sizeClasses = {
sm: "px-1.5 py-0.5 text-xs",
md: "px-3 py-1.5 text-sm",
lg: "px-4 py-2 text-base",
};
const radiusClasses = {
none: "rounded-none",
sm: "rounded-sm",
md: "rounded-md",
lg: "rounded-lg",
full: "rounded-full",
};
export function Snippet({
children,
symbol = "$",
variant = "flat",
color = "default",
size = "md",
radius = "md",
hideSymbol = false,
hideCopyButton = false,
disableCopy = false,
disableTooltip = false,
className,
codeString,
onCopy,
}: SnippetProps) {
const [copied, setCopied] = useState(false);
const isMultiLine = Array.isArray(children);
const lines = isMultiLine ? children : [children];
const textToCopy = codeString || (isMultiLine ? lines.join("\n") : String(children));
const handleCopy = async () => {
if (disableCopy) return;
try {
await navigator.clipboard.writeText(textToCopy);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
onCopy?.(textToCopy);
} catch (error) {
console.error("Failed to copy:", error);
}
};
const symbolElement = hideSymbol ? null : (
{symbol}{typeof symbol === "string" ? " " : ""}
);
const copyButton = hideCopyButton ? null : (
{copied ? (
✓
) : (
📋
)}
{copied ? "Copied!" : "Copy to clipboard"}
);
return (
{isMultiLine ? (
{lines.map((line, index) => (
{symbolElement}
{line}
))}
) : (
{symbolElement}
{children}
)}
{copyButton}
);
}
// Usage
npm install @heroui/react
```
## 总结
1. **组件已移除**:v3 不再提供 `Snippet` 组件。
2. **导入调整**:移除 `import { Snippet } from "@heroui/react"`。
3. **使用原生元素**:改用原生 ``、`` 等元素。
4. **手动复制**:使用 Clipboard API 实现复制。
5. **样式**:直接用 Tailwind CSS 类表达变体、颜色、尺寸。
6. **Tooltip**:复制按钮的提示请使用 v3 Tooltip。
7. **Button**:复制按钮请使用 v3 Button。
## 迁移步骤
1. **移除导入**:从 `@heroui/react` 的导入中删除 `Snippet`。
2. **替换组件**:将所有 `` 替换为原生 HTML 结构。
3. **实现复制**:使用 `navigator.clipboard.writeText()` 等方法。
4. **添加复制按钮**:使用 v3 的 Button 与 Tooltip。
5. **应用样式**:用 Tailwind CSS 表达变体、颜色、尺寸。
6. **处理多行**:多行场景对数组做映射渲染。
7. **(可选)** 在应用中封装可复用的 Snippet 组件。
## Clipboard API 说明
Clipboard API 需要:
* HTTPS(本地开发可使用 localhost)
* 用户手势触发(不能自动调用)
* 现代浏览器支持
如需兼容旧浏览器,可采用回退方案:
```tsx
const handleCopy = async (text: string) => {
try {
await navigator.clipboard.writeText(text);
} catch (error) {
// 旧版浏览器回退
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.position = "fixed";
textArea.style.opacity = "0";
document.body.appendChild(textArea);
textArea.select();
document.execCommand("copy");
document.body.removeChild(textArea);
}
};
```
# Spacer
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/spacer
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/spacer.mdx
> Spacer 从 HeroUI v2 到 v3 的迁移指南。
Spacer 组件已在 HeroUI v3 中**移除**。请改为直接使用 Tailwind CSS 的 margin 工具类。
## 关键变化
### 1. 组件移除
**v2:** 来自 `@heroui/react` 的 `` 组件\
**v3:** Tailwind CSS margin 工具类(`ml-*`、`mr-*`、`mt-*`、`mb-*`、`mx-*`、`my-*`)
### 2. Prop 对应关系
v2 的 Spacer 组件提供以下 prop,可映射到 Tailwind 工具类:
| v2 prop | v3 对应 | 说明 |
| ---------- | ------------------------ | ------------ |
| `x={n}` | `ml-{n}` 或 `mx-{n}` | 水平外边距(左侧或两侧) |
| `y={n}` | `mt-{n}` 或 `my-{n}` | 垂直外边距(顶部或两侧) |
| `isInline` | `inline-block` 或 `block` | 显示类型 |
### 3. 间距刻度
v2 中的间距数值与 Tailwind 的 spacing 刻度一致:
* `x={1}` → `ml-1`(0.25rem / 4px)
* `x={2}` → `ml-2`(0.5rem / 8px)
* `x={4}` → `ml-4`(1rem / 16px)
* `y={4}` → `mt-4`(1rem / 16px)
* 以此类推。
## 迁移示例
### 基础间距
```tsx
import { Spacer } from "@heroui/react";
{/* Vertical spacing */}
{/* Horizontal spacing */}
```
```tsx
{/* 垂直间距 */}
{/* 水平间距 */}
```
### 使用 gap(推荐)
在 flex 与 grid 布局中,使用 `gap` 往往比 Spacer 更合适:
```tsx
import { Spacer } from "@heroui/react";
{/* Horizontal layout */}
{/* Vertical layout */}
Button 1
Button 2
```
```tsx
{/* 水平布局 */}
{/* 垂直布局 */}
Button 1
Button 2
```
## 完整示例
```tsx
import { Spacer, Button } from "@heroui/react";
export default function App() {
return (
Title
Description text
Cancel
Submit
);
}
```
```tsx
import { Button } from "@heroui/react";
export default function App() {
return (
Title
Description text
Cancel
Submit
);
}
```
## 间距刻度参考
Tailwind CSS spacing 刻度(与 v2 Spacer 数值对应):
| 数值 | 尺寸 | Tailwind class |
| ----- | ------------- | --------------------------- |
| `0` | 0px | `m-0`、`ml-0`、`mt-0` 等 |
| `px` | 1px | `m-px`、`ml-px`、`mt-px` 等 |
| `0.5` | 0.125rem(2px) | `m-0.5`、`ml-0.5`、`mt-0.5` 等 |
| `1` | 0.25rem(4px) | `m-1`、`ml-1`、`mt-1` 等 |
| `2` | 0.5rem(8px) | `m-2`、`ml-2`、`mt-2` 等 |
| `3` | 0.75rem(12px) | `m-3`、`ml-3`、`mt-3` 等 |
| `4` | 1rem(16px) | `m-4`、`ml-4`、`mt-4` 等 |
| `5` | 1.25rem(20px) | `m-5`、`ml-5`、`mt-5` 等 |
| `6` | 1.5rem(24px) | `m-6`、`ml-6`、`mt-6` 等 |
| `8` | 2rem(32px) | `m-8`、`ml-8`、`mt-8` 等 |
| `10` | 2.5rem(40px) | `m-10`、`ml-10`、`mt-10` 等 |
| `12` | 3rem(48px) | `m-12`、`ml-12`、`mt-12` 等 |
| `16` | 4rem(64px) | `m-16`、`ml-16`、`mt-16` 等 |
| `20` | 5rem(80px) | `m-20`、`ml-20`、`mt-20` 等 |
## 最佳实践
### 1. 在 flex / grid 布局中使用 gap
用 `gap` 工具类替代 Spacer 组件:
```tsx
// ✅ 推荐
Button 1
Button 2
// ❌ 不推荐
```
### 2. 垂直列表使用 space 工具类
```tsx
// ✅ 推荐
// 备选
```
### 3. 直接使用 margin 工具类
将 margin 直接写在元素上,而不是使用 Spacer:
```tsx
// ✅ 推荐
Content
// ❌ 不推荐
<>
Content
>
```
## 总结
1. **组件已移除**:v3 中不再提供 `Spacer` 组件。
2. **导入调整**:移除 `import { Spacer } from "@heroui/react"`。
3. **使用 Tailwind 工具类**:改用 margin 工具类(`ml-*`、`mt-*`、`mx-*`、`my-*` 等)。
4. **优先使用 gap**:在 flex / grid 布局中优先使用 `gap-*`。
5. **优先使用 space**:在列表中优先使用 `space-y-*` 与 `space-x-*` 保持一致的间距。
## 迁移步骤
1. **移除导入**:从 `@heroui/react` 的导入中删除 `Spacer`。
2. **替换 Spacer**:将 ` ` 替换为 `ml-{n}` 或 `mx-{n}` 等 class。
3. **替换 Spacer**:将 ` ` 替换为 `mt-{n}` 或 `my-{n}` 等 class。
4. **使用 gap**:在 flex / grid 布局中用 `gap-{n}` 替代 Spacer。
5. **使用 space**:在垂直 / 水平列表中用 `space-y-{n}` 或 `space-x-{n}`。
## 常见模式
### 垂直堆叠与间距
```tsx
// 使用 space-y 工具类
```
### 水平行与间距
```tsx
// 使用 gap 工具类
Button 1
Button 2
Button 3
```
### 特定元素之间的自定义间距
```tsx
Item 1
Item 2 (with custom spacing)
Item 3
```
# Spinner
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/spinner
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/spinner.mdx
> Spinner 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Spinner 文档](/docs/react/components/spinner)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,Spinner 支持多种变体与标签:
```tsx
import { Spinner } from "@heroui/react";
export default function App() {
return ;
}
```
在 v3 中,Spinner 已简化为单一圆形变体:
```tsx
import { Spinner } from "@heroui/react";
export default function App() {
return ;
}
```
## 主要变化
### 1. 组件行为
**v2:** 支持多种变体(`default`、`simple`、`gradient`、`wave`、`dots`、`spinner`)以及标签\
**v3:** 仅保留单一圆形旋转变体,不再支持标签
### 2. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ------------ | ------- | ------------------------------------------------------------ |
| `label` | — | 如有需要,请使用单独的元素手动添加标签 |
| `variant` | — | 仅提供一种 spinner 变体 |
| `labelColor` | — | 不再支持标签 |
| `classNames` | — | 直接使用 `className` prop |
| `size` | `size` | 取值变化:`"sm" \| "md" \| "lg"` → `"sm" \| "md" \| "lg" \| "xl"` |
| `color` | `color` | 取值有变化(详见下文) |
### 3. 颜色值变化
* `"default"` → `"current"`(继承当前文本颜色)
* `"primary"` → `"accent"`
* `"secondary"` → 已移除(请改用 `"current"` 或 `"accent"`)
* `"success"`、`"warning"`、`"danger"` → 仍可使用
## 迁移示例
### 带标签
```tsx
{/* Simple label */}
{/* With label color and custom styling */}
```
```tsx
{/* Simple label */}
Loading...
{/* With custom styling */}
Loading...
```
### 尺寸与颜色
```tsx
{/* Sizes */}
{/* Colors */}
```
```tsx
{/* Sizes */}
{/* Colors */}
```
### 变体(已移除)
```tsx
```
```tsx
{/* Only one variant available - circular spinner */}
{/* For other spinner styles, consider using CSS animations */}
```
## 颜色映射
迁移颜色时,可参考以下映射:
| v2 颜色 | v3 颜色 | 说明 |
| ------------- | ------------------------ | --------- |
| `"default"` | `"current"` | 继承当前文本颜色 |
| `"primary"` | `"accent"` | 使用强调色 |
| `"secondary"` | `"current"` 或 `"accent"` | 改用当前色或强调色 |
| `"success"` | `"success"` | 不变 |
| `"warning"` | `"warning"` | 不变 |
| `"danger"` | `"danger"` | 不变 |
## 总结
1. **不再支持标签**:移除 `label` prop——如有需要请手动添加标签
2. **不再有变体**:移除 `variant` prop——仅保留单一圆形 spinner
3. **不再有标签颜色**:移除 `labelColor` prop——请自行为标签设置样式
4. **颜色值变化**:`"default"` → `"current"`、`"primary"` → `"accent"`、`"secondary"` 已移除
5. **新增尺寸**:新增 `"xl"` 尺寸可选
6. **移除 classNames**:直接使用 `className` prop
# Switch
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/switch
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/switch.mdx
> Switch 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Switch 文档](/docs/react/components/switch)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,`Switch` 结构简单,children 作为标签:
```tsx
import { Switch } from "@heroui/react";
export default function App() {
return Enable notifications ;
}
```
在 v3 中,`Switch` 需要复合组件:
```tsx
import { Switch, Label } from "@heroui/react";
export default function App() {
return (
Enable notifications
);
}
```
## 主要变化
### 1. 组件结构
**v2:** children 作为标签的简单 Switch\
**v3:** 复合组件(`Switch.Content`、`Switch.Control`、`Switch.Thumb`)与 `Label` 组件
### 2. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ------------------ | ---------- | ----------------------------------- |
| `onValueChange` | `onChange` | 事件处理函数已重命名 |
| `size` | `size` | 仍在根上(`sm` \| `md` \| `lg`) |
| `label` | — | 请使用 `Label` 组件 |
| `color` | — | 已移除(请使用 Tailwind CSS) |
| `thumbIcon` | — | 请在 `Switch.Thumb` 内使用 `Switch.Icon` |
| `startContent` | — | 请直接自定义控件 |
| `endContent` | — | 请直接自定义控件 |
| `classNames` | — | 请在各子组件上使用 `className` |
| `disableAnimation` | — | 已移除(动画机制已不同) |
### 3. 新组件
* `SwitchGroup` — 用于将多个 Switch 成组
* `Switch.Content` — 可点击的标签,包裹控件与 `Label`
* `Switch.Icon` — 拇指(Thumb)内的图标
## 迁移示例
### 受控 Switch
```tsx
import { useState } from "react";
const [isSelected, setIsSelected] = useState(true);
Airplane mode
```
```tsx
import { useState } from "react";
const [isSelected, setIsSelected] = useState(true);
Airplane mode
```
### 无标签
```tsx
```
```tsx
```
### 带拇指图标
```tsx
}>Enable notifications
```
```tsx
Enable notifications
```
### 带起始 / 结束内容
```tsx
}
endContent={ }
>
Dark mode
```
```tsx
Dark mode
```
### 带标签与描述
```tsx
Enable notifications
```
```tsx
import { Switch, Label, Description } from "@heroui/react";
Enable notifications
You will receive notifications for all activity
```
### 尺寸与颜色
```tsx
{/* Sizes */}
Small
Medium
Large
{/* Colors */}
Primary
Success
Danger
```
```tsx
{/* Sizes */}
Small
Medium
Large
{/* Colors - Use Tailwind CSS classes */}
Primary
Success
Danger
```
### Switch 组
```tsx
{/* No built-in group component in v2 */}
Allow Notifications
Marketing emails
```
```tsx
import { SwitchGroup } from "@heroui/react";
Allow Notifications
Marketing emails
```
## 组件组成
v3 `Switch` 的结构如下:
```
Switch (Root)
├── Switch.Content (the clickable label)
│ ├── Switch.Control
│ │ └── Switch.Thumb
│ │ └── Switch.Icon (optional)
│ └── Label
├── Description (optional, sibling)
└── FieldError (optional, sibling)
```
分组时:
```
SwitchGroup
├── Switch
│ ├── Switch.Content
│ │ ├── Switch.Control
│ │ │ └── Switch.Thumb
│ │ └── Label
│ └── Description (optional)
└── Switch
├── Switch.Content
│ ├── Switch.Control
│ │ └── Switch.Thumb
│ └── Label
└── Description (optional)
```
## 说明
### 事件处理函数
* **v2:** `onValueChange` prop
* **v3:** `onChange` prop(签名相同:`(isSelected: boolean) => void`)
### 标签
* **v2:** children 作为标签
* **v3:** `Label` 放在 `Switch.Content`(可点击的标签)内;`Description` / `FieldError` 作为 `Switch.Content` 的同级元素
### 图标
* **v2:** `thumbIcon` 用于拇指内图标,`startContent` / `endContent` 用于外侧图标
* **v3:** 拇指内图标用 `Switch.Icon`(置于 `Switch.Thumb` 内);起始 / 结束内容请自定义 `Switch.Control`
## 总结
1. **组件结构**:必须使用复合组件(`Switch.Content`、`Switch.Control`、`Switch.Thumb`)
2. **可点击的标签**:用 `Switch.Content` 包裹 `Switch.Control` 与 `Label`;将 `Description` / `FieldError` 作为 `Switch.Content` 的同级元素
3. **标签**:不再用 children 作为标签 — 请在 `Switch.Content` 内使用 `Label`
4. **事件处理函数**:`onValueChange` → `onChange`
5. **样式相关 prop 已移除**:`color` — 请使用 Tailwind CSS
6. **图标相关 prop 已移除**:`thumbIcon`、`startContent`、`endContent` — 请用子组件或直接自定义
7. **`classNames` 已移除**:请在各子组件上使用 `className` prop
8. **新组件**:`SwitchGroup` 用于成组;`Switch.Content` 作为可点击的标签,包裹控件与 `Label`
# Table
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/table
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/table.mdx
> Table 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Table 文档](/docs/react/components/table)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,Table 对每个部分使用独立的命名导入:
```tsx
import { Table, TableHeader, TableColumn, TableBody, TableRow, TableCell } from "@heroui/react";
export default function App() {
return (
);
}
```
在 v3 中,Table 使用点语法的复合组件,并新增 `Table.ScrollContainer` 与 `Table.Content`:
```tsx
import { Table } from "@heroui/react";
export default function App() {
return (
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 独立的命名导入(`Table`、`TableHeader`、`TableColumn`、`TableBody`、`TableRow`、`TableCell`)\
**v3:** 单一 `Table` 导入配合点语法:`Table`、`Table.ScrollContainer`、`Table.Content`、`Table.Header`、`Table.Column`、`Table.Body`、`Table.Row`、`Table.Cell`、`Table.Footer`
### 2. 新增包裹层
* **`Table`**:根容器(样式外层)
* **`Table.ScrollContainer`**:横向滚动与自定义滚动条
* **`Table.Content`**:实际的 `` 元素(`aria-label`、`selectionMode`、`sortDescriptor` 等放在这里)
* **`Table.Footer`**:替代 `bottomContent`,用于分页等底部内容
### 3. Prop 变更
| v2 prop | v3 对应位置 | 说明 |
| ------------------------------ | ------------------------------ | ----------------------------------------- |
| `aria-label` | `Table.Content` 的 `aria-label` | 移到 `Table.Content` |
| `selectionMode` | `Table.Content` | 移到 `Table.Content` |
| `selectedKeys` | `Table.Content` | 移到 `Table.Content` |
| `defaultSelectedKeys` | `Table.Content` | 移到 `Table.Content` |
| `onSelectionChange` | `Table.Content` | 移到 `Table.Content` |
| `sortDescriptor` | `Table.Content` | 移到 `Table.Content` |
| `onSortChange` | `Table.Content` | 移到 `Table.Content` |
| `disabledKeys` | `Table.Content` | 移到 `Table.Content` |
| `disallowEmptySelection` | `Table.Content` | 移到 `Table.Content` |
| `selectionBehavior` | `Table.Content` | 移到 `Table.Content` |
| `disabledBehavior` | `Table.Content` | 移到 `Table.Content` |
| `onRowAction` | `Table.Content` | 移到 `Table.Content` |
| `onCellAction` | `Table.Content` | 移到 `Table.Content` |
| `topContent` | — | 放在 `Table` 内、`Table.ScrollContainer` 之前 |
| `bottomContent` | — | 使用 `Table.Footer` |
| `topContentPlacement` | — | 已移除(直接组合布局) |
| `bottomContentPlacement` | — | 已移除(直接组合布局) |
| `color` | — | 已移除(请用 Tailwind CSS) |
| `variant` | `Table` 的 `variant` | 变为 `"primary"`(卡片式,默认)或 `"secondary"`(扁平) |
| `layout` | — | 已移除 |
| `radius` | — | 已移除(请用 Tailwind CSS) |
| `shadow` | — | 已移除(请用 Tailwind CSS) |
| `isStriped` | — | 已移除(请用 Tailwind CSS) |
| `isCompact` | — | 已移除(请用 Tailwind CSS) |
| `isHeaderSticky` | — | 已移除(请用 Tailwind CSS,例如 `sticky top-0`) |
| `fullWidth` | — | 已移除(默认全宽) |
| `removeWrapper` | — | 已移除(直接组合布局) |
| `hideHeader` | — | 已移除(省略 `Table.Header` 或用 CSS) |
| `isVirtualized` | — | 使用 React Aria 的 `Virtualizer` 包裹 |
| `maxTableHeight` | — | 使用 CSS 或 Virtualizer |
| `rowHeight` | — | 与 Virtualizer 一起使用 `TableLayout` |
| `isKeyboardNavigationDisabled` | — | 已移除 |
| `disableAnimation` | — | 已移除 |
| `classNames` | — | 在各复合子组件上使用 `className` |
### 4. 使用 Checkbox 的选择
**v2:** 设置 `selectionMode` 后由表格自动渲染 Checkbox。\
**v3:** 在列与行中显式使用带 `slot="selection"` 的 `Checkbox`。
### 5. 加载与空状态
**v2:** `TableBody` 上的 `loadingState`、`loadingContent`、`emptyContent`。\
**v3:** `Table.Body` 上的 `renderEmptyState`;无限滚动加载用 `Table.LoadMore`。
### 6. 分页
**v2:** `Table` 上的 `bottomContent`。\
**v3:** `Table.Footer` 复合组件。
### 7. 列宽调整
**v2:** 非内置能力。\
**v3:** `Table.ResizableContainer` + `Table.ColumnResizer` 复合组件。
## 迁移示例
### 基础表格
```tsx
import { Table, TableHeader, TableColumn, TableBody, TableRow, TableCell } from "@heroui/react";
Name
Role
Status
Kate Moore
CEO
Active
John Doe
Developer
Active
```
```tsx
import { Table } from "@heroui/react";
Name
Role
Status
Kate Moore
CEO
Active
John Doe
Developer
Active
```
### 动态行
```tsx
const columns = [
{ key: "name", label: "Name" },
{ key: "role", label: "Role" },
];
const rows = [
{ key: "1", name: "Kate", role: "CEO" },
{ key: "2", name: "John", role: "Developer" },
];
{(column) => {column.label} }
{(item) => (
{(columnKey) => {item[columnKey]} }
)}
```
```tsx
const columns = [
{ id: "name", label: "Name" },
{ id: "role", label: "Role" },
];
const rows = [
{ id: "1", name: "Kate", role: "CEO" },
{ id: "2", name: "John", role: "Developer" },
];
{(column) => {column.label} }
{(item) => (
{item.name}
{item.role}
)}
```
### 选择
```tsx
const [selectedKeys, setSelectedKeys] = useState(new Set(["1"]));
Name
Role
Kate
CEO
John
Developer
```
```tsx
import { Table, Checkbox } from "@heroui/react";
const [selectedKeys, setSelectedKeys] = useState(new Set(["1"]));
Name
Role
Kate
CEO
John
Developer
```
### 排序
```tsx
const [sortDescriptor, setSortDescriptor] = useState({
column: "name",
direction: "ascending",
});
Name
Role
{(item) => (
{(columnKey) => {item[columnKey]} }
)}
```
```tsx
const [sortDescriptor, setSortDescriptor] = useState({
column: "name",
direction: "ascending",
});
Name
Role
{(item) => (
{item.name}
{item.role}
)}
```
### 分页(底部内容)
```tsx
}
bottomContentPlacement="outside"
>
{/* ... */}
```
```tsx
{/* Header and Body */}
{/* Pagination component */}
```
### 空状态
```tsx
{[]}
```
```tsx
(
No rows to display.
)}
>
{[]}
```
## 样式相关变化
### v2:`classNames` prop
```tsx
```
### v3:直接使用 `className`
```tsx
```
## 组件结构(Anatomy)
v3 的 Table 结构如下:
```
Table (Root container)
├── Table.ScrollContainer (horizontal scroll)
│ └── Table.Content ( element, aria-label, selectionMode, etc.)
│ ├── Table.Header ()
│ │ └── Table.Column (, allowsSorting, etc.)
│ │ └── Table.ColumnResizer (optional)
│ └── Table.Body ( , items, renderEmptyState)
│ ├── Table.Row ()
│ │ └── Table.Cell ()
│ └── Table.LoadMore (optional, infinite scroll)
│ └── Table.LoadMoreContent
└── Table.Footer (optional, pagination, etc.)
```
## 数据项标识
**v2:** React 的 `key` 同时用于列表调和与选择状态。\
**v3:** 在 `Table.Row` 与 `Table.Column` 上使用 `id` 承载选择/排序状态;列表渲染仍保留 React 的 `key`。
## 总结
1. **导入方式**:由多个命名导入 → 单一 `Table` 导入配合点语法。
2. **新增包裹层**:`Table.ScrollContainer` 与 `Table.Content` 包裹表格结构。
3. **prop 迁移**:`aria-label`、`selectionMode`、`sortDescriptor` 等从 `Table` 移到 `Table.Content`。
4. **底部区域**:`bottomContent` → `Table.Footer`。
5. **顶部区域**:`topContent` → 放在 `Table` 内、`Table.ScrollContainer` 之前。
6. **选择用 Checkbox**:由自动渲染 → 显式使用 `slot="selection"` 的 `Checkbox`。
7. **空状态**:`emptyContent` → `Table.Body` 的 `renderEmptyState`。
8. **加载**:`loadingState` / `loadingContent` → 无限滚动场景用 `Table.LoadMore`。
9. **列宽调整**:新增 `Table.ResizableContainer` 与 `Table.ColumnResizer`。
10. **标识字段**:行/列由 `key` 表达业务标识 → 使用 `id`。
11. **样式类 prop 移除**:`color`、`radius`、`shadow`、`isStriped`、`isCompact` 等 → 请用 Tailwind CSS。
12. **`classNames` 移除**:在各复合子组件上使用 `className`。
# Tabs
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/tabs
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/tabs.mdx
> Tabs 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Tabs 文档](/docs/react/components/tabs)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,Tabs 使用 `Tab` 子组件,通过 `title` prop 设置标题,子节点作为面板内容:
```tsx
import { Tabs, Tab } from "@heroui/react";
export default function App() {
return (
Content here
);
}
```
在 v3 中,Tabs 改为复合组件,Tab 与 Panel 分别独立:
```tsx
import { Tabs } from "@heroui/react";
export default function App() {
return (
Photos
Content here
);
}
```
## 主要变化
### 1. 组件结构
**v2:** `Tabs` 与 `Tab` 子节点(`title` prop + 子节点作为面板)\
**v3:** 复合组件(`Tabs.ListContainer`、`Tabs.List`、`Tabs.Tab`、`Tabs.Indicator`、`Tabs.Separator`、`Tabs.Panel`)
### 2. Prop 变更
| v2 prop | v3 位置 | 说明 |
| ------------------------ | --------------------- | --------------------------------- |
| `key`(在 Tab 上) | `id`(在 Tab 与 Panel 上) | prop 名称变更 |
| `title`(在 Tab 上) | — | 内容直接放入 `Tabs.Tab` |
| `isVertical` | `orientation` | 改为 `"horizontal"` \| `"vertical"` |
| `placement` | — | 通过 `orientation` 与布局表达 |
| `variant` | `variant` | 简化为仅 `primary` \| `secondary` |
| `color` | — | 已移除(请改用 Tailwind CSS) |
| `size` | — | 已移除(请改用 Tailwind CSS) |
| `radius` | — | 已移除(请改用 Tailwind CSS) |
| `classNames` | — | 改在各子组件上使用 `className` prop |
| `disableCursorAnimation` | — | 改用 `Tabs.Indicator` 子组件控制 |
| `disableAnimation` | — | 已移除(动画机制已不同) |
| `fullWidth` | — | 已移除(请改用 Tailwind CSS) |
### 3. 组件层面的变化
* **Tab 标识**:`key` → `id`(`Tabs.Tab` 与 `Tabs.Panel` 之间必须一致)
* **Tab 内容**:`title` prop → 直接将子节点放入 `Tabs.Tab`
* **Panel 内容**:原本作为 Tab 子节点 → 改为独立的 `Tabs.Panel` 组件
* **指示器**:自动光标 → 显式的 `Tabs.Indicator` 子组件
* **分隔符**:新增 `Tabs.Separator` 组件,用于在 Tab 之间显示分隔线
## 迁移示例
### 受控 Tabs
```tsx
import { useState } from "react";
const [selected, setSelected] = useState("photos");
Content
Content
```
```tsx
import { useState } from "react";
const [selected, setSelected] = useState("photos");
Photos
Music
Content
Content
```
### 带图标
```tsx
Photos>}>
Content
```
```tsx
Photos
Content
```
### 带分隔符
在 v3 中,你可以在每个 `Tabs.Tab` 内部(除第一个之外)放入 `Tabs.Separator` 来显示 Tab 之间的分隔线。这是 v2 没有的新增能力。
```tsx
Photos
Music
Videos
Photos content
Music content
Videos content
```
## 组件结构
v3 Tabs 遵循以下结构:
```
Tabs (Root)
├── Tabs.ListContainer
│ └── Tabs.List
│ └── Tabs.Tab
│ ├── Tabs.Separator (optional, omit on first tab)
│ └── Tabs.Indicator (optional)
└── Tabs.Panel (one per tab, matching id)
```
## 总结
1. **组件结构**:必须使用复合组件(`Tabs.ListContainer`、`Tabs.List`、`Tabs.Tab`、`Tabs.Indicator`、`Tabs.Separator`、`Tabs.Panel`)
2. **Tab 标识**:`key` → `id`(Tab 与 Panel 之间需保持一致)
3. **Tab 内容**:移除 `title` prop——内容直接放入 `Tabs.Tab`
4. **Panel 分离**:面板内容移至独立的 `Tabs.Panel` 组件
5. **指示器**:必须显式在每个 Tab 内使用 `Tabs.Indicator`
6. **方向**:`isVertical` → `orientation` prop
7. **样式**:`variant` 简化为 `primary` | `secondary`;`color`、`size`、`radius`、`placement` 已移除——更多自定义请使用 Tailwind
8. **移除 classNames**:改在各子组件上使用 `className` prop
9. **分隔符(新增)**:在 `Tabs.Tab` 内使用 `Tabs.Separator` 显示 Tab 之间的分隔线(v2 无对应能力)
# TimeInput
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/timeinput
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/timeinput.mdx
> TimeInput → TimeField 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 TimeField 文档](/docs/react/components/time-field)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,TimeInput 是单个组件,通过 prop 配置:
```tsx
import { TimeInput } from "@heroui/react";
export default function App() {
return ;
}
```
在 v3 中,TimeField 改为复合组件,需要配合 DateInputGroup 与分段(segment)的 render prop:
```tsx
import { TimeField, DateInputGroup, Label } from "@heroui/react";
export default function App() {
return (
Time
{(segment) => }
);
}
```
## 主要变化
### 1. 组件命名
**v2:** `TimeInput`\
**v3:** `TimeField`
### 2. 组件结构
**v2:** 单个组件,通过 prop 配置\
**v3:** 复合组件:`TimeField`(根)+ `DateInputGroup` 与 `DateInputGroup.Input`(render prop)+ `DateInputGroup.Segment`;可选地搭配 `DateInputGroup.Prefix` 与 `DateInputGroup.Suffix`
### 3. Prop 变更
| v2 prop | v3 位置 | 说明 |
| --------------------------------------------------------- | ------------------------------ | --------------------------------------------------- |
| `label` | — | 改用 `Label` 组件 |
| `description` | — | 改用 `Description` 组件 |
| `errorMessage` | — | 改用 `FieldError` 组件 |
| `value`、`defaultValue`、`onChange` | `TimeField` | 与 React Aria 一致 |
| `minValue`、`maxValue`、`granularity`、`placeholderValue` | `TimeField` | 相同 |
| `isRequired`、`isDisabled`、`isReadOnly`、`isInvalid`、`name` | `TimeField` | 相同 |
| `validationBehavior`、`shouldForceLeadingZeros` | `TimeField` | 相同 |
| `variant` | `DateInputGroup` | 简化为仅 `primary` \| `secondary` |
| `fullWidth` | `TimeField` 或 `DateInputGroup` | 设置在根或分组上 |
| `color` | — | 已移除(请改用 Tailwind CSS) |
| `size` | — | 已移除(请改用 Tailwind CSS) |
| `radius` | — | 已移除(请改用 Tailwind CSS) |
| `labelPlacement` | — | 通过布局自行处理 |
| `startContent` | `DateInputGroup.Prefix` | 改用 Prefix 子组件 |
| `endContent` | `DateInputGroup.Suffix` | 改用 Suffix 子组件 |
| `classNames` | — | 在 `TimeField` 与 `DateInputGroup` 各部分上使用 `className` |
| `groupProps` | — | 在 `DateInputGroup` 上使用 `className` 或标准 DOM 属性 |
| `labelProps` | — | 在 `Label` 上使用 `className` |
| `fieldProps` | — | 在 `DateInputGroup` 上使用 `className` |
| `innerWrapperProps` | — | 在分组 / 输入相关部分上使用 `className` |
| `descriptionProps` | — | 在 `Description` 上使用 `className` |
| `errorMessageProps` | — | 在 `FieldError` 上使用 `className` |
| `inputRef` | — | 由 `TimeField` / React Aria 处理 ref |
## 迁移示例
### 带描述与错误信息
```tsx
```
```tsx
import { Description, FieldError, Label } from "@heroui/react";
Start time
{(segment) => }
Select start time
Time
{(segment) => }
Please enter a valid time
```
### 受控
```tsx
import { parseTime } from "@internationalized/date";
import { useState } from "react";
const [value, setValue] = useState(null);
```
```tsx
import type { TimeValue } from "@internationalized/date";
import { useState } from "react";
const [value, setValue] = useState(null);
Time
{(segment) => }
```
### 最小 / 最大值与粒度
```tsx
import { parseTime } from "@internationalized/date";
```
```tsx
import { parseTime } from "@internationalized/date";
Time
{(segment) => }
```
### Start / End 内容
```tsx
}
label="Time"
name="time"
startContent={ }
/>
```
```tsx
Time
{(segment) => }
```
## 组件结构
v3 TimeField 遵循以下结构:
```
TimeField (Root)
├── Label (optional)
├── DateInputGroup
│ ├── DateInputGroup.Prefix (optional)
│ ├── DateInputGroup.Input → (segment) => DateInputGroup.Segment
│ └── DateInputGroup.Suffix (optional)
├── Description (optional)
└── FieldError (optional)
```
## 总结
1. **组件重命名**:`TimeInput` → `TimeField`
2. **组件结构**:必须使用复合组件:`TimeField`(根)配合 `DateInputGroup` 与 `DateInputGroup.Input`(render prop)+ `DateInputGroup.Segment`
3. **标签 / 描述 / 错误**:改用独立组件(`Label`、`Description`、`FieldError`)
4. **时间相关 prop 不变**:`value`、`defaultValue`、`onChange`、`minValue`、`maxValue`、`granularity`、`placeholderValue`、`isRequired`、`isDisabled`、`isInvalid`、`name`、`validationBehavior`、`shouldForceLeadingZeros` 继续保留在 `TimeField` 上
5. **DateInputGroup 的变体**:v3 仅支持 `DateInputGroup` 上的 `variant="primary"` 与 `variant="secondary"`;`color`、`size`、`radius` 已移除——请改用 Tailwind CSS
6. **Start / End 内容**:`startContent` / `endContent` → `DateInputGroup.Prefix` 与 `DateInputGroup.Suffix`
7. **移除 labelPlacement**:`labelPlacement` 已移除——请通过布局自行实现
8. **DOM / className prop**:`groupProps`、`labelProps`、`fieldProps`、`classNames` 已移除——请在相关部分使用 `className`(以及标准 DOM 属性)
# Toast
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/toast
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/toast.mdx
> Toast 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Toast 文档](/docs/react/components/toast)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,Toast 采用 Provider + Hook 模式:
```tsx
import { ToastProvider, useToast } from "@heroui/react";
function App() {
return (
);
}
function MyComponent() {
const { toast } = useToast();
return (
toast.show("Hello!")}>
Show Toast
);
}
```
在 v3 中,Toast 改为 Provider 组件 + 全局 `toast()` 函数:
```tsx
import { Toast } from "@heroui/react";
function App() {
return (
<>
>
);
}
function MyComponent() {
return (
toast("Hello!")}>
Show Toast
);
}
```
## 主要变化
### 1. Provider 模式
**v2:** 必须使用 `ToastProvider` 包裹\
**v3:** 改用 `Toast.Provider` 组件(可放置在任意位置)
### 2. Hook → 函数
**v2:** 使用 `useToast()` Hook\
**v3:** 直接调用 `toast()` 函数
### 3. API 变更
**v2:** 使用 `toast.show()` 方法\
**v3:** `toast()` 是带有辅助方法的函数(`toast.success()`、`toast.danger()` 等)
### 4. 变体名称
**v2:** 变体如 `success`、`error`、`warning`、`info`\
**v3:** 变体:`default`、`accent`、`success`、`warning`、`danger`
### 5. 复合组件结构
**v3:** Toast 通过复合组件支持自定义渲染:
* `Toast` —— Toast 主容器
* `Toast.Content` —— 内容包装器
* `Toast.Title` —— 标题文字
* `Toast.Description` —— 描述文字
* `Toast.Indicator` —— 图标 / 指示器
* `Toast.CloseButton` —— 关闭按钮
* `Toast.ActionButton` —— 操作按钮
### 6. Promise 支持
**v3:** 内置 `toast.promise()` 方法,可处理异步操作
## 迁移示例
### 带标题与描述的 Toast
```tsx
const { toast } = useToast();
toast.show({
title: "Success",
description: "Your changes have been saved",
variant: "success"
});
```
```tsx
import { toast } from "@heroui/react";
toast.success("Success", {
description: "Your changes have been saved"
});
```
### 各变体的辅助方法
```tsx
const { toast } = useToast();
toast.show({ variant: "success", title: "Success" });
toast.show({ variant: "error", title: "Error" });
toast.show({ variant: "warning", title: "Warning" });
toast.show({ variant: "info", title: "Info" });
```
```tsx
import { toast } from "@heroui/react";
toast.success("Success");
toast.danger("Error");
toast.warning("Warning");
toast.info("Info");
```
### Promise 支持
```tsx
const { toast } = useToast();
const handleAsync = async () => {
try {
await someAsyncOperation();
toast.show({ title: "Success", variant: "success" });
} catch {
toast.show({ title: "Error", variant: "error" });
}
};
```
```tsx
import { toast } from "@heroui/react";
const handleAsync = async () => {
toast.promise(someAsyncOperation(), {
loading: "Processing...",
success: "Operation completed!",
error: "Operation failed"
});
};
```
### 自定义 Toast 渲染
```tsx
const { toast } = useToast();
toast.show({
title: "Custom",
render: (toast) => (
Custom content
)
});
```
```tsx
import { Toast, ToastContent, ToastTitle } from "@heroui/react";
{({ toast: toastItem }) => (
Custom content
)}
```
## 总结
* 用 `Toast.Provider` 取代 `ToastProvider`
* 用 `toast()` 函数取代 `useToast()` Hook
* 更新变体名称(`error` → `danger`、`info` → `accent`)
* 使用辅助方法:`toast.success()`、`toast.danger()` 等
* 用 `toast.promise()` 处理异步操作
* 通过复合组件结构实现自定义渲染
* 更好的 TypeScript 支持与队列管理
# Tooltip
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/tooltip
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/tooltip.mdx
> Tooltip 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 [v3 Tooltip 文档](/docs/react/components/tooltip)。本指南只关注从 HeroUI v2 的迁移。
## 结构变化
在 v2 中,Tooltip 使用 `content` prop:
```tsx
import { Tooltip, Button } from "@heroui/react";
export default function App() {
return (
Hover me
);
}
```
在 v3 中,Tooltip 需要复合组件:
```tsx
import { Tooltip, Button } from "@heroui/react";
export default function App() {
return (
Hover me
I am a tooltip
);
}
```
## 主要变化
### 1. 组件结构
**v2:** 通过 `content` prop 与作为触发器的 children 使用 Tooltip\
**v3:** 复合组件(`Tooltip.Trigger`、`Tooltip.Content`、`Tooltip.Arrow`)
### 2. Prop 变更
| v2 prop | v3 位置 | 说明 |
| --------------------------------- | ------------------------ | ---------------------------------------- |
| `content` | — | 请使用 `Tooltip.Content` 的 children |
| `showArrow` | `showArrow`(在 Content 上) | 移至 `Tooltip.Content` |
| `placement` | `placement`(在 Content 上) | 移至 `Tooltip.Content` |
| `offset` | `offset`(在 Content 上) | 移至 `Tooltip.Content` |
| `color` | — | 已移除(请使用 Tailwind CSS) |
| `size` | — | 已移除(请使用 Tailwind CSS) |
| `radius` | — | 已移除(请使用 Tailwind CSS) |
| `shadow` | — | 已移除(请使用 Tailwind CSS) |
| `classNames` | — | 请在各子组件上使用 `className` |
| `motionProps` | — | 已移除(动画机制已不同) |
| `trigger` | `trigger`(在根上) | 仍存在:`"hover"` \| `"focus"`(默认 `"hover"`) |
| `isDisabled` | `isDisabled`(在根上) | v3 新增:可完全禁用 Tooltip |
| `delay` | `delay`(在根上) | 仍存在(默认由 `0` 改为 `700`) |
| `closeDelay` | `closeDelay`(在根上) | 仍存在(默认 `0`) |
| `portalContainer` | — | 不再对外暴露 |
| `updatePositionDeps` | — | 不再对外暴露 |
| `containerPadding`, `crossOffset` | — | 不再对外暴露 |
| `shouldFlip` | — | 自动处理 |
| `triggerScaleOnOpen` | — | 不可用 |
| `isKeyboardDismissDisabled` | — | 不可用 |
| `isDismissable` | — | 不可用 |
| `shouldCloseOnBlur` | — | 不可用 |
| `shouldCloseOnInteractOutside` | — | 不可用 |
| `onClose` | — | 请改用 `onOpenChange` |
### 3. 移至 `Tooltip.Content` 的 prop
* `showArrow` — 现位于 `Tooltip.Content`
* `placement` — 现位于 `Tooltip.Content`
* `offset` — 现位于 `Tooltip.Content`
## 迁移示例
### 内容配置
```tsx
{/* With arrow */}
Hover me
{/* With placement */}
Hover me
{/* With offset */}
Hover me
```
```tsx
{/* With arrow */}
Hover me
I am a tooltip
{/* With placement */}
Hover me
Tooltip
{/* With offset */}
Hover me
Tooltip
```
### 受控 Tooltip
```tsx
import { useState } from "react";
const [isOpen, setIsOpen] = useState(false);
Hover me
```
```tsx
import { useState } from "react";
const [isOpen, setIsOpen] = useState(false);
Hover me
I am a tooltip
```
### 带延迟
```tsx
Hover me
```
```tsx
Hover me
Tooltip
```
### 自定义内容
```tsx
Title
Description
}
>
Hover me
```
```tsx
Hover me
```
### 自定义触发器
```tsx
Custom trigger
```
```tsx
Custom trigger
Tooltip
```
## 组件组成
v3 Tooltip 的结构如下:
```
Tooltip (Root)
├── Tooltip.Trigger
│ └── [Trigger element]
└── Tooltip.Content
├── Tooltip.Arrow (optional)
└── [Tooltip content]
```
## v3 新增 prop
### isDisabled
`isDisabled` prop 可完全禁用 Tooltip。禁用后,悬停或聚焦时都不会显示提示:
```tsx
No tooltip
This will not show
```
### trigger
`trigger` prop 控制 Tooltip 的激活方式,取值为 `"hover"`(默认)或 `"focus"`:
```tsx
{/* Show tooltip only on focus */}
Focus me
Shown on focus only
```
### 自定义渲染函数
`Tooltip.Content` 与 `Tooltip.Arrow` 均支持 `render` prop,可在高级场景下用自定义渲染函数覆盖默认 DOM 元素。
## 说明
### `content` prop
* **v2:** 使用 `content` prop 传入提示文本 / 内容
* **v3:** 内容作为 `Tooltip.Content` 的 children
### 箭头
* **v2:** 由根上的 `showArrow` 控制
* **v3:** 在 `Tooltip.Content` 上使用 `showArrow`,并包含 `Tooltip.Arrow` 组件
### placement 与 offset
* **v2:** `placement` 与 `offset` 在根上
* **v3:** `placement` 与 `offset` 移至 `Tooltip.Content`
### 触发元素
* **v2:** children 自动作为触发器
* **v3:** 必须将触发元素包在 `Tooltip.Trigger` 内
### 默认延迟
* **v2:** `delay` 默认为 `0`
* **v3:** `delay` 默认为 `700`(注意:示例中使用 `delay={0}` 以贴近 v2 行为)
## 总结
1. **组件结构**:必须使用复合组件(`Tooltip.Trigger`、`Tooltip.Content`、`Tooltip.Arrow`)
2. **`content` prop 已移除**:请使用 `Tooltip.Content` 的 children
3. **prop 迁移**:`showArrow`、`placement`、`offset` 移至 `Tooltip.Content`
4. **样式相关 prop 已移除**:`color`、`size`、`radius`、`shadow` — 请使用 Tailwind CSS
5. **`classNames` 已移除**:请在各子组件上使用 `className`
6. **`motionProps` 已移除**:动画机制已不同
7. **高级 prop 已移除**:大量定位与行为相关 prop 已移除
8. **默认延迟变更**:默认由 `0` 改为 `700`
9. **`isDisabled` prop**:v3 新增,可完全禁用 Tooltip
10. **`trigger` prop**:取 `"hover"`(默认)或 `"focus"`,用于控制激活方式
11. **渲染 prop**:`Tooltip.Content` 与 `Tooltip.Arrow` 支持 `render` prop,用于自定义 DOM 渲染
# User
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/user
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(components)/user.mdx
> User 从 HeroUI v2 到 v3 的迁移指南。
HeroUI v3 中的 User 组件已被**移除**。请使用 Avatar、文本元素与 Tailwind CSS 类手动组合用户展示。
## 主要变化
### 1. 组件已移除
**v2:** 来自 `@heroui/react` 的 `` 组件\
**v3:** 使用 `Avatar` + 文本元素手动组合
### 2. 功能映射
v2 User 组件包含以下需要替换的功能:
| v2 功能 | v3 等效项 | 说明 |
| ------------------ | -------------- | --------------------- |
| `name` prop | 文本元素 | 将名称渲染为文本或标题 |
| `description` prop | 文本元素 | 将描述渲染为文本 |
| `avatarProps` prop | `Avatar` 组件 | 直接使用 v3 Avatar 组件 |
| `isFocusable` prop | 手动焦点处理 | 按需添加 `tabIndex` 与焦点样式 |
| `classNames` prop | Tailwind CSS 类 | 直接将类应用到元素上 |
## 结构变化
### v2:User 组件
在 v2 中,`User` 是一个将 Avatar 与名称组合在一起的便捷组件:
```tsx
import { User } from "@heroui/react";
export default function App() {
return (
);
}
```
### v3:手动组合
在 v3 中,请使用 Avatar 和文本元素手动组合用户展示:
```tsx
import { Avatar } from "@heroui/react";
export default function App() {
return (
);
}
```
## 迁移示例
### 带描述
```tsx
import { User } from "@heroui/react";
```
```tsx
import { Avatar } from "@heroui/react";
JG
Junior Garcia
Software Engineer
```
### 使用默认头像(姓名首字母)
```tsx
import { User } from "@heroui/react";
name
.split(" ")
.map((n) => n[0])
.join(""),
}}
/>
```
```tsx
import { Avatar } from "@heroui/react";
function getInitials(name: string) {
return name
.split(" ")
.map((n) => n[0])
.join("");
}
{getInitials("Junior Garcia")}
Junior Garcia
```
### 带链接描述
```tsx
import { User, Link } from "@heroui/react";
@jrgarciadev
}
avatarProps={{
src: "https://example.com/avatar.jpg",
}}
/>
```
```tsx
import { Avatar, Link } from "@heroui/react";
JG
Junior Garcia
@jrgarciadev
```
### 可点击的用户
```tsx
import { User } from "@heroui/react";
{/* Focusable */}
{/* As button */}
```
```tsx
import { Avatar } from "@heroui/react";
{/* Focusable */}
JG
Junior Garcia
{/* As button */}
JG
Junior Garcia
```
## 创建可复用的 User 组件(推荐)
由于用户展示很常见,可以创建一个可复用组件:
```tsx
import { User } from "@heroui/react";
```
```tsx
import { Avatar, Link } from "@heroui/react";
import { ReactNode } from "react";
import { cn } from "@/lib/utils"; // 或你的 cn 工具函数
interface UserProps {
name: string | ReactNode;
description?: string | ReactNode;
avatarSrc?: string;
avatarAlt?: string;
avatarFallback?: string;
className?: string;
isFocusable?: boolean;
as?: "div" | "button" | "a";
href?: string;
onClick?: () => void;
}
function getInitials(name: string): string {
return name
.split(" ")
.map((n) => n[0])
.join("")
.toUpperCase()
.slice(0, 2);
}
export function User({
name,
description,
avatarSrc,
avatarAlt,
avatarFallback,
className,
isFocusable = false,
as = "div",
href,
onClick,
}: UserProps) {
const Component = as === "a" ? "a" : as === "button" ? "button" : "div";
const fallback = avatarFallback || (typeof name === "string" ? getInitials(name) : "?");
const content = (
<>
{avatarSrc && (
)}
{fallback}
{name}
{description && (
{description}
)}
>
);
const baseClasses = cn(
"inline-flex items-center gap-2 rounded-sm outline-none",
isFocusable && "focus-visible:ring-2 focus-visible:ring-focus",
className
);
if (Component === "button") {
return (
{content}
);
}
if (Component === "a") {
return (
{content}
);
}
return (
{content}
);
}
// Usage
```
## 样式参考
v2 User 组件使用了以下基础样式,迁移时可以按需复用:
* **基础容器**:`inline-flex items-center gap-2 rounded-sm`
* **包装层(用于名称 / 描述)**:`inline-flex flex-col items-start`
* **名称**:`text-sm`(text-small)
* **描述**:`text-xs text-muted`(text-tiny text-foreground-400)
## 总结
1. **组件已移除**:v3 中不再提供 `User` 组件
2. **导入变更**:移除 `import { User } from "@heroui/react"`
3. **手动组合**:使用 Avatar + 文本元素组合用户展示
4. **Avatar 变更**:使用 v3 Avatar 的复合组件模式
5. **样式**:直接应用 Tailwind CSS 类
6. **焦点处理**:如有需要,手动实现焦点样式
## 迁移步骤
1. **移除导入**:从 `@heroui/react` 导入中移除 `User`
2. **替换组件**:将所有 `` 实例替换为手动组合
3. **使用 Avatar**:使用 v3 Avatar 复合组件模式
4. **添加文本元素**:将名称和描述作为文本元素添加
5. **应用样式**:使用 Tailwind CSS 类处理布局与样式
6. **处理焦点**:如果使用过 `isFocusable`,请添加焦点样式
7. **可选**:为你的应用创建可复用的 User 组件
## 常见模式
### 用户列表
```tsx
{users.map((user) => (
{getInitials(user.name)}
{user.name}
{user.role && (
{user.role}
)}
))}
```
### 可点击的用户
```tsx
handleUserClick(user)}
>
{getInitials(user.name)}
{user.name}
{user.email}
```
# 代理迁移指南 - 完整迁移
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/agent-guide-full
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(workflows)/agent-guide-full.mdx
> AI 助手的完整迁移指南,帮助将 HeroUI v2 迁移到 v3
## 概述
本指南专为人工智能助手(代理)设计,帮助用户从 HeroUI v2 迁移到 v3。它实现了**完整迁移方法**,首先分析项目并以可管理的批次迁移组件,而不是一次性迁移全部组件。
**关键约束**:HeroUI v2 和 v3 **不应在同一项目中共存**。该项目将在迁移过程中被破坏。始终在功能分支中工作并在切换依赖项之前迁移所有组件代码。
## 关键原则
1. **完整迁移**:不要试图在一次改动中迁移所有组件;应按可管理的批次推进。大型项目同样需要采用完整迁移流程(在切换依赖前完成全部组件适配)。
2. **首先进行项目分析**:在创建迁移计划之前,始终分析代码库以了解组件的使用情况。
3. **损坏的状态管理**: **严重**:项目将在迁移过程中损坏。为此制定计划:
* 在功能分支中迁移
* 在切换依赖项之前准备好所有代码更改
* 制定回滚计划
* 首先在隔离环境中进行测试
4. **全有或全无依赖关系切换**:依赖关系更新到 v3 后,必须迁移所有组件。在切换依赖关系之前规划组件迁移。
## v3 的主要变化
* **依赖项**:将 React 升级到 v19+、HeroUI 包升级到 v3、Tailwind CSS 升级到 v4,并移除 Framer Motion
* **无需 Provider**:v3 不再需要 `HeroUIProvider`
* **组件 API 更新**:许多组件改用 React Aria Components 模式
* **复合组件**:全新的复合组件模式带来了更好的定制能力。详情请参阅各组件的迁移指南。
* **已移除的 Hooks**:v2 中的组件 Hooks(如 `useSwitch`、`useInput`)已被移除——请改用复合组件。`useDisclosure` 已被替换为 `useOverlayState`。详情请参阅 [Hooks 迁移指南](/docs/react/migration/hooks)。
* **配置变更**:从 Tailwind 配置中移除 `heroui()` 插件,更新 CSS 导入,并删除 `hero.ts` 文件
* **条目标识**:集合类组件(Dropdown、ListBox、Select、Accordion 等)在 v3 中改用 `id` 和 `textValue`;列表本身仍需保留 React 的 `key`。
## 详细迁移步骤
有关详细的分步说明,请参阅[完整迁移指南](/docs/react/migration/full-migration)。该指南涵盖:
* 依赖项更新
* 主题配置更改
* 删除 HeroUIProvider
* 组件导入和迁移
* 钩子迁移
* 样式迁移
* 测试
**代理注意**:依赖项更新(React 19、Tailwind v4)可以在切换 HeroUI 之前完成(不会破坏项目)。但是,HeroUI 包切换只能在所有组件代码迁移之后进行。
## 钩子迁移
HeroUI v2 提供了组件挂钩(例如`useSwitch`, `useInput`, `useCheckbox`等)和实用程序挂钩,例如`useDisclosure`。 HeroUI v3 删除了大多数组件挂钩,转而使用复合组件,并替换了`useDisclosure`和`useOverlayState`.
**何时迁移钩子:**
* **组件迁移期间**:替换组件钩子(`useSwitch`, `useInput`等)当您迁移每个组件以使用复合组件时
* **组件迁移后**:迁移`useDisclosure` → `useOverlayState`用于样式迁移之前的覆盖状态管理
**迁移策略:**
1. **识别钩子用法**:搜索代码库以从以下位置导入`@heroui/react`包括钩子名称(`useSwitch`, `useInput`, `useCheckbox`, `useRadio`, `useDisclosure`, ETC。)
2. **替换组件钩子**:使用复合组件而不是带有 prop getter 的钩子(在组件迁移期间完成)
3. **替换 useDisclosure**:迁移到`useOverlayState`用于覆盖状态管理(使用`get_hooks_migration_guide`MCP 工具)
4. **参考指南**:使用`get_hooks_migration_guide`用于钩子迁移的 MCP 工具,`get_component_migration_guides`特定于组件的指南
## 组件导入更改
请参阅[完整迁移指南](/docs/react/migration/full-migration#step-5-update-component-imports)了解详细的组件导入更改。
## 组件迁移参考
可以通过下表快速查找每个组件的迁移指南。点击「迁移指南」列中的链接,即可跳转到对应的详细迁移说明。
**组件开发状态**:标有 🔄 进行中或 📋 计划中的组件仍在开发中。可以查看[路线图](https://herouiv3.featurebase.app/roadmap)了解任务状态。这些组件的迁移指南将在开发完成后提供。
| v2 组件 | v3 组件 | 状态 | 迁移指南 |
| ---------------- | -------------------------- | ------ | ------------------------------------------------------------------------ |
| Accordion | Accordion | ✅ 可用 | [查看指南 →](/docs/react/migration/accordion) |
| Alert | Alert | ✅ 可用 | [查看指南 →](/docs/react/migration/alert) |
| Autocomplete | ComboBox | ✅ 已重命名 | [查看指南 →](/docs/react/migration/autocomplete) |
| Avatar | Avatar | ✅ 可用 | [查看指南 →](/docs/react/migration/avatar) |
| Badge | Badge | ✅ 可用 | [查看指南 →](/docs/react/migration/badge) |
| Breadcrumbs | Breadcrumbs | ✅ 可用 | [查看指南 →](/docs/react/migration/breadcrumbs) |
| Button | Button | ✅ 可用 | [查看指南 →](/docs/react/migration/button) |
| ButtonGroup | ButtonGroup | ✅ 可用 | [查看指南 →](/docs/react/migration/button-group) |
| Calendar | Calendar | ✅ 可用 | [查看指南 →](/docs/react/migration/calendar) |
| Card | Card | ✅ 可用 | [查看指南 →](/docs/react/migration/card) |
| Checkbox | Checkbox | ✅ 可用 | [查看指南 →](/docs/react/migration/checkbox) |
| CheckboxGroup | CheckboxGroup | ✅ 可用 | [查看指南 →](/docs/react/migration/checkbox-group) |
| Chip | Chip | ✅ 可用 | [查看指南 →](/docs/react/migration/chip) |
| Code | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/code) |
| DateInput | DateField | ✅ 已重命名 | [查看指南 →](/docs/react/migration/dateinput) |
| DatePicker | DatePicker | ✅ 可用 | [查看指南 →](/docs/react/migration/date-picker) |
| DateRangePicker | DateRangePicker | ✅ 可用 | [查看指南 →](/docs/react/migration/date-range-picker) |
| TimeInput | TimeField | ✅ 已重命名 | [查看指南 →](/docs/react/migration/timeinput) |
| Divider | Separator | ✅ 已重命名 | [查看指南 →](/docs/react/migration/divider) |
| Drawer | Drawer | ✅ 可用 | [查看指南 →](/docs/react/migration/drawer) |
| Dropdown | Dropdown | ✅ 可用 | [查看指南 →](/docs/react/migration/dropdown) |
| Form | Form | ✅ 可用 | [查看指南 →](/docs/react/migration/form) |
| Image | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/image) |
| Input | TextField、Input、InputGroup | ✅ 可用 | [查看指南 →](/docs/react/migration/input) |
| InputOTP | InputOTP | ✅ 可用 | [查看指南 →](/docs/react/migration/input-otp) |
| Kbd | Kbd | ✅ 可用 | [查看指南 →](/docs/react/migration/kbd) |
| Link | Link | ✅ 可用 | [查看指南 →](/docs/react/migration/link) |
| Listbox | ListBox | ✅ 可用 | [查看指南 →](/docs/react/migration/listbox) |
| Modal | Modal | ✅ 可用 | [查看指南 →](/docs/react/migration/modal) |
| Navbar | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/navbar) |
| NumberInput | NumberField | ✅ 已重命名 | [查看指南 →](/docs/react/migration/numberinput) |
| Pagination | Pagination | ✅ 可用 | [查看指南 →](/docs/react/migration/pagination) |
| Popover | Popover | ✅ 可用 | [查看指南 →](/docs/react/migration/popover) |
| Progress | ProgressBar | ✅ 已重命名 | [查看指南 →](/docs/react/migration/progress) |
| CircularProgress | ProgressCircle | ✅ 已重命名 | [查看指南 →](/docs/react/migration/circular-progress) |
| Radio | Radio | ✅ 可用 | [查看指南 →](/docs/react/migration/radio) |
| RadioGroup | RadioGroup | ✅ 可用 | [查看指南 →](/docs/react/migration/radio-group) |
| RangeCalendar | RangeCalendar | ✅ 可用 | [查看指南 →](/docs/react/migration/range-calendar) |
| Ripple | ❌ | ❌ 已移除 | [参见 Button 的水波纹效果 →](/docs/react/components/button#adding-ripple-effect) |
| ScrollShadow | ScrollShadow | ✅ 可用 | [查看指南 →](/docs/react/migration/scroll-shadow) |
| Select | Select | ✅ 可用 | [查看指南 →](/docs/react/migration/select) |
| Skeleton | Skeleton | ✅ 可用 | [查看指南 →](/docs/react/migration/skeleton) |
| Slider | Slider | ✅ 可用 | [查看指南 →](/docs/react/migration/slider) |
| Snippet | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/snippet) |
| Spacer | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/spacer) |
| Spinner | Spinner | ✅ 可用 | [查看指南 →](/docs/react/migration/spinner) |
| Switch | Switch | ✅ 可用 | [查看指南 →](/docs/react/migration/switch) |
| Table | Table | ✅ 可用 | [查看指南 →](/docs/react/migration/table) |
| Tabs | Tabs | ✅ 可用 | [查看指南 →](/docs/react/migration/tabs) |
| Toast | Toast | ✅ 可用 | [查看指南 →](/docs/react/migration/toast) |
| Tooltip | Tooltip | ✅ 可用 | [查看指南 →](/docs/react/migration/tooltip) |
| User | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/user) |
**已删除/进行中/计划的组件**:对于标记为 ❌ 已删除、🔄 进行中或 📋 计划的组件,请在迁移过程中将其替换为标准 HTML 元素。一旦 HeroUI 组件在 v3 中可用,您就可以迁移回它们。
使用`get_component_migration_guides`MCP 工具可获取每个组件的详细指南。
## v3 中的新组件
v3 引入了一系列 v2 中尚未提供的全新组件:
| 组件 | 用途 | 文档 |
| --------------- | -------------------------------------------------------------------- | ------------------------------------------------- |
| TextField | 增强型文本输入框,支持 label 与 description | [查看文档 →](/docs/react/components/text-field) |
| TextArea | 多行文本输入组件 | [查看文档 →](/docs/react/components/text-area) |
| AlertDialog | 用于确认与提醒的模态对话框 | [查看文档 →](/docs/react/components/alert-dialog) |
| Label | 无障碍的表单标签组件 | [查看文档 →](/docs/react/components/label) |
| Description | 表单字段的辅助说明文本 | [查看文档 →](/docs/react/components/description) |
| FieldError | 表单字段的错误信息显示 | [查看文档 →](/docs/react/components/field-error) |
| Fieldset | 对相关表单字段进行分组 | [查看文档 →](/docs/react/components/fieldset) |
| InputGroup | 将多个输入框组合在一起 | [查看文档 →](/docs/react/components/input-group) |
| Surface | 带有层级样式的容器组件 | [查看文档 →](/docs/react/components/surface) |
| Disclosure | 可展开 / 可折叠的内容区域 | [查看文档 →](/docs/react/components/disclosure) |
| DisclosureGroup | 用于管理多个 Disclosure 区域的复合组件 | [查看文档 →](/docs/react/components/disclosure-group) |
| SearchField | 带清除按钮与可选加载状态的搜索输入框 | [查看文档 →](/docs/react/components/search-field) |
| DateField | 配合日历选择器的日期输入框 | [查看文档 →](/docs/react/components/date-field) |
| TimeField | 时间输入组件 | [查看文档 →](/docs/react/components/time-field) |
| Tag、TagGroup | 用于选择或展示的 Tag 与 TagGroup | [查看文档 →](/docs/react/components/tag-group) |
| ColorPicker | 颜色选择(ColorArea、ColorField、ColorSlider、ColorSwatch、ColorSwatchPicker) | [查看文档 →](/docs/react/components/color-picker) |
| CloseButton | 用于关闭或解除浮层的触发按钮 | [查看文档 →](/docs/react/components/close-button) |
| ErrorMessage | 表单字段错误信息展示(基于 React Aria 集成) | [查看文档 →](/docs/react/components/error-message) |
## 自定义主题覆盖
请参阅[完整迁移指南](/docs/react/migration/full-migration#step-7-update-custom-theme-overrides)用于自定义主题覆盖迁移。
## 样式迁移
请参阅[完整迁移指南](/docs/react/migration/full-migration#step-9-styling-migration)有关详细的样式迁移说明。
**代理注意**:使用`get_styling_migration_guide`用于全面样式迁移详细信息的 MCP 工具。
**重要**:样式迁移发生在组件迁移和依赖关系切换之后。
## 完整的迁移工作流程
**重要**:由于 v2 和 v3 无法共存,因此迁移分两个主要阶段进行:
1. **准备阶段**:迁移所有组件代码,同时仍依赖 v2 依赖项(代码将被破坏)
2. **切换阶段**:将依赖项更新到 v3 并修复任何剩余问题
**⚠️ 关键:不要构建来检查迁移过程中的错误**
* 使用**类型检查**(例如,`tsc --noEmit`) 如果可以检查 TypeScript 错误
* 使用**lint**(例如,`eslint`, `biome check`) 如果可以检查代码质量
* **不要**运行构建命令(例如,`npm run build`, `next build`, `vite build`)
* **请勿** 在迁移期间尝试启动/运行项目
### 第 0 阶段:设置和分析
1. **创建迁移分支**
* 创建一个功能分支用于迁移工作
* 例子:`git checkout -b migrate/heroui-v3`
2. **验证迁移 MCP 是否已配置**
* 检查迁移 MCP 服务器是否已连接
* 确保`heroui-react`MCP 未连接(以避免混淆)
* 验证 MCP 工具可用
3. **分析项目并创建迁移计划**
* 使用`get_migration_workflow`获取本指南
* 扫描 HeroUI v2 导入,识别所有组件和使用情况
* 映射组件依赖关系
* 创建分阶段迁移计划(例如,每个阶段 3-5 个组件,按依赖项策略)
### 第 1 阶段:依赖项准备(代码更改之前)
这些步骤可以在切换 HeroUI 依赖项之前完成,并且不会破坏项目:
1. **将 React 更新到 v19**(如果还没有)
* 这可以在切换 HeroUI 之前完成
2. **将 Tailwind CSS 更新至 v4**(如果尚未更新)
* 这可以在切换 HeroUI 之前完成
3. **🛑 检查点:停止并等待用户批准**
* **不要自动进入下一阶段**
* 解释配置更改
* 在继续之前等待明确的用户批准
### 第 2-N 阶段:代码迁移(v2 依赖项仍然有效)
**关键**:在此阶段,代码将引用 v3 API,但仍安装 v2 依赖项。该项目将被破坏。这是预期的且正常的。
对于迁移计划中的每个组件组:
1. **获取特定于组件的指南**
* 使用`get_component_migration_guides`适用于每个组件的 MCP 工具
* 审查 API 更改、prop 迁移、结构更改
2. **应用代码迁移**
* 将组件代码迁移到 v3 API 模式
* 更新导入、道具、组件结构
* **注意**:在切换依赖关系之前,代码将被破坏
3. **处理依赖关系**
* 如果组件有依赖,先迁移依赖
* 检查依赖项是否已经迁移
* 根据需要迁移共享代码
4. **🛑 检查点:停止并等待用户批准**
* **不要自动进入下一阶段**
* 总结一下本阶段迁移的内容
* 在继续之前等待明确的用户批准
5. **记录迁移状态**
* 跟踪哪些组件已迁移
* 注意任何问题或疑虑
### 最终阶段:依赖关系切换和修复
**关键**:仅当所有组件都已迁移到 v3 API 模式时才继续。
1. **更新依赖关系**
* 消除`@heroui/react`和`@heroui/theme`(v2)
* 安装`@heroui/react`和`@heroui/styles`(v3)
* 消除`framer-motion`如果存在
* 更新 CSS 导入(添加`@import "@heroui/styles";`)
* 从应用程序根目录中删除 HeroUIProvider
* 更新 Tailwind 配置(删除`heroui()`插件)
2. **修复剩余问题**
* 运行 typecheck/lint(如果可用)(不构建)
* 修复类型检查报告的任何 TypeScript 错误
* 修复任何 linting 错误
* 注意:请勿在迁移期间尝试构建或运行项目
3. **🛑 检查点:停止并等待用户批准**
* **不要自动进行样式设置**
* 验证组件是否正常工作
* 在样式迁移之前等待明确的用户批准
4. **继续样式迁移**
* 使用 `get_styling_migration_guide` MCP 工具获取样式迁移指南
* 系统地应用样式更新
5. **应用样式更新**
* 使用`get_styling_migration_guide`MCP工具
* 更新实用程序类、颜色标记、CSS 变量
* 测试视觉外观
6. **最终验证**
* 最后运行一次类型检查/lint(不构建)
* 验证所有样式是否正确更新
* 注意:完整测试(视觉、功能、可访问性)应在迁移完成后完成,而不是在迁移期间完成
## 迁移策略
### 策略 1:按依赖关系(推荐)
* 首先迁移基础组件(按钮、输入、卡等)
* 然后迁移依赖它们的组件
* 最适合具有复杂组件层次结构的项目
* 确保依赖项先于依赖项准备就绪
**订单示例**:
1. 按钮、输入、链接(基础)
2. 卡片、模态(使用按钮)
3. 表单、下拉菜单(使用输入、按钮)
4. 复杂组件(使用多个依赖项)
### 策略 2:按功能
* 将功能/模块中的所有组件迁移到一起
* 适合基于功能的代码组织
* 允许逐个功能测试
* 可能需要先迁移依赖项
**例子**:
* 功能:用户身份验证
* 迁移:输入、按钮、表单、模态(所有与身份验证相关)
* 功能:仪表板
* 迁移:卡、选项卡、选择(所有与仪表板相关)
### 策略 3:按频率
* 首先迁移最常用的组件
* 提供快速获胜和早期验证
* 适合具有清晰使用模式的大型代码库
* 仍然需要处理依赖关系
**例子**:
1. 按钮(使用150次)
2. 输入(使用120次)
3. 卡(已使用80次)
4. ...(按使用次数继续)
## 代理最佳实践
1. **关于损坏状态的警告**
* 始终通知用户项目在迁移过程中将被破坏
* 推荐使用功能分支
* 设定项目何时再次运作的预期
2. **切换依赖项之前迁移所有代码**
* 首先完成所有组件代码迁移
* 仅当迁移所有组件时才切换依赖项
* 这最大限度地减少了破坏状态的持续时间
3. **每个阶段使用 MCP 工具**
* 使用`get_migration_workflow`, `get_component_migration_guides`, `get_styling_migration_guide`根据需要
* **关键**:始终在检查点停止并等待阶段之间的用户批准
4. **在功能分支工作**
* 始终建议创建迁移分支
* 允许用户继续在主分支上工作
* 如果需要,启用轻松回滚
5. **记录迁移状态**
* 跟踪哪些组件已迁移
* 注意任何问题或疑虑
* 保持清单可见
* 每个阶段后更新状态
6. **优雅地处理错误**
* 如果组件迁移失败,请记录原因
* 继续其他组件
* 依赖切换后返回失败的组件
* 更新依赖项后,某些问题可能会得到解决
7. **依赖关系切换后验证**
* 仅在依赖项更新到 v3 后
* 运行类型检查/lint(不构建)
* 修复出现的类型/lint 错误
* 在类型检查/lint 通过之前不要继续进行样式设置
## 常见场景
### 大型项目(100 多个组件)
* 使用较小的批量(3-5 个组件)
* 按依赖性或频率确定优先级
* 允许多个会话
* 在阶段之间创建检查点
* 使用“按依赖”策略
* 清楚地记录进度
### 小型项目(\<20 个组件)
* 可以使用更大的批次(5-10 个组件)
* 可以通过更少的阶段完成
* 仍然增量验证(全迁移方式)
* 可以使用任何策略
* 在切换 deps 之前仍然需要迁移所有代码
### 混合 v2/v3 使用(完全迁移)
* **完全迁移中不可能**:v2 和 v3 不能在完全迁移方法中共存
* 在切换依赖关系之前必须迁移所有组件
* 使用功能分支来维护工作主分支
* 一次性完成迁移
## 下一步
完成迁移后:
1. 删除 v2 依赖项(已在依赖切换步骤中完成)
2. 从迁移 MCP 切换到用于 v3 开发的 `heroui-react` MCP
3. 更新文档中的引用
4. 运行最终验证
5. 将迁移分支合并回主分支
本代理迁移指南旨在与迁移 MCP 服务器配合使用。在开始迁移之前,请确保 MCP 服务器已正确配置并且工具可用。
# 代理迁移指南 - 共存增量迁移
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/agent-guide-incremental
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(workflows)/agent-guide-incremental.mdx
> AI 助手增量共存迁移指南,帮助将 HeroUI v2 迁移到 v3
## 概述
本指南专为人工智能助手(代理)设计,帮助用户使用**增量共存迁移**从 HeroUI v2 迁移到 v3。这种方法允许 v2 和 v3 组件并行工作,从而实现逐个组件迁移,同时保持项目正常运行。
**主要区别**:与完全迁移不同,增量共存迁移允许项目在迁移期间保持功能。 v2 和 v3 组件可以暂时共存。
## 关键原则
1. **增量组件迁移**:一次迁移一个组件,在继续之前测试每个组件
2. **项目保持功能**:与完全迁移不同,项目应始终保持工作状态
3. **策略识别**:确定项目使用哪种共存策略(A:pnpm 别名或 B:组件包)
4. **逐个组件测试**:在移动到下一个组件之前测试每个迁移的组件
5. **CSS 冲突管理**:监控并解决共存期间 v2 和 v3 之间的样式冲突
6. **已移除组件**:没有 v3 对位的 v2 组件(**Code**、**Image**、**Navbar**、**Ripple**、**Snippet**、**Spacer**、**User**)可在共存期间保留。除非用户明确要求替换,否则不要迁移它们。
## v3 的主要变化
* **依赖项**:将 React 升级到 v19+、HeroUI 包升级到 v3、Tailwind CSS 升级到 v4,并移除 Framer Motion
* **无需 Provider**:v3 不再需要 `HeroUIProvider`
* **组件 API 更新**:许多组件改用 React Aria Components 模式
* **复合组件**:全新的复合组件模式带来了更好的定制能力。详情请参阅各组件的迁移指南。
* **已移除的 Hooks**:v2 中的组件 Hooks(如 `useSwitch`、`useInput`)已被移除——请改用复合组件。`useDisclosure` 已被替换为 `useOverlayState`。详情请参阅 [Hooks 迁移指南](/docs/react/migration/hooks)。
* **配置变更**:从 Tailwind 配置中移除 `heroui()` 插件,更新 CSS 导入,并删除 `hero.ts` 文件
* **条目标识**:集合类组件(Dropdown、ListBox、Select、Accordion 等)在 v3 中改用 `id` 和 `textValue`;列表本身仍需保留 React 的 `key`。
## 增量迁移设置
有关详细设置和迁移说明,请参阅[增量迁移指南](/docs/react/migration/incremental-migration)。该指南涵盖:
* 策略选择(A:pnpm 别名或 B:组件包)
* 每个策略的详细设置
* 共存的 CSS 配置
* 逐个组件的迁移过程
* CSS 冲突处理
* 完成迁移
## 策略识别
对于使用增量共存策略的项目,代理应该:
1. **确定策略**:检查项目是否使用pnpm别名(策略A)或组件包(策略B)
* **策略 A**:寻找别名,例如`"@heroui-v3/react": "npm:@heroui/react@latest"`在 package.json 中
* **策略 B**:寻找特定于组件的包,例如`@heroui/button`, `@heroui/card`旁边`@heroui/react`
2. **验证设置**:确保共存设置正确:
* 策略A:两者兼而有之`@heroui/react`(v2)和`@heroui-v3/react`(v3 别名)已安装
* 策略B:`@heroui/react`(v3) 和组件包,例如`@heroui/button`(v2) 已安装
* CSS 已针对两个版本进行配置(请参阅 CSS 配置部分)
## 逐个组件的迁移指南
### 对于策略 A(pnpm 别名):
1. **识别要迁移的组件**
* 查看组件迁移参考表
* 使用`get_component_migration_guides`用于获取特定于组件的指南的 MCP 工具
2. **更新导入**
* 将导入从 `@heroui/react` 改为 `@heroui-v3/react`
* 例子:`import {Button} from "@heroui/react"` → `import {Button} from "@heroui-v3/react"`
3. **更新组件代码**
* 遵循组件迁移指南`get_component_migration_guides`工具
* 更新 props、组件结构和 API 调用
* 如果需要,用复合组件替换挂钩
4. **测试迁移的组件**
* 验证组件正确渲染
* 测试功能和交互
* 检查样式冲突
5. **文档迁移**
* 跟踪哪些组件已迁移
* 注意任何问题或疑虑
### 对于策略 B(组件包):
1. **识别要迁移的组件**
* 查看组件迁移参考表
* 使用`get_component_migration_guides`用于获取特定于组件的指南的 MCP 工具
2. **移除组件包**
* 从依赖项中删除 v2 组件包(例如,`@heroui/button`)
* 更新package.json
3. **更新导入**
* 将组件包中的导入更改为`@heroui/react`(v3)
* 例子:`import {Card} from "@heroui/card"` → `import {Card} from "@heroui/react"`
4. **更新组件代码**
* 遵循组件迁移指南`get_component_migration_guides`工具
* 更新 props、组件结构和 API 调用
* 如果需要,用复合组件替换挂钩
5. **测试迁移的组件**
* 验证组件正确渲染
* 测试功能和交互
* 检查样式冲突
6. **文档迁移**
* 跟踪哪些组件已迁移
* 注意任何问题或疑虑
### 处理移除的组件(无 v3 对应项)
当遇到没有 v3 对位的 v2 组件(**Code**、**Image**、**Navbar**、**Ripple**、**Snippet**、**Spacer**、**User**)时:
* **将它们留在原处** - 不要尝试迁移它们,除非用户明确请求删除
* **如果用户想要删除它们**:使用`get_component_migration_guides`(如果有)或[组件迁移参考](/docs/react/migration#component-migration-reference)获取指南并帮助替换为原生 HTML 或手动实现
## CSS 冲突处理
共存期间,v2 和 v3 CSS 系统都会被加载。代理应该:
1. **监控冲突**
* 注意样式不一致
* 检查v2和v3样式是否冲突
* 验证两个 CSS 导入均存在且顺序正确
2. **指导冲突解决**
* 确保 CSS 导入顺序:`tailwindcss`首先,然后`@heroui/styles`
* 检查 Tailwind 配置是否配置了 v2 插件
* 验证 v3 CSS 是否正确导入
3. **每次迁移后测试样式**
* 验证迁移的组件看起来正确
* 检查是否有意外的样式覆盖
* 确保 v2 组件的样式仍然正确
## 组件迁移参考
可以通过下表快速查找每个组件的迁移指南。点击「迁移指南」列中的链接,即可跳转到对应的详细迁移说明。
**组件开发状态**:标有 🔄 进行中或 📋 计划中的组件仍在开发中。可以查看[路线图](https://herouiv3.featurebase.app/roadmap)了解任务状态。这些组件的迁移指南将在开发完成后提供。
| v2 组件 | v3 组件 | 状态 | 迁移指南 |
| ---------------- | -------------------------- | ------ | ------------------------------------------------------------------------ |
| Accordion | Accordion | ✅ 可用 | [查看指南 →](/docs/react/migration/accordion) |
| Alert | Alert | ✅ 可用 | [查看指南 →](/docs/react/migration/alert) |
| Autocomplete | ComboBox | ✅ 已重命名 | [查看指南 →](/docs/react/migration/autocomplete) |
| Avatar | Avatar | ✅ 可用 | [查看指南 →](/docs/react/migration/avatar) |
| Badge | Badge | ✅ 可用 | [查看指南 →](/docs/react/migration/badge) |
| Breadcrumbs | Breadcrumbs | ✅ 可用 | [查看指南 →](/docs/react/migration/breadcrumbs) |
| Button | Button | ✅ 可用 | [查看指南 →](/docs/react/migration/button) |
| ButtonGroup | ButtonGroup | ✅ 可用 | [查看指南 →](/docs/react/migration/button-group) |
| Calendar | Calendar | ✅ 可用 | [查看指南 →](/docs/react/migration/calendar) |
| Card | Card | ✅ 可用 | [查看指南 →](/docs/react/migration/card) |
| Checkbox | Checkbox | ✅ 可用 | [查看指南 →](/docs/react/migration/checkbox) |
| CheckboxGroup | CheckboxGroup | ✅ 可用 | [查看指南 →](/docs/react/migration/checkbox-group) |
| Chip | Chip | ✅ 可用 | [查看指南 →](/docs/react/migration/chip) |
| Code | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/code) |
| DateInput | DateField | ✅ 已重命名 | [查看指南 →](/docs/react/migration/dateinput) |
| DatePicker | DatePicker | ✅ 可用 | [查看指南 →](/docs/react/migration/date-picker) |
| DateRangePicker | DateRangePicker | ✅ 可用 | [查看指南 →](/docs/react/migration/date-range-picker) |
| TimeInput | TimeField | ✅ 已重命名 | [查看指南 →](/docs/react/migration/timeinput) |
| Divider | Separator | ✅ 已重命名 | [查看指南 →](/docs/react/migration/divider) |
| Drawer | Drawer | ✅ 可用 | [查看指南 →](/docs/react/migration/drawer) |
| Dropdown | Dropdown | ✅ 可用 | [查看指南 →](/docs/react/migration/dropdown) |
| Form | Form | ✅ 可用 | [查看指南 →](/docs/react/migration/form) |
| Image | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/image) |
| Input | TextField、Input、InputGroup | ✅ 可用 | [查看指南 →](/docs/react/migration/input) |
| InputOTP | InputOTP | ✅ 可用 | [查看指南 →](/docs/react/migration/input-otp) |
| Kbd | Kbd | ✅ 可用 | [查看指南 →](/docs/react/migration/kbd) |
| Link | Link | ✅ 可用 | [查看指南 →](/docs/react/migration/link) |
| Listbox | ListBox | ✅ 可用 | [查看指南 →](/docs/react/migration/listbox) |
| Modal | Modal | ✅ 可用 | [查看指南 →](/docs/react/migration/modal) |
| Navbar | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/navbar) |
| NumberInput | NumberField | ✅ 已重命名 | [查看指南 →](/docs/react/migration/numberinput) |
| Pagination | Pagination | ✅ 可用 | [查看指南 →](/docs/react/migration/pagination) |
| Popover | Popover | ✅ 可用 | [查看指南 →](/docs/react/migration/popover) |
| Progress | ProgressBar | ✅ 已重命名 | [查看指南 →](/docs/react/migration/progress) |
| CircularProgress | ProgressCircle | ✅ 已重命名 | [查看指南 →](/docs/react/migration/circular-progress) |
| Radio | Radio | ✅ 可用 | [查看指南 →](/docs/react/migration/radio) |
| RadioGroup | RadioGroup | ✅ 可用 | [查看指南 →](/docs/react/migration/radio-group) |
| RangeCalendar | RangeCalendar | ✅ 可用 | [查看指南 →](/docs/react/migration/range-calendar) |
| Ripple | ❌ | ❌ 已移除 | [参见 Button 的水波纹效果 →](/docs/react/components/button#adding-ripple-effect) |
| ScrollShadow | ScrollShadow | ✅ 可用 | [查看指南 →](/docs/react/migration/scroll-shadow) |
| Select | Select | ✅ 可用 | [查看指南 →](/docs/react/migration/select) |
| Skeleton | Skeleton | ✅ 可用 | [查看指南 →](/docs/react/migration/skeleton) |
| Slider | Slider | ✅ 可用 | [查看指南 →](/docs/react/migration/slider) |
| Snippet | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/snippet) |
| Spacer | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/spacer) |
| Spinner | Spinner | ✅ 可用 | [查看指南 →](/docs/react/migration/spinner) |
| Switch | Switch | ✅ 可用 | [查看指南 →](/docs/react/migration/switch) |
| Table | Table | ✅ 可用 | [查看指南 →](/docs/react/migration/table) |
| Tabs | Tabs | ✅ 可用 | [查看指南 →](/docs/react/migration/tabs) |
| Toast | Toast | ✅ 可用 | [查看指南 →](/docs/react/migration/toast) |
| Tooltip | Tooltip | ✅ 可用 | [查看指南 →](/docs/react/migration/tooltip) |
| User | ❌ | ❌ 已移除 | [查看指南 →](/docs/react/migration/user) |
使用`get_component_migration_guides`MCP 工具可获取每个组件的详细指南。
## v3 中的新组件
v3 引入了一系列 v2 中尚未提供的全新组件:
| 组件 | 用途 | 文档 |
| --------------- | -------------------------------------------------------------------- | ------------------------------------------------- |
| TextField | 增强型文本输入框,支持 label 与 description | [查看文档 →](/docs/react/components/text-field) |
| TextArea | 多行文本输入组件 | [查看文档 →](/docs/react/components/text-area) |
| AlertDialog | 用于确认与提醒的模态对话框 | [查看文档 →](/docs/react/components/alert-dialog) |
| Label | 无障碍的表单标签组件 | [查看文档 →](/docs/react/components/label) |
| Description | 表单字段的辅助说明文本 | [查看文档 →](/docs/react/components/description) |
| FieldError | 表单字段的错误信息显示 | [查看文档 →](/docs/react/components/field-error) |
| Fieldset | 对相关表单字段进行分组 | [查看文档 →](/docs/react/components/fieldset) |
| InputGroup | 将多个输入框组合在一起 | [查看文档 →](/docs/react/components/input-group) |
| Surface | 带有层级样式的容器组件 | [查看文档 →](/docs/react/components/surface) |
| Disclosure | 可展开 / 可折叠的内容区域 | [查看文档 →](/docs/react/components/disclosure) |
| DisclosureGroup | 用于管理多个 Disclosure 区域的复合组件 | [查看文档 →](/docs/react/components/disclosure-group) |
| SearchField | 带清除按钮与可选加载状态的搜索输入框 | [查看文档 →](/docs/react/components/search-field) |
| DateField | 配合日历选择器的日期输入框 | [查看文档 →](/docs/react/components/date-field) |
| TimeField | 时间输入组件 | [查看文档 →](/docs/react/components/time-field) |
| Tag、TagGroup | 用于选择或展示的 Tag 与 TagGroup | [查看文档 →](/docs/react/components/tag-group) |
| ColorPicker | 颜色选择(ColorArea、ColorField、ColorSlider、ColorSwatch、ColorSwatchPicker) | [查看文档 →](/docs/react/components/color-picker) |
| CloseButton | 用于关闭或解除浮层的触发按钮 | [查看文档 →](/docs/react/components/close-button) |
| ErrorMessage | 表单字段错误信息展示(基于 React Aria 集成) | [查看文档 →](/docs/react/components/error-message) |
## 完成步骤
迁移所有组件后:
1. **删除 v2 依赖项**
* 策略A:删除`@heroui/react`, `@heroui/theme`和别名
* 策略B:删除所有剩余的`@heroui/*`组件包
* 如果项目仍包含上述已移除 v2 组件(**Code**、**Image**、**Navbar**、**Ripple**、**Snippet**、**Spacer**、**User**),请告知用户:他们可稍后在代理协助下按指南移除或替换。
2. **更新所有导入**
* 策略A:改变`@heroui-v3/react` → `@heroui/react`
* 策略 B:所有导入应已指向 `@heroui/react`(v3)
3. **更新CSS配置**
* 从配置中删除 v2 Tailwind 插件
* 仅保留`@import "@heroui/styles";`
* 删除 v2 CSS 导入
4. **完成样式迁移**
* 遵循样式迁移指南
* 使用`get_styling_migration_guide`MCP工具
* 更新实用程序类、颜色标记等。
## 与完全迁移的差异
**主要区别:**
* **项目状态**:项目在迁移过程中保持功能(无损坏状态)
* **迁移速度**:可以在较长时间内逐个组件进行迁移
* **测试**:可以在完全迁移之前测试 v3 组件和 v2 组件
* **分支策略**:功能分支不太重要(尽管仍然推荐)
* **依赖管理**:两个版本暂时共存
* **CSS 处理**:两个 CSS 系统在共存期间加载
**何时使用增量共存:**
* 需要逐步迁移的大型代码库
* 迁移期间必须保持功能的项目
* 想要增量测试 v3 组件的团队
* 已使用特定于组件的包的项目(策略 B)
**何时使用完全迁移:**
* 可以快速迁移的较小项目
* 可以接受临时破坏状态的项目
* 喜欢一次性迁移的团队
* 项目使用统一`@heroui/react`包(策略 A 可以工作,但完全迁移可能更简单)
## 代理最佳实践
1. **验证项目仍然正常运行**
* 每个组件迁移后,确保项目仍然有效
* 测试已迁移和未迁移的组件
* 立即报告任何问题
2. **指导逐个组件迁移**
* 帮助一次迁移一个组件
* 使用`get_component_migration_guides`适用于每个组件的 MCP 工具
* 在继续之前彻底测试
3. **监控 CSS 冲突**
* 注意 v2 和 v3 之间的样式问题
* 指导解决冲突
* 确保 CSS 配置正确
4. **跟踪迁移进度**
* 保留已迁移组件的清单
* 记录任何问题或疑虑
* 注意正在使用哪种策略
5. **指导完成步骤**
* 迁移所有组件后,指导删除 v2 依赖项
* 帮助将所有导入更新为仅限 v3
* 指导样式迁移完成
## 常见场景
### 大型项目(100 多个组件)
* 随着时间的推移逐步迁移组件
* 彻底测试每个组件
* 监控 CSS 冲突
* 可能需要数周或数月才能完成
### 小型项目(\<20 个组件)
* 可以更快地迁移
* 仍然测试每个组件
* 减少 CSS 冲突风险
* 可能在几天内完成
### 混合战略项目
* 有些项目可能对某些组件使用策略 A,对其他组件使用策略 B
* 指导每个组件进行适当的导入更新
* 尽可能确保方法一致
### 项目使用 v2 导航栏(或其他已删除的组件)
* 留在原处 —— v2 Navbar 与其他已移除组件(**Code**、**Image**、**Ripple**、**Snippet**、**Spacer**、**User**)在共存期间仍会继续工作
* 告知用户:若不再需要,可在代理协助下移除;替换方案见 [组件迁移参考](/docs/react/migration#component-migration-reference)。
## 下一步
完成迁移后:
1. 删除 v2 依赖项(已在完成步骤中完成)
2. 从迁移 MCP 切换到用于 v3 开发的 `heroui-react` MCP
3. 更新文档中的引用
4. 运行最终验证
5. 完成样式迁移
本代理迁移指南旨在与迁移 MCP 服务器配合使用。在开始迁移之前,请确保 MCP 服务器已正确配置并且工具可用。
# 完全迁移
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/full-migration
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(workflows)/full-migration.mdx
> 使用完整迁移方法将 HeroUI v2 迁移到 v3 的分步指南
## 概述
完全迁移是一种从 HeroUI v2 迁移到 v3 的结构化方法。此方法首先迁移所有组件代码,然后切换依赖关系,确保干净的过渡。
**重要:** 完全迁移意味着项目在迁移过程中将被破坏(v2 和 v3 不能共存)。在功能分支中工作以维护工作主分支。
### AI 助理资源
AI 助手可以协助迁移。你可以使用 [迁移 MCP 服务器](/docs/react/migration/mcp-server) 获取工具与提示词,使用 [迁移 Agent Skills](/docs/react/migration/agent-skills) 获取基于技能的知识,或使用 [用于迁移的 AGENTS.md](/docs/react/migration/agents-md) 将迁移文档下载到项目中。
## 迁移工作流程
**重要**:由于 v2 和 v3 无法共存,因此迁移分两个主要阶段进行:
1. **准备阶段**:迁移所有组件代码,同时仍依赖 v2 依赖项(代码将被破坏)
2. **切换阶段**:将依赖项更新到 v3 并修复任何剩余问题
**⚠️ 关键:不要构建来检查迁移过程中的错误**
* 使用**类型检查**(例如,`tsc --noEmit`) 如果可以检查 TypeScript 错误
* 使用**lint**(例如,`eslint`, `biome check`) 如果可以检查代码质量
* **不要**运行构建命令(例如,`npm run build`, `next build`, `vite build`)
* **请勿** 在迁移期间尝试启动/运行项目
## 分步迁移指南
### 第 1 步:更新依赖项
#### 更新反应
v3 需要 React 19+。更新你的 React 版本:
```bash
npm install react@^19.0.0 react-dom@^19.0.0
```
```bash
pnpm add react@^19.0.0 react-dom@^19.0.0
```
```bash
yarn add react@^19.0.0 react-dom@^19.0.0
```
```bash
bun add react@^19.0.0 react-dom@^19.0.0
```
**注意**:这些依赖项更新可以在切换 HeroUI 之前完成(不会破坏项目)。但是,HeroUI 包切换只能在所有组件代码迁移之后进行。
#### 更新 HeroUI 包
**重要**:在迁移所有组件代码后执行此操作。删除 v2 软件包并安装 v3:
```bash
npm uninstall @heroui/react @heroui/theme
npm install @heroui/styles @heroui/react
```
```bash
pnpm remove @heroui/react @heroui/theme
pnpm add @heroui/styles @heroui/react
```
```bash
yarn remove @heroui/react @heroui/theme
yarn add @heroui/styles @heroui/react
```
```bash
bun remove @heroui/react @heroui/theme
bun add @heroui/styles @heroui/react
```
#### 删除Framer Motion
v3 不再需要 Framer Motion:
```bash
npm uninstall framer-motion
```
```bash
pnpm remove framer-motion
```
```bash
yarn remove framer-motion
```
```bash
bun remove framer-motion
```
#### 更新 Tailwind CSS
确保您使用的是 Tailwind CSS v4:
```bash
npm install tailwindcss@^4.0.0
```
```bash
pnpm add tailwindcss@^4.0.0
```
```bash
yarn add tailwindcss@^4.0.0
```
```bash
bun add tailwindcss@^4.0.0
```
### 第 2 步:更新主题配置
#### 删除 Tailwind 插件配置
**v2 配置:**
```js
// tailwind.config.js
const {heroui} = require("@heroui/react");
module.exports = {
content: [
"./src/**/*.{js,ts,jsx,tsx}",
"./node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}",
],
plugins: [heroui()],
// ... other config
};
```
**v3 配置:**
删除`heroui()`来自 Tailwind 配置的插件。如果您仅将 Tailwind 用于 HeroUI 并且没有其他自定义,则可以删除`tailwind.config.js`完全。否则,保留该文件但删除 HeroUI 插件配置。
#### 更新 CSS 导入
**v2 CSS:**
```css
/* globals.css or main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
```
**v3 CSS:**
```css
/* globals.css or main.css */
@import "tailwindcss";
@import "@heroui/styles"; /* [!code highlight]*/
```
**重要提示:** 导入顺序很重要:先导入 `tailwindcss`,再导入 `@heroui/styles`。
#### 删除主题插件文件
如果您创建了一个`hero.ts`v2 文件,您可以将其删除:
```bash
rm hero.ts
```
### 第3步:删除HeroUIProvider
v3 不需要 Provider 组件。从应用程序根目录中删除它。
**v2 代码:**
```tsx
// app.tsx or App.tsx
import {HeroUIProvider} from "@heroui/react";
function App() {
return (
);
}
```
**v3 代码:**
```tsx
// app.tsx or App.tsx
function App() {
return ;
}
```
**如果您使用 Provider 道具:**
如果您使用 Provider 道具,例如`navigate`, `useHref`, `locale`, `disableAnimation`等等,您需要以不同的方式处理这些:
* **路由器集成**:直接使用 React Router 或您的路由库
* **区域设置**:使用 React Aria 的`I18nProvider`如果需要直接
* **动画**:请参阅下面的动画更改部分
#### 动画变化
v3 删除了 Framer Motion 依赖性并以不同方式处理动画:
* **基于 CSS 的动画**:v3 使用原生 CSS 动画和过渡,而不是基于 JavaScript 的动画
* **更好的性能**:CSS 动画提供更好的性能和更流畅的动画
* **无全局禁用**:与 v2 的 Provider 不同`disableAnimation`道具,v3 中没有全局动画切换
* **每个组件控制**:通过 CSS 或特定于组件的 props(如果可用)控制动画
* **自定义动画**:使用标准 CSS`@keyframes`和自定义动画的过渡属性
### 第 4 步:更换拆下的挂钩
HeroUI v2 提供了组件挂钩(例如`useSwitch`, `useInput`, `useCheckbox`等)和实用程序挂钩,例如`useDisclosure`。 HeroUI v3 删除了大多数组件挂钩,转而使用复合组件,并替换了`useDisclosure`和`useOverlayState`.
查看综合[Hooks 迁移指南](/docs/react/migration/hooks)为了:
* 组件挂钩移除并迁移到复合组件
* `useDisclosure` → `useOverlayState`迁移
* 迁移策略和示例
### 第 5 步:更新组件导入
所有组件现在都从单个包导入:
**v2 导入:**
```tsx
// Individual packages (if used)
import {Button} from "@heroui/button";
import {Card} from "@heroui/card";
// Or from main package
import {Button, Card} from "@heroui/react";
```
**v3 导入:**
```tsx
// All components from single package
import {Button, Card} from "@heroui/react";
```
#### TypeScript 注意事项
如果您使用 TypeScript,请注意 v3 中的类型更改:
```tsx
// Import types alongside components
import {Button, type ButtonProps} from "@heroui/react";
// Compound component types are properly exported
import {Checkbox, type CheckboxProps} from "@heroui/react";
// Type names may have changed - check component documentation
type MyButtonProps = ButtonProps & {
customProp?: string;
};
```
**常见类型变化:**
* 组件 prop 接口可能有不同的名称或属性
* 复合零部件有自己的类型导出
* 更新了 Ref 类型以匹配 React 19 模式
### 第6步:组件迁移
使用[组件迁移参考](/docs/react/migration#component-migration-reference)表查找每个组件的迁移指南。根据组件的特定指南迁移组件。
**要点:**
* 根据需要查看各个组件迁移指南
* 迁移组件 API、props 和结构
* 更新复合组件模式(复选框、单选、开关、卡片、模态等)
* 更新组组件(ButtonGroup、CheckboxGroup、RadioGroup)
* 处理已移除组件(**Code**、**Image**、**Navbar**、**Ripple**、**Snippet**、**Spacer**、**User**)——替换为原生 HTML 或自定义实现
* 处理进行中/计划中的组件 - 替换为 HTML 元素,直到 v3 组件可用
### 第 7 步:更新自定义主题覆盖
如果您在 v2 中有自定义主题覆盖,则需要针对 v3 的基于 CSS 的主题系统更新它们。
**v2 主题定制:**
```js
// tailwind.config.js
const {heroui} = require("@heroui/react");
module.exports = {
plugins: [
heroui({
themes: {
light: {
colors: {
primary: {
// custom colors
},
},
},
},
}),
],
};
```
**v3 主题定制:**
v3 使用 CSS 变量。在 CSS 中覆盖它们:
```css
/* globals.css */
@import "tailwindcss";
@import "@heroui/styles";
:root {
--color-primary: /* your color */;
/* other CSS variables */
}
```
检查[主题文档](/docs/react/getting-started/handbook/theming)获取可用的 CSS 变量。
这是在继续样式迁移之前暂停并验证组件功能的好时机。
### 第8步:Hooks迁移
组件迁移完成后,确保所有钩子都已迁移:
1. **替换组件钩子**:将 `useSwitch`、`useInput`、`useCheckbox` 等替换为复合组件用法
2. **迁移 useDisclosure**:将 `useDisclosure` 替换为 `useOverlayState` 以管理浮层状态
3. **参考 Hooks 指南**:参阅 [Hooks 迁移指南](/docs/react/migration/hooks) 获取详细步骤与示例
### 第 9 步:样式迁移
挂钩迁移完成后,继续进行样式更改。这是一个单独的步骤,以确保在解决视觉更改之前验证组件功能。
**样式迁移指南:**
查看综合[样式迁移指南](/docs/react/migration/styling)为了:
* 实用程序类更改(`text-tiny` → `text-xs`, `rounded-small` → `rounded-sm`, ETC。)
* 颜色标记更新(`bg-primary` → `bg-accent`, `bg-content1` → `bg-surface`, ETC。)
* 组件样式差异(大小、间距、边框半径)
* CSS 变量更改
* 视觉差异和对齐变化
**主要样式变化:**
* **实用程序类**:自定义实用程序替换为标准 Tailwind 类
* **颜色标记**:`primary` → `accent`, `secondary`删除,`content1-4` → `surface`/`overlay`
* **编号比例**:颜色比例如`primary-50`, `primary-100`已删除
* **边框半径**:默认值已更改(v3 中更小)
* **组件样式**:更新了默认大小、填充和间距
**迁移清单:**
* 审查[样式迁移指南](/docs/react/migration/styling)
* 更新实用程序类(`text-tiny` → `text-xs`, ETC。)
* 更新颜色标记(`bg-primary` → `bg-accent`, ETC。)
* 更新内容颜色(`bg-content1` → `bg-surface`或者`bg-overlay`)
* 更新编号色阶(`bg-primary-50` → `bg-accent-soft`)
* 检查组件样式更改(大小、间距、边框半径)
* 测试视觉外观并根据需要进行调整
### 第10步:测试
迁移后,彻底测试您的应用程序:
1. **视觉测试**:检查所有组件正确渲染
2. **功能**:测试所有交互和行为
3. **辅助功能**:验证键盘导航和屏幕阅读器支持
4. **响应式设计**:在不同的屏幕尺寸上进行测试
5. **性能**:检查包大小和运行时性能
## 迁移清单
使用此清单来跟踪您的完整迁移进度:
### 依赖关系
* 将 React 更新至 v19+
* 将 HeroUI 包更新到 v3(组件迁移后)
* 删除Framer Motion
* 将 Tailwind CSS 更新至 v4
### 配置
* 消除`heroui()`Tailwind 配置中的插件
* 更新 CSS 导入
* 消除`hero.ts`文件(如果存在)
### 应用程序代码-组件迁移
* 消除`HeroUIProvider`包装纸
* 处理提供者道具迁移(路由器、区域设置、动画)
* 将所有组件导入更新为`@heroui/react`
* 迁移重命名组件(Divider → Separator、Autocomplete → Combobox、NumberInput → NumberField)
* 更新复合组件模式(复选框、单选、开关、卡片、模态等)
* 更新组组件(ButtonGroup、CheckboxGroup、RadioGroup)
* 更新 TypeScript 类型引用(如果使用组件类型)
* 处理已移除组件(**Code**、**Image**、**Navbar**、**Ripple**、**Snippet**、**Spacer**、**User**)——替换为原生 HTML 或自定义实现
* 处理进行中/计划中的组件 - 替换为 HTML 元素,直到 v3 组件可用
* 考虑使用`asChild`灵活构图的属性
* **在进行钩子迁移之前验证组件功能**
### 应用程序代码-Hooks迁移
* 审查[Hooks 迁移指南](/docs/react/migration/hooks)
* 更换组件挂钩(`useSwitch`, `useInput`, `useCheckbox`等)与复合组件
* 代替`useDisclosure`和`useOverlayState`用于覆盖状态管理
* 根据迁移指南更新所有钩子用法
* **在继续样式迁移之前验证钩子迁移**
### 应用程序代码 - 样式迁移
* 审查[样式迁移指南](/docs/react/migration/styling)
* 更新实用程序类(`text-tiny` → `text-xs`, `rounded-small` → `rounded-sm`, ETC。)
* 更新颜色标记(`bg-primary` → `bg-accent`, `bg-secondary` → `bg-default`, ETC。)
* 更新内容颜色(`bg-content1` → `bg-surface`或者`bg-overlay`)
* 更新编号色阶(`bg-primary-50` → `bg-accent-soft`, ETC。)
* 更新转换实用程序(`.transition-background` → `transition-colors`, ETC。)
* 检查组件样式更改(大小、间距、边框半径)
* 将自定义主题覆盖更新为 CSS 变量
* 测试视觉外观并根据需要进行调整
### 测试
* 视觉回归测试
* 功能测试
* 辅助功能测试
* 性能测试
## 下一步
完成完整迁移后:
1. 回顾[v3 组件文档](/docs/react/components)
2. 探索 v3 中可用的新组件
3. 查看[样式指南](/docs/react/getting-started/handbook/styling)
4. 了解[构图模式](/docs/react/getting-started/handbook/composition)
# 增量迁移
**Category**: react
**URL**: https://v3.heroui.com/cn/docs/react/migration/incremental-migration
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/react/migration/(workflows)/incremental-migration.mdx
> 将 HeroUI v2 增量迁移到 v3 同时保持两个版本并行工作的分步指南
## 概述
通过共存的增量迁移,您可以逐个组件地从 HeroUI v2 迁移到 v3,同时保持项目在整个迁移过程中正常运行。此方法使用特殊的设置策略来允许 v2 和 v3 组件并行工作。
### AI 助理资源
AI 助手可以协助迁移。你可以使用 [迁移 MCP 服务器](/docs/react/migration/mcp-server) 获取工具与提示词,使用 [迁移 Agent Skills](/docs/react/migration/agent-skills) 获取基于技能的知识,或使用 [用于迁移的 AGENTS.md](/docs/react/migration/agents-md) 将迁移文档下载到项目中。
### 限制和注意事项
在选择此方法之前,请注意:
* **捆绑包大小**:迁移期间将包含两个版本,从而增加捆绑包大小
* **样式冲突**:v2 和 v3 样式可能会冲突;彻底测试
* **类型冲突**:如果两个版本都导入到同一个文件中,TypeScript 可能会显示冲突
* **提供商**:v2 需要`HeroUIProvider`,v3 没有 - 您可能需要条件提供者包装
* **React 版本**:v3 需要 React 19+,v2 支持 React 18+ - 确保安装 React 19
* **设置复杂性**:与完全迁移相比,需要更复杂的初始设置
### 没有 v3 对应组件的组件
由于 v2 和 v3 可以共存,因此没有 v3 对应项的 v2 组件可以保留在您的项目中。这些组件将在迁移期间继续工作:
* **Code**、**Image**、**Navbar**、**Ripple**、**Snippet**、**Spacer**、**User**
您无需替换它们即可完成迁移。如果您希望删除它们并使用本机 HTML 或手动实现,您可以要求您的代理帮助使用[组件迁移参考](/docs/react/migration#component-migration-reference)指南。
## 策略选择
根据您当前导入 v2 组件的方式选择策略:
* **使用`@heroui/react`**:使用策略 A(pnpm 别名)
* **使用组件包** (`@heroui/button`, `@heroui/card`等):使用策略B(组件包)
## 策略 A:使用 pnpm 别名
该策略使用 pnpm 包别名以不同的名称安装 v3 包,从而允许两个版本共存。
### 设置
1. 使用别名安装 v3 软件包:
```json
{
"dependencies": {
"@heroui/react": "2.8.6",
"@heroui/theme": "2.4.24",
"@heroui-v3/react": "npm:@heroui/react@latest",
"@heroui-v3/styles": "npm:@heroui/styles@latest"
}
}
```
2. 从以下位置导入 v2 组件`@heroui/react`:
```tsx
import {Button} from "@heroui/react"; // v2
```
3. 从以下位置导入 v3 组件`@heroui-v3/react`:
```tsx
import {Button} from "@heroui-v3/react"; // v3
```
### 迁移过程
1. **一次迁移一个组件:**
* 更新导入以使用`@heroui-v3/react`
* 将组件代码更新为 v3 API
* 测试迁移的组件
* 验证样式看起来正确
* 对于没有 v3 对位的 v2 组件(**Code**、**Image**、**Navbar**、**Ripple**、**Snippet**、**Spacer**、**User**),请将它们保留在原处,它们在共存期间仍会继续工作。
2. **继续,直到所有组件均已迁移**
* 跟踪哪些组件已迁移
* 彻底测试每个迁移的组件
* 如果您想稍后删除 v2 删除的组件,请使用[组件迁移参考](/docs/react/migration#component-migration-reference)指南并要求您的代理帮助将其替换为原生 HTML 或手动实施。
3. **迁移所有组件后,切换到仅 v3:**
* 删除 v2 依赖项(`@heroui/react`, `@heroui/theme`)
* 删除别名
* 将所有导入更新为`@heroui/react`(消除`-v3`后缀)
* 完成样式迁移
### 注意事项
* 两个 CSS 系统都将被加载(v2 通过 Tailwind 插件,v3 通过 CSS 导入)
* 您暂时需要两个 Tailwind 配置
* 迁移过程中捆绑包大小会变大
* 可能会发生一些样式冲突
## 策略 B:使用组件包
该策略将 v2 的特定于组件的包与 v3 的统一包一起使用。
### 设置
1. 安装v3主包和v2组件包:
```json
{
"dependencies": {
"@heroui/react": "latest", // v3
"@heroui/styles": "latest", // v3
"@heroui/button": "2.8.6", // v2
"@heroui/card": "2.8.6", // v2
// ... other v2 component packages as needed
}
}
```
2. 从以下位置导入 v3 组件`@heroui/react`:
```tsx
import {Button} from "@heroui/react"; // v3
```
3. 从组件包中导入 v2 组件:
```tsx
import {Card} from "@heroui/card"; // v2
```
### 迁移过程
1. **安装 v3 软件包** (`@heroui/react`, `@heroui/styles`)
2. **为尚未迁移的组件安装 v2 组件包**
3. **一次迁移一个组件:**
* 从依赖中删除 v2 组件包
* 更新导入以使用`@heroui/react`(v3)
* 将组件代码更新为 v3 API
* 测试迁移的组件
* 对于没有 v3 对位的 v2 组件(**Code**、**Image**、**Navbar**、**Ripple**、**Snippet**、**Spacer**、**User**),请将它们保留在原处,它们在共存期间仍会继续工作。
4. **继续,直到所有组件均已迁移**
* 如果您想稍后删除 v2 删除的组件,请使用[组件迁移参考](/docs/react/migration#component-migration-reference)指南并要求您的代理帮助将其替换为原生 HTML 或手动实施。
5. **删除剩余的 v2 组件包**
6. **完成样式迁移**
### 注意事项
* 仅当您的项目使用特定于组件的包时才有效
* 需要管理多个包依赖关系
* v3 没有组件包,因此这是一种单向迁移路径
## 共存的 CSS 配置
在共存期间,您将需要两个 CSS 系统:
```css
/* globals.css */
@import "tailwindcss";
/* v2 styles via Tailwind plugin */
/* (configured in tailwind.config.js) */
/* v3 styles */
@import "@heroui/styles";
```
**重要提示:** 导入顺序很重要:先导入 `tailwindcss`,再导入 `@heroui/styles`。
## Tailwind配置
您暂时需要两个 Tailwind 配置:
**v2 配置(tailwind.config.js):**
```js
const {heroui} = require("@heroui/react");
module.exports = {
plugins: [heroui()],
// ... other config
};
```
**v3 配置:** 不需要插件,但确保安装 Tailwind v4。
## 组件迁移
对于您迁移的每个组件:
1. **查看组件迁移指南**
* 使用[组件迁移参考](/docs/react/migration#component-migration-reference)表
* 检查特定于组件的迁移指南
2. **更新导入**
* 策略A:改变`@heroui/react` → `@heroui-v3/react`
* 策略 B:更改组件包 →`@heroui/react`
3. **更新组件代码**
* 遵循组件迁移指南
* 更新 props、组件结构和 API 调用
* 如果需要,用复合组件替换挂钩
4. **测试迁移的组件**
* 验证组件正确渲染
* 测试功能和交互
* 检查样式冲突
* 确保 v2 组件的样式仍然正确
5. **文档迁移**
* 跟踪哪些组件已迁移
* 注意任何问题或疑虑
## CSS 冲突处理
共存期间,v2 和 v3 CSS 系统都会被加载。监控冲突:
1. **注意样式不一致**
* 检查v2和v3样式是否冲突
* 验证两个 CSS 导入均存在且顺序正确
2. **指导冲突解决**
* 确保 CSS 导入顺序:`tailwindcss`首先,然后`@heroui/styles`
* 检查 Tailwind 配置是否配置了 v2 插件
* 验证 v3 CSS 是否正确导入
3. **每次迁移后测试样式**
* 验证迁移的组件看起来正确
* 检查是否有意外的样式覆盖
* 确保 v2 组件的样式仍然正确
## 完成迁移
迁移所有组件后:
1. **删除 v2 依赖项:**
* 消除`@heroui/react`和`@heroui/theme`(策略A)
* 全部删除`@heroui/*`组件包(策略B)
* 删除别名(策略 A)
* 如果你将已移除的 v2 组件(**Code**、**Image**、**Navbar**、**Ripple**、**Snippet**、**Spacer**、**User**)保留在项目中,有两个选择:(1) 在删除 v2 依赖前按迁移指南替换它们,或 (2) 暂时保留 v2 依赖,直到你准备好替换。你也可以请代理依据指南协助移除或替换。
2. **更新所有导入:**
* 改变`@heroui-v3/react` → `@heroui/react`(策略A)
* 更改组件包导入 →`@heroui/react`(策略B)
3. **更新CSS:**
* 从配置中删除 v2 Tailwind 插件
* 仅保留`@import "@heroui/styles";`
4. **完成样式迁移:**
* 遵循[样式迁移指南](/docs/react/migration/styling)
* 更新实用程序类、颜色标记等。
## 共存迁移清单
使用此清单来跟踪增量迁移进度:
### 初始设置
* 选择策略(A:别名或B:组件包)
* 安装 v3 软件包(使用别名或直接安装)
* 为两个版本配置 CSS
* 确保已安装 React 19+
* 临时设置两个 Tailwind 配置
### 组件迁移(对每个组件重复)
* 查看组件迁移指南
* 更新导入(策略 A 或 B)
* 将组件代码更新为 v3 API
* 测试迁移的组件
* 检查样式冲突
* 记录迁移进度
* 识别已移除的 v2 组件(**Code**、**Image**、**Navbar**、**Ripple**、**Snippet**、**Spacer**、**User**)——保留在原处或按指南替换
### 完成
* 迁移所有组件
* 可选:在删除 v2 deps 之前使用迁移指南替换已删除的组件,或者仅在替换所有已删除的组件后删除 v2 deps
* 删除 v2 依赖项
* 删除别名(策略 A)
* 将所有导入更新为仅限 v3
* 删除 v2 Tailwind 插件
* 将 CSS 更新为仅限 v3
* 完成样式迁移
* 测试整个应用程序
## 下一步
完成增量迁移后:
1. 回顾[v3 组件文档](/docs/react/components)
2. 探索 v3 中可用的新组件
3. 查看[样式指南](/docs/react/getting-started/handbook/styling)
4. 了解[构图模式](/docs/react/getting-started/handbook/composition)