)
│ │ └── 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**: native
**URL**: https://v3.heroui.com/cn/docs/native/components
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/index.mdx
> 浏览 HeroUI Native 提供的全部组件;更多组件将陆续推出。
## 按钮
## 集合
## 控件
## 表单
## 导航
## 浮层
## 反馈
## 布局
## 媒体
## 数据展示
## 工具
# 介绍
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/getting-started
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/getting-started/index.mdx
> 开源 React Native UI 组件库,用于构建美观且易于访问的移动界面。
HeroUI Native 是基于 [Tailwind v4](https://tailwindcss.com/blog/tailwindcss-v4) 与 [Uniwind](https://uniwind.dev/) 的 React Native 组件库,并面向现代移动端技术栈。每个组件都带有流畅动画、精致细节与内置无障碍支持——开箱即用,亦可深度定制。
## 为什么选择 HeroUI Native?
**默认即美观** — 专业观感开箱即有,无需额外堆样式。
**无障碍优先** — 遵循移动端无障碍最佳实践,内置合理的焦点管理、触控可达性与读屏支持。
**高度可组合** — 每个组件由可替换的子部件构成;按需改动,其余保持不变。
**开发者友好** — 类型完备的 API、可预期的模式与出色的自动补全。
**持续维护** — 由团队负责更新、修复与新特性;你只需升级依赖。
**轻量按需** — 支持 Tree-shaking,仅打包实际使用的部分。
**面向未来** — 兼容最新 [Expo](https://expo.dev/),并通过 [Uniwind](https://uniwind.dev/) 建立在 [Tailwind v4](https://tailwindcss.com/blog/tailwindcss-v4) 之上,同时便于 AI 辅助开发。
## 一个精心打造的组件库,而非复制粘贴
复制粘贴的代码在依赖停滞时会变成维护负担。
HeroUI Native 则不同,它是与你共同演进的组件库:
* 自动更新和修复
* 无需额外工作即可获得新功能
* 组件与 React Native、Tailwind 与移动平台保持同步
* 深度定制,而非浅层主题调整
* 面向代码生成的 AI 友好 API
## HeroUI 生态
* **🌐 HeroUI v3(Web)** — 基于 Tailwind CSS v4 的 React 组件
* **📱 HeroUI Native(移动端)** — 面向 React Native 的美观组件
* **🤖 [HeroUI Chat](https://heroui.chat?ref=heroui-v3)**(自然语言生成应用)— 用对话创建应用
* **🧠 面向 LLM 的 UI** — 全新平台与 MCP 即将推出
## 常见问题
**HeroUI Native 是否免费?**\
是的,基于 Apache License 2.0 完全免费且开源。
**是否可用于生产?**\
可以。HeroUI v3 已经稳定,可放心用于生产环境。
**能否自定义组件?**\
可以。可更新默认样式与动画,或重新组合子部件;每个插槽都可定制。
**是否支持 TypeScript?**\
完整类型定义,IDE 体验与自动补全良好。
**无障碍方面如何?**\
遵循移动端无障碍最佳实践,内置焦点管理、触控可达性与读屏支持。
**是否有 Figma 资源?**\
有!欢迎访问我们的设计系统:[HeroUI Figma Kit V3](https://www.figma.com/community/file/1546526812159103429)。
## 参与其中
加入社区、分享反馈或参与贡献:
* [GitHub Discussions](https://github.com/heroui-inc/heroui-native/discussions)
* [Discord](https://discord.gg/9b6yyZKmH4)
* [X/Twitter](https://x.com/hero_ui)
* [贡献指南](https://github.com/heroui-inc/heroui-native/blob/main/CONTRIBUTING.md)
HeroUI Native 采用 [Apache License 2.0](https://github.com/heroui-inc/heroui-native/blob/main/LICENSE) 发布。
# Beta 10
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases/beta-10
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/beta-10.mdx
> Bottom Sheet 组件、PressableFeedback 重构、动画 API 的 State 扩展、use-theme-color 多色选取与问题修复
2025 年 12 月 30 日
本版本新增 [Bottom Sheet](/docs/native/components/bottom-sheet) 组件;重构 [PressableFeedback](/docs/native/components/pressable-feedback) 并改进 API;为动画 API 增加 State Prop 支持;增强 `use-theme-color` 以支持一次选取多色;并包含若干问题修复与文档改进。
## 安装
升级到最新版本:
```bash
npm i heroui-native@beta
```
```bash
pnpm add heroui-native@beta
```
```bash
yarn add heroui-native@beta
```
```bash
bun add heroui-native@beta
```
**使用 AI 助手?** 直接提示「Hey Cursor,把 HeroUI Native 升级到最新版本」,助手会自动比对版本并完成必要修改。了解更多请参见 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)。
## 更新亮点
### 新组件
#### Bottom Sheet
本版本新增 **Bottom Sheet** 组件:自屏幕底部滑入的通用遮罩层,带动画过渡与下滑关闭手势。
**特性:**
* 平滑动画过渡与手势支持
* 多档吸附高度,布局更灵活
* Detached 模式,支持自定义定位
* 可自定义遮罩与模糊效果
* 完整无障碍支持
* 基于 [@gorhom/bottom-sheet](https://gorhom.dev/react-native-bottom-sheet)
**用法:**
```tsx
import { BottomSheet, Button } from 'heroui-native';
Open Bottom Sheet
Title
Description
```
完整文档与示例见 [Bottom Sheet 组件页](/docs/native/components/bottom-sheet)。
**相关 PR:** [#174](https://github.com/heroui-inc/heroui-native/pull/174)
## 组件改进
### PressableFeedback 重构
[PressableFeedback](/docs/native/components/pressable-feedback) 已重构,API 更清晰,动画控制更好。
**改进:**
* 动画配置 API 增强
* 更好支持自定义动画状态
* 性能与流畅度提升
* 反馈定位选项更灵活
在提供更多按压反馈动画控制能力的同时,保持向后兼容。
**相关 PR:** [#182](https://github.com/heroui-inc/heroui-native/pull/182)
## API 增强
### 动画 API:State 属性扩展
动画 API 新增 `state` 属性,可在自定义属性的同时关闭动画,实现更细粒度的行为控制。
**新能力:**
```tsx
```
`state` 可取:
* `'disabled'`:关闭动画,仍允许自定义属性
* `'disable-all'`:关闭所有动画(含子级)
* `boolean`:简单开关
便于在不启用动画的情况下微调动画相关属性,利于精细调整组件行为。
**相关 PR:** [#176](https://github.com/heroui-inc/heroui-native/pull/176)
### use-theme-color 多色选取
`use-theme-color` 已重构,支持一次选取多种颜色,主题定制更灵活。
**增强:**
* 支持同时选取多种颜色
* 颜色选取逻辑改进
* 多色场景下性能更好
便于在需要多色协同应用的主题场景中组合使用。
**相关 PR:** [#170](https://github.com/heroui-inc/heroui-native/pull/170)
## 文档
### 动画样式指南注释
为动画样式指南补充注释与说明,便于开发者理解与正确使用动画能力。
**改进:**
* 示例代码附详细注释
* 动画模式说明更清晰
* 不同动画方案的选用指引更明确
**相关 PR:** [#179](https://github.com/heroui-inc/heroui-native/pull/179)
## 问题修复
本版本包含以下修复:
* **[Issue #173](https://github.com/heroui-inc/heroui-native/issues/173)**:修复 `classNames={{ container: "bg-x" }}` 无法为 TextField.Input 容器设置 `backgroundColor` 的问题。
* **[Issue #177](https://github.com/heroui-inc/heroui-native/issues/177)**:修复按钮缩放动画有时停留在 0.9 倍、松手后无法回弹的问题。
* **[Issue #178](https://github.com/heroui-inc/heroui-native/issues/178)**:修复影响组件功能的问题。
## 文档更新
以下文档页面已随本版本更新:
* [动画指南](/docs/native/getting-started/animation) — 补充动画 API State 属性说明
* [颜色指南](/docs/native/getting-started/colors) — 补充 use-theme-color 多色选取说明
* [PressableFeedback 组件](/docs/native/components/pressable-feedback) — 更新重构后的 API 文档
## 链接
* [组件文档](../components)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-native)
## 贡献者
感谢所有为本版本做出贡献的朋友!
# Beta 11
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases/beta-11
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/beta-11.mdx
> Bottom Sheet 关闭协同增强、Dialog 侧滑关闭修复、TextField 改进,以及面向高级场景的 PortalHost 导出
2026 年 1 月 6 日
Beta 11 聚焦多块核心能力的可靠性与开发者体验:增强 Bottom Sheet 在各关闭路径下的一致性;修复 Dialog 侧滑关闭手势;解决 TextField 样式与行为问题;并新增 `PortalHost` 导出以支持高级 Portal 挂载。交互更顺滑,对组件行为的控制也更充分。
## 安装
升级到最新版本:
```bash
npm i heroui-native@beta
```
```bash
pnpm add heroui-native@beta
```
```bash
yarn add heroui-native@beta
```
```bash
bun add heroui-native@beta
```
**使用 AI 助手?** 直接提示「Hey Cursor,把 HeroUI Native 升级到最新版本」,助手会自动比对版本并完成必要修改。了解更多请参见 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)。
## 抢先体验
通过预览应用在真机上体验 Beta 11 的改进!你可以查看增强后的 Bottom Sheet、Dialog、TextField 与 PortalHost 相关能力。
### 环境要求
请确保手机已安装最新版本的 [Expo Go](https://expo.dev/go)。
### 如何访问
**方式一:扫描二维码**
使用手机相机或 Expo Go 应用扫描:
> **Android 用户请注意:** 若使用系统相机或其他扫码应用会跳转到浏览器并出现 404,请先打开 Expo Go,使用其内置扫码功能扫描。
**方式二:点击链接**
**[📱 在 Expo Go 中打开演示应用](https://link.heroui.com/native-demo)**
若设备已安装 Expo Go,将自动在其中打开应用。
## 组件改进
### Bottom Sheet 关闭协同增强
[Bottom Sheet](/docs/native/components/bottom-sheet) 已增强各类关闭路径之间的协同。
**改进:**
* 下滑关闭、点击遮罩、关闭按钮与程序化关闭之间的同步更好
* 关闭过程中的状态管理改进,减少竞态
* 各关闭场景下 `onOpenChange` 触发更可靠
* 动画进度与关闭态切换的衔接更顺畅
Bottom Sheet 支持下滑、点遮罩、点关闭按钮或代码关闭。此前这些路径偶发冲突或表现不一致;本更新使各路径协调一致,体验更可预期。
**相关 PR:** [#201](https://github.com/heroui-inc/heroui-native/pull/201)
### Dialog 侧滑关闭手势修复
[Dialog](/docs/native/components/dialog) 已修复侧滑关闭手势的处理。
**改进:**
* 侧滑关闭的手势检测与处理修复
* 滑动过程中手势状态管理改进
* 松手时与动画的衔接增强
* 超过阈值后侧滑关闭更可靠
Dialog 支持下滑关闭。本修复解决滑动过程中手势偶发无响应或行为异常的问题。
**相关 PR:** [#193](https://github.com/heroui-inc/heroui-native/pull/193)
### TextField 样式与行为修复
[TextField](/docs/native/components/text-field) 的样式与行为问题已修复。
**改进:**
* 输入框样式不一致问题修复
* 动画状态管理问题修复
* 聚焦/失焦处理改进
* 错误态视觉反馈增强
* 占位符与选中颜色应用修复
确保 TextField 在聚焦、失焦、非法等状态下显示正确,并向用户提供一致的视觉反馈。
**相关 PR:** [#202](https://github.com/heroui-inc/heroui-native/pull/202)
## API 增强
### PortalHost 导出(高级场景)
`PortalHost` 现从主 Provider 模块导出,支持高级 Portal 宿主挂载。
**新能力:**
```tsx
import { HeroUINativeProvider, PortalHost } from "heroui-native";
export function CustomLayout() {
return (
<>
{/* 应用内容 */}
{/* 在自定义位置手动挂载 PortalHost */}
>
);
}
```
便于在自定义布局中手动挂载 Portal 宿主,例如需在 BottomSheet、Modal 或其他遮罩内指定渲染位置时。默认 `HeroUINativeProvider` 已包含标准场景的 `PortalHost`;现可额外创建具名宿主以支持多宿主架构。
**适用场景:**
* 在 Bottom Sheet 内挂载 Portal
* 在 Modal 中创建 Portal 宿主
* 自定义遮罩渲染
* 多宿主 Portal 架构
**相关 PR:** [#185](https://github.com/heroui-inc/heroui-native/pull/185)
## 问题修复
本版本包含以下修复:
* **[Issue #187](https://github.com/heroui-inc/heroui-native/issues/187)**:修复通过滑动手势关闭后,需多次点击才能再次打开 Bottom Sheet 或 Dialog 的问题。内部状态现与关闭动画正确同步,无论以何种方式关闭均可立即再次打开。
* **[Issue #189](https://github.com/heroui-inc/heroui-native/issues/189)**:修复含文本输入的 Dialog 在侧滑关闭时应用卡死的问题。
* **[Issue #196](https://github.com/heroui-inc/heroui-native/issues/196)**:修复 TextField 多行输入行为,与 React Native `TextInput` 多行语义一致。
* **[Issue #199](https://github.com/heroui-inc/heroui-native/issues/199)**:修复 TextField Input 内占位符文字位置问题。
**相关 PR:**
* [#201](https://github.com/heroui-inc/heroui-native/pull/201)
* [#202](https://github.com/heroui-inc/heroui-native/pull/202)
* [#193](https://github.com/heroui-inc/heroui-native/pull/193)
## 链接
* [组件文档](../components)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-native)
## 贡献者
感谢所有为本版本做出贡献的朋友!
# Beta 12
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases/beta-12
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/beta-12.mdx
> InputOTP、Label、Description 组件,Popover 关闭修复,受控状态改进,圆角修复,以及变体样式属性支持
2026 年 1 月 13 日
Beta 12 新增三个核心表单组件——InputOTP、Label、Description——强化 React Native 中的表单搭建能力。另含 Popover 关闭行为、弹层受控状态、圆角配置等关键修复,并为多个表单组件增加变体样式属性支持,使表单组件更稳健、样式与行为更易控。
## 安装
升级到最新版本:
```bash
npm i heroui-native@beta
```
```bash
pnpm add heroui-native@beta
```
```bash
yarn add heroui-native@beta
```
```bash
bun add heroui-native@beta
```
**使用 AI 助手?** 直接提示「Hey Cursor,把 HeroUI Native 升级到最新版本」,助手会自动比对版本并完成必要修改。了解更多请参见 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)。
## 抢先体验
通过预览应用在真机上体验 Beta 12!你可以探索 InputOTP、Label、Description,以及 Popover 修复、受控状态改进、圆角修复与变体样式属性支持。
### 环境要求
请确保手机已安装最新版本的 [Expo Go](https://expo.dev/go)。
### 如何访问
**方式一:扫描二维码**
使用手机相机或 Expo Go 应用扫描:
> **Android 用户请注意:** 若使用系统相机或其他扫码应用会跳转到浏览器并出现 404,请先打开 Expo Go,使用其内置扫码功能扫描。
**方式二:点击链接**
**[📱 在 Expo Go 中打开演示应用](https://link.heroui.com/native-demo)**
若设备已安装 Expo Go,将自动在其中打开应用。
## 更新亮点
### 新组件
本版本新增 **3** 个核心表单组件:
* **[InputOTP](/docs/native/components/input-otp)**:一次性密码输入,独立字符格、动画与校验支持。
* **[Label](/docs/native/components/label)**:表单与界面元素标签文本,支持必填标记与校验态。
* **[Description](/docs/native/components/description)**:无障碍说明与辅助文案,用于表单等场景。
#### InputOTP
InputOTP 为双因素认证、验证码、PIN 等场景提供完整方案:独立字符格与流畅动画、可自定义分组与分隔符、全面校验支持。
**特性:**
* 独立字符格、流畅动画与光标指示
* 灵活分组与分隔符
* 基于模式的输入限制(数字、字符或自定义正则)
* 受控/非受控值管理
* 校验态与视觉反馈
* 每位占位符可配置
* 粘贴支持与转换函数
* 完整无障碍支持
**用法:**
```tsx
import { InputOTP, Label, Description } from "heroui-native";
export function Example() {
return (
<>
Verify account
console.log(code)}>
We've sent a code to your email
>
);
}
```
完整文档与示例见 [InputOTP 组件页](/docs/native/components/input-otp)。
**相关 PR:** [#214](https://github.com/heroui-inc/heroui-native/pull/214)
#### Label
Label 为表单字段提供无障碍标签,内置必填星号、校验态与禁用态,并随字段校验状态自适应样式。
**特性:**
* 必填字段自动显示星号
* 非法态样式
* 禁用态支持
* 复合结构便于自定义布局
* 通过 nativeID 关联的完整无障碍支持
* 支持 `className`、`classNames`、`styles` 定制样式
**用法:**
```tsx
import { Label, TextField } from "heroui-native";
export function Example() {
return (
Password
);
}
```
完整文档与示例见 [Label 组件页](/docs/native/components/label)。
**相关 PR:** [#214](https://github.com/heroui-inc/heroui-native/pull/214)
#### Description
Description 为表单字段提供无障碍辅助说明,默认弱化样式,并可通过 nativeID 与字段关联以支持读屏。
**特性:**
* 适合辅助文案的弱化文本样式
* 通过 nativeID 与 `aria-describedby` 关联无障碍
* 与表单组件无缝集成
* 支持自定义样式
**用法:**
```tsx
import { Description, TextField } from "heroui-native";
export function Example() {
return (
Email address
We'll never share your email with anyone else.
);
}
```
完整文档与示例见 [Description 组件页](/docs/native/components/description)。
**相关 PR:** [#214](https://github.com/heroui-inc/heroui-native/pull/214)
## 组件改进
### Popover 通过 ref 关闭修复
[Popover](/docs/native/components/popover) 已修复通过 ref 程序化关闭时的行为。
**改进:**
* 基于 ref 的关闭方法现能正确触发关闭动画
* ref 调用与组件内部状态的同步改进
* 程序化关闭更可靠
确保调用 `popoverRef.current?.close()` 时能可靠关闭并正确管理状态与动画。
**相关 PR:** [#207](https://github.com/heroui-inc/heroui-native/pull/207)
### 弹层受控状态修复
Dialog、Bottom Sheet、Popover 等弹层组件已修复通过 `isOpen` 的受控状态。
**改进:**
* 受控状态同步修复
* 外部状态变更的处理改进
* 受控模式下行为更可预期
**相关 PR:** [#215](https://github.com/heroui-inc/heroui-native/pull/215)
### Button、Chip、Tabs 圆角修复
[Button](/docs/native/components/button)、[Chip](/docs/native/components/chip)、[Tabs](/docs/native/components/tabs) 已修复对全局圆角配置的尊重。
**改进:**
* Button 全局圆角应用修复
* Chip 圆角应用修复
* Tabs 圆角应用修复
* 使用全局主题配置的组件间一致性提升
**相关 PR:** [#218](https://github.com/heroui-inc/heroui-native/pull/218)
### TextField.Input 属性精简
[TextField](/docs/native/components/text-field) 的 Input 子组件已移除 `animation` 与 `isAnimatedStyleActive`。
**变更:**
* 自 TextField.Input 移除 `animation`
* 自 TextField.Input 移除 `isAnimatedStyleActive`
* API 简化,更易维护
动画行为现由组件内部统一处理,无需手动配置动画属性。
**相关 PR:** [#220](https://github.com/heroui-inc/heroui-native/pull/220)
## API 增强
### HeroUINativeProvider 的 devInfo 配置
`HeroUINativeProvider` 现支持 `devInfo` 配置项,便于开发与调试。
**新能力:**
```tsx
import { HeroUINativeProvider } from "heroui-native";
export function App() {
return (
{/* 应用内容 */}
);
}
```
**相关 PR:** [#217](https://github.com/heroui-inc/heroui-native/pull/217)
### 变体样式属性支持
[Checkbox](/docs/native/components/checkbox)、[Radio](/docs/native/components/radio)、[TextField](/docs/native/components/text-field)、[InputOTP](/docs/native/components/input-otp) 现支持通过 `variant` 样式属性更便捷地覆盖变体样式。
**新能力:**
```tsx
import { Checkbox, Radio, TextField, InputOTP } from "heroui-native";
Option 1
Option 2
```
除组件 `variant` 属性外,也可通过 style 中的变体信息灵活调整外观。
**相关 PR:** [#220](https://github.com/heroui-inc/heroui-native/pull/220)
## 样式修复
### 圆角配置
修复全局圆角未正确应用到部分组件的问题。
**修复:**
* Button 未尊重全局圆角
* Chip 圆角应用
* Tabs 圆角应用
### 样式优化
* **圆角一致性**:Button、Chip、Tabs 的圆角应用更一致
* **主题配置**:主题传播增强,组件更一致地尊重全局设置
## 问题修复
本版本包含以下修复:
* **[Issue #93](https://github.com/heroui-inc/heroui-native/issues/93)**:修复在 Unwind 场景下 Button 未应用全局圆角的问题,现与主题全局圆角一致。
* **[Issue #213](https://github.com/heroui-inc/heroui-native/issues/213)**:修复 Select 受控模式(`isOpen`)不生效的问题;提供 `isOpen` 时可在外部可靠管理开闭。
**相关 PR:**
* [#218](https://github.com/heroui-inc/heroui-native/pull/218)
* [#215](https://github.com/heroui-inc/heroui-native/pull/215)
## 链接
* [组件文档](../components)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-native)
## 贡献者
感谢所有为本版本做出贡献的朋友!
# Beta 13
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases/beta-13
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/beta-13.mdx
> TextArea 组件、Button outline 变体、Tabs 改进、表单原语拆分、弹层动画重构、样式类名导出与关键问题修复
2026 年 2 月 3 日
Beta 13 引入多行输入组件 TextArea、Button outline 变体,并为所有组件导出样式类名。本版本显著改进 Tabs(动画与变体命名更清晰)、将表单相关能力拆为独立原语、重构弹层动画系统以提升一致性与 Android 兼容性,并修复中文输入、主题色计算、Uniwind Pro 兼容、Bottom Sheet 打开与摇树(tree-shaking)等关键问题,整体提升开发者体验与组件可靠性。
## 安装
升级到最新版本:
```bash
npm i heroui-native@beta
```
```bash
pnpm add heroui-native@beta
```
```bash
yarn add heroui-native@beta
```
```bash
bun add heroui-native@beta
```
**使用 AI 助手?** 直接提示「Hey Cursor,把 HeroUI Native 升级到最新版本」,助手会自动比对版本并完成必要修改。了解更多请参见 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)。
## 抢先体验
通过预览应用在真机上体验 Beta 13!你可以探索 TextArea、CloseButton、Button outline、Tabs 改进、细粒度导出与各项修复。
### 环境要求
请确保手机已安装最新版本的 [Expo Go](https://expo.dev/go)。
### 如何访问
**方式一:扫描二维码**
使用手机相机或 Expo Go 应用扫描:
> **Android 用户请注意:** 若使用系统相机或其他扫码应用会跳转到浏览器并出现 404,请先打开 Expo Go,使用其内置扫码功能扫描。
**方式二:点击链接**
**[📱 在 Expo Go 中打开演示应用](https://link.heroui.com/native-demo)**
若设备已安装 Expo Go,将自动在其中打开应用。
## 更新亮点
### 新组件
本版本新增 **3** 个重要组件:
* **[TextArea](/docs/native/components/text-area)**:多行文本输入,带样式边框与背景,适用于较长内容。
* **[Input](/docs/native/components/input)**:单行文本输入,带样式边框与背景;现作为独立于 TextField 的组件提供(此前仅 `TextField.Input`)。
* **[CloseButton](/docs/native/components/close-button)**:可复用关闭按钮,用于 Dialog、Modal 等遮罩场景,样式在各遮罩间一致。
#### TextArea
TextArea 面向评论、消息、描述与较长表单字段等多行场景;可与 TextField 组合成完整表单结构,支持校验态与多种视觉变体。
**特性:**
* 多行输入,行数可配置
* 与 TextField 无缝组合
* 校验态与视觉反馈
* primary / secondary 等变体
* 禁用与只读
* `className` 与 `styles` 定制
* 完整无障碍支持
**用法:**
```tsx
import { Description, Label, TextArea, TextField } from "heroui-native";
export function Example() {
return (
Message
Please provide as much detail as possible.
);
}
```
完整文档与示例见 [TextArea 组件页](/docs/native/components/text-area)。
**相关 PR:** [#254](https://github.com/heroui-inc/heroui-native/pull/254)
#### Input
Input 现可作为独立组件使用,提供带边框与背景的单行输入。此前仅能通过 `TextField.Input` 使用;现可单独使用,或与 TextField、ControlField 等组合。
**特性:**
* 单行输入,带样式边框与背景
* 独立使用或与表单组件组合
* 校验态与视觉反馈
* primary / secondary 变体
* 禁用与只读
* `className` 与 `styles`
* 完整无障碍支持
**用法:**
```tsx
import { Description, Input, Label, TextField } from "heroui-native";
export function Example() {
return (
Email
We'll never share your email.
);
}
```
完整文档与示例见 [Input 组件页](/docs/native/components/input)。
**相关 PR:** [#247](https://github.com/heroui-inc/heroui-native/pull/247)
#### CloseButton
CloseButton 为关闭 Dialog、Modal、Popover 等遮罩提供统一实现:跨遮罩一致样式、可配置图标属性,并与 Dialog、Popover、Select、Bottom Sheet 等集成。
**特性:**
* 各遮罩间关闭按钮样式一致
* 图标尺寸与颜色可配置
* 支持自定义子节点替换默认图标
* 禁用态
* 与 Dialog、Popover、Select、Bottom Sheet 集成
* 默认样式针对遮罩场景优化
**用法:**
```tsx
import { CloseButton } from "heroui-native";
// 独立使用
// 作为 Dialog、Popover、Select、Bottom Sheet 的一部分
```
完整文档与示例见 [CloseButton 组件页](/docs/native/components/close-button)。
**相关 PR:** [#237](https://github.com/heroui-inc/heroui-native/pull/237)
### 新子组件
#### Tabs.Separator
Tabs 新增 `Separator` 子组件,在触发器之间提供随当前选项卡变化的显隐动画,便于做视觉分隔。
**特性:**
* 随活动选项卡变化的显隐过渡
* `betweenValues` 控制显示区间
* 动画时长与不透明度可配置
* 可设为始终可见的静态分隔
**用法:**
```tsx
import { Tabs } from "heroui-native";
General
Notifications
```
**相关 PR:** [#228](https://github.com/heroui-inc/heroui-native/pull/228)
## 组件改进
### Button outline 变体
[Button](/docs/native/components/button) 新增 `outline` 变体:透明背景 + 边框,丰富按钮视觉层次。
**改进:**
* 新增 `outline` 有边框样式
* 与其他 Button 变体风格一致
* outline 的悬停与聚焦态正确
* 与既有 Button API 无缝衔接
**用法:**
```tsx
import { Button } from "heroui-native";
Outline Button
```
**相关 PR:** [#235](https://github.com/heroui-inc/heroui-native/pull/235)
### Tabs 指示条动画重构
[Tabs](/docs/native/components/tabs) 指示条动画由宽高动画改为 `translateX` 变换,过渡更顺滑、性能更好。
**改进:**
* 指示条迁移到 `translateX`
* 动画性能与流畅度提升
* 切换时视觉更一致
* 减少动画期间的布局重算
**相关 PR:** [#227](https://github.com/heroui-inc/heroui-native/pull/227)
### Popover 箭头尺寸与视觉衔接
[Popover](/docs/native/components/popover) 改进箭头尺寸及与内容的视觉衔接。
**改进:**
* 箭头尺寸相对内容更合理
* 箭头与弹层连接更自然
* 对齐与间距优化
**相关 PR:** [#243](https://github.com/heroui-inc/heroui-native/pull/243)
### 表单组件拆分为原语
表单相关能力拆为独立原语,组合更灵活、职责更清晰。
**改进:**
* 表单能力原子化
* 复用性与组合性提升
* 关注点分离更好
* 自定义表单布局更自由
**相关 PR:** [#247](https://github.com/heroui-inc/heroui-native/pull/247)
### Input Android 阴影修复
[Input](/docs/native/components/input) 为 Android 增加平台相关阴影,跨端视觉更一致。
**改进:**
* Android 平台阴影
* iOS / Android 观感对齐
* Android 上层次(elevation)观感改善
**相关 PR:** [#248](https://github.com/heroui-inc/heroui-native/pull/248)
### 弹层动画系统重构
Popover、Select、Dialog、BottomSheet 的动画系统已重构:统一进出场逻辑、遮罩组合与内容动画,标准化各弹层行为并修复 Android 指针事件问题。
**改进:**
* Dialog 等呈现统一使用 FadeInDown / FadeOutDown 等进出场
* 遮罩动画钩子同时支持基于 progress 与进出场两类动画
* 遮罩组合更多使用 Dialog.Overlay、Popover.Overlay,减少单纯 Pressable 包裹
* 修复影响弹层交互的 Android pointer events 问题
* 示例中显式写出 `presentation`(popover、dialog、bottom-sheet)
* 动画 API 简化,更易维护、跨组件更一致
**相关 PR:** [#263](https://github.com/heroui-inc/heroui-native/pull/263)
## API 增强
### 细粒度导出以优化包体
库现为各组件提供细粒度导出路径,可按需 import 以减小包体。
**新能力:**
```tsx
// 细粒度导入——仅需少量组件时推荐
import { HeroUINativeProvider } from "heroui-native/provider";
import { Button } from "heroui-native/button";
import { Card } from "heroui-native/card";
// 总入口导入——会拉取整库,适合大量使用组件时
import { Button, Card } from "heroui-native";
```
细粒度导入适合只用少数组件的场景;从 `heroui-native` 总入口导入会包含完整库,适合全站大量使用。
**可用细粒度路径:**
* `heroui-native/provider` — Provider
* `heroui-native/[component-name]` — 各组件
* `heroui-native/portal` — Portal 工具
* `heroui-native/utils` — 工具函数
* `heroui-native/hooks` — 自定义 Hooks
**重要**:为控制包体,请在整个项目中**一致地**使用细粒度导入。只要存在一处从 `heroui-native` 总入口的导入,摇树优化策略即可能失效。
**相关 PR:** [#233](https://github.com/heroui-inc/heroui-native/pull/233)
### 样式类名导出
所有组件现导出对应样式类名,便于在代码中引用类名或搭建自定义主题方案。
**新能力:**
```tsx
import { buttonClassNames } from "heroui-native";
const customStyles = {
base: buttonClassNames.base,
variant: buttonClassNames.variant,
};
```
**相关 PR:** [#252](https://github.com/heroui-inc/heroui-native/pull/252)
## 样式修复
### 样式优化
* **移除 quaternary 变体**:删除第四级变体并打磨样式以提升一致性
* **多组件样式打磨**:视觉一致性增强
* **阴影与圆角**:跨组件阴影、圆角更统一
* **主题变量整理**:简化变量并减少冗余 `color-mix` 计算
**相关 PR:** [#246](https://github.com/heroui-inc/heroui-native/pull/246)
## ⚠️ 破坏性变更
### Tabs `variant` 重命名
Tabs 的 `variant` 由 `pill` / `line` 改为 `primary` / `secondary`,与其他组件命名更一致。
**迁移:**
```tsx
// 之前
{/* content */}
{/* content */}
// 之后
{/* content */}
{/* content */}
```
**可选项:**
* `"primary"` — 原 `"pill"`
* `"secondary"` — 原 `"line"`
**相关 PR:** [#236](https://github.com/heroui-inc/heroui-native/pull/236)
### Tabs 指示条动画实现变更
[Tabs](/docs/native/components/tabs) 指示条由 `left` 定位改为 `translateX` 变换以利用 GPU。若自定义过指示条动画,需更新配置。
**迁移:**
```tsx
// 之前
{/* content */}
// 之后
{/* content */}
```
**变更摘要:**
* `TabsIndicatorAnimation` 中由 `left` 改为 `translateX`
* 指示条定位基于 `translateX` 变换
* 指示条样式增加 `left-0` 基准类以保持初始位置
* 观感应与此前一致,仅底层实现变化
**相关 PR:** [#227](https://github.com/heroui-inc/heroui-native/pull/227)
### Divider 更名为 Separator
`Divider` 已更名为 `Separator`,命名更统一,并避免与其他「分隔线」实现混淆。
**迁移:**
```tsx
// 之前
import { Divider } from "heroui-native";
// 之后
import { Separator } from "heroui-native";
```
**相关 PR:** [#238](https://github.com/heroui-inc/heroui-native/pull/238)
### 移除 quaternary 变体
[Surface](/docs/native/components/surface) 与 [Card](/docs/native/components/card) 已移除 `quaternary` 变体,简化设计系统。
**迁移:**
```tsx
// 之前
{/* content */}
{/* content */}
// 之后:使用 default、secondary、tertiary 或自定义 className
{/* content */}
{/* content */}
{/* content */}
```
**可用变体:** `"default"`、`"secondary"`、`"tertiary"`
**相关 PR:** [#246](https://github.com/heroui-inc/heroui-native/pull/246)
### 表单原语拆分与重命名
表单拆分过程中若干组件重命名、结构调整,以获得更灵活的组合方式。
**重命名:**
* `FormField` → `ControlField`
* `ErrorView` → `FieldError`
**迁移:**
```tsx
// 之前
import { FormField, ErrorView, TextField } from "heroui-native";
Error message
// 之后
import { ControlField, FieldError, Input, TextField } from "heroui-native";
Error message
```
**移除 TextField.Input:**
请改用独立 `Input`:
```tsx
// 之前
import { TextField } from "heroui-native";
// 之后
import { Input, TextField } from "heroui-native";
```
**组合方式:**
`RadioGroup`、`TextField`、`ControlField` 现直接使用 `Label`、`Description`、`FieldError`:
```tsx
import { ControlField, Description, FieldError, Input, Label, RadioGroup, TextField } from "heroui-native";
Email
We'll never share your email.
Invalid email address
Select option
Option 1
Choose one option
Please select an option
Custom Field
Additional information
Validation error
```
**相关 PR:** [#247](https://github.com/heroui-inc/heroui-native/pull/247)
### CloseButton 与移除 Close 的 asChild
新增可复用 `CloseButton`;Dialog、Popover、Select、BottomSheet 的关闭实现统一基于该组件。各 `*.Close` 已移除 `asChild`。
**迁移:**
```tsx
// 之前
import { Button, Dialog } from "heroui-native";
Cancel
// 之后:用受控 open + 自定义按钮处理关闭
import { Button, Dialog } from "heroui-native";
const [isOpen, setIsOpen] = useState(false);
setIsOpen(false)}>Cancel
```
**变更摘要:**
* 新增 `CloseButton`,默认 `variant="tertiary"`、`size="sm"`、`isIconOnly={true}`
* `Dialog.Close`、`Popover.Close`、`Select.Close`、`BottomSheet.Close` 内部基于 `CloseButton`
* 所有 Close 组件移除 `asChild`
* Close 仍支持 Button 的 `variant`、`size`、`iconProps` 与自定义 `children`
* 使用完全自定义按钮时需自行处理关闭逻辑
**相关 PR:** [#237](https://github.com/heroui-inc/heroui-native/pull/237)
### 弹层动画系统重构(API)
弹层动画重构带来若干需改代码的 API 调整。
**迁移要点:**
* 从 `Dialog.Root` 移除 `closeDelay`、`isDismissKeyboardOnClose`
* 从 `Dialog.Root` 的 `animation` 移除自定义 `entering`/`exiting`(仅保留禁用类开关);自定义进出场请在 `Dialog.Content` 上使用 Keyframe 动画配置
* 从 `Dialog.Content` 移除 `isAnimatedStyleActive`、`onLayout`
* 从 `BottomSheet.Root` 移除 `isDismissKeyboardOnClose`
* `BottomSheet.Overlay` 的 `animation` 不再支持 `entering`/`exiting`
* 所有 `Popover.Content`、`Select.Content` 必须显式传入 `presentation`(由可选改为必填)
* `useBottomSheetAnimation()` 不再返回 `bottomSheetState`;`useDialogAnimation()` 不再返回 `dialogState`
**变更摘要:**
* `Dialog.Root`:移除 `closeDelay`、`isDismissKeyboardOnClose`;`animation` 类型由支持自定义进场的 `DialogRootAnimation` 收窄为仅禁用标志的 `AnimationRootDisableAll`
* `Dialog.Content`:移除 `isAnimatedStyleActive`、`onLayout`
* `BottomSheet.Root`:移除 `isDismissKeyboardOnClose`
* `BottomSheet.Overlay`:`animation` 不再含 `entering`/`exiting`
* `Popover.Content`、`Select.Content`:`presentation` 必填(此前可选,默认 `"popover"`)
* 上述动画钩子返回值精简
**相关 PR:** [#263](https://github.com/heroui-inc/heroui-native/pull/263)
## 问题修复
本版本包含以下修复:
* **[Issue #181](https://github.com/heroui-inc/heroui-native/issues/181)**:修复 TextField 输入中文等多字节字符报错;现正确处理多字节与国际输入,含中日韩等语言。
* **[Issue #219](https://github.com/heroui-inc/heroui-native/issues/219)**:修复 Button `childrenToString()` 在多子节点时返回 `"[object Object]"`;现正确处理 React 元素与复杂子树,避免错误字符串化。
* **[Issue #232](https://github.com/heroui-inc/heroui-native/issues/232)**:修复 HeroUINativeProvider 与新版 Uniwind Pro 不兼容;现可正常配合最新 Uniwind。
* **[Issue #259](https://github.com/heroui-inc/heroui-native/issues/259)**:修复 Bottom Sheet 在快速打开并随即进行手势操作后偶发无法再次打开等问题;进出场动画逻辑重构后已缓解。
* **[Issue #261](https://github.com/heroui-inc/heroui-native/issues/261)**:修复 `@gorhom/bottom-sheet` 无法被摇树剔除的问题;未使用依赖可被更好剔除。
**其他修复:**
* 修复主题计算色在部分场景下数值错误
* 修复 `childrenToString`,避免错误地将 React 元素转为字符串
**相关 PR:**
* [#226](https://github.com/heroui-inc/heroui-native/pull/226)
* [#239](https://github.com/heroui-inc/heroui-native/pull/239)
## 链接
* [组件文档](../components)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-native)
## 贡献者
感谢所有为本版本做出贡献的朋友!
# create-heroui-native-app
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases/create-heroui-native-app
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/create-heroui-native-app.mdx
> 全新的命令行工具,用于一键创建预先配置好的 HeroUI Native + Expo Router 项目 —— Expo SDK 56、React Native 0.85、Uniwind,所有 peer 依赖均已就绪,并提供两套起始模板(单页面与 Tabs)。
2026 年 6 月 4 日
🎉 全新的 CLI —— [`create-heroui-native-app`](https://www.npmjs.com/package/create-heroui-native-app) —— 现已成为启动 HeroUI Native 项目最快捷的方式。一条命令即可生成一个完整的 Expo Router 应用,内置 HeroUI Native、Uniwind、Tailwind CSS、所有必需的 peer 依赖以及 Provider 包装,无需再手动配置 `global.css`、`metro.config.js` 或 `app/_layout.tsx`。
## 快速开始
```bash
npx create-heroui-native-app@latest my-app
```
```bash
pnpm create heroui-native-app@latest my-app
```
```bash
yarn create heroui-native-app my-app
```
```bash
bun create heroui-native-app@latest my-app
```
随后启动开发服务器:
```bash
cd my-app
npm run start
```
到此为止。如需完整流程,请直接阅读[快速开始指南](/docs/native/getting-started/quick-start)。
## 起始模板
CLI 自带两套模板。可在交互式选择器中选择,或通过命令行参数跳过提示:
| 参数 | 模板 | 说明 |
| ------------- | ----------- | ----------------------------------------------- |
| `--expo` | `expo` | 单页面 Expo Router 应用,含 HeroUI `Button` 演示。 |
| `--expo-tabs` | `expo-tabs` | 含两个 Tabs(`Button` + `Card` 演示)的 Expo Router 布局。 |
```bash
npx create-heroui-native-app@latest my-app --expo
npx create-heroui-native-app@latest my-app --expo-tabs --use-pnpm
```
## 你将得到什么
每个生成的项目都已预先配置:
* **Expo SDK 56** + **Expo Router**,并启用类型化路由
* **React 19.2** + **React Native 0.85.2** + Hermes v1(SDK 56 默认启用)
* **HeroUI Native**:在 `app/_layout.tsx` 中由 `HeroUINativeProvider` 与 `GestureHandlerRootView` 包裹
* **Uniwind** + **Tailwind CSS**:通过 `metro.config.js` 与 `global.css` 完成接入
* 所有 HeroUI Native **必需的 peer 依赖**已锁定到兼容版本:`react-native-reanimated`、`react-native-gesture-handler`、`react-native-worklets`、`react-native-safe-area-context`、`react-native-svg`、`tailwind-variants`、`tailwind-merge`
* 内置 `react-native-screens`,让 HeroUI 的遮罩组件(`Dialog`、`Menu`、`Popover`、`Select`、`BottomSheet`、`Toast`)开箱即用
* `@expo/metro-runtime`(SDK 56 上 Expo Router 需要的 peer 依赖)
* 仅使用 `babel-preset-expo` 的 `babel.config.js`(worklets 已由该 preset 处理)
* 启用 `strict: true` 与 `@/*` 路径别名的 **TypeScript** 配置
## CLI 参考
```text
create-heroui-native-app [project-name] [options]
```
| 选项 | 说明 |
| ------------------------------------------------------- | ---------------------------------- |
| `[project-name]` | 要创建的目录名。未提供时会进行交互提示。必须是合法的 npm 包名。 |
| `--expo` | 使用单页面 Expo 模板。 |
| `--expo-tabs` | 使用 Expo + Tabs 模板。 |
| `--template ` | 指定模板 id(`expo` 或 `expo-tabs`)。 |
| `--use-npm` / `--use-yarn` / `--use-pnpm` / `--use-bun` | 强制使用指定包管理器(默认自动检测)。 |
| `--skip-install` | 跳过依赖安装步骤。 |
| `--skip-git` | 不初始化 git 仓库。 |
| `-h`, `--help` | 打印用法并退出。 |
### 示例
```bash
# 完全交互式 —— 同时提示项目名与模板
npx create-heroui-native-app@latest
# 已指定项目名,仍展示模板选择器
npx create-heroui-native-app@latest my-app
# 完全非交互式
npx create-heroui-native-app@latest my-app --expo
# 使用 Tabs 模板,并通过 pnpm 安装依赖
npx create-heroui-native-app@latest my-app --expo-tabs --use-pnpm
# 仅生成项目,不安装依赖、不初始化 git
npx create-heroui-native-app@latest my-app --expo --skip-install --skip-git
```
## 系统要求
* **Node.js 20.19.4+**(Expo SDK 56 / React Native 0.85 的要求)
* 构建原生 iOS 时需要 **iOS 16.4+**(Expo SDK 56 的部署目标)
* macOS、Linux 或 Windows
**已经有应用了?** 本 CLI 仅用于创建新项目。若要将 HeroUI Native 添加到既有的 React Native 或 Expo 应用,请参阅[快速开始中的方案 2](/docs/native/getting-started/quick-start#option-2-add-to-an-existing-project)。
## 链接
* [快速开始指南](/docs/native/getting-started/quick-start)
* [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)
# 所有版本
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/index.mdx
> HeroUI Native 的全部更新与变更,包括新功能、问题修复与破坏性变更。
**使用 AI 助手?** 直接提示「Hey Cursor,把 HeroUI Native 升级到最新版本」,助手会自动比对版本并完成必要修改。了解更多请参见 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)。
## 最新版本
### `create-heroui-native-app`
**2026 年 6 月**
🎉 全新的 CLI,用于一键创建 HeroUI Native 项目。`npx create-heroui-native-app@latest my-app` 即可生成一个完整的 Expo Router 应用,内置 HeroUI Native、Uniwind、Tailwind CSS、所有必需的 peer 依赖以及 Provider 包装 —— 还可在单页面与 Tabs 两套起始模板中任选其一。
[阅读完整更新说明 →](/docs/native/releases/create-heroui-native-app)
### v1.0.4
**2026 年 5 月**
本补丁版本将 `Text` 排版组件重命名为 `Typography`(保留 `Text` 的弃用导出以兼容旧代码),调整 `Alert`、`Avatar`、`Button`、`Chip`、`Toast` 等组件的 soft 前景色主题令牌,并新增可选的 `heroui-native/styles/vibrant` 鲜亮配色;为 `Menu`、`Popover`、`Select` 补充 iOS 原生模态偏移的处理说明;将示例应用升级至 Expo 56 / React Native 0.85,并将 `@gorhom/bottom-sheet` 对等依赖升至 `^5.2.9`。
[阅读完整更新说明 →](/docs/native/releases/v1-0-4)
### v1.0.3
**2026 年 5 月**
本补丁版本引入全新的 `Text` 排版组件,提供 `Heading`、`Paragraph`、`Code` 子组件;修复 `ScrollShadow` 对反向列表的支持以及 `Tabs` 指示器在 RTL 布局下的对齐;并在使用自定义 children 时统一 `Select.TriggerIndicator` 的动画。同时将 `Avatar` 的 `alt` 属性改为可选,微调 `Button`、`Chip`、`Input` 的样式,并修正 `TextField` 与 `SearchField` 的内部内边距行为。
[阅读完整更新说明 →](/docs/native/releases/v1-0-3)
### v1.0.2
**2026 年 4 月**
本补丁版本为 PressableFeedback 与 Surface 引入 `asChild` 插槽模式;为所有基于 Portal 的遮罩增加 VoiceOver 模态包容支持;修复 Android 上 Button outline 变体的样式问题;并微调 Input 与 Select 的视觉效果。
[阅读完整更新说明 →](/docs/native/releases/v1-0-2)
### v1.0.1
**2026 年 4 月**
本补丁版本修复 Toast 提供程序中 `total` SharedValue 与实际 Toast 数量不同步的竞态;将七个组件的禁用态样式改为使用原生 `disabled:` 修饰符;并为主题系统新增供遮罩组件使用的 `--backdrop` 变量。
[阅读完整更新说明 →](/docs/native/releases/v1-0-1)
### v1.0.0
**2026 年 3 月**
🎉 HeroUI Native 迎来首个稳定版本,从 beta 与候选发布阶段毕业。本里程碑包含全新 LinkButton 组件、子菜单冲突处理、可选的 `@gorhom/bottom-sheet` 对等依赖,以及更强的 `useThemeColor` 类型安全。
[阅读完整更新说明 →](/docs/native/releases/v1-0-0)
### RC 4
**2026 年 3 月**
本版本引入用于嵌套可展开菜单的 SubMenu 复合组件(带动画展开/收起);以基于插槽的样式与 `textProps` 透传重构 Slider Output;并修复快速连按时 PressableFeedback 水波纹动画闪烁。
[阅读完整更新说明 →](/docs/native/releases/rc-4)
### RC 3
**2026 年 2 月**
本版本新增 TagGroup、Menu、InputGroup 三个组件;为所有基于 Bottom Sheet 的遮罩增加 Android 实体返回键支持;并通过关键的 `combineStyles` 修复保留 Reanimated 动画样式绑定,实现 Expo 55 兼容。
[阅读完整更新说明 →](/docs/native/releases/rc-3)
### RC 2
**2026 年 2 月**
本版本新增 SearchField、ListGroup、Slider 三个组件;为 Select 增加由类型安全泛型支撑的多选模式;将 Button 反馈 API 重构为统一的 `feedbackVariant` + `animation`。放宽对等依赖约束以更好兼容 Expo SDK 55,并修复若干 Select 与 Avatar 问题。
[阅读完整更新说明 →](/docs/native/releases/rc-2)
### RC 1
**2026 年 2 月**
本版本引入含五种状态变体与无障碍原语的 Alert 复合组件;将 Radio 抽为可双模式运行的独立组件;新增带动画的 Select.TriggerIndicator。另提供用于精简包体的 HeroUINativeProviderRaw、用于 iOS 调试的 `disableFullWindowOverlay`、六个组件统一的 `styles` 属性,以及以各主题显式定义取代计算型的 surface 主题变量重构。
[阅读完整更新说明 →](/docs/native/releases/rc-1)
### Beta 13
**2026 年 2 月**
本版本引入多行输入组件 TextArea、Button outline 变体,为所有组件导出样式类名,并提供可复用的 CloseButton。同时重构 Tabs(动画与变体命名改进)、将表单相关能力拆为独立原语,并增加细粒度导出以优化包体。另含主题色与组件字符串化等关键修复。
[阅读完整更新说明 →](/docs/native/releases/beta-13)
### Beta 12
**2026 年 1 月**
本版本新增 InputOTP、Label、Description 三个核心表单组件,强化 React Native 中的表单搭建能力。另含 Popover 关闭行为、弹层受控状态、圆角配置等关键修复,并为多个表单组件增加变体样式属性支持。
[阅读完整更新说明 →](/docs/native/releases/beta-12)
### Beta 11
**2026 年 1 月**
本版本通过 Bottom Sheet 关闭协同改进、Dialog 侧滑关闭手势修复、TextField 样式优化,以及面向高级 Portal 挂载场景的 PortalHost 导出,提升组件可靠性与开发者体验,使交互更顺滑、自定义布局更灵活。
[阅读完整更新说明 →](/docs/native/releases/beta-11)
### Beta 10
**2025 年 12 月**
本版本引入新的 [Bottom Sheet](/docs/native/components/bottom-sheet) 组件;重构 [PressableFeedback](/docs/native/components/pressable-feedback) 并改进 API;扩展动画 API 以支持 State Prop;增强 `use-theme-color` 钩子以支持多色选取;并包含若干问题修复与文档改进。
[阅读完整更新说明 →](/docs/native/releases/beta-10)
## 发布周期
HeroUI Native 遵循常规发布周期:
* **稳定版**:v1.0.0 已于 2026 年第一季度发布
* **补丁版**:按需发布问题修复与小幅改进
## 参与贡献
发现问题或想参与贡献?请访问我们的 [GitHub 仓库](https://github.com/heroui-inc/heroui-native)。
# RC 1
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases/rc-1
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/rc-1.mdx
> Alert 组件、独立 Radio 组件、Select TriggerIndicator、HeroUINativeProviderRaw、disableFullWindowOverlay、styles 属性扩展、主题 surface 重构
2026 年 2 月 12 日
RC 1 是 HeroUI Native 的首个候选发布(Release Candidate),表明库已接近生产可用。本版本引入以无障碍为先、带状态变体的 Alert 复合组件;将 Radio 抽为可双模式运行的独立组件;新增带动画的 Select.TriggerIndicator 子组件。还提供轻量 `HeroUINativeProviderRaw` 以优化包体、用于 iOS 调试的 `disableFullWindowOverlay`、六个组件统一的 `styles` 插槽式样式,以及用主题显式变量替代计算型 surface 色的主题重构。另含 BottomSheet 内 InputOTP、Toast 文字裁切与元素检查器兼容等关键修复。
## 安装
升级到最新版本:
```bash
npm i heroui-native
```
```bash
pnpm add heroui-native
```
```bash
yarn add heroui-native
```
```bash
bun add heroui-native
```
**使用 AI 助手?** 直接提示「Hey Cursor,把 HeroUI Native 升级到最新版本」,助手会自动比对版本并完成必要修改。了解更多请参见 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)。
## 抢先体验
通过预览应用在真机上体验 RC 1 的全部改进!你可以探索新的 Alert 与 Radio、Select TriggerIndicator,以及各项修复。
### 环境要求
请确保手机已安装最新版本的 [Expo Go](https://expo.dev/go)。
### 如何访问
**方式一:扫描二维码**
使用手机相机或 Expo Go 应用扫描:
> **Android 用户请注意:** 若使用系统相机或其他扫码应用会跳转到浏览器并出现 404,请先打开 Expo Go,使用其内置扫码功能扫描。
**方式二:点击链接**
**[📱 在 Expo Go 中打开演示应用](https://link.heroui.com/native-demo)**
若设备已安装 Expo Go,将自动在其中打开应用。
## 更新亮点
### 新组件
本版本新增 **1** 个组件:
* **[Alert](/docs/native/components/alert)**:无障碍告警组件,五种状态变体,复合子组件灵活组合内容。
#### Alert
Alert 提供无障碍告警展示,内置五种状态变体:default、accent、success、warning、danger。遵循复合组件模式,含 `Alert.Indicator`、`Alert.Content`、`Alert.Title`、`Alert.Description`,便于布局与定制。原语层自动提供 `role="alert"`、`aria-labelledby` 与 `aria-describedby` 关联。
**特性:**
* 五种状态变体:default、accent、success、warning、danger
* 默认 SVG 状态图标,通过 `useStatusColor` 随主题着色
* 复合架构:Indicator、Content、Title、Description
* 支持自定义指示器(如替换为 Spinner)
* 无障碍原语:`role="alert"`、`aria-labelledby`、`aria-describedby`
* 所有子组件支持 `asChild` 插槽
* 所有部件支持 ref 转发与 `className`
**用法:**
```tsx
import { Alert } from "heroui-native";
export function Example() {
return (
Payment successful
Your payment has been processed successfully.
);
}
```
完整文档与示例见 [Alert 组件页](/docs/native/components/alert)。
**相关 PR:** [#284](https://github.com/heroui-inc/heroui-native/pull/284)
### 新子组件
#### Select.TriggerIndicator
[Select](/docs/native/components/select) 新增 `TriggerIndicator` 子组件,显示带动画的 V 形图标以表示开/关状态,开闭时以 Reanimated 弹簧物理旋转。
**特性:**
* 开闭过渡时旋转的 V 形动画
* `animation` 可配置旋转值与弹簧参数
* `iconProps` 自定义尺寸、颜色
* 支持自定义子节点替换默认 V 形
* 与 Select 开闭状态自动同步
**用法:**
```tsx
import { Select } from "heroui-native";
{/* Select items */}
```
**相关 PR:** [#274](https://github.com/heroui-inc/heroui-native/pull/274)
## 组件改进
### Toast 样式与堆叠重构
[Toast](/docs/native/components/toast) 样式由边框模拟内边距改为真实 `p-4` 与上下占位视图,改善多 Toast 堆叠时的内容可见性。
**改进:**
* 以 `p-4` 取代 `border-[16px]` 内边距变通写法
* 新增 `useVerticalPlaceholderStyles` 用于占位视图样式
* 顶部与底部绝对定位占位 View,避免堆叠时内容露出
* 阴影系统改用 `shadow-overlay` 令牌
* 统一各主题(alpha、mint、sky)的遮罩阴影,降低不透明度
确保不同高度 Toast 堆叠时内容仍被正确遮挡,样式更易维护、可预期。
**相关 PR:** [#229](https://github.com/heroui-inc/heroui-native/pull/229)
### Dialog 遮罩手势关闭动画时序
[Dialog](/docs/native/components/dialog) 在手势关闭时弹层动画时序已修复:进度值按延迟正确排队,确保关闭动画播完再重置。
**改进:**
* 手势关闭时进度在 300ms 延迟后过渡到 2
* 350ms 后进度重置为 0,保证动画完成
* 移除 `isOpen` 为 false 时立即 `progress.set(2)` 的调用
* 侧滑关闭时关闭动画可正常播放
**相关 PR:** [#277](https://github.com/heroui-inc/heroui-native/pull/277)
### 主题 Surface 变量重构
主题系统以主题文件中的显式 surface 变量取代计算色,跨主题更可控、更一致。
**改进:**
* `surface-secondary`、`surface-tertiary`(及对应前景)在各主题(alpha、lavender、mint、sky、variables.css)中显式定义
* 基础主题使用 `var(--surface-secondary)`、`var(--surface-tertiary)`,不再用 `color-mix` 计算
* 从 theme.css 移除 `on-surface`、`on-surface-secondary`、`on-surface-tertiary` 调色板
* 主题文档更新变量结构与示例
主题作者可直接控制 surface 色值,不再依赖 `color-mix`,各主题 surface 表现更可预期。
**相关 PR:** [#281](https://github.com/heroui-inc/heroui-native/pull/281)
## API 增强
### 多组件统一 `styles` 属性
六个组件现支持统一的 `styles` 插槽式样式 API。
**涉及组件:**
* **Accordion**:`container`、`separator` 插槽
* **AvatarFallback**:`container`、`text` 插槽
* **FieldError**:`container`、`text` 插槽
* **Label**:`text`、`asterisk` 插槽(并修复 `style` 处理)
* **PressableFeedback Ripple**:`container`、`ripple` 插槽(取代 `containerStyle` 与 `rippleStyle`)
* **SelectContentDialog**:`wrapper`、`content` 插槽
**新能力:**
```tsx
import { Accordion, Label } from "heroui-native";
// 对指定插槽应用样式
Username
{/* Accordion items */}
```
变更保持与既有 `style` 的向后兼容,二者同时提供时会正确合并。
**相关 PR:** [#271](https://github.com/heroui-inc/heroui-native/pull/271)
### `disableFullWindowOverlay` 属性
基于 Portal 的组件现支持 `disableFullWindowOverlay`,便于在 iOS 开发时使用 React Native 元素检查器。
**涉及组件:**
* `BottomSheet.Portal`
* `Dialog.Portal`
* `Popover.Portal`
* `Select.Portal`
* `ToastProvider`
**新能力:**
```tsx
import { Dialog } from "heroui-native";
// 在 iOS 上启用元素检查器
{/* content */}
```
iOS 上 `FullWindowOverlay` 使用独立原生窗口,会阻挡元素检查器。将 `disableFullWindowOverlay` 设为 `true` 时内容绘于主窗口,开发期可检查元素;代价是遮罩不再叠在原生模态或键盘之上。Android 上该属性无效果。
使用 `HeroUINativeProvider` 时,Toast 通过 `config.toast` 传入该属性。
**相关 PR:** [#283](https://github.com/heroui-inc/heroui-native/pull/283)
### HeroUINativeProviderRaw
新增轻量提供者变体 `HeroUINativeProviderRaw`,不包含 `ToastProvider` 与 `PortalHost`,由使用方完全控制打包依赖。
**新能力:**
```tsx
import { HeroUINativeProviderRaw } from "heroui-native/provider-raw";
// 无 Toast 与 Portal 的轻量提供者
export function App() {
return (
{/* Your app content */}
);
}
```
`react-native-screens`、`@gorhom/bottom-sheet`、`react-native-svg` 由此可作为完全可选的对等依赖。Raw 提供者仅含 `SafeAreaListener`、`GlobalAnimationSettingsProvider`、`TextComponentProvider`。需要 Toast 或 Portal 时可自行组合。
**相关 PR:** [#285](https://github.com/heroui-inc/heroui-native/pull/285)
### Select.Trigger 的 `variant` 属性
`Select.Trigger` 现支持 `variant`:`"default"` 与 `"unstyled"`,便于与 Button 等自定义触发器组合。
**新能力:**
```tsx
import { Button, Select } from "heroui-native";
// 默认变体(预置样式触发器)
// 无样式变体,用于自定义组合
```
**相关 PR:** [#274](https://github.com/heroui-inc/heroui-native/pull/274)
### ControlField 的 Radio 变体
[ControlField](/docs/native/components/control-field) 的 `ControlField.Indicator` 现支持 `"radio"` 变体,与既有 `"switch"`、`"checkbox"` 并列,渲染独立 Radio 组件。
**新能力:**
```tsx
import { ControlField } from "heroui-native";
Radio option
```
**相关 PR:** [#286](https://github.com/heroui-inc/heroui-native/pull/286)
## ⚠️ 破坏性变更
### PressableFeedback Ripple:统一 `styles` 属性
PressableFeedback Ripple 的 `containerStyle` 与 `rippleStyle` 已合并为统一 `styles`。
**迁移:**
将所有单独样式属性改为 `styles`:
```tsx
// 之前
// 之后
```
**相关 PR:** [#271](https://github.com/heroui-inc/heroui-native/pull/271)
### Select.Trigger 默认样式
`Select.Trigger` 默认 `variant="default"`,会应用容器样式(`flex-row items-center justify-between h-12 px-4 rounded-2xl bg-surface shadow-surface`)。若此前为自定义样式触发器,需加 `variant="unstyled"` 以免套用默认样式。
**迁移:**
```tsx
// 之前(自定义样式触发器)
{/* content */}
// 之后(加 variant="unstyled" 保留自定义)
{/* content */}
```
**相关 PR:** [#274](https://github.com/heroui-inc/heroui-native/pull/274)
### Surface 主题变量结构调整
基础主题已移除 `on-surface`、`on-surface-secondary`、`on-surface-tertiary` 及其 hover/focus 变体 CSS 变量。secondary/tertiary surface 色现于各主题文件中显式定义。
**迁移:**
若自定义样式引用上述变量,请改为主题中定义的对应 surface 前景变量。
```css
/* 之前 */
color: var(--on-surface);
color: var(--on-surface-secondary);
/* 之后 */
color: var(--surface-foreground);
color: var(--surface-secondary-foreground);
```
**相关 PR:** [#281](https://github.com/heroui-inc/heroui-native/pull/281)
### 移除 RadioGroup.Indicator
`RadioGroup.Indicator` 与 `RadioGroup.IndicatorThumb` 已移除,改为独立 `Radio` 组件。相关类型 `RadioGroupIndicatorProps`、`RadioGroupIndicatorThumbProps`、`RadioGroupIndicatorThumbAnimation` 亦不再导出。
**迁移:**
将所有 `RadioGroup.Indicator` / `RadioGroup.IndicatorThumb` 替换为 `Radio`:
```tsx
// 之前
import { RadioGroup } from "heroui-native";
Option 1
// 之后
import { Radio, RadioGroup } from "heroui-native";
Option 1
```
**相关 PR:** [#286](https://github.com/heroui-inc/heroui-native/pull/286)
## 问题修复
本版本包含以下修复:
* **[Issue #229](https://github.com/heroui-inc/heroui-native/issues/229)**:修复 BottomSheet 内 InputOTP 不可用。现可在 BottomSheet 遮罩内正常聚焦与输入,解决此前无法输入 OTP 的问题。
* **[Issue #265](https://github.com/heroui-inc/heroui-native/issues/265)**:修复 Toast 描述首字符被裁切。Toast 样式重构后以真实内边距与占位视图取代边框变通,任意堆叠配置下文字均可完整显示。
* **[Issue #272](https://github.com/heroui-inc/heroui-native/issues/272)**:修复 FullWindowOverlay 在 iOS 上阻挡 React Native 元素检查器。Portal 组件新增 `disableFullWindowOverlay`,开发期可将遮罩内容绘于主窗口以恢复检查器。
**相关 PR:**
* [#229](https://github.com/heroui-inc/heroui-native/pull/229)
* [#283](https://github.com/heroui-inc/heroui-native/pull/283)
## 文档更新
以下文档页面已随本版本更新:
* [Alert](/docs/native/components/alert) — 新组件:用法示例与 API 参考
* [Radio](/docs/native/components/radio) — 独立 Radio 组件文档
* [Radio Group](/docs/native/components/radio-group) — 反映移除 RadioGroup.Indicator 及与 Radio 的集成
* [Control Field](/docs/native/components/control-field) — 新增 radio 变体说明
* [Select](/docs/native/components/select) — TriggerIndicator 子组件与 Trigger variant
* [Toast](/docs/native/components/toast) — 更新样式实现说明
* [Bottom Sheet](/docs/native/components/bottom-sheet) — 补充 disableFullWindowOverlay
* [Dialog](/docs/native/components/dialog) — 补充 disableFullWindowOverlay
* [Popover](/docs/native/components/popover) — 补充 disableFullWindowOverlay
* [Provider](/docs/native/getting-started/provider) — HeroUINativeProviderRaw 与提供者层级
* [Theming](/docs/native/getting-started/theming) — 更新 surface 变量结构与示例
* [Accordion](/docs/native/components/accordion) — 补充 styles 属性
* [Avatar](/docs/native/components/avatar) — 补充 styles 属性
* [Label](/docs/native/components/label) — 补充 styles 属性
* [Field Error](/docs/native/components/field-error) — 补充 styles 属性
## 链接
* [组件文档](../components)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-native)
## 贡献者
感谢所有为本版本做出贡献的朋友!
# RC 2
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases/rc-2
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/rc-2.mdx
> SearchField、ListGroup、Slider 组件,Select 多选模式,Button 反馈 API 重构,放宽对等依赖约束
2026 年 2 月 20 日
RC 2 继续向生产就绪推进:新增 SearchField、ListGroup、Slider 三个组件;Select 支持由类型安全泛型支撑的多选模式;Button 按压反馈 API 重构为统一的 `feedbackVariant` + `animation`;放宽对等依赖约束以更好兼容 Expo SDK 55。另含若干 Select 与 Avatar 的问题修复。
## 安装
升级到最新版本:
```bash
npm i heroui-native
```
```bash
pnpm add heroui-native
```
```bash
yarn add heroui-native
```
```bash
bun add heroui-native
```
**使用 AI 助手?** 直接提示「Hey Cursor,把 HeroUI Native 升级到最新版本」,助手会自动比对版本并完成必要修改。了解更多请参见 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)。
## 抢先体验
通过预览应用在真机上体验 RC 2 的全部改进!你可以探索 SearchField、ListGroup、Slider、Select 多选模式、更新后的 Button 反馈 API,以及各项修复。
### 环境要求
请确保手机已安装最新版本的 [Expo Go](https://expo.dev/go)。
### 如何访问
**方式一:扫描二维码**
使用手机相机或 Expo Go 应用扫描:
> **Android 用户请注意:** 若使用系统相机或其他扫码应用会跳转到浏览器并出现 404,请先打开 Expo Go,使用其内置扫码功能扫描。
**方式二:点击链接**
**[📱 在 Expo Go 中打开演示应用](https://link.heroui.com/native-demo)**
若设备已安装 Expo Go,将自动在其中打开应用。
## 更新亮点
### 新组件
本版本新增 **3** 个组件:
* **[Slider](/docs/native/components/slider)**:支持单值与区间模式、横/纵方向、自定义数字格式与弹簧动画拇指反馈。
* **[ListGroup](/docs/native/components/list-group)**:基于 Surface 的分组列表,可按压项、前后缀槽位,默认右箭头导航指示。
* **[SearchField](/docs/native/components/search-field)**:用于筛选与查询的复合组件,内置搜索图标、可清空输入与空值时自动隐藏清除按钮。
#### Slider
Slider 支持单值与区间(双拇指)模式、横纵布局、通过 `Intl.NumberFormat` 自定义数字格式,以及弹簧动画拇指反馈。原语层独立处理手势与数值逻辑,便于换肤实现复用。
**特性:**
* 复合子组件:`Slider.Output`、`Slider.Track`、`Slider.Fill`、`Slider.Thumb`
* 区间滑块:`defaultValue`/`value` 传入数组,在 `Slider.Track` 上使用渲染函数渲染多个拇指
* `orientation="vertical"` 纵向
* `formatOptions` 接受 `Intl.NumberFormatOptions`(货币、百分比、单位等)
* 基于 gesture-handler 的拖拽与轨道点击定位
* 数值钳制、步进与多拇指支持
* 可通过 `animation` 配置弹簧缩放拇指动画
* 无障碍:每个拇指 `role="slider"`,完整 `accessibilityValue`(min、max、now、text)
* `useSlider` 暴露上下文供高级用法
**用法:**
```tsx
import { Slider } from "heroui-native";
export function BasicSlider() {
return (
);
}
export function RangeSlider() {
return (
{({ thumbs }) => (
<>
{thumbs.map((_, i) => (
))}
>
)}
);
}
```
完整文档与示例见 [Slider 组件页](/docs/native/components/slider)。
**相关 PR:** [#305](https://github.com/heroui-inc/heroui-native/pull/305)
#### ListGroup
ListGroup 在 Surface 容器内渲染分组列表项,适用于设置页、菜单与内容浏览等导航列表模式。每项支持前缀、内容(标题 + 描述)与后缀槽位,默认带右箭头导航指示。
**特性:**
* 基于 Surface 的圆角容器与一致间距
* 复合子组件:`ListGroup.Item`、`ListGroup.ItemPrefix`、`ListGroup.ItemContent`、`ListGroup.ItemTitle`、`ListGroup.ItemDescription`、`ListGroup.ItemSuffix`
* `ItemSuffix` 默认内置 `ChevronRightIcon`
* 可按压项,集成 PressableFeedback
* 槽位可完全自定义图标、徽标等
**用法:**
```tsx
import { ListGroup } from "heroui-native";
export function Example() {
return (
console.log("Profile")}>
Profile
Manage your account
console.log("Settings")}>
Settings
App preferences
);
}
```
完整文档与示例见 [ListGroup 组件页](/docs/native/components/list-group)。
**相关 PR:** [#302](https://github.com/heroui-inc/heroui-native/pull/302)
#### SearchField
SearchField 为搜索与筛选场景提供专用输入,采用与 TextField 相同的复合组件模式:搜索图标、可清空输入、值为空时自动隐藏清除按钮。
**特性:**
* 复合子组件:`SearchField.Group`、`SearchField.SearchIcon`、`SearchField.Input`、`SearchField.ClearButton`
* `ClearButton` 在值为空时自动隐藏,按压清除搜索文本
* `SearchIcon` 支持自定义子节点替换默认放大镜 SVG
* 校验态视觉反馈
* 禁用态支持
* 与 Label、Description、FieldError 无缝集成
**用法:**
```tsx
import { Label, SearchField } from "heroui-native";
export function Example() {
return (
Search
);
}
```
完整文档与示例见 [SearchField 组件页](/docs/native/components/search-field)。
**相关 PR:** [#299](https://github.com/heroui-inc/heroui-native/pull/299)
### Select 多选模式
[Select](/docs/native/components/select) 现通过 `selectionMode` 支持多选。`RootProps` 对 `SelectionMode` 泛型化,TypeScript 按模式解析 `value` 与 `onValueChange`——单选为 `SelectOption`,多选为 `SelectOption[]`。
**特性:**
* `selectionMode="multiple"` 可多选切换;多选模式下 `closeOnPress` 默认 `false`
* 类型安全泛型:`RootProps` 通过 `SelectValueType` 解析 `value`/`onValueChange`
* `Select.Value` 使用 `formatSelectedLabels` 将多标签格式化为「Apple, Banana and Cherry」
* 单选模式完全向后兼容(默认)
**用法:**
```tsx
import { Select } from "heroui-native";
export function MultiSelect() {
return (
);
}
```
**相关 PR:** [#298](https://github.com/heroui-inc/heroui-native/pull/298)
### 新子组件
#### PressableFeedback.Scale
新增复合子组件,用于可选的缩放按压动画组合。`PressableFeedback.Scale` 可为任意可按压元素(如 `ListGroup.Item`)增加缩放反馈,而无需根级 `PressableFeedback` 管理缩放。
**用法:**
```tsx
import { PressableFeedback } from "heroui-native";
{/* item content */}
```
**相关 PR:** [#302](https://github.com/heroui-inc/heroui-native/pull/302)
## 组件改进
### Select 触发器与状态修复
[Select](/docs/native/components/select) 针对触发器与受控状态有多项修复。
**改进:**
* 自定义 `className` 现正确参与触发器样式计算,修复用户类名被静默丢弃的情况
* `useControllableState` 在从受控切到非受控时重置内部状态,避免陈旧选中残留
* 触发器通过 `onLayout` 测量位置,正确支持 `isDefaultOpen`
* 触发器样式更新为 `gap-3`、`py-3.5`,值文本使用 `flex-1` 改善布局
**相关 PR:** [#298](https://github.com/heroui-inc/heroui-native/pull/298)
### Avatar asChild 图片修复
[Avatar](/docs/native/components/avatar) 的 `AvatarImage` 在向底层原语转发时正确分离 `source`、`style` 与 `asChild` 及其余属性,修复使用 `asChild` 时错误将全部属性展开到图片组件的问题。
**相关 PR:** [#298](https://github.com/heroui-inc/heroui-native/pull/298)
## API 增强
### Button 反馈 API 重构
[Button](/docs/native/components/button) 的按压反馈 API 重构为统一的类型安全 `feedbackVariant` + `animation`,取代此前多属性拼写。
**新能力:**
```tsx
import { Button } from "heroui-native";
// 缩放 + 高亮(默认)
Press me
// 缩放 + 水波纹
Press me
// 仅缩放
Press me
// 自定义动画配置
Press me
```
`animation` 为按变体区分的联合类型,各反馈配置均有完整类型推导。
`button.utils.ts` 中新增 `resolveAnimationObject` 与 `isAnimationDisabled`,集中解析动画属性。
**相关 PR:** [#302](https://github.com/heroui-inc/heroui-native/pull/302)
## 依赖
### 放宽对等依赖约束
对等依赖版本约束已放宽为 caret(`^`)与范围(`>=`),替代偏紧的 tilde(`~`)或固定版本,便于使用方在较新依赖版本上解析,尤其 Expo SDK 55。
**变更:**
* `react-native-reanimated`:`~4.1.1` → `^4.1.1`(允许次版本更新)
* `react-native-safe-area-context`:`~5.6.0` → `^5.6.0`
* `react-native-svg`:`15.12.1` → `^15.12.1`
* `react-native-worklets`:`0.5.1` → `>=0.5.1`
无运行时逻辑变更——此前已满足约束的项目无需修改即可继续工作。
**相关 PR:** [#306](https://github.com/heroui-inc/heroui-native/pull/306)
## ⚠️ 破坏性变更
### Button 反馈 API
已移除 Button 上的 `pressableFeedbackVariant`、`pressableFeedbackHighlightProps`、`pressableFeedbackRippleProps`。请迁移到 `feedbackVariant` 与统一的 `animation`。
**迁移:**
更新所有 Button 反馈相关属性:
```tsx
// 之前
Press me
// 之后
Press me
```
**变体映射:**
* `"highlight"` → `"scale-highlight"`(默认)
* `"ripple"` → `"scale-ripple"`
* `"none"` → `"scale"` 或 `"none"`
**可选项:**
* `"scale-highlight"` — 缩小 + 高亮遮罩(默认)
* `"scale-ripple"` — 缩小 + 水波纹
* `"scale"` — 仅缩小
* `"none"` — 无反馈动画
**相关 PR:** [#302](https://github.com/heroui-inc/heroui-native/pull/302)
## 问题修复
本版本包含以下修复:
* **[Issue #291](https://github.com/heroui-inc/heroui-native/issues/291)**:修复 `Select.Trigger` 的 `variant` 被 `className` 覆盖的问题。传入触发器的自定义类名现正确参与样式计算,不再被静默丢弃。
* **[Issue #294](https://github.com/heroui-inc/heroui-native/issues/294)**:兼容 `react-native-worklets` 0.7.x 与 `react-native-reanimated` 4.2.x(Expo SDK 55)。放宽对等依赖约束,接受上述新版本而不产生解析告警。
**相关 PR:**
* [#298](https://github.com/heroui-inc/heroui-native/pull/298)
* [#306](https://github.com/heroui-inc/heroui-native/pull/306)
## 文档更新
以下文档页面已随本版本更新:
* [SearchField](/docs/native/components/search-field) — 新组件文档:用法示例与 API 参考
* [ListGroup](/docs/native/components/list-group) — 新组件文档:用法示例与 API 参考
* [Slider](/docs/native/components/slider) — 新组件文档:用法示例与 API 参考
* [Select](/docs/native/components/select) — 多选模式、触发器 className 修复与受控状态改进
* [Button](/docs/native/components/button) — 更新反馈 API 文档:`feedbackVariant` 与 `animation`
* [Avatar](/docs/native/components/avatar) — 修复 `asChild` 图片属性展开说明
* [PressableFeedback](/docs/native/components/pressable-feedback) — 新增 `PressableFeedback.Scale` 子组件文档
## 链接
* [组件文档](../components)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-native)
## 贡献者
感谢所有为本版本做出贡献的朋友!
# RC 3
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases/rc-3
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/rc-3.mdx
> TagGroup、Menu、InputGroup 组件,Bottom Sheet Android 返回键修复,Expo 55 兼容
2026 年 2 月 26 日
RC 3 带来三个新组件:用于可选标签管理的 TagGroup、基于 Popover/Bottom Sheet 的下拉菜单 Menu,以及带自动测量前后缀槽位的装饰性输入 InputGroup。本版本还为所有基于 Bottom Sheet 的遮罩增加 Android 实体返回键支持;通过关键的 `combineStyles` 修复保留 Reanimated 动画样式绑定,实现 Expo 55 兼容;并包含若干依赖升级。
## 安装
升级到最新版本:
```bash
npm i heroui-native
```
```bash
pnpm add heroui-native
```
```bash
yarn add heroui-native
```
```bash
bun add heroui-native
```
**使用 AI 助手?** 直接提示「Hey Cursor,把 HeroUI Native 升级到最新版本」,助手会自动比对版本并完成必要修改。了解更多请参见 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)。
## 抢先体验
通过预览应用在真机上体验 RC 3 的全部改进!你可以探索 TagGroup、Menu、InputGroup,以及 Bottom Sheet 的 Android 返回键支持与各项修复。
### 环境要求
请确保手机已安装最新版本的 [Expo Go](https://expo.dev/go)。
### 如何访问
**方式一:扫描二维码**
使用手机相机或 Expo Go 应用扫描:
> **Android 用户请注意:** 若使用系统相机或其他扫码应用会跳转到浏览器并出现 404,请先打开 Expo Go,使用其内置扫码功能扫描。
**方式二:点击链接**
**[📱 在 Expo Go 中打开演示应用](https://link.heroui.com/native-demo)**
若设备已安装 Expo Go,将自动在其中打开应用。
## 更新亮点
### 新组件
本版本新增 **3** 个组件:
* **[TagGroup](/docs/native/components/tag-group)**:用于展示与管理可选标签的复合组件,支持可选移除、单选/多选及表单字段集成。
* **[Menu](/docs/native/components/menu)**:下拉菜单系统,支持 Popover 与 Bottom Sheet 呈现、单选/多选、菜单项变体与按压动画反馈。
* **[InputGroup](/docs/native/components/input-group)**:装饰性文本输入,前后缀槽位绝对定位并自动测量宽度,为输入区应用匹配内边距。
#### TagGroup
TagGroup 以复合组件模式渲染可选标签组,并支持可选移除。支持单选与多选、受控/非受控 API、两种视觉变体(default 与 surface)、三种尺寸、禁用态,以及与 Label、Description、FieldError 的完整表单集成。
**特性:**
* 复合子组件:`TagGroup.List`、`TagGroup.Item`、`TagGroup.ItemLabel`、`TagGroup.ItemRemoveButton`
* 单选/多选模式,受控/非受控 API
* 两种视觉变体:`default` 与 `surface`
* 三种尺寸:`sm`、`md`、`lg`
* 单项禁用与 `disabledKeys`
* 通过 `onRemove` 与 `TagGroup.ItemRemoveButton` 实现移除
* `TagGroup.List` 上 `renderEmptyState` 渲染空状态
* 与 Label、Description、FieldError、`isInvalid`、`isRequired` 的表单集成
* `useTagGroup` 与 `useTagGroupItem` 供高级用法
**用法:**
```tsx
import { TagGroup } from "heroui-native";
export function BasicTagGroup() {
return (
React
Vue
Svelte
);
}
export function RemovableTagGroup() {
const [items, setItems] = useState(["React", "Vue", "Svelte"]);
return (
setItems((prev) => prev.filter((i) => !keys.has(i)))}>
{items.map((item) => (
{item}
))}
);
}
```
完整文档与示例见 [TagGroup 组件页](/docs/native/components/tag-group)。
**相关 PR:** [#309](https://github.com/heroui-inc/heroui-native/pull/309)
#### Menu
Menu 提供基于复合组件的下拉菜单,支持 Popover 与 Bottom Sheet 两种呈现。包含基于 Reanimated 的按压动画、单选/多选、菜单项变体(default 与 danger)、指示器样式及可配置 placement。
**特性:**
* 复合子组件:`Menu.Trigger`、`Menu.Portal`、`Menu.Overlay`、`Menu.Content`、`Menu.Label`、`Menu.Group`、`Menu.Item`、`Menu.ItemTitle`、`Menu.ItemDescription`、`Menu.ItemIndicator`
* 两种呈现:`popover` 与 `bottom-sheet`,placement 可配置(`top`、`bottom`、`left`、`right`)
* `Menu.Group` 上通过 `selectedKeys`/`onSelectionChange` 实现单选/多选
* 菜单项按压动画(缩放 + 背景色),Reanimated 实现,可通过 `animation` 自定义
* 菜单项变体:`default` 与 `danger`
* 指示器变体:`checkmark`、`dot` 或自定义内容
* `Menu.Label` 用于分区标题
* 分组级 `shouldCloseOnSelect` 控制
**用法:**
```tsx
import { Menu } from "heroui-native";
export function BasicMenu() {
return (
Open Menu
Edit
Duplicate
Delete
);
}
export function MenuWithSections() {
return (
Actions
View
List View
Grid View
);
}
```
完整文档与示例见 [Menu 组件页](/docs/native/components/menu)。
**相关 PR:** [#312](https://github.com/heroui-inc/heroui-native/pull/312)
#### InputGroup
InputGroup 提供装饰性文本输入,`Prefix` 与 `Suffix` 子组件绝对定位,通过 `onLayout` 自动测量宽度并为 Input 应用匹配的水平内边距。`isDecorative` 布尔值可一次性处理装饰性附加内容的无障碍与指针事件样板;根级 `isDisabled` 通过上下文级联到所有子节点。
**特性:**
* 复合子组件:`InputGroup.Prefix`、`InputGroup.Suffix`、`InputGroup.Input`
* 自动内边距:通过 `onLayout` 测量 Prefix/Suffix 宽度,自动作为 Input 的 `paddingLeft`/`paddingRight`
* Prefix/Suffix 上 `isDecorative` 统一设置 `pointerEvents="none"`、`accessibilityElementsHidden` 与 `importantForAccessibility`
* 根级 `isDisabled` 通过上下文级联(Prefix/Suffix 透明度与 pointer-events、Input 可编辑性)
* `InputGroup.Input` 为直接透传——由使用方在 Input 上管理 `value`/`onChangeText`
**用法:**
```tsx
import { InputGroup } from "heroui-native";
export function SearchInput() {
return (
);
}
export function DisabledInput() {
return (
);
}
```
完整文档与示例见 [InputGroup 组件页](/docs/native/components/input-group)。
**相关 PR:** [#313](https://github.com/heroui-inc/heroui-native/pull/313)
## 组件改进
### Bottom Sheet Android 返回键支持
[Bottom Sheet](/docs/native/components/bottom-sheet) 共享容器现处理 Android 实体返回键:按下时关闭当前打开的 Bottom Sheet。`BackHandler` 仅在 Bottom Sheet 打开时注册,避免已关闭实例抢占事件。该修复全局作用于所有基于 Bottom Sheet 的组件。
**涉及组件:**
* [Bottom Sheet](/docs/native/components/bottom-sheet)
* [Popover](/docs/native/components/popover)
* [Select](/docs/native/components/select)
实现使用 React Native 的 `BackHandler` API,在 iOS 上为空操作,无需分平台分支。
**相关 PR:** [#308](https://github.com/heroui-inc/heroui-native/pull/308)
### Slot 的 `combineStyles` 修复
Slot 原语的 `combineStyles` 现返回样式数组,而不再使用 `StyleSheet.flatten`——后者会通过深拷贝样式对象破坏 Reanimated 的 `SharedValue` 与 `useAnimatedStyle` 绑定。
**改进:**
* `combineStyles` 通过返回数组保留 Reanimated 动画样式绑定
* React Native 原生支持嵌套样式数组,对使用方行为无影响
* 修复通过 Slot 原语组合的组件上的动画断裂问题
**相关 PR:** [#314](https://github.com/heroui-inc/heroui-native/pull/314)
## 依赖
### Expo 55 兼容
依赖版本已更新以兼容 Expo SDK 55:
* `uniwind`:1.2.7 → 1.3.2
* `@gorhom/bottom-sheet`:^5 → ^5.2.8
上述 `combineStyles` 修复是支持 Expo 55 的主要代码变更:此前 `StyleSheet.flatten` 会在新 SDK 下破坏 Reanimated 样式绑定。
**相关 PR:** [#314](https://github.com/heroui-inc/heroui-native/pull/314)
## 问题修复
本版本包含以下修复:
* **[Issue #272](https://github.com/heroui-inc/heroui-native/issues/272)**:解决 `FullWindowOverlay` 干扰 React Native 元素检查器的问题。
* **[Issue #280](https://github.com/heroui-inc/heroui-native/issues/280)**:修复 Expo 55 下 Avatar 等依赖 Reanimated 的组件失效。`combineStyles` 曾通过 `StyleSheet.flatten` 破坏动画绑定;现改为返回样式数组以保留 `SharedValue` 与 `useAnimatedStyle`。
**相关 PR:**
* [#308](https://github.com/heroui-inc/heroui-native/pull/308)
* [#314](https://github.com/heroui-inc/heroui-native/pull/314)
## 文档更新
以下文档页面已随本版本更新:
* [TagGroup](/docs/native/components/tag-group) — 新组件文档:用法示例与 API 参考
* [Menu](/docs/native/components/menu) — 新组件文档:用法示例与 API 参考
* [InputGroup](/docs/native/components/input-group) — 新组件文档:用法示例与 API 参考
## 链接
* [组件文档](../components)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-native)
## 贡献者
感谢所有为本版本做出贡献的朋友!
# RC 4
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases/rc-4
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/rc-4.mdx
> SubMenu 组件、Slider Output 组合重构、PressableFeedback 水波纹修复、Bottom Sheet 返回键处理修复
2026 年 3 月 6 日
RC 4 引入用于嵌套可展开菜单的 SubMenu 复合组件,配套弹簧动画展开/收起;重构 Slider Output,采用基于插槽的样式并支持 `textProps` 透传;为 `Menu.Group` 增加 `disallowEmptySelection`,实现类单选框行为。本版本还通过双层缓冲修复快速连按时 PressableFeedback 水波纹闪烁,并修复 Bottom Sheet 在 Android 上未尊重 `enablePanDownToClose` 的返回键处理。
## 安装
升级到最新版本:
```bash
npm i heroui-native
```
```bash
pnpm add heroui-native
```
```bash
yarn add heroui-native
```
```bash
bun add heroui-native
```
**使用 AI 助手?** 直接提示「Hey Cursor,把 HeroUI Native 升级到最新版本」,助手会自动比对版本并完成必要修改。了解更多请参见 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)。
## 抢先体验
通过预览应用在真机上体验 RC 4 的全部改进!你可以探索全新的 SubMenu 组件、改进后的 Slider Output,以及各项修复。
### 环境要求
请确保手机已安装最新版本的 [Expo Go](https://expo.dev/go)。
### 如何访问
**方式一:扫描二维码**
使用手机相机或 Expo Go 应用扫描:
> **Android 用户请注意:** 若使用系统相机或其他扫码应用会跳转到浏览器并出现 404,请先打开 Expo Go,使用其内置扫码功能扫描。
**方式二:点击链接**
**[📱 在 Expo Go 中打开演示应用](https://link.heroui.com/native-demo)**
若设备已安装 Expo Go,将自动在其中打开应用。
## 更新亮点
### SubMenu 组件
[Menu](/docs/native/components/menu) 现通过新的 `SubMenu` 复合组件支持嵌套可展开子菜单。SubMenu 嵌套在 `Menu.Content` 内,按压后以弹簧动画展开/收起额外项,并带动指示器旋转。
**特性:**
* 复合子组件:`SubMenu`、`SubMenu.Trigger`、`SubMenu.TriggerIndicator`、`SubMenu.Content`
* 弹簧动画展开/收起与指示器旋转
* 无头原语层:上下文、受控/非受控打开状态,以及无障碍属性(`role`、`aria-expanded`、`aria-disabled`)
* 父级菜单协同:打开 SubMenu 时 popover 缩至 0.98、移除阴影,非 SubMenu 项淡出至 40% 不透明度并 `pointer-events-none`
* 打开 SubMenu 时菜单内容切换为 `FadeOut` 退出动画,避免与缩放动画冲突
* `useSubMenu` 钩子供高级场景使用
**用法:**
```tsx
import { Menu, SubMenu } from "heroui-native";
export function MenuWithSubMenu() {
return (
Open Menu
Edit
More Options
Import
Export
);
}
```
完整文档与示例见 [Menu 组件页](/docs/native/components/menu)。
**相关 PR:** [#331](https://github.com/heroui-inc/heroui-native/pull/331)
## 组件改进
### Slider Output 组合重构
[Slider](/docs/native/components/slider) 的 Output 已重构为基于插槽的架构,包含 `container` 与 `text` 插槽,并新增 `textProps` 用于向内层文本元素透传属性。
**改进:**
* 基于插槽的样式:将 `output` 类拆为 `container` 与 `text`,新增 `classNames`(`classNames={{ container, text }}`)以便精细覆盖样式
* 组合修复:仅在默认内容时渲染 `HeroText`;自定义子节点直接渲染,无额外文本包装
* `Slider.Output` 新增 `textProps`,可向内部文本传递任意属性(如 `maxFontSizeMultiplier`)
* 从样式模块导出 `OutputSlots` 类型供外部使用
**相关 PR:** [#328](https://github.com/heroui-inc/heroui-native/pull/328)
### Menu.Group 的 `disallowEmptySelection`
[Menu](/docs/native/components/menu) 的 `Menu.Group` 现支持 `disallowEmptySelection`,在 `single` 选择模式下禁止取消最后一项选中,实现类单选框行为。
**用法:**
```tsx
List View
Grid View
```
**相关 PR:** [#331](https://github.com/heroui-inc/heroui-native/pull/331)
### Bottom Sheet 与 `enablePanDownToClose` 一致
[Bottom Sheet](/docs/native/components/bottom-sheet) 现会在 Android 硬件返回键行为上正确尊重 `enablePanDownToClose`。此前即使 `enablePanDownToClose` 为 `false`,返回键仍会关闭 Bottom Sheet。
**改进:**
* `enablePanDownToClose` 透传至 `BottomSheetContentContainer`(默认 `true`)
* 仅在 `isOpen` 与 `enablePanDownToClose` 均为 `true` 时注册 `BackHandler` 监听
* `enablePanDownToClose={false}` 时不再可通过 Android 返回键关闭
**相关 PR:** [#327](https://github.com/heroui-inc/heroui-native/pull/327)
## ⚠️ 破坏性变更
### Chip 组件尺寸
[Chip](/docs/native/components/chip) 的尺寸变体已由固定高度改为基于内边距,以在较大无障碍字号下适配动态文字缩放。
**迁移:**
若自定义样式依赖此前的 `h-5`/`h-6`/`h-7` 高度,请改为新的内边距方案:
```tsx
// 之前 — 固定高度
// Chip 使用 h-5(sm)、h-6(md)、h-7(lg)
// 之后 — 基于内边距
// Chip 使用 py-0.5(sm)、py-[3px](md)、py-1(lg)
// 圆角更新:rounded-xl → rounded-2xl / rounded-3xl
```
## 文档更新
以下文档页面已随本版本更新:
* [Menu](/docs/native/components/menu) — SubMenu 文档:结构分解、用法示例、完整 API 参考及 `useSubMenu` 钩子说明
* [Slider](/docs/native/components/slider) — 更新 Output 文档:基于插槽的样式与 `textProps`
## 链接
* [组件文档](../components)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-native)
## 贡献者
感谢所有为本版本做出贡献的朋友!
# v1.0.0
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases/v1-0-0
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/v1-0-0.mdx
> LinkButton 组件、子菜单冲突处理、可选的 @gorhom/bottom-sheet 对等依赖、ThemeColorValue 品牌类型
2026 年 3 月 19 日
🎉 HeroUI Native 正式发布 v1.0.0——这是首个稳定版本,标志着库已从 beta 与候选发布阶段毕业,成为可用于生产环境的 React Native 应用基础。伴随这一里程碑,本版本还包含全新的 LinkButton 组件、子菜单冲突处理、可选的 `@gorhom/bottom-sheet` 对等依赖,以及针对 `useThemeColor` 的更强类型安全。
## 安装
升级到最新版本:
```bash
npm i heroui-native
```
```bash
pnpm add heroui-native
```
```bash
yarn add heroui-native
```
```bash
bun add heroui-native
```
**使用 AI 助手?** 直接提示「Hey Cursor,把 HeroUI Native 升级到最新版本」,助手会自动比对版本并完成必要修改。了解更多请参见 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)。
## 抢先体验
通过我们的预览应用,在真机上体验 v1.0.0 的全部改进!你可以探索全新的 LinkButton 组件、改进后的子菜单行为,以及各项修复。
### 环境要求
请确保手机已安装最新版本的 [Expo Go](https://expo.dev/go)。
### 如何访问
**方式一:扫描二维码**
使用手机相机或 Expo Go 应用扫描:
> **Android 用户请注意:** 若使用系统相机或其他扫码应用会跳转到浏览器并出现 404,请先打开 Expo Go,使用其内置扫码功能扫描。
**方式二:点击链接**
**[📱 在 Expo Go 中打开演示应用](https://link.heroui.com/native-demo)**
若设备已安装 Expo Go,将自动在其中打开应用。
## 更新亮点
### LinkButton 组件
全新的 [LinkButton](/docs/native/components/button) 复合组件会渲染 ghost 变体按钮,且无高亮反馈,适用于「服务条款」「隐私政策」等行内链接式交互。它完全复用现有 Button 基础设施,并在内部强制 ghost 变体且关闭高亮反馈。
**特性:**
* 复合子组件:`LinkButton.Label`,用于样式化文本内容
* 内部强制 ghost 变体——不对外暴露 `variant` 属性
* 通过 `resolveAnimationObject` 默认关闭高亮反馈
* `h-auto p-0` 基础类移除默认按钮高度与内边距,便于行内使用
* 合并使用方动画配置时仍保持 `highlight: false`
**用法:**
```tsx
import { LinkButton } from "heroui-native";
export function TermsLink() {
return (
openURL("https://example.com/terms")}>
Terms of Service
);
}
```
**相关 PR:** [#341](https://github.com/heroui-inc/heroui-native/pull/341)
## 组件改进
### 子菜单单开约束与点击背景关闭
[Menu](/docs/native/components/menu) 子菜单系统已重构为按 ID 跟踪当前子菜单,而非简单布尔值,从而在同级子菜单之间强制「同时仅开一个」。子菜单打开时,会在菜单内容区域上方渲染可点击的背景层,用户点击外部即可关闭。
**改进:**
* `openSubMenuId` 取代布尔标记——同一时间只能有一个子菜单处于打开状态;打开新的会自动关闭上一个
* 子菜单打开时,在菜单内容上渲染 `Pressable` 遮罩,支持点击关闭
* 非当前打开的子菜单触发器通过新的 `isOtherSubMenuOpen` 样式变体获得 `opacity-40` 与 `pointer-events-none`
* 打开的子菜单内容使用 `z-50`,关闭的为 `z-40`,避免层叠问题
* 演示应用新增「两个子菜单」示例,展示同一菜单中的多个子菜单
**相关 PR:** [#343](https://github.com/heroui-inc/heroui-native/pull/343)
### Button Label 的 ref 类型修复
`ButtonLabel` 的 ref 类型已从 `View` 更正为 `TextRef`,并从 `button.tsx` 中移除了未使用的 `View` 导入,使 ref 类型与实际渲染元素一致。
**相关 PR:** [#341](https://github.com/heroui-inc/heroui-native/pull/341)
## API 增强
### 用于 `useThemeColor` 的 `ThemeColorValue` 品牌类型
`useThemeColor` 在单次取色调用时现返回 `ThemeColorValue` 品牌类型,误用数组解构时 IDE 会立即将类型标为 `never`。非空断言运算符(`!`)也已替换为安全的空值合并回退。
**新行为:**
```tsx
import { useThemeColor } from "heroui-native";
// 正确 — 直接赋值
const mutedColor = useThemeColor("muted");
// 错误 — IDE 会立即标出 `never` 类型
const [color] = useThemeColor("muted"); // color: never
```
`ThemeColorValue` 继承自 `string`,因此凡可接受 `string` 处均可赋值。现有调用点在运行时不受影响。`_colorValueBrand` 符号以 `declare const` 声明,无运行时体积。
**相关 PR:** [#337](https://github.com/heroui-inc/heroui-native/pull/337)
### 公开钩子 `useBottomSheetAwareHandlers`
`useBottomSheetAwareHandlers` 现已作为公开 API 导出,便于在 Bottom Sheet 内显式控制键盘避让与 `Input`、`InputOTP` 的衔接。此前在 `Input` 与 `InputOTP` 上可用的隐式 `isBottomSheetAware` 属性已被取代。
**用法:**
```tsx
import { useBottomSheetAwareHandlers, Input } from "heroui-native";
export function BottomSheetInput() {
const { onFocus, onBlur } = useBottomSheetAwareHandlers();
return ;
}
```
**相关 PR:** [#347](https://github.com/heroui-inc/heroui-native/pull/347)
## ⚠️ 破坏性变更
### Bottom Sheet 内的 `Input` 与 `InputOTP`
已从 `Input` 与 `InputOTP` 移除 `isBottomSheetAware` 属性。此前 Bottom Sheet 内的键盘避让在底层自动处理;现在必须显式使用 `useBottomSheetAwareHandlers` 并自行传入处理器。这样可减轻 Input 组件负担,去掉隐式 `@gorhom/bottom-sheet` 导入,使该包对不使用 Bottom Sheet 的项目成为可选对等依赖。
**迁移:**
```tsx
// 之前
// 之后
import { useBottomSheetAwareHandlers } from "heroui-native";
const { onFocus, onBlur } = useBottomSheetAwareHandlers();
```
凡在 `BottomSheet` 内渲染的 `Input` 或 `InputOTP` 均需按此调整;在 Bottom Sheet 外使用的组件不受影响。
**相关 PR:** [#347](https://github.com/heroui-inc/heroui-native/pull/347)
## 问题修复
本版本包含以下修复:
* **[Issue #330](https://github.com/heroui-inc/heroui-native/issues/330)**:修复 `Input` 在模块顶层无条件导入 `@gorhom/bottom-sheet` 的问题,即使 `isBottomSheetAware` 为 `false`。现通过可选的 `try/catch` 包装懒加载该包,不使用 Bottom Sheet 的项目无需再安装它。
* **[Issue #340](https://github.com/heroui-inc/heroui-native/issues/340)**:修复子菜单内容出现在同级子菜单触发器后方的问题。子菜单系统现按 ID 跟踪活动子菜单、强制单开,并应用正确的 z-index 分层(打开 `z-50`,关闭 `z-40`)。
**相关 PR:**
* [#347](https://github.com/heroui-inc/heroui-native/pull/347)
* [#343](https://github.com/heroui-inc/heroui-native/pull/343)
## 文档更新
以下文档页面已随本版本更新:
* [LinkButton](/docs/native/components/link-button) — LinkButton 复合组件文档:结构分解、用法示例与 API 参考
* [Menu](/docs/native/components/menu) — 更新子菜单文档:单开约束与点击背景关闭行为
* [Input](/docs/native/components/input) — 更新 Bottom Sheet 用法示例,采用 `useBottomSheetAwareHandlers` 模式
* [InputOTP](/docs/native/components/input-otp) — 同上,Bottom Sheet 示例采用 `useBottomSheetAwareHandlers`
## 链接
* [组件文档](../components)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-native)
## 贡献者
感谢所有为本版本做出贡献的朋友!
# v1.0.1
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases/v1-0-1
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/v1-0-1.mdx
> Toast 竞态修复、使用 disabled 修饰符的禁用态样式、背景层样式变量 backdrop
2026 年 4 月 1 日
HeroUI Native v1.0.1 是一次侧重可靠性与开发者体验的补丁版本。它修复了 Toast 提供程序中导致动画值过期的竞态条件,将七个组件的禁用态样式改为使用原生 `disabled:` 修饰符,并为主题系统新增 `--backdrop` 变量,供遮罩类组件使用。
## 安装
升级到最新版本:
```bash
npm i heroui-native
```
```bash
pnpm add heroui-native
```
```bash
yarn add heroui-native
```
```bash
bun add heroui-native
```
**使用 AI 助手?** 直接提示「Hey Cursor,把 HeroUI Native 升级到最新版本」,助手会自动比对版本并完成必要修改。了解更多请参见 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)。
## 抢先体验
通过预览应用在真机上体验 v1.0.1 的全部改进!你可以查看 Toast 修复、改进后的禁用态样式以及新的背景层变量。
### 环境要求
请确保手机已安装最新版本的 [Expo Go](https://expo.dev/go)。
### 如何访问
**方式一:扫描二维码**
使用手机相机或 Expo Go 应用扫描:
> **Android 用户请注意:** 若使用系统相机或其他扫码应用会跳转到浏览器并出现 404,请先打开 Expo Go,使用其内置扫码功能扫描。
**方式二:点击链接**
**[📱 在 Expo Go 中打开演示应用](https://link.heroui.com/native-demo)**
若设备已安装 Expo Go,将自动在其中打开应用。
## API 增强
### `--backdrop` 主题变量
主题系统新增顶层样式变量 `--backdrop`,为 Dialog、Bottom Sheet 等遮罩组件背后的变暗层提供专用色令牌。默认值为 `oklch(0% 0 0 / 20%)`,呈现轻微但可见的背景变暗效果。
**新能力:**
```tsx
import { Dialog } from "heroui-native";
// Dialog 与 Bottom Sheet 遮罩现自动使用 bg-backdrop
{/* 内容绘制在主题化背景层之上 */}
```
`--backdrop` 已纳入所有内置主题的浅色与深色模式,对应的 Tailwind 工具类 `bg-backdrop` 也可用于自定义组件样式。
**相关 PR:** [#366](https://github.com/heroui-inc/heroui-native/pull/366)
## 样式修复
### 禁用态修饰符
七个组件的禁用态样式已改为使用 `disabled:` 前缀修饰符,而非无条件应用样式。这样可确保 `opacity-disabled`、`pointer-events-none` 等仅在组件真实处于禁用态时生效,并尊重来自 Uniwind 的原生 `disabled` 修饰符。
**涉及组件:**
* [Button](/docs/native/components/button)
* [Checkbox](/docs/native/components/checkbox)
* [Input](/docs/native/components/input)
* [Menu](/docs/native/components/menu)
* [Switch](/docs/native/components/switch)
* [Tabs](/docs/native/components/tabs)
* [TagGroup](/docs/native/components/tag-group)
所有 `isDisabled` 变体类现均使用 `disabled:` 前缀(例如 `disabled:opacity-disabled disabled:pointer-events-none`),正确限定在禁用伪状态,并在禁用态动态切换时消除样式冲突。
**相关 PR:** [#361](https://github.com/heroui-inc/heroui-native/pull/361)
## 问题修复
本版本包含以下修复:
* **[Issue #359](https://github.com/heroui-inc/heroui-native/issues/359)**:修复 Toast 提供程序中 `total` SharedValue 与实际 Toast 数量可能不同步的竞态。原先手动增减在 `hide` 与 `show` 同一帧执行、或自动消失与手动隐藏竞速时,易出现闭包陈旧导致不一致。`total` 现通过 `useEffect` 由 `toasts.length` 派生,使透明度、缩放、translateY 等插值始终反映真实数量。
* **[Issue #356](https://github.com/heroui-inc/heroui-native/issues/356)**:修复在 `isDisabled` 为 true 时禁用样式被无条件应用、阻碍开发者主题化或自定义禁用外观的问题。上述七个组件均已改为 `disabled:` 前缀,将样式正确限定在禁用伪状态。
**相关 PR:**
* [#360](https://github.com/heroui-inc/heroui-native/pull/360)
* [#361](https://github.com/heroui-inc/heroui-native/pull/361)
## 文档更新
以下文档页面已随本版本更新:
* [Colors](/docs/native/getting-started/colors) — 在颜色参考中补充新的 `--backdrop` 变量
* [Theming](/docs/native/getting-started/theming) — 主题指南中增加 `--backdrop` 变量说明
## 链接
* [组件文档](../components)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-native)
## 贡献者
感谢所有为本版本做出贡献的朋友!
# v1.0.2
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases/v1-0-2
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/v1-0-2.mdx
> PressableFeedback 与 Surface 的 asChild 插槽模式、Portal 无障碍 modal 属性、Button Android 变体修复、Input 与 Select 样式微调
2026 年 4 月 15 日
HeroUI Native v1.0.2 为 PressableFeedback 与 Surface 引入 `asChild` 插槽模式,为所有基于 Portal 的遮罩组件增加 VoiceOver 模态包容支持,并修复 Android 上 Button 某变体的样式问题。本版本还微调了 Input 与 Select 的视觉效果,并在 RadioGroup 文档中内嵌 API 参考表。
## 安装
升级到最新版本:
```bash
npm i heroui-native
```
```bash
pnpm add heroui-native
```
```bash
yarn add heroui-native
```
```bash
bun add heroui-native
```
**使用 AI 助手?** 直接提示「Hey Cursor,把 HeroUI Native 升级到最新版本」,助手会自动比对版本并完成必要修改。了解更多请参见 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)。
## 抢先体验
通过预览应用在真机上体验 v1.0.2 的全部改进!你可以探索新的 `asChild` 插槽模式、Portal 无障碍改进、Android 上更可靠的 Button 行为,以及优化后的 Input 与 Select 样式。
### 环境要求
请确保手机已安装最新版本的 [Expo Go](https://expo.dev/go)。
### 如何访问
**方式一:扫描二维码**
使用手机相机或 Expo Go 应用扫描:
> **Android 用户请注意:** 若使用系统相机或其他扫码应用会跳转到浏览器并出现 404,请先打开 Expo Go,使用其内置扫码功能扫描。
**方式二:点击链接**
**[📱 在 Expo Go 中打开演示应用](https://link.heroui.com/native-demo)**
若设备已安装 Expo Go,将自动在其中打开应用。
## API 增强
### PressableFeedback 与 Surface 的 `asChild` 插槽模式
[PressableFeedback](/docs/native/components/pressable-feedback) 与 [Surface](/docs/native/components/surface) 现支持 `asChild` 属性,采用 Slot 模式实现多态渲染。当 `asChild` 为 `true` 时,组件会把自身行为与样式合并到单个子元素上,而不再额外包一层节点。
**PressableFeedback** 使用 `Animated.createAnimatedComponent(Slot.Pressable)`,将按压处理与动画样式合并到子元素。**Surface** 使用 `Slot.View`,将表面样式(海拔、背景、`className`)合并到子元素。
**新能力:**
```tsx
import { PressableFeedback, Surface } from "heroui-native";
// PressableFeedback 将按压处理合并到子元素
console.log("pressed")}>
// Surface 将表面样式合并到子元素
```
`asChild` 默认为 `false`,保持既有行为,无需迁移。启用 `asChild` 时子节点须为单个 React 元素。两处实现均遵循代码库中已有的 Slot 原语模式。
**相关 PR:** [#380](https://github.com/heroui-inc/heroui-native/pull/380)
### Portal 组件的 `unstable_accessibilityContainerViewIsModal`
所有基于 Portal 的遮罩组件新增 `unstable_accessibilityContainerViewIsModal` 属性,用于控制 iOS VoiceOver 是否将遮罩窗口视为模态容器。启用后,VoiceOver 焦点限制在遮罩内,无法导航到背后内容。
**支持的组件:**
* [BottomSheet](/docs/native/components/bottom-sheet)(`BottomSheet.Portal`)
* [Dialog](/docs/native/components/dialog)(`Dialog.Portal`)
* [Menu](/docs/native/components/menu)(`Menu.Portal`)
* [Popover](/docs/native/components/popover)(`Popover.Portal`)
* [Select](/docs/native/components/select)(`Select.Portal`)
* [Toast](/docs/native/components/toast)(`ToastProvider`)
**新能力:**
```tsx
import { Dialog } from "heroui-native";
{/* 在 iOS 上 VoiceOver 焦点限制在此遮罩内 */}
```
该属性默认为 `false`,保持既有行为。标记为 `unstable` 是因为它直接映射到 `react-native-screens` 中 `FullWindowOverlay` 的原生 `accessibilityViewIsModal`,未来可能随该库版本变化。
**相关 PR:** [#383](https://github.com/heroui-inc/heroui-native/pull/383)
## 样式修复
### Input 与 Select 视觉微调
优化了 [Input](/docs/native/components/input) 与 [Select](/docs/native/components/select) 的视觉样式,使外观更干净、比例更协调。
**调整:**
* **Input**:边框宽度由 `border-2`(2px)改为 `border-[1.5px]`,边框更轻、不抢眼
* **Select**:触发器垂直内边距由 `py-3.5` 改为 `py-3`,布局更紧凑
以上均为纯视觉调整,无 API 或行为变更。建议在 iOS 与 Android 上做视觉回归确认。
**相关 PR:** [#381](https://github.com/heroui-inc/heroui-native/pull/381)
## 问题修复
本版本包含以下修复:
* **[Issue #363](https://github.com/heroui-inc/heroui-native/issues/363)**:修复 Android 上 Button 的 outline 变体在通过条件属性切换到其他变体时边框仍残留的问题。Android 上的 React Native 在变体切换时有时会保留 `borderWidth`。除 `outline` 外的所有按钮变体现均包含显式 `border-0` 类,确保变体切换时将 `borderWidth` 重置为 `0`。
* **[Issue #357](https://github.com/heroui-inc/heroui-native/issues/357)**:响应在 Card 上支持 `asChild` 以实现可按压卡片模式的诉求。PressableFeedback 与 Surface 上的新 `asChild` 插槽模式,使开发者可将按压与表面样式合并到单个子元素,无需额外包装节点。
**相关 PR:**
* [#370](https://github.com/heroui-inc/heroui-native/pull/370)
* [#380](https://github.com/heroui-inc/heroui-native/pull/380)
## 文档
### RadioGroup 内联 API 参考
[RadioGroup](/docs/native/components/radio-group) 文档现于页面内嵌 `Radio`、`Radio.Indicator`、`Radio.IndicatorThumb` 的完整 API 表,读者无需再跳转到单独的 Radio 文档即可了解在 `RadioGroup.Item` 中组合时的可用属性。
**改进:**
* `Radio`、`Radio.Indicator`、`Radio.IndicatorThumb` 的完整属性表内嵌展示
* 补充 `RadioRenderProps`、`RadioRootAnimation`、`RadioIndicatorThumbAnimation` 类型说明
* 将外链式 Markdown 链接统一为内联代码格式以保持一致性
**相关 PR:** [#384](https://github.com/heroui-inc/heroui-native/pull/384)
## 文档更新
以下文档页面已随本版本更新:
* [RadioGroup](/docs/native/components/radio-group) — 内联 API 表:`Radio`、`Radio.Indicator`、`Radio.IndicatorThumb`
* [PressableFeedback](/docs/native/components/pressable-feedback) — 补充 `asChild` 属性说明
* [Surface](/docs/native/components/surface) — 补充 `asChild` 属性说明
* [BottomSheet](/docs/native/components/bottom-sheet) — 补充 `unstable_accessibilityContainerViewIsModal` 说明
* [Dialog](/docs/native/components/dialog) — 同上
* [Menu](/docs/native/components/menu) — 同上
* [Popover](/docs/native/components/popover) — 同上
* [Select](/docs/native/components/select) — 同上
* [Toast](/docs/native/components/toast) — 同上
## 链接
* [组件文档](../components)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-native)
## 贡献者
感谢所有为本版本做出贡献的朋友!
# v1.0.3
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases/v1-0-3
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/v1-0-3.mdx
> 全新 Text 排版组件、ScrollShadow 反向列表支持、Tabs RTL 指示器修复、Avatar alt 属性可选化、Select 指示器统一、表单字段样式微调
2026 年 5 月 11 日
HeroUI Native v1.0.3 引入全新的 `Text` 排版基元,提供面向标题、段落与内联代码的复合 API;同时为 `ScrollShadow` 在反向列表下的渲染、`Tabs` 指示器在 RTL 布局下的对齐、以及自定义 `Select.TriggerIndicator` `children` 时的动画问题带来重要修复。本版本还对 `Button`、`Chip`、`Input` 的视觉样式进行了微调,将 `Avatar` 的 `alt` 属性改为可选,并修正 `TextField` 与 `SearchField` 的内部内边距行为。
## 安装
升级到最新版本:
```bash
npm i heroui-native
```
```bash
pnpm add heroui-native
```
```bash
yarn add heroui-native
```
```bash
bun add heroui-native
```
**使用 AI 助手?** 直接提示「Hey Cursor,把 HeroUI Native 升级到最新版本」,助手会自动比对版本并完成必要修改。了解更多请参见 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)。
## 真机体验
## 新增
### 新组件
本版本新增 **1 个** 排版组件:
* **[Text](/docs/native/components/text)**:带语义 `type` 变体的排版基元组件,附带 `Heading`、`Paragraph`、`Code` 子组件。([文档](/docs/native/components/text))
#### Text
`Text` 组件是一个排版基元,通过语义化预设渲染样式化文本。它提供 `Text.Heading`、`Text.Paragraph`、`Text.Code` 等子组件构成的复合 API,并在 `tailwind-variants` 基础上叠加互不耦合的 `align`、`color`、`weight`、`truncate` 属性,使排版可以组合复用,无需在每个调用点重复定义样式。
**特性:**
* 语义化 `type` 变体:`h1`–`h6`、`body`、`body-sm`、`body-xs`、`code`
* `Text.Heading` 自动设置 `accessibilityRole="header"`,并将 `type` 收窄为标题级别
* `Text.Paragraph` 将 `type` 收窄为正文变体,适合长文本可读性
* `Text.Code` 渲染为 chip 样式的内联等宽文本,采用平台合适的 `fontFamily`(iOS 为 Menlo,其他平台为 `monospace`)
* RTL 感知的 `align` 属性,支持 `start`、`center`、`end`、`justify`(justify 仅 iOS 生效)
* 语义化 `color` 预设(`default`、`muted`),其他主题色可通过 `className` 外挂
* `weight` 覆盖:借助 `tailwind-merge` 始终优先于 `type` 暗含的字重
* `truncate` 布尔属性:等价于 `numberOfLines={1}`;显式 `numberOfLines` 始终优先
**用法:**
```tsx
import { Text } from "heroui-native";
import { View } from "react-native";
export function Example() {
return (
Welcome
Getting Started
This is a body paragraph rendered with the Text component.
Smaller supporting text for captions or footnotes.
npm install heroui-native
);
}
```
完整文档与示例见 [Text 组件页面](/docs/native/components/text)。
**相关 PR:** [#400](https://github.com/heroui-inc/heroui-native/pull/400)
## 组件改进
### ScrollShadow 反向子列表支持
[ScrollShadow](/docs/native/components/scroll-shadow) 现已正确处理反向的可滚动子组件,如 `` 或 ``。
**改进点:**
* `ScrollShadowRoot` 现会读取可滚动子组件上的 `inverted` 属性,沿用既有的 `childHorizontal` 自动检测模式
* 内部交换驱动各视觉边缘的动画样式,使阴影渲染到正确的一侧
* 公共 API 无变化——仅当子组件设置 `inverted={true}` 时启用该修复,此前在此情况下渐变会出现在错误的边缘
此前,包裹反向列表时渐变阴影会渲染在顶部,而可滚动内容位于其下;即便仍有更多内容也不会出现底部阴影。修复后,反向 feed、聊天列表等反向滚动表面上的指示器方向已正确。
**相关 PR:** [#398](https://github.com/heroui-inc/heroui-native/pull/398)
### Tabs RTL 指示器对齐
[Tabs](/docs/native/components/tabs) 的指示器在 React Native 处于 RTL 模式时定位现已正确。
**改进点:**
* 通过 tabs 测量上下文跟踪标签条的宽度
* 固定布局使用 `Tabs.List` 的布局宽度
* 滚动布局使用 `Tabs.ScrollView` 的内容宽度
* 仅当 `I18nManager.isRTL` 启用时,对指示器的 `translateX` 进行镜像
指示器使用绝对定位 `left` 锚点配合测量得到的 `translateX`。在 RTL 模式下,React Native 会自动翻转绝对锚点,但所测量的 transform 仍需手动镜像——本次修复内部完成了这一处理。固定与滚动两类标签列表均已修复,且公共 API 未变。
**相关 PR:** [#396](https://github.com/heroui-inc/heroui-native/pull/396)
### Select 触发器指示器渲染统一
[Select](/docs/native/components/select) 的 `Select.TriggerIndicator` 现无论使用默认图标还是自定义 `children`,都会一致地应用展开/收起的旋转动画。
**改进点:**
* 自定义 `children` 现可获得与默认图标相同的动画容器样式
* 通过 `children ?? ` 将 `ChevronDownIcon` 作为回退渲染
* 将此前分散的渲染分支合并为统一的渲染路径
此前,向 `Select.TriggerIndicator` 传入自定义 `children` 会绕过旋转动画,导致开合时指示器保持静止。统一分支确保旋转动画始终生效,且公共 API 未变。
**相关 PR:** [#409](https://github.com/heroui-inc/heroui-native/pull/409)
## API 增强
### Avatar 的 `alt` 属性变为可选
[Avatar](/docs/native/components/avatar) 的 `alt` 属性现已可选,默认为 `'Avatar'`,在保留无障碍支持的同时,减少装饰性或语境明确场景下的样板代码。
**新能力:**
```tsx
import { Avatar } from "heroui-native";
JD
;
```
显式传入 `alt` 的既有代码继续正常工作——该属性仅在缺省时获得一个合理的默认值。`RootProps` 类型现已反映为 `alt?: string`,并通过 JSDoc 标注 `@default 'Avatar'`,组件文档同步更新。
**相关 PR:** [#404](https://github.com/heroui-inc/heroui-native/pull/404)
## 样式修复
### Button、Chip、Input 样式微调
对 [Button](/docs/native/components/button)、[Chip](/docs/native/components/chip)、[Input](/docs/native/components/input) 的尺寸与色彩样式进行了微调,统一改用 Tailwind 工具类与语义化的 soft 颜色令牌。
**修复:**
* **Button**:尺寸改用 Tailwind 高度工具类(`h-10`、`h-12`、`h-14`),不再使用任意像素值;`sm` 高度由 36px 调整为 40px,提升触控目标的人体工学
* **Chip**:`md`/`lg` 的垂直内边距微调(`py-[3px]` → `py-1`,`py-1` → `py-1.5`),间距更协调
* **Chip**:soft 变体改用语义化 `bg-{color}-soft` 令牌,取代基于不透明度的 `bg-{color}/15` 背景
* **Input**:由 `py-3.5` 改为 `min-h-12`,无论内容如何字段都保持稳定高度
* **Input**:primary 变体的边框现正确使用 `border-field-border` 令牌,而非 `border-field`
Button `sm` 尺寸(+4px)与 Chip `md`/`lg` 内边距上的像素级差异建议进行视觉回归确认。无 API 变更——仅更新了 `button.styles.ts`、`chip.styles.ts`、`input.styles.ts` 中的样式令牌。
**相关 PR:** [#406](https://github.com/heroui-inc/heroui-native/pull/406)
### TextField 与 SearchField 内部内边距修复
[TextField](/docs/native/components/text-field) 与 [SearchField](/docs/native/components/search-field) 不再对嵌套的 `Label`、`Description`、`FieldError` 施加额外的水平内边距。
**修复:**
* `TextField` 现在向其 `FormFieldContext` 提供 `hasFieldPadding: false`
* `SearchField` 现在向其 `FormFieldContext` 提供 `hasFieldPadding: false`
* 渲染在上述字段内的 `Label`、`Description`、`FieldError` 不再继承额外的 `px-1.5` 侧边距
* 所有表单字段容器(`ControlField`、`RadioGroup`、`TagGroup` 已采用 `hasFieldPadding: false`)的视觉对齐现已统一
此次变更仅涉及两处为 `FormFieldContext` 提供值的 `useMemo`。如果你此前依赖这一非预期的内部内边距,可通过为受影响的子组件添加 `className="px-1.5"` 恢复原有间距。
**相关 PR:** [#407](https://github.com/heroui-inc/heroui-native/pull/407)
## 问题修复
本版本包含以下修复:
* **[Issue #334](https://github.com/heroui-inc/heroui-native/issues/334)**:修复 RTL 布局下 `Tabs.Indicator` 的定位错乱。指示器以绝对定位 `left` 锚点配合测量的 `translateX` 实现位移;在 RTL 模式下 React Native 会自动翻转锚点,但测量得到的 transform 仍需手动镜像。tabs 测量上下文现会跟踪标签条宽度,仅当 `I18nManager.isRTL` 启用时镜像 `translateX`,固定与滚动两类标签列表均已修复。
* **[Issue #393](https://github.com/heroui-inc/heroui-native/issues/393)**:修复 `ScrollShadow` 忽略子组件 `inverted` 属性的问题。`ScrollShadowRoot` 现会读取可滚动子组件上的 `inverted`(沿用既有的 `childHorizontal` 自动检测),并交换驱动各视觉边缘的动画样式,使反向 feed 与聊天式列表上的渐变出现在正确的一侧。
**相关 PR:**
* [#396](https://github.com/heroui-inc/heroui-native/pull/396)
* [#398](https://github.com/heroui-inc/heroui-native/pull/398)
## 文档更新
以下文档页面已随本版本更新:
* [Text](/docs/native/components/text) — 新组件文档,含结构、用法与完整 API 参考
* [Avatar](/docs/native/components/avatar) — `alt` 属性记为可选,默认值 `'Avatar'`
* [组件总览](/docs/native/components) — 在既有分类外新增 Typography 分类
## 链接
* [组件文档](../components)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-native)
## 贡献者
感谢所有为本版本做出贡献的朋友!
# v1.0.4
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/releases/v1-0-4
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/releases/v1-0-4.mdx
> Typography 组件替代 Text、调整后的柔和前景色主题令牌与可选鲜亮配色、iOS 原生模态偏移说明、示例应用升级至 Expo 56 / React Native 0.85
2026 年 5 月 26 日
HeroUI Native v1.0.4 将排版基元 `Text` 重命名为 `Typography`,让出 React Native 自身的 `Text` 名称,同时更贴近语义化排版用法;既有的 `Text` 导出保留为弃用别名,可平滑升级。本版本还在 Alert、Avatar、Button、Chip、Toast 中调整了 soft 前景色令牌,使其在 soft 背景上具备更好的对比度;新增可选的 `heroui-native/styles/vibrant` 配色;为 Menu、Popover、Select 补充 iOS 原生模态偏移的处理说明;并将示例应用升级到 Expo 56 / React Native 0.85。
## 安装
升级到最新版本:
```bash
npm i heroui-native
```
```bash
pnpm add heroui-native
```
```bash
yarn add heroui-native
```
```bash
bun add heroui-native
```
**使用 AI 助手?** 直接提示「Hey Cursor,把 HeroUI Native 升级到最新版本」,助手会自动比对版本并完成必要修改。了解更多请参见 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server)。
## 真机体验
## 新增
### Typography 组件(由 `Text` 重命名而来)
库内的排版基元已从 `Text` 重命名为 `Typography`,避免与 React Native 内建的 `Text` 重名,并更准确地体现其作为语义化排版系统的定位。组件的 API、变体与行为保持不变——只是公开名称发生变化。
```tsx
import { Typography } from "heroui-native";
import { View } from "react-native";
export function Example() {
return (
Welcome
Getting Started
This is a body paragraph rendered with the Typography component.
Smaller supporting text for captions or footnotes.
npm install heroui-native
);
}
```
**变更要点:**
* 主导出改为 `Typography`,子组件为 `Typography.Heading`、`Typography.Paragraph`、`Typography.Code`
* 新增 `typographyClassNames` 样式 API 与 `Typography*` 类型别名,取代原先的 `Text*` 命名
* 既有的 `Text`、`textClassNames` 与 `Text*` 类型作为**弃用**的重导出保留以兼容旧代码
* 组件目录中的示例页由 `text` 重命名为 `typography`
* JSDoc 与组件文档更新为引用 Typography 以及新的文档 URL
**无需迁移。** 既有的 `import { Text } from "heroui-native"` 通过弃用重导出仍然可用。请在未来某个大版本移除弃用别名之前,按你的节奏迁移到 `Typography`。
**相关 PR:** [#417](https://github.com/heroui-inc/heroui-native/pull/417)
### 鲜亮主题配色(可选)
新增可选的 `heroui-native/styles/vibrant` 样式表,适合希望在 soft 变体上保持更饱和品牌色的应用。它在保留调整后的柔和前景令牌带来的可读性改进的同时,仍为 soft 背景上的图标与文字保留较高的色彩饱和度。
**用法:**
```ts
// 默认调整后的柔和前景配色
import "heroui-native/styles";
// 或者,启用鲜亮配色
import "heroui-native/styles";
import "heroui-native/styles/vibrant";
```
在引入基础样式之后再引入 `heroui-native/styles/vibrant`,即可用更饱和的取值覆盖柔和前景令牌。无需修改任何组件代码即可启用。
**相关 PR:** [#420](https://github.com/heroui-inc/heroui-native/pull/420)
## 组件改进
### 多组件柔和前景色调整
[Alert](/docs/native/components/alert)、[Avatar](/docs/native/components/avatar)、[Button](/docs/native/components/button)、[Chip](/docs/native/components/chip)、[Toast](/docs/native/components/toast) 现使用新的 `*-soft-foreground` 主题令牌渲染 soft 背景上的标签与图标,呈现更好的对比度,并在亮色与暗色主题下保持更统一的视觉效果。
**改进点:**
* `theme.css` 中通过 `color-mix` 计算柔和前景令牌(`accent-soft-foreground`、`success-soft-foreground`、`warning-soft-foreground`、`danger-soft-foreground`、`default-soft-foreground`),提升 soft 背景上的可读性
* Alert、Avatar、Button、Chip、Toast 的样式(及其内部 hooks)改为使用新的柔和前景令牌,不再直接使用 `text-accent`、`text-success` 等原始语义色
* 新增可选的 `heroui-native/styles/vibrant` 导出,为希望保留饱和品牌色的应用保留更鲜亮的 soft 变体外观
* 示例应用中 Alert、Avatar、Button、Chip、Toast 的演示同步更新以匹配新的取色方式
组件属性与公开 API 均无变化——仅 soft 变体的取色发生变化。沿用默认主题的应用将开箱获得 soft 变体上更柔和、可读性更佳的图标与标签颜色。
**相关 PR:** [#420](https://github.com/heroui-inc/heroui-native/pull/420)
### TextArea 垂直内边距修复
[TextArea](/docs/native/components/text-area) 现已应用合适的内部垂直内边距,多行内容不再紧贴输入区上边缘。
**改进点:**
* `TextArea` 现使用 `h-32 py-2`,提供一致的内部间距
* 无 API 变更——仅为组件内的样式调整
**相关 PR:** [#421](https://github.com/heroui-inc/heroui-native/pull/421)
## API 增强
### `useThemeColor` 令牌更新
[`useThemeColor`](/docs/native/hooks/use-theme-color) hook 新增表面色与柔和色相关令牌,并对遮罩背景令牌进行了重命名以更清晰表达语义。
**新能力:**
```tsx
import { useThemeColor } from "heroui-native";
const colors = useThemeColor([
"default-soft",
"default-soft-foreground",
"surface-foreground",
"backdrop",
]);
```
**变更:**
* 新增 `default-soft` 与 `default-soft-foreground` 令牌
* 新增表面前景色令牌(如 `surface-foreground`),可显式取用表面文字颜色
* 将 `overlay-backdrop` 重命名为 `backdrop`,与底层 CSS 变量命名保持一致
如果你使用了已移除的 `useThemeColor` 键(`on-surface-*`)或旧的 `overlay-backdrop` 键,请改用更新后的令牌名。组件属性与视觉默认值保持不变。
**相关 PR:** [#420](https://github.com/heroui-inc/heroui-native/pull/420)
## 依赖
### `@gorhom/bottom-sheet` 对等依赖范围更新
`@gorhom/bottom-sheet` 的对等依赖范围由 `^5.2.8` 升级到 `^5.2.9`。如果你的应用使用 HeroUI Native 的 [BottomSheet](/docs/native/components/bottom-sheet)(或 `Menu` / `Popover` / `Select` 配合 `presentation="bottom-sheet"`),请确保安装兼容版本:
```bash
npm i @gorhom/bottom-sheet@^5.2.9
```
这是本版本对消费者唯一的对等依赖变更。库的 `react`(`>=19.0.0`)与 `react-native`(`>=0.81.0`)对等依赖范围保持不变。
**相关 PR:** [#421](https://github.com/heroui-inc/heroui-native/pull/421)
### 示例应用升级至 Expo 56 / React Native 0.85
仓库内的示例应用升级到了最新的 Expo 与 React Native 工具链。这不会直接影响库的消费者,但为运行示例的贡献者带来收益:
* Expo `56`
* React Native `0.85.3`
* React `19.2.3`
* React Native Reanimated `4.3.1`
* `react-native-worklets` `0.8.3`
* Uniwind `^1.6.3`
本次升级还包含以下整理:
* 重写 `metro.config.js`,将对等依赖解析锁定到示例的 `node_modules`,修复当 `uniwind` 或 `react` 从 workspace 根解析出两份副本时 Hermes 抛出的「Maximum call stack size exceeded」崩溃
* 在 input-otp 与展示页中以 `StyleSheet.absoluteFill` 替换已弃用的 `StyleSheet.absoluteFillObject`
* 将 `useFocusEffect` / `useHeaderHeight` 的导入迁移至 `expo-router`
* 移除未使用的 `example/src/components/safe-area-view.tsx`、`eas.json` 以及陈旧的 `newArchEnabled` / `edgeToEdgeEnabled` 标记
* 示例应用的 slug/bundle 重命名为 `heroui-native-oss`;调整 Android 上 `WithStateToggle` 的内边距
**相关 PR:** [#421](https://github.com/heroui-inc/heroui-native/pull/421)
## 文档
### Menu、Popover、Select 的 iOS 原生模态偏移说明
[Menu](/docs/native/components/menu)、[Popover](/docs/native/components/popover)、[Select](/docs/native/components/select) 文档新增\*\*原生模态(iOS)\*\*章节,解释当触发器位于以原生模态(`presentation: "modal" | "formSheet" | "pageSheet"`)呈现的页面内时,遮罩内容为何会向上偏移渲染,以及如何补偿。
**改进点:**
* 各组件文档说明了 Fabric / `FullWindowOverlay` 的坐标不一致:触发器坐标相对于模态原点,而遮罩锚定在窗口
* 文档化了配合 `react-native-safe-area-context` 中 `useSafeAreaInsets` 使用的 `offset={insets.top}` 推荐方案
* 提供示例应用中相关文件(`popover-native-modal.tsx`、`select-native-modal.tsx`)的链接,便于查阅完整用法
**用法示例:**
```tsx
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { Popover } from "heroui-native";
const insets = useSafeAreaInsets();
{/* ... */}
;
```
这是纯文档变更,未改动任何 API 或运行时行为。
**相关 PR:** [#419](https://github.com/heroui-inc/heroui-native/pull/419)
## 问题修复
本版本包含以下修复:
* **[Issue #405](https://github.com/heroui-inc/heroui-native/issues/405)**:补充了在 React Navigation 原生模态中 `Menu` 渲染错位的处理方案。Menu 文档新增\*\*原生模态(iOS)\*\*章节,解释 Fabric / `FullWindowOverlay` 的坐标不一致,并演示如何用 `useSafeAreaInsets` 配合 `offset={insets.top}` 补偿。
* **[Issue #418](https://github.com/heroui-inc/heroui-native/issues/418)**:修复将示例应用升级到 Expo 56 后出现的 Hermes 崩溃(「Maximum call stack size exceeded」)及 `BottomSheet` 内容不可见的问题。示例的 `metro.config.js` 现已将对等依赖解析(`react`、`react-native`、`uniwind` 等)锁定到示例的本地 `node_modules`,防止从 workspace 根加载到重复副本。
**相关 PR:**
* [#419](https://github.com/heroui-inc/heroui-native/pull/419)
* [#421](https://github.com/heroui-inc/heroui-native/pull/421)
## 弃用
### `Text` 排版导出
`Text` 组件与相关导出现已**弃用**,推荐使用 `Typography`。既有导入仍可继续工作,不会产生运行时错误——仅会在 TypeScript / IDE 中提示弃用。请在未来某个大版本移除弃用别名之前完成迁移。
**弃用 → 推荐:**
```tsx
// 弃用(仍可用)
import { Text, textClassNames, type TextProps } from "heroui-native";
Welcome ;
// 推荐
import {
Typography,
typographyClassNames,
type TypographyProps,
} from "heroui-native";
Welcome ;
```
**相关 PR:** [#417](https://github.com/heroui-inc/heroui-native/pull/417)
## 文档更新
以下文档页面已随本版本更新:
* [Typography](/docs/native/components/typography) — 组件由 Text 重命名而来;完整 API、Anatomy 与用法已更新
* [Menu](/docs/native/components/menu) — 新增\*\*原生模态(iOS)\*\*章节,含 `offset={insets.top}` 方案
* [Popover](/docs/native/components/popover) — 新增\*\*原生模态(iOS)\*\*章节,含 `offset={insets.top}` 方案
* [Select](/docs/native/components/select) — 新增\*\*原生模态(iOS)\*\*章节,含 `offset={insets.top}` 方案
* [快速开始](/docs/native/getting-started/quick-start) — 可选对等依赖 `@gorhom/bottom-sheet` 更新到 `^5.2.9`
## 链接
* [组件文档](../components)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-native)
## 贡献者
感谢所有为本版本做出贡献的朋友!
# 所有组件
**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)
## 贡献者
感谢所有为本次发布做出贡献的人!
# Button 按钮
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/button
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(buttons)/button.mdx
> 按下时触发操作的交互组件。
## 导入
```tsx
import { Button } from 'heroui-native';
```
## 结构
```tsx
...
```
* **Button**:主容器,负责按压交互、动画与变体。字符串子节点会渲染为标签,也可使用复合子组件自定义布局。
* **Button.Label**:按钮文字,继承父级 Button 上下文中的尺寸与变体样式。
## 用法
### 基础用法
`Button` 可直接传入字符串子节点,会自动渲染为标签。
```tsx
基础按钮
```
### 使用复合子组件
使用 `Button.Label` 显式控制标签部分。
```tsx
点我
```
### 与图标组合
将图标与文字组合,增强可读性。
```tsx
添加项目
下载
```
### 仅图标
使用 `isIconOnly` 创建方形纯图标按钮。
```tsx
```
### 尺寸
通过三种尺寸控制按钮大小。
```tsx
小
中
大
```
### 变体
提供七种视觉变体,用于不同强调层级。
```tsx
主要
次要
第三级
描边
幽灵
危险
柔和危险
```
### 反馈变体
`feedbackVariant` 控制渲染哪些按压反馈效果:
* `'scale-highlight'`(默认):内置缩放 + 高亮遮罩
* `'scale-ripple'`:内置缩放 + 水波纹遮罩
* `'scale'`:仅内置缩放(无遮罩)
* `'none'`:无任何反馈动画
```tsx
{/* 缩放 + 高亮(默认) */}
高亮效果
{/* 缩放 + 水波纹 */}
水波纹效果
{/* 仅缩放 */}
仅缩放
{/* 无反馈 */}
无反馈
```
### 自定义动画
`animation` 控制各子动画,其结构取决于 `feedbackVariant`。
```tsx
{/* 自定义缩放与高亮(默认 feedbackVariant) */}
自定义高亮
{/* 自定义缩放与水波纹 */}
自定义水波纹
```
### 关闭部分子动画
将某个子动画设为 `false` 即可单独关闭:
```tsx
{/* 关闭缩放,保留高亮 */}
无缩放
{/* 关闭高亮,保留缩放 */}
无高亮
{/* 两者都关 */}
无动画
```
### 关闭全部动画
使用 `animation={false}` 关闭所有反馈,或使用 `animation="disable-all"` 级联关闭:
```tsx
已禁用动画
全部禁用(级联)
```
### 加载态与 Spinner
配合 Spinner 展示加载状态。
```tsx
const themeColorAccentForeground = useThemeColor('accent-foreground');
{
setIsDownloading(true);
setTimeout(() => {
setIsDownloading(false);
}, 3000);
}}
isIconOnly={isDownloading}
className="self-center"
>
{isDownloading ? (
) : (
'立即下载'
)}
;
```
### 使用 LinearGradient 自定义背景
通过绝对定位元素添加渐变背景。使用 `feedbackVariant="none"` 关闭默认高亮遮罩,或使用 `feedbackVariant="scale-ripple"` 自定义水波纹。
```tsx
import { Button, PressableFeedback } from 'heroui-native';
import { LinearGradient } from 'expo-linear-gradient';
import { StyleSheet } from 'react-native';
{/* 无反馈遮罩的渐变 */}
渐变
{/* 带自定义水波纹的渐变 */}
带水波纹的渐变
```
## 示例
```tsx
import { Button, useThemeColor } from 'heroui-native';
import { Ionicons } from '@expo/vector-icons';
import { View } from 'react-native';
export default function ButtonExample() {
const [
themeColorAccentForeground,
themeColorAccentSoftForeground,
themeColorDangerForeground,
themeColorDefaultForeground,
] = useThemeColor([
'accent-foreground',
'accent-soft-foreground',
'danger-foreground',
'default-foreground',
]);
return (
添加项目
了解更多
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/button.tsx)。
## API 参考
### Button
`Button` 继承 [PressableFeedback](./pressable-feedback) 的全部属性(`animation` 除外,已重新定义),并增加按钮专用属性。
| prop | type | default | description |
| ----------------- | --------------------------------------------------------------------------------------------- | ------------------- | ----------------------------- |
| `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'outline' \| 'ghost' \| 'danger' \| 'danger-soft'` | `'primary'` | 按钮视觉变体 |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 按钮尺寸 |
| `isIconOnly` | `boolean` | `false` | 是否为仅图标按钮(方形比例) |
| `feedbackVariant` | `'scale-highlight' \| 'scale-ripple' \| 'scale' \| 'none'` | `'scale-highlight'` | 决定渲染哪些反馈效果 |
| `animation` | `ButtonAnimation` | - | 动画配置(结构取决于 `feedbackVariant`) |
继承属性(含 `isDisabled`、`className`、`children` 及所有 Pressable 属性)见 [PressableFeedback API 参考](./pressable-feedback#api-reference)。
#### ButtonAnimation
`animation` 是按 `feedbackVariant` 区分的联合类型,遵循 `AnimationRoot` 控制流:
* `true` 或 `undefined`:使用默认动画
* `false` 或 `"disabled"`:关闭所有反馈动画
* `"disable-all"`:级联关闭所有动画(含子复合部件)
* `object`:自定义子动画配置(见下)
**当 `feedbackVariant="scale-highlight"`(默认)时:**
| prop | type | default | description |
| ----------- | ---------------------------------------- | ------- | --------------------- |
| `scale` | `PressableFeedbackScaleAnimation` | - | 缩放动画配置(`false` 为关闭) |
| `highlight` | `PressableFeedbackHighlightAnimation` | - | 高亮遮罩配置(`false` 为关闭) |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | 在保留配置的同时控制动画状态(运行时切换) |
**当 `feedbackVariant="scale-ripple"` 时:**
| prop | type | default | description |
| -------- | ---------------------------------------- | ------- | --------------------- |
| `scale` | `PressableFeedbackScaleAnimation` | - | 缩放动画配置(`false` 为关闭) |
| `ripple` | `PressableFeedbackRippleAnimation` | - | 水波纹遮罩配置(`false` 为关闭) |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | 在保留配置的同时控制动画状态(运行时切换) |
**当 `feedbackVariant="scale"` 时:**
| prop | type | default | description |
| ------- | ---------------------------------------- | ------- | --------------------- |
| `scale` | `PressableFeedbackScaleAnimation` | - | 缩放动画配置(`false` 为关闭) |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | 在保留配置的同时控制动画状态(运行时切换) |
**当 `feedbackVariant="none"` 时:**
仅接受字符串 `'disable-all'`。所有反馈效果均被禁用。
动画子类型(`PressableFeedbackScaleAnimation`、`PressableFeedbackHighlightAnimation`、`PressableFeedbackRippleAnimation`)详见 [PressableFeedback API 参考](./pressable-feedback#api-reference)。
### Button.Label
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------- |
| `children` | `React.ReactNode` | - | 作为标签渲染的内容 |
| `className` | `string` | - | 额外 CSS 类 |
| `...TextProps` | `TextProps` | - | 支持全部标准 Text 属性 |
## Hooks
### useButton
用于读取 Button 上下文,返回尺寸、变体与禁用状态。
```tsx
import { useButton } from 'heroui-native';
const { size, variant, isDisabled } = useButton();
```
#### 返回值
| property | type | description |
| ------------ | --------------------------------------------------------------------------------------------- | ----------- |
| `size` | `'sm' \| 'md' \| 'lg'` | 按钮尺寸 |
| `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'outline' \| 'ghost' \| 'danger' \| 'danger-soft'` | 按钮视觉变体 |
| `isDisabled` | `boolean` | 是否禁用 |
**说明:** 必须在 `Button` 内使用;在按钮上下文外调用会抛错。
# CloseButton 关闭按钮
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/close-button
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(buttons)/close-button.mdx
> 用于关闭对话框、模态框或收起内容的按钮组件。
## 导入
```tsx
import { CloseButton } from 'heroui-native';
```
## 用法
### 基础用法
CloseButton 渲染带默认样式的关闭图标按钮。
```tsx
```
### 自定义图标颜色
通过 `iconProps` 自定义图标颜色。
```tsx
```
### 自定义图标尺寸
通过 `iconProps` 调整图标大小。
```tsx
```
### 自定义子节点
用自定义内容替换默认关闭图标。
```tsx
```
### 禁用态
禁用按钮以禁止交互。
```tsx
```
## 示例
```tsx
import { CloseButton, useThemeColor } from 'heroui-native';
import { Ionicons } from '@expo/vector-icons';
import { View } from 'react-native';
import { withUniwind } from 'uniwind';
const StyledIonicons = withUniwind(Ionicons);
export default function CloseButtonExample() {
const themeColorForeground = useThemeColor('foreground');
const themeColorDanger = useThemeColor('danger');
return (
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/close-button.tsx)。
## API 参考
### CloseButton
CloseButton 继承 [Button](./button) 的全部属性。默认 `variant='tertiary'`、`size='sm'`、`isIconOnly=true`。
| prop | type | default | description |
| ----------- | ---------------------- | ------- | -------------- |
| `iconProps` | `CloseButtonIconProps` | - | 自定义关闭图标属性 |
| `children` | `React.ReactNode` | - | 自定义内容,替换默认关闭图标 |
`isDisabled`、`className`、`animation`、`feedbackVariant` 以及所有 Pressable 相关继承属性见 [Button API 参考](./button#api-reference)。
#### CloseButtonIconProps
| prop | type | default | description |
| ------- | -------- | ---------------------- | ----------- |
| `size` | `number` | `20` | 图标尺寸 |
| `color` | `string` | Uses theme muted color | 图标颜色 |
# LinkButton 链接按钮
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/link-button
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(buttons)/link-button.mdx
> 幽灵样式按钮,无高亮按压反馈,适合行内链接式交互。
## 导入
```tsx
import { LinkButton } from 'heroui-native';
```
## 结构
```tsx
...
```
* **LinkButton**:根级可按压容器。内部渲染 `variant="ghost"` 的 `Button`,并强制关闭高亮反馈;使用者无法覆盖上述行为。
* **LinkButton.Label**:链接按钮文字,继承父级上下文中的尺寸与变体样式。
## 用法
### 基础用法
行内链接风格文字,响应按压。
```tsx
了解更多
```
### 尺寸
使用 `size` 控制文字尺寸。
```tsx
小
中
大
```
### 禁用状态
禁用后不可交互。
```tsx
已禁用的链接
```
### 自定义样式
在根与 `Label` 上使用 `className`。
```tsx
样式化链接
```
### 与正文混排
与普通文字混排,用于条款、政策或上下文导航。
```tsx
我同意
服务条款
与
隐私政策
```
## 示例
```tsx
import { Button, Checkbox, ControlField, LinkButton } from 'heroui-native';
import React from 'react';
import { Alert, View, Text } from 'react-native';
export default function LinkButtonExample() {
const [isAgreed, setIsAgreed] = React.useState(false);
const handleTermsPress = () => Alert.alert('条款', '跳转至服务条款');
const handlePrivacyPress = () =>
Alert.alert('隐私', '跳转至隐私政策');
return (
我同意
服务条款
与
隐私政策
注册
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/link-button.tsx)。
## API 参考
### LinkButton
继承 [Button](./button#button) 的全部属性,**除 `variant` 外**(内部固定为 `ghost`)。
**内部强制行为:**
| override | value | description |
| ----------- | ------------ | -------------- |
| `variant` | `ghost` | 始终为 ghost,不可修改 |
| `highlight` | `false` | 高亮反馈关闭,不可修改 |
| `className` | `h-auto p-0` | 移除默认按钮高度与内边距 |
### LinkButton.Label
与 [Button.Label](./button#buttonlabel) 等价,属性相同。
# Menu 菜单
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/menu
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(collections)/menu.mdx
> 浮动上下文菜单,支持定位、选择分组与多种呈现方式。
## 导入
```tsx
import { Menu, SubMenu } from 'heroui-native';
```
## 结构
```tsx
...
...
...
...
...
...
...
```
* **Menu**:主容器,管理开闭状态与定位,并向子组件提供上下文。
* **Menu.Trigger**:可点击元素,用于切换菜单显隐。
* **Menu.Portal**:在 Portal 层渲染菜单内容,叠于其他内容之上。
* **Menu.Overlay**:可选背景遮罩,用于捕获外部点击并关闭菜单。
* **Menu.Content**:菜单内容容器;两种呈现:带定位与碰撞检测的浮动 Popover,或底部抽屉式 Bottom Sheet。
* **Menu.Close**:关闭按钮,按下后关闭菜单。
* **Menu.Label**:菜单内的非交互分区标题。
* **Menu.Group**:对菜单项分组,可选选择模式(无 / 单选 / 多选)。
* **Menu.Item**:可按压菜单项,带按压动画反馈;可独立使用或置于 Group 内参与选择。
* **Menu.ItemTitle**:菜单项主标题文本。
* **Menu.ItemDescription**:菜单项次要说明文本。
* **Menu.ItemIndicator**:菜单项选中指示(对勾或圆点)。
* **SubMenu**:子菜单根容器,管理展开/收起状态并为子级提供动画上下文。
* **SubMenu.Trigger**:可按压行,切换子菜单开闭;样式与普通菜单项一致。
* **SubMenu.TriggerIndicator**:动画 V 形图标(默认 chevron-right),随子菜单开闭旋转;放在 `SubMenu.Trigger` 内。
* **SubMenu.Content**:绝对定位容器,子菜单开闭时带动画高度变化;其内放置 `Menu.Item` 等。
## 用法
### 基础用法
Menu 通过复合部件组成浮动上下文菜单。
```tsx
...
View Profile
Settings
```
### 带副标题
在标题旁为菜单项添加次要说明文字。
```tsx
...
New file
Create a new file
Copy link
Copy the file link
```
### 单选
使用 `Menu.Group` 并设置 `selectionMode="single"`,同一时间仅允许选中一项。
```tsx
const [theme, setTheme] = useState>(() => new Set(['system']));
...
Appearance
Light
Dark
System
;
```
### 多选
使用 `selectionMode="multiple"` 可同时选中多项。
```tsx
const [textStyles, setTextStyles] = useState>(
() => new Set(['bold', 'italic'])
);
...
Text Style
Bold
Italic
Underline
;
```
### 子菜单
在 `Menu.Content` 内嵌套 `SubMenu`,按压后展开更多项。
```tsx
Editor Menu
New Space
Focus
Zen Mode
Reader Mode
Lock Tab
Heading 1
```
### 危险变体
对破坏性操作在菜单项上使用 `variant="danger"`。
```tsx
...
Edit
Delete
```
### 方位
控制菜单相对触发器出现的位置。
```tsx
...
Option A
Option B
```
### Bottom Sheet 呈现
使用 `presentation="bottom-sheet"` 以底部抽屉形式展示菜单内容。
```tsx
...
Option A
Option B
```
### 圆点指示器
在 `Menu.ItemIndicator` 上使用 `variant="dot"` 显示实心圆点,而非对勾。
```tsx
...
Left
Center
Right
```
## 示例
```tsx
import type { MenuKey } from 'heroui-native';
import { Button, Menu, Separator } from 'heroui-native';
import { useState } from 'react';
import { Text, View } from 'react-native';
export default function MenuExample() {
const [textStyles, setTextStyles] = useState>(
() => new Set(['bold', 'italic'])
);
const [alignment, setAlignment] = useState>(
() => new Set(['left'])
);
return (
Styles
Text Style
Bold
⌘ B
Italic
⌘ I
Underline
⌘ U
Text Alignment
Left
Center
Right
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/menu.tsx)。
## API 参考
### Menu
| prop | type | default | description |
| --------------- | ----------------------------- | ----------- | ------------------------------ |
| `children` | `React.ReactNode` | - | 菜单内容 |
| `presentation` | `'popover' \| 'bottom-sheet'` | `'popover'` | 菜单内容的呈现方式 |
| `isOpen` | `boolean` | - | 受控开闭状态 |
| `isDefaultOpen` | `boolean` | - | 非受控:首次渲染时是否打开 |
| `isDisabled` | `boolean` | - | 是否禁用菜单 |
| `animation` | `MenuRootAnimation` | - | 菜单根级动画配置 |
| `onOpenChange` | `(open: boolean) => void` | - | 开闭状态变化时触发 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
#### MenuRootAnimation
菜单根组件的动画配置,可为:
* `"disable-all"`:关闭所有动画(含子级)
* `true` 或 `undefined`:使用默认动画
### Menu.Trigger
| prop | type | default | description |
| ------------------- | ----------------- | ------- | ----------------------------------- |
| `children` | `React.ReactNode` | - | 触发器内容 |
| `className` | `string` | - | 触发器额外 class |
| `isDisabled` | `boolean` | `false` | 是否禁用触发器 |
| `asChild` | `boolean` | - | 使用 Slot 模式将行为合并到单个子元素 |
| `...PressableProps` | `PressableProps` | - | 支持 React Native `Pressable` 的全部标准属性 |
### Menu.Portal
| prop | type | default | description |
| -------------------------------------------- | ----------------- | ------- | --------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Portal 内容 |
| `className` | `string` | - | Portal 容器额外 class |
| `disableFullWindowOverlay` | `boolean` | `false` | 在 iOS 上使用普通 `View` 替代 `FullWindowOverlay` |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | 控制 VoiceOver 是否将遮罩窗口视为模态容器。为 `true` 时焦点限制在遮罩内。仅 iOS。不稳定:可能随 `react-native-screens` 更新变化 |
| `hostName` | `string` | - | Portal 宿主元素的可选名称 |
| `forceMount` | `boolean` | - | 无论开闭状态是否强制挂载 Portal |
### Menu.Overlay
| prop | type | default | description |
| ----------------------- | ---------------------- | ------- | ----------------------------------- |
| `className` | `string` | - | 遮罩额外 class |
| `closeOnPress` | `boolean` | `true` | 点击遮罩时是否关闭菜单 |
| `animation` | `MenuOverlayAnimation` | - | 遮罩动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `forceMount` | `boolean` | - | 无论开闭是否强制挂载遮罩 |
| `...PressableProps` | `PressableProps` | - | 支持 React Native `Pressable` 的全部标准属性 |
#### MenuOverlayAnimation
菜单遮罩的动画配置,可为:
* `false` 或 `"disabled"`:关闭所有动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ------------------------ | ----------------------- | ----------------------- | --------------- |
| `state` | `'disabled' \| boolean` | - | 关闭动画的同时仍允许自定义属性 |
| `opacity.entering.value` | `EntryOrExitLayoutType` | `FadeIn.duration(200)` | 遮罩进入动画 |
| `opacity.exiting.value` | `EntryOrExitLayoutType` | `FadeOut.duration(150)` | 遮罩退出动画 |
### Menu.Content(Popover)
当 `presentation="popover"` 时的属性。
| prop | type | default | description |
| ----------------- | ------------------------------------------------ | --------------- | ------------------------------ |
| `children` | `React.ReactNode` | - | 菜单内容 |
| `presentation` | `'popover'` | - | 呈现方式(须与 Menu 根一致) |
| `placement` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | 相对触发器的弹出方位 |
| `align` | `'start' \| 'center' \| 'end'` | `'center'` | 沿对齐轴相对触发器的对齐方式 |
| `avoidCollisions` | `boolean` | `true` | 是否自动避让屏幕边缘 |
| `offset` | `number` | `9` | 与触发器的间距(像素) |
| `alignOffset` | `number` | `0` | 沿对齐轴的偏移(像素) |
| `width` | `'content-fit' \| 'trigger' \| 'full' \| number` | `'content-fit'` | 内容宽度策略 |
| `className` | `string` | - | 内容容器额外 class |
| `animation` | `MenuContentAnimation` | - | 内容动画配置 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
#### MenuContentAnimation
Popover 内容动画配置,可为:
* `false` 或 `"disabled"`:关闭所有动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ---------------- | ----------------------- | ------------------------------- | --------------- |
| `state` | `'disabled' \| boolean` | - | 关闭动画的同时仍允许自定义属性 |
| `entering.value` | `EntryOrExitLayoutType` | Scale + fade entering animation | 自定义进入动画 |
| `exiting.value` | `EntryOrExitLayoutType` | Scale + fade exiting animation | 自定义退出动画 |
### Menu.Content(Bottom Sheet)
当 `presentation="bottom-sheet"` 时的属性。继承 `@gorhom/bottom-sheet` 的 BottomSheet 属性。
| prop | type | default | description |
| --------------------------- | ---------------------------------------- | ------- | ------------------------------- |
| `children` | `React.ReactNode` | - | 底部抽屉内容 |
| `presentation` | `'bottom-sheet'` | - | 呈现方式(须与 Menu 根一致) |
| `className` | `string` | - | 底部抽屉额外 class |
| `backgroundClassName` | `string` | - | 背景额外 class |
| `handleIndicatorClassName` | `string` | - | 把手指示条额外 class |
| `contentContainerClassName` | `string` | - | 内容容器额外 class |
| `contentContainerProps` | `Omit` | - | 内容容器属性 |
| `animation` | `AnimationDisabled` | - | 设为 `false` 或 `"disabled"` 可关闭动画 |
| `...BottomSheetProps` | `Partial` | - | 支持 `@gorhom/bottom-sheet` 的全部属性 |
### Menu.Close
继承 `CloseButtonProps`。按下后自动关闭菜单。
| prop | type | default | description |
| ---------------- | ---------------------- | ------- | ---------------- |
| `iconProps` | `CloseButtonIconProps` | - | 自定义关闭图标属性 |
| `...ButtonProps` | `ButtonRootProps` | - | 支持 Button 根级全部属性 |
### Menu.Group
| prop | type | default | description |
| --------------------- | ---------------------------------- | -------- | ------------------------------ |
| `children` | `React.ReactNode` | - | 分组内容(`Menu.Item` 等) |
| `selectionMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | 分组内允许的选择类型 |
| `selectedKeys` | `Iterable` | - | 当前选中键(受控) |
| `defaultSelectedKeys` | `Iterable` | - | 初始选中键(非受控) |
| `isDisabled` | `boolean` | `false` | 是否禁用整个分组 |
| `disabledKeys` | `Iterable` | - | 应禁用的项键集合 |
| `shouldCloseOnSelect` | `boolean` | - | 选中项时是否关闭菜单 |
| `className` | `string` | - | 分组容器额外 class |
| `onSelectionChange` | `(keys: Set) => void` | - | 选中变化时回调 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
### Menu.Label
| prop | type | default | description |
| -------------- | ----------------- | ------- | ------------------------------ |
| `children` | `React.ReactNode` | - | 标签文本内容 |
| `className` | `string` | - | 标签额外 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部标准属性 |
### Menu.Item
| prop | type | default | description |
| ----------------------- | ---------------------------------------------------------------- | ----------- | ----------------------------------- |
| `children` | `React.ReactNode \| ((props: MenuItemRenderProps) => ReactNode)` | - | 子元素或渲染函数 |
| `id` | `MenuKey` | - | 唯一标识;在 `Menu.Group` 内时必填 |
| `variant` | `'default' \| 'danger'` | `'default'` | 菜单项视觉变体 |
| `isDisabled` | `boolean` | `false` | 是否禁用该项 |
| `isSelected` | `boolean` | - | 独立项时的受控选中状态 |
| `shouldCloseOnSelect` | `boolean` | `true` | 按压该项是否关闭菜单 |
| `className` | `string` | - | 菜单项额外 class |
| `animation` | `MenuItemAnimation` | - | 按压反馈动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `onSelectedChange` | `(selected: boolean) => void` | - | 独立项选中状态变化时回调 |
| `...PressableProps` | `PressableProps` | - | 支持 React Native `Pressable` 的全部标准属性 |
#### MenuItemRenderProps
当 `children` 为函数时传入渲染函数的参数。
| prop | type | description |
| ------------ | ----------------------- | ----------- |
| `isSelected` | `boolean` | 当前项是否选中 |
| `isDisabled` | `boolean` | 是否禁用 |
| `isPressed` | `SharedValue` | 是否处于按压中 |
| `variant` | `'default' \| 'danger'` | 项的视觉变体 |
#### MenuItemAnimation
菜单项按压反馈动画配置,可为:
* `false` 或 `"disabled"`:关闭项动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ------------------------------ | ------------------ | -------------------------- | ----------- |
| `scale.value` | `number` | `0.98` | 按压时的缩放值 |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | 缩放的动画配置 |
| `backgroundColor.value` | `string` | `useThemeColor('default')` | 按压时背景色 |
| `backgroundColor.timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | 背景色过渡时间配置 |
### Menu.ItemTitle
| prop | type | default | description |
| -------------- | ----------------- | ------- | ------------------------------ |
| `children` | `React.ReactNode` | - | 标题文本内容 |
| `className` | `string` | - | 标题额外 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部标准属性 |
### Menu.ItemDescription
| prop | type | default | description |
| -------------- | ----------------- | ------- | ------------------------------ |
| `children` | `React.ReactNode` | - | 说明文本内容 |
| `className` | `string` | - | 说明额外 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部标准属性 |
### Menu.ItemIndicator
| prop | type | default | description |
| -------------- | ---------------------------- | ------------- | ------------------------------ |
| `children` | `React.ReactNode` | - | 自定义指示内容;默认为对勾或圆点 |
| `variant` | `'checkmark' \| 'dot'` | `'checkmark'` | 指示器视觉变体 |
| `iconProps` | `MenuItemIndicatorIconProps` | - | 图标配置(对勾变体) |
| `forceMount` | `boolean` | `true` | 无论是否选中都强制挂载指示器 |
| `className` | `string` | - | 指示器额外 class |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
#### MenuItemIndicatorIconProps
| prop | type | default | description |
| ------- | -------- | ------- | --------------- |
| `size` | `number` | `16` | 指示图标尺寸(圆点变体为 8) |
| `color` | `string` | `muted` | 指示图标颜色 |
### SubMenu
| prop | type | default | description |
| --------------- | ------------------------- | ------- | ------------------------------ |
| `children` | `React.ReactNode` | - | 子菜单内容(触发器、内容区等) |
| `isOpen` | `boolean` | - | 受控开闭状态 |
| `isDefaultOpen` | `boolean` | - | 非受控:首次渲染时是否打开 |
| `isDisabled` | `boolean` | `false` | 是否禁用子菜单 |
| `className` | `string` | - | 根容器额外 class |
| `animation` | `SubMenuRootAnimation` | - | 子菜单动画配置 |
| `onOpenChange` | `(open: boolean) => void` | - | 开闭状态变化时回调 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
##### SubMenuRootAnimation
SubMenu 根组件动画配置,可为:
* `"disable-all"`:关闭所有动画(含子级)
* `false` 或 `"disabled"`:仅关闭根级动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ------------------------------- | ----------------------- | ------------------------------------------- | --------------- |
| `state` | `'disabled' \| boolean` | - | 关闭动画的同时仍允许自定义属性 |
| `rootContent.marginHorizontal` | `number` | `-16` | 子菜单打开时水平外边距 |
| `rootContent.marginVertical` | `number` | `-16` | 子菜单打开时垂直外边距 |
| `rootContent.paddingHorizontal` | `number` | `6` | 子菜单打开时水平内边距 |
| `rootContent.paddingTop` | `number` | `12` | 子菜单打开时顶部内边距 |
| `rootContent.springConfig` | `WithSpringConfig` | `{ damping: 100, stiffness: 950, mass: 3 }` | 展开/收起的弹簧配置 |
#### SubMenu.Trigger
| prop | type | default | description |
| ------------------- | ----------------- | ------- | ----------------------------------- |
| `children` | `React.ReactNode` | - | 触发器内容(标题、图标、指示器等) |
| `textValue` | `string` | - | 读屏播报的无障碍文本 |
| `className` | `string` | - | 触发器额外 class |
| `isDisabled` | `boolean` | `false` | 是否禁用 |
| `asChild` | `boolean` | - | 使用 Slot 模式合并到单个子元素 |
| `...PressableProps` | `PressableProps` | - | 支持 React Native `Pressable` 的全部标准属性 |
#### SubMenu.TriggerIndicator
子菜单开闭时旋转的指示图标,默认为向右 V 形(chevron-right)。
| prop | type | default | description |
| ----------------------- | ---------------------------------- | ------- | ------------------------------ |
| `children` | `React.ReactNode` | - | 自定义指示内容(替换默认 V 形) |
| `className` | `string` | - | 指示器额外 class |
| `iconProps` | `SubMenuTriggerIndicatorIconProps` | - | 默认 V 形的图标配置 |
| `animation` | `SubMenuTriggerIndicatorAnimation` | - | 指示器旋转动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
##### SubMenuTriggerIndicatorIconProps
| prop | type | default | description |
| ------- | -------- | ------- | ----------- |
| `size` | `number` | `14` | 指示图标尺寸 |
| `color` | `string` | `muted` | 指示图标颜色 |
##### SubMenuTriggerIndicatorAnimation
触发器指示旋转的动画配置,可为:
* `false` 或 `"disabled"`:关闭所有动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ----------------------- | ----------------------- | -------------------------------------------- | ------------------ |
| `state` | `'disabled' \| boolean` | - | 关闭动画的同时仍允许自定义属性 |
| `rotation.value` | `[number, number]` | `[0, 90]` | 旋转角度 \[收起, 展开],单位度 |
| `rotation.springConfig` | `WithSpringConfig` | `{ damping: 140, stiffness: 1000, mass: 4 }` | 旋转弹簧配置 |
#### SubMenu.Content
| prop | type | default | description |
| ------------------- | ----------------- | ------- | ----------------------------------- |
| `children` | `React.ReactNode` | - | 子菜单项(`Menu.Item`、`Menu.Group` 等) |
| `className` | `string` | - | 内容容器额外 class |
| `...PressableProps` | `PressableProps` | - | 支持 React Native `Pressable` 的全部标准属性 |
## Hooks
### useMenu
访问菜单根上下文,须在 `Menu` 内使用。
```tsx
import { useMenu } from 'heroui-native';
const { isOpen, onOpenChange, presentation, isDisabled } = useMenu();
```
#### 返回值
| property | type | description |
| -------------- | ----------------------------- | ----------- |
| `isOpen` | `boolean` | 菜单是否打开 |
| `onOpenChange` | `(open: boolean) => void` | 修改开闭状态的回调 |
| `presentation` | `'popover' \| 'bottom-sheet'` | 当前呈现模式 |
| `isDisabled` | `boolean \| undefined` | 是否禁用 |
| `nativeID` | `string` | 菜单实例唯一标识 |
### useMenuItem
访问菜单项上下文,须在 `Menu.Item` 内使用。
```tsx
import { useMenuItem } from 'heroui-native';
const { id, isSelected, isDisabled, variant } = useMenuItem();
```
#### 返回值
| property | type | description |
| ------------ | ----------------------- | ----------- |
| `id` | `MenuKey \| undefined` | 项标识 |
| `isSelected` | `boolean` | 是否选中 |
| `isDisabled` | `boolean` | 是否禁用 |
| `variant` | `'default' \| 'danger'` | 项的视觉变体 |
### useMenuAnimation
访问菜单动画上下文,须在 `Menu` 内使用。
```tsx
import { useMenuAnimation } from 'heroui-native';
const { progress, isDragging } = useMenuAnimation();
```
#### 返回值
| property | type | description |
| ------------ | ---------------------- | -------------------- |
| `progress` | `SharedValue` | 动画进度(0=空闲,1=打开,2=关闭) |
| `isDragging` | `SharedValue` | Bottom Sheet 是否正在被拖拽 |
### useSubMenu
访问子菜单上下文,须在 `SubMenu` 内使用。
```tsx
import { useSubMenu } from 'heroui-native';
const { isOpen, onOpenChange, isDisabled } = useSubMenu();
```
#### 返回值
| property | type | description |
| -------------- | ------------------------- | ----------- |
| `isOpen` | `boolean` | 子菜单是否打开 |
| `onOpenChange` | `(open: boolean) => void` | 修改开闭状态的回调 |
| `isDisabled` | `boolean` | 是否禁用 |
| `nativeID` | `string` | 子菜单实例唯一标识 |
## 特别说明
### 元素检查器(iOS)
Menu 在 iOS 上使用 `FullWindowOverlay`。开发时若需启用 React Native 元素检查器,请在 `Menu.Portal` 上设置 `disableFullWindowOverlay={true}`。代价是菜单将无法叠在原生模态之上。
### 原生模态(iOS)
当 `Menu` 位于以原生模态形式呈现的页面内时(`presentation: 'modal' | 'formSheet' | 'pageSheet'`),菜单内容可能会向上偏移渲染。在新架构(Fabric)中,`react-native-screens` 将 `RNSModalScreen` 标记为 Fabric 根节点,因此触发器的坐标是相对于模态原点上报的,而 `FullWindowOverlay`(菜单挂载点)锚定在 iOS 应用窗口上。可通过将 `safeAreaInsets.top` 加到 `offset` 来补偿:
```tsx
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const insets = useSafeAreaInsets();
...
;
```
# TagGroup 标签组
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/tag-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(collections)/tag-group.mdx
> 用于展示与管理可选标签的复合组件,支持可选移除。
## 导入
```tsx
import { TagGroup } from 'heroui-native';
```
## 结构
```tsx
...
```
* **TagGroup**:主容器,管理标签选中状态、禁用键与移除能力,并向子组件提供尺寸与变体上下文。
* **TagGroup.List**:渲染标签列表的容器,可渲染空状态。
* **TagGroup.Item**:组内单个标签。支持字符串子节点(自动包在 `TagGroup.ItemLabel`)、渲染函数子节点或自定义布局。
* **TagGroup.ItemLabel**:标签文字。提供字符串子节点时会自动渲染,也可显式使用。
* **TagGroup.ItemRemoveButton**:移除按钮;需要移除能力时需显式放置。仅当 `TagGroup` 传入 `onRemove` 时生效。
## 用法
### 基础用法
展示一个简单的可选标签组。
```tsx
新闻
旅行
游戏
```
### 单选模式
同一时间只能选中一个标签。
```tsx
新闻
旅行
游戏
```
### 多选模式
允许多个标签同时选中。
```tsx
新闻
旅行
游戏
```
### 受控选中
通过 `selectedKeys` 与 `onSelectionChange` 控制选中状态。
```tsx
const [selected, setSelected] = useState(new Set(['news']));
新闻
旅行
游戏
;
```
### 变体
为标签应用不同视觉变体。
```tsx
新闻
旅行
新闻
旅行
```
### 尺寸
控制组内所有标签的尺寸。
```tsx
新闻
新闻
新闻
```
### 带移除按钮
提供 `onRemove`,并在每个条目中放置 `TagGroup.ItemRemoveButton` 以显示移除按钮。
```tsx
const [tags, setTags] = useState([
{ id: 'news', name: '新闻' },
{ id: 'travel', name: '旅行' },
]);
const onRemove = (keys) => {
setTags((prev) => prev.filter((tag) => !keys.has(tag.id)));
};
{tags.map((tag) => (
{tag.name}
))}
;
```
### 渲染函数子节点
使用渲染函数访问 `isSelected`、`isDisabled` 以自定义布局。
```tsx
{({ isSelected }) => (
<>
新闻
>
)}
```
### 空状态
列表无标签时渲染自定义内容。
```tsx
(
暂无分类
)}
>
{tags.map((tag) => (
{tag.name}
))}
```
### 禁用标签
禁用单个标签或整个组。
```tsx
新闻
旅行
游戏
```
## 示例
```tsx
import { TagGroup, Label, Description, FieldError } from 'heroui-native';
import { useState, useMemo } from 'react';
import { View } from 'react-native';
export default function TagGroupExample() {
const [selected, setSelected] = useState(new Set());
const isInvalid = useMemo(
() => Array.from(selected).length === 0,
[selected]
);
return (
设施
洗衣
健身房
停车
泳池
早餐
{`已选:${Array.from(selected).join('、')}`}
请至少选择一个分类
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/tag-group.tsx)。
## API 参考
### TagGroup
| prop | type | default | description |
| --------------------- | ---------------------------------- | ----------- | ------------------------------- |
| `children` | `React.ReactNode` | - | 渲染在标签组内的子节点 |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 组内所有标签的尺寸 |
| `variant` | `'default' \| 'surface'` | `'default'` | 组内所有标签的视觉变体 |
| `selectionMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | 允许的选中类型 |
| `selectedKeys` | `Iterable` | - | 当前选中键(受控) |
| `defaultSelectedKeys` | `Iterable` | - | 初始选中键(非受控) |
| `disabledKeys` | `Iterable` | - | 应被禁用的标签键 |
| `isDisabled` | `boolean` | `false` | 是否禁用整个标签组 |
| `isInvalid` | `boolean` | `false` | 是否处于非法状态 |
| `isRequired` | `boolean` | `false` | 是否必填 |
| `className` | `string` | - | 标签组容器的额外 class |
| `style` | `StyleProp` | - | 标签组容器的额外样式 |
| `animation` | `"disable-all" \| undefined` | - | 设为 `"disable-all"` 可禁用全部动画(含子级) |
| `onSelectionChange` | `(keys: Set) => void` | - | 选中变化时调用 |
| `onRemove` | `(keys: Set) => void` | - | 移除标签时调用 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
#### TagKey
`string | number` — 在 `TagGroup` 内标识标签的键类型。
#### Animation
使用 `animation="disable-all"` 可禁用全部动画(含子级)。省略或使用 `undefined` 为默认动画。
### TagGroup.List
| prop | type | default | description |
| ------------------ | ----------------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 列表内的子节点 |
| `className` | `string` | - | 列表容器的额外 class |
| `style` | `StyleProp` | - | 列表容器的额外样式 |
| `renderEmptyState` | `() => React.ReactNode` | - | 无标签时调用的渲染函数 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
### TagGroup.Item
| prop | type | default | description |
| ------------------- | ----------------------------------------------------------------------- | ------- | -------------------------------------- |
| `children` | `React.ReactNode \| ((renderProps: TagRenderProps) => React.ReactNode)` | - | 标签内容:字符串、元素,或接收 `TagRenderProps` 的渲染函数 |
| `id` | `TagKey` | - | 该标签的唯一标识 |
| `isDisabled` | `boolean` | - | 是否禁用该标签 |
| `className` | `string` | - | 标签的额外 class |
| `style` | `StyleProp` | - | 标签的额外样式 |
| `...PressableProps` | `PressableProps` | - | 支持 React Native `Pressable` 的全部属性 |
#### TagRenderProps
| prop | type | description |
| ------------ | --------- | ----------------------------------- |
| `isSelected` | `boolean` | 当前是否选中 |
| `isDisabled` | `boolean` | 是否禁用(根级、`disabledKeys` 与条目属性合并后的结果) |
### TagGroup.ItemLabel
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 要渲染的文字内容 |
| `className` | `string` | - | 标签文字的额外 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部属性 |
### TagGroup.ItemRemoveButton
| prop | type | default | description |
| ------------------- | -------------------------- | ------- | --------------------------------- |
| `children` | `React.ReactNode` | - | 自定义图标或内容;省略时默认为关闭图标 |
| `className` | `string` | - | 移除按钮的额外 class |
| `iconProps` | `TagRemoveButtonIconProps` | - | 自定义默认关闭图标的属性;仅在没有 `children` 时生效 |
| `hitSlop` | `number` | `8` | 扩大可点击区域 |
| `...PressableProps` | `PressableProps` | - | 支持 React Native `Pressable` 的全部属性 |
#### TagRemoveButtonIconProps
| prop | type | default | description |
| ------- | -------- | ------- | ----------- |
| `size` | `number` | `12` | 图标尺寸 |
| `color` | `string` | - | 图标颜色 |
## Hooks
### useTagGroup
访问标签组根上下文,必须在 `TagGroup` 内使用。
```tsx
import { useTagGroup } from 'heroui-native';
const {
selectedKeys,
disabledKeys,
selectionMode,
onSelectionChange,
onRemove,
isDisabled,
isInvalid,
isRequired,
} = useTagGroup();
```
#### 返回值
| property | type | description |
| ------------------- | -------------------------------------------- | ----------- |
| `selectionMode` | `'none' \| 'single' \| 'multiple'` | 允许的选中类型 |
| `selectedKeys` | `Set` | 当前选中的标签键 |
| `disabledKeys` | `Set` | 被禁用的标签键 |
| `onSelectionChange` | `(keys: Set) => void` | 选中变化回调 |
| `onRemove` | `((keys: Set) => void) \| undefined` | 移除标签回调 |
| `isDisabled` | `boolean` | 是否禁用整个标签组 |
| `isInvalid` | `boolean` | 是否处于非法状态 |
| `isRequired` | `boolean` | 是否必填 |
### useTagGroupItem
访问单个标签上下文,必须在 `TagGroup.Item` 内使用。
```tsx
import { useTagGroupItem } from 'heroui-native';
const { id, isSelected, isDisabled, allowsRemoving } = useTagGroupItem();
```
#### 返回值
| property | type | description |
| ---------------- | --------- | ------------------------------------------ |
| `id` | `TagKey` | 该标签的唯一标识 |
| `isSelected` | `boolean` | 当前是否选中 |
| `isDisabled` | `boolean` | 是否禁用 |
| `allowsRemoving` | `boolean` | 是否允许移除(当 `TagGroup` 提供 `onRemove` 时为 true) |
# Slider 滑块
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/slider
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(controls)/slider.mdx
> 在有限区间内通过拖拽选择单个值或区间的输入控件。
## 导入
```tsx
import { Slider } from 'heroui-native';
```
## 结构
```tsx
```
* **Slider**:主容器,管理滑块数值、方向,并为所有子组件提供上下文。支持单值与区间模式。
* **Slider.Output**:可选,显示当前值;支持渲染函数以自定义格式;默认显示格式化后的数值标签。
* **Slider.Track**:为 Fill 与 Thumb 提供尺寸的容器;上报布局尺寸用于位置计算;支持点击定位与渲染函数子节点(例如区间滑块的多拇指)。
* **Slider.Fill**:沿轨道交叉轴铺满的填充条;仅计算主轴位置与尺寸。
* **Slider.Thumb**:基于 react-native-gesture-handler 的可拖拽拇指;由 Track 布局在交叉轴居中;通过 react-native-reanimated 在按压时缩放。每个拇指具备 `role="slider"` 与完整 `accessibilityValue`。
## 用法
### 基础用法
Slider 通过复合部件组成可拖拽的数值输入。
```tsx
```
### 标签与输出
在数值输出旁显示标签。
```tsx
Volume
```
### 纵向
将 `orientation` 设为 `"vertical"` 以纵向渲染。
```tsx
```
### 区间滑块
将 `value`/`defaultValue` 设为数组,并在 `Slider.Track` 上使用渲染函数以渲染多个拇指。
```tsx
Price range
{({ state }) => (
<>
{state.values.map((_, i) => (
))}
>
)}
```
### 受控值
使用 `value` 与 `onChange` 进入受控模式。`onChangeEnd` 在拖拽结束或点击定位完成后触发。
```tsx
const [volume, setVolume] = useState(50);
save(v)}>
;
```
### 自定义样式
在拇指等子组件上使用 `className`、`classNames` 或 `styles` 自定义样式。
```tsx
```
### 禁用
禁用整个滑块以禁止交互。
```tsx
```
## 示例
```tsx
import { Label, Slider } from 'heroui-native';
import { useState } from 'react';
import { View, Text } from 'react-native';
export default function SliderExample() {
const [price, setPrice] = useState([200, 800]);
return (
Volume
Price range
{({ state }) => (
<>
{state.values.map((_, i) => (
))}
>
)}
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/slider.tsx)。
## API 参考
### Slider
| prop | type | default | description |
| --------------- | ------------------------------------- | -------------- | ------------------------------ |
| `children` | `React.ReactNode` | - | 滑块内部子元素 |
| `value` | `number \| number[]` | - | 当前值(受控) |
| `defaultValue` | `number \| number[]` | `0` | 默认值(非受控) |
| `minValue` | `number` | `0` | 最小值 |
| `maxValue` | `number` | `100` | 最大值 |
| `step` | `number` | `1` | 步进 |
| `formatOptions` | `Intl.NumberFormatOptions` | - | 数值标签的 `Intl` 格式化选项 |
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | 方向 |
| `isDisabled` | `boolean` | `false` | 是否禁用 |
| `className` | `string` | - | 额外 class |
| `animation` | `AnimationRootDisableAll` | - | 根级动画配置 |
| `onChange` | `(value: number \| number[]) => void` | - | 交互过程中数值变化时触发 |
| `onChangeEnd` | `(value: number \| number[]) => void` | - | 交互结束(拖放结束或点击定位)时触发 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
#### AnimationRootDisableAll
滑块根组件动画配置,可为:
* `"disable-all"`:关闭所有动画(含子级)
* `undefined`:使用默认动画
### Slider.Output
| prop | type | default | description |
| -------------- | -------------------------------------------------------------------- | ------- | ------------------------------ |
| `children` | `React.ReactNode \| ((props: SliderRenderProps) => React.ReactNode)` | - | 自定义内容或接收滑块状态的渲染函数;默认显示格式化数值标签 |
| `className` | `string` | - | 额外 class |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
#### SliderRenderProps
| prop | type | description |
| ------------- | ------------------- | ----------- |
| `state` | `SliderState` | 当前滑块状态 |
| `orientation` | `SliderOrientation` | 滑块方向 |
| `isDisabled` | `boolean` | 是否禁用 |
#### SliderState
| prop | type | description |
| -------------------- | --------------------------- | --------------- |
| `values` | `number[]` | 按拇指索引的当前数值数组 |
| `getThumbValueLabel` | `(index: number) => string` | 返回指定拇指的格式化字符串标签 |
### Slider.Track
| prop | type | default | description |
| -------------- | -------------------------------------------------------------------- | ------- | ------------------------------ |
| `children` | `React.ReactNode \| ((props: SliderRenderProps) => React.ReactNode)` | - | 子内容或接收滑块状态的渲染函数,用于动态渲染多拇指等 |
| `className` | `string` | - | 额外 class |
| `hitSlop` | `number` | `8` | 轨道周围扩展点击区域(像素) |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
### Slider.Fill
| prop | type | default | description |
| -------------- | ----------- | ------- | ------------------------------ |
| `className` | `string` | - | 额外 class |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
### Slider.Thumb
| prop | type | default | description |
| -------------- | ---------------------------------------- | ------- | ------------------------------ |
| `children` | `React.ReactNode` | - | 自定义拇指内容;默认可动画圆钮 |
| `index` | `number` | `0` | 该拇指在滑块中的索引 |
| `isDisabled` | `boolean` | - | 是否仅禁用该拇指 |
| `className` | `string` | - | 拇指容器额外 class |
| `classNames` | `ElementSlots` | - | 各拇指插槽的额外 class |
| `styles` | `Partial>` | - | 各拇指插槽的行内样式 |
| `hitSlop` | `number` | `12` | 拇指周围扩展点击区域(像素) |
| `animation` | `SliderThumbAnimation` | - | 拇指圆钮动画配置 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
#### ElementSlots\
| prop | type | description |
| ---------------- | -------- | --------------- |
| `thumbContainer` | `string` | 外层拇指容器自定义 class |
| `thumbKnob` | `string` | 内层圆钮自定义 class |
#### styles
| prop | type | description |
| ---------------- | ----------- | ----------- |
| `thumbContainer` | `ViewStyle` | 外层拇指容器样式 |
| `thumbKnob` | `ViewStyle` | 内层圆钮样式 |
#### SliderThumbAnimation
拇指缩放动画配置,可为:
* `false` 或 `"disabled"`:关闭拇指动画
* `undefined`:使用默认动画
* `object`:自定义缩放动画
| prop | type | default | description |
| -------------------- | ------------------ | -------------------------------------------- | -------------- |
| `scale.value` | `[number, number]` | `[1, 0.9]` | 缩放值 \[空闲, 拖拽中] |
| `scale.springConfig` | `WithSpringConfig` | `{ damping: 15, stiffness: 200, mass: 0.5 }` | 缩放弹簧配置 |
## Hooks
### useSlider
访问滑块上下文,须在 `Slider` 内使用。
```tsx
import { useSlider } from 'heroui-native';
const { values, orientation, isDisabled, getThumbValueLabel } = useSlider();
```
#### 返回值
| property | type | description |
| -------------------- | -------------------------------------------- | ---------------------- |
| `values` | `number[]` | 当前各拇指的数值 |
| `minValue` | `number` | 最小值 |
| `maxValue` | `number` | 最大值 |
| `step` | `number` | 步进 |
| `orientation` | `'horizontal' \| 'vertical'` | 当前方向 |
| `isDisabled` | `boolean` | 是否禁用 |
| `formatOptions` | `Intl.NumberFormatOptions \| undefined` | 标签数字格式化选项 |
| `getThumbPercent` | `(index: number) => number` | 返回指定拇指位置百分比(0–1) |
| `getThumbValueLabel` | `(index: number) => string` | 返回指定拇指的格式化标签 |
| `getThumbMinValue` | `(index: number) => number` | 返回指定拇指允许的最小值 |
| `getThumbMaxValue` | `(index: number) => number` | 返回指定拇指允许的最大值 |
| `updateValue` | `(index: number, newValue: number) => void` | 按索引更新拇指数值 |
| `isThumbDragging` | `(index: number) => boolean` | 指定拇指是否正在拖拽 |
| `setThumbDragging` | `(index: number, dragging: boolean) => void` | 设置拇指拖拽状态 |
| `trackSize` | `number` | 轨道布局宽度(横向)或高度(纵向),单位像素 |
| `thumbSize` | `number` | 已测量的拇指尺寸(主轴方向),单位像素 |
# Switch 开关
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/switch
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(controls)/switch.mdx
> 在开与关两种状态之间切换的拨动控件。
## 导入
```tsx
import { Switch } from 'heroui-native';
```
## 结构
```tsx
...
...
...
```
* **Switch**:主容器,处理开关状态与用户交互。未提供子节点时渲染默认拇指;根据选中状态对缩放(按压)与背景色做动画;整块可点以切换。
* **Switch.Thumb**:可选滑动拇指,在位置间移动,弹簧过渡。可放自定义内容(图标等)或通过样式与动画定制。
* **Switch.StartContent**:可选,显示在开关左侧;常用于关态时的图标或文字;在容器内绝对定位。
* **Switch.EndContent**:可选,显示在开关右侧;常用于开态时的图标或文字;在容器内绝对定位。
## 用法
### 基础用法
未提供子节点时,Switch 使用默认拇指渲染。
```tsx
```
### 自定义拇指
通过 Thumb 子组件替换默认拇指。
```tsx
...
```
### 首尾内容
在开关两侧添加图标或文字。
```tsx
...
...
```
### 渲染函数
根据开关状态用渲染函数动态渲染内容。
```tsx
{({ isSelected, isDisabled }) => (
<>
{({ isSelected }) => (isSelected ? : )}
>
)}
```
### 自定义动画
为开关根与拇指自定义动画。
```tsx
```
### 关闭动画
可整体关闭动画,或仅关闭部分组件的动画。
```tsx
{
/* 关闭所有动画(含子级) */
}
;
{
/* 仅关闭根动画,拇指仍可动画 */
}
;
```
## 示例
```tsx
import { Switch } from 'heroui-native';
import { Ionicons } from '@expo/vector-icons';
import React from 'react';
import { View } from 'react-native';
import Animated, { ZoomIn } from 'react-native-reanimated';
export default function SwitchExample() {
const [darkMode, setDarkMode] = React.useState(false);
return (
{darkMode && (
)}
{!darkMode && (
)}
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/switch.tsx)。
## API 参考
### Switch
| prop | type | default | description |
| --------------------------- | -------------------------------------------------------------------- | ----------- | ----------------------------- |
| `children` | `React.ReactNode \| ((props: SwitchRenderProps) => React.ReactNode)` | `undefined` | 开关内部内容或渲染函数 |
| `isSelected` | `boolean` | `undefined` | 是否选中 |
| `isDisabled` | `boolean` | `false` | 是否禁用、不可交互 |
| `className` | `string` | `undefined` | 根节点自定义 class |
| `animation` | `SwitchRootAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `onSelectedChange` | `(isSelected: boolean) => void` | - | 选中状态变化时回调 |
| `...AnimatedPressableProps` | `AnimatedProps` | - | 支持 Reanimated Pressable 的全部属性 |
#### SwitchRenderProps
| prop | type | description |
| ------------ | --------- | ----------- |
| `isSelected` | `boolean` | 是否选中 |
| `isDisabled` | `boolean` | 是否禁用 |
#### SwitchRootAnimation
Switch 根组件动画配置,可为:
* `false` 或 `"disabled"`:仅关闭根动画
* `"disable-all"`:关闭所有动画(含子级)
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ------------------------------ | ---------------------------------------- | -------------------------------------------------------------- | --------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | 关闭动画的同时仍允许自定义属性 |
| `scale.value` | `[number, number]` | `[1, 0.96]` | 缩放值 \[未按压, 按压] |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | 动画时间配置 |
| `backgroundColor.value` | `[string, string]` | 使用主题色 | 背景色 \[未选中, 选中] |
| `backgroundColor.timingConfig` | `WithTimingConfig` | `{ duration: 175, easing: Easing.bezier(0.25, 0.1, 0.25, 1) }` | 背景色过渡时间配置 |
### Switch.Thumb
| prop | type | default | description |
| ----------------------- | -------------------------------------------------------------------- | ----------- | ------------------------------ |
| `children` | `React.ReactNode \| ((props: SwitchRenderProps) => React.ReactNode)` | `undefined` | 拇指内内容或渲染函数 |
| `className` | `string` | `undefined` | 拇指元素自定义 class |
| `animation` | `SwitchThumbAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
#### SwitchThumbAnimation
`Switch.Thumb` 动画配置,可为:
* `false` 或 `"disabled"`:关闭全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ------------------------------ | ----------------------- | -------------------------------------------------------------- | ----------------- |
| `state` | `'disabled' \| boolean` | - | 关闭动画的同时仍允许自定义属性 |
| `left.value` | `number` | `2` | 距边缘偏移(未选中偏左,选中偏右) |
| `left.springConfig` | `WithSpringConfig` | `{ damping: 120, stiffness: 1600, mass: 2 }` | 拇指位置弹簧配置 |
| `backgroundColor.value` | `[string, string]` | `['white', theme accent-foreground color]` | 背景色 \[未选中, 选中] |
| `backgroundColor.timingConfig` | `WithTimingConfig` | `{ duration: 175, easing: Easing.bezier(0.25, 0.1, 0.25, 1) }` | 背景色过渡时间配置 |
### Switch.StartContent
| prop | type | default | description |
| -------------- | ----------------- | ----------- | ------------------------------ |
| `children` | `React.ReactNode` | `undefined` | 左侧区域内容 |
| `className` | `string` | `undefined` | 内容区域自定义 class |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
### Switch.EndContent
| prop | type | default | description |
| -------------- | ----------------- | ----------- | ------------------------------ |
| `children` | `React.ReactNode` | `undefined` | 右侧区域内容 |
| `className` | `string` | `undefined` | 内容区域自定义 class |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
## Hooks
### useSwitch
用于访问 Switch 上下文,便于在子组件中读取开关状态或封装自定义结构。
**返回值:**
| Property | Type | Description |
| ------------ | --------- | ----------- |
| `isSelected` | `boolean` | 是否选中 |
| `isDisabled` | `boolean` | 是否禁用 |
**示例:**
```tsx
import { useSwitch } from 'heroui-native';
function CustomSwitchContent() {
const { isSelected, isDisabled } = useSwitch();
return (
Status: {isSelected ? 'On' : 'Off'}
{isDisabled && Disabled }
);
}
// 用法
;
```
## 特别说明
### 边框样式
若需为开关根节点加边框,请使用 `outline` 相关样式而非 `border`,避免影响拇指位置的内部宽度计算:
```tsx
```
使用 `outline` 可在不改变内部宽度计算的前提下显示边框,确保拇指动画正确。
### 与 ControlField 组合
Switch 可与 ControlField 组合以共享按压态、扩大点击区域:
```tsx
import { Description, ControlField, Label } from 'heroui-native';
Enable notifications
Receive push notifications
```
包在 ControlField 内时,整个容器上的按压都会驱动开关,触控目标更大、体验更好。
# Chip 标签
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/chip
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(data-display)/chip.mdx
> 以胶囊形态展示的小型元素。
## 导入
```tsx
import { Chip } from 'heroui-native';
```
## 结构
```tsx
...
```
* **Chip**:主容器,展示紧凑元素
* **Chip.Label**:芯片上的文字内容
## 用法
### 基础用法
Chip 以胶囊形态展示文字或自定义内容。
```tsx
基础芯片
```
### 尺寸
使用 `size` 控制尺寸。
```tsx
小
中
大
```
### 变体
使用 `variant` 切换视觉风格。
```tsx
主要
次要
第三级
柔和
```
### 颜色
使用 `color` 应用不同主题色。
```tsx
强调
默认
成功
警告
危险
```
### 搭配图标
通过复合组件在文字旁添加图标或自定义内容。
```tsx
精选
关闭
```
### 自定义样式
通过 `className` 或 `style` 传入样式。
```tsx
自定义
```
### 禁用全部动画
将 `animation` 设为 `"disable-all"` 可禁用自身及子级的全部动画。
```tsx
{
/* 禁用自身及子级的全部动画 */
}
无动画 ;
```
## 示例
```tsx
import { Chip } from 'heroui-native';
import { View, Text } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
export default function ChipExample() {
return (
小
中
大
主要
成功
高级
移除
自定义
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/chip.tsx)。
## API 参考
### Chip
| prop | type | default | description |
| ------------------- | ------------------------------------------------------------- | ----------- | ---------------------------------- |
| `children` | `React.ReactNode` | - | 芯片内要渲染的内容 |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 芯片尺寸 |
| `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'soft'` | `'primary'` | 视觉变体 |
| `color` | `'accent' \| 'default' \| 'success' \| 'warning' \| 'danger'` | `'accent'` | 颜色主题 |
| `className` | `string` | - | 额外的 class |
| `animation` | `"disable-all" \| undefined` | `undefined` | 动画配置;`"disable-all"` 可禁用自身及子级的全部动画 |
| `...PressableProps` | `PressableProps` | - | 支持 `Pressable` 的全部属性 |
### Chip.Label
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 作为标签渲染的文字或内容 |
| `className` | `string` | - | 额外的 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部属性 |
## Hooks
### useChip
访问 Chip 上下文,返回尺寸、变体与颜色。
```tsx
import { useChip } from 'heroui-native';
const { size, variant, color } = useChip();
```
#### 返回值
| property | type | description |
| --------- | ------------------------------------------------------------- | ----------- |
| `size` | `'sm' \| 'md' \| 'lg'` | 芯片尺寸 |
| `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'soft'` | 视觉变体 |
| `color` | `'accent' \| 'default' \| 'success' \| 'warning' \| 'danger'` | 颜色主题 |
**说明:** 必须在 `Chip` 内使用;在上下文外调用将抛出错误。
# Alert 警告
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/alert
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(feedback)/alert.mdx
> 向用户展示重要消息与通知,并带有状态指示。
## 导入
```tsx
import { Alert } from 'heroui-native';
```
## 结构
```tsx
...
...
```
* **Alert**:根容器,`role="alert"`,按状态应用样式;通过原语上下文向子组件提供状态。
* **Alert.Indicator**:默认渲染与状态匹配的图标;可传入自定义子节点覆盖;支持 `iconProps` 调整尺寸与颜色。
* **Alert.Content**:包裹标题与描述,提供文字布局结构。
* **Alert.Title**:标题文字,颜色随状态变化;通过 `aria-labelledby` 与根关联。
* **Alert.Description**:正文,弱化色;通过 `aria-describedby` 与根关联。
## 用法
### 基础用法
使用复合子部件展示带图标、标题与描述的通知。
```tsx
新功能已上线
查看最新更新,包括深色模式支持与无障碍改进等。
```
### 状态变体
使用 `status` 控制图标与标题颜色。可选:`default`、`accent`、`success`、`warning`、`danger`。
```tsx
成功
...
计划维护
...
无法连接
...
```
### 仅标题
省略 `Alert.Description` 以得到紧凑单行提示。
```tsx
资料已成功更新
```
### 操作按钮
在内容旁放置按钮等额外元素。
```tsx
有可用更新
应用有新版本可用。
刷新
```
### 自定义指示器
向 `Alert.Indicator` 传入自定义子节点以替换默认状态图标。
```tsx
正在处理请求
请稍候,正在同步您的数据。
```
### 自定义样式
在根与各复合部件上使用 `className`。
```tsx
...
...
```
## 示例
```tsx
import { Alert, Button, CloseButton } from 'heroui-native';
import { View } from 'react-native';
export default function AlertExample() {
return (
有可用更新
应用有新版本。请刷新以获取最新功能与问题修复。
刷新
无法连接服务器
无法连接到服务器。请检查网络后重试。
重试
资料已成功更新
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/alert.tsx)。
## API 参考
### Alert
| prop | type | default | description |
| -------------- | ------------------------------------------------------------- | ----------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 渲染在 Alert 内的子节点 |
| `status` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | `'default'` | 状态,控制图标与着色 |
| `id` | `string \| number` | - | 唯一标识;未提供时自动生成 |
| `className` | `string` | - | 额外的 class |
| `style` | `ViewStyle` | - | 根容器额外样式 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
### Alert.Indicator
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 自定义子节点,替代默认状态图标 |
| `className` | `string` | - | 额外的 class |
| `iconProps` | `AlertIconProps` | - | 传给默认状态图标的属性(尺寸、颜色等) |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
#### AlertIconProps
| prop | type | default | description |
| ------- | -------- | ------- | ----------- |
| `size` | `number` | `18` | 图标尺寸(像素) |
| `color` | `string` | 随状态着色 | 图标颜色字符串 |
### Alert.Content
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------- |
| `children` | `React.ReactNode` | - | 子节点(通常为 `Alert.Title` 与 `Alert.Description`) |
| `className` | `string` | - | 额外的 class |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
### Alert.Title
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 标题文字 |
| `className` | `string` | - | 额外的 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部属性 |
### Alert.Description
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 描述文字 |
| `className` | `string` | - | 额外的 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部属性 |
## Hooks
### useAlert
访问 Alert 根上下文,必须在 `Alert` 内使用。
```tsx
import { useAlert } from 'heroui-native';
const { status, nativeID } = useAlert();
```
#### 返回值
| property | type | description |
| ---------- | ------------------------------------------------------------- | ----------------- |
| `status` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | 当前状态,供子组件样式使用 |
| `nativeID` | `string` | 无障碍与 ARIA 使用的唯一标识 |
# SkeletonGroup 骨架屏组
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/skeleton-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(feedback)/skeleton-group.mdx
> 协调多个骨架屏占位,并提供统一的动画与加载态控制。
## 导入
```tsx
import { SkeletonGroup } from 'heroui-native';
```
## 结构
```tsx
```
* **SkeletonGroup**:根容器,为所有骨架项提供统一控制
* **SkeletonGroup.Item**:单个骨架项,继承父级组的属性
## 用法
### 基础用法
SkeletonGroup 用共享的加载态与动画管理多个骨架项。
```tsx
```
### 容器布局
在组上使用 `className` 控制骨架项布局。
```tsx
```
### isSkeletonOnly(纯骨架布局)
当组内仅有骨架与布局用 `View`(加载完成后无真实内容)时,使用 `isSkeletonOnly`。`isLoading` 为 `false` 时整个组会隐藏,避免空容器影响布局。
```tsx
{/* 该 View 仅用于布局,无加载后内容 */}
```
### 动画变体
为组内所有项统一设置动画变体。
```tsx
```
### 自定义动画配置
为整组配置 shimmer 或 pulse。
```tsx
```
### 进出场动画
组出现或消失时应用 Reanimated 过渡。
```tsx
```
## 示例
```tsx
import { Card, SkeletonGroup, Avatar } from 'heroui-native';
import { useState } from 'react';
import { Text, View, Image } from 'react-native';
export default function SkeletonGroupExample() {
const [isLoading, setIsLoading] = useState(true);
return (
John Doe
@johndoe
This is the first line of the post content.
Second line with more interesting content to read.
Last line is shorter.
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/skeleton-group.tsx)。
## API 参考
### SkeletonGroup
| prop | type | default | description |
| ----------------------- | -------------------------------- | ----------- | -------------------------------------------- |
| `children` | `React.ReactNode` | - | `SkeletonGroup.Item` 与布局元素 |
| `isLoading` | `boolean` | `true` | 骨架项是否处于加载中 |
| `isSkeletonOnly` | `boolean` | `false` | 为 `true` 时,`isLoading` 为 `false` 隐藏整组(纯骨架布局) |
| `variant` | `'shimmer' \| 'pulse' \| 'none'` | `'shimmer'` | 组内所有项的动画变体 |
| `animation` | `SkeletonRootAnimation` | - | 动画配置 |
| `className` | `string` | - | 组容器额外 class |
| `style` | `StyleProp` | - | 组容器自定义样式 |
| `...Animated.ViewProps` | `AnimatedProps` | - | 支持 Reanimated `Animated.View` 全部属性 |
#### SkeletonRootAnimation
SkeletonGroup 动画配置,可为:
* `false` 或 `"disabled"`:仅关闭根动画
* `"disable-all"`:关闭所有动画(含子级)
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ------------------------ | ---------------------------------------- | --------------------------- | --------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | 关闭动画的同时仍允许自定义属性 |
| `entering.value` | `EntryOrExitLayoutType` | `FadeIn` | 自定义进入动画 |
| `exiting.value` | `EntryOrExitLayoutType` | `FadeOut` | 自定义退出动画 |
| `shimmer.duration` | `number` | `1500` | 动画时长(毫秒) |
| `shimmer.speed` | `number` | `1` | 速度倍率 |
| `shimmer.highlightColor` | `string` | - | 微光高光色 |
| `shimmer.easing` | `EasingFunction` | `Easing.linear` | 缓动函数 |
| `pulse.duration` | `number` | `1000` | 动画时长(毫秒) |
| `pulse.minOpacity` | `number` | `0.5` | 最小不透明度 |
| `pulse.maxOpacity` | `number` | `1` | 最大不透明度 |
| `pulse.easing` | `EasingFunction` | `Easing.inOut(Easing.ease)` | 缓动函数 |
### SkeletonGroup.Item
| prop | type | default | description |
| ----------------------- | -------------------------------- | ------- | ---------------------------------- |
| `children` | `React.ReactNode` | - | 非加载态显示的内容 |
| `isLoading` | `boolean` | 继承组 | 是否加载中(覆盖组设置) |
| `variant` | `'shimmer' \| 'pulse' \| 'none'` | 继承组 | 动画变体(覆盖组设置) |
| `animation` | `SkeletonRootAnimation` | 继承组 | 动画配置(覆盖组设置) |
| `className` | `string` | - | 单项额外 class |
| `...Animated.ViewProps` | `AnimatedProps` | - | 支持 Reanimated `Animated.View` 全部属性 |
## 特别说明
### 属性继承
`SkeletonGroup.Item` 从父级 `SkeletonGroup` 继承所有与动画相关的属性:
* `isLoading`
* `variant`
* `animation`
单项可通过自身属性覆盖继承值。
# Skeleton 骨架屏
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/skeleton
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(feedback)/skeleton.mdx
> 展示加载占位,支持微光(shimmer)或脉冲(pulse)等动画效果。
## 导入
```tsx
import { Skeleton } from 'heroui-native';
```
## 结构
Skeleton 为简单包装器,在内容加载时渲染占位,无子部件 API。
```tsx
```
## 用法
### 基础用法
在内容加载期间显示带动画的占位。
```tsx
```
### 与内容切换
加载中显示 Skeleton,就绪后显示真实内容。
```tsx
Loaded Content
```
### 动画变体
用 `variant` 控制动画样式。
```tsx
```
### 自定义微光
自定义时长、速度与高光色。
```tsx
...
```
### 自定义脉冲
配置脉冲时长与不透明度范围。
```tsx
...
```
### 形状变化
通过 `className` 控制占位形状。
```tsx
```
### 自定义进出场
Skeleton 出现或消失时使用自定义 Reanimated 过渡。
```tsx
...
```
## 示例
```tsx
import { Avatar, Card, Skeleton } from 'heroui-native';
import { useState } from 'react';
import { Image, Text, View } from 'react-native';
export default function SkeletonExample() {
const [isLoading, setIsLoading] = useState(true);
return (
John Doe
@johndoe
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/skeleton.tsx)。
## API 参考
### Skeleton
| prop | type | default | description |
| ----------------------- | -------------------------------- | ----------- | ----------------------------------- |
| `children` | `React.ReactNode` | - | 非加载态时显示的内容 |
| `isLoading` | `boolean` | `true` | 是否处于加载中 |
| `variant` | `'shimmer' \| 'pulse' \| 'none'` | `'shimmer'` | 动画变体 |
| `animation` | `SkeletonRootAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `className` | `string` | - | 额外样式 class |
| `...Animated.ViewProps` | `AnimatedProps` | - | 支持 Reanimated `Animated.View` 的全部属性 |
#### SkeletonRootAnimation
Skeleton 根动画配置,可为:
* `false` 或 `"disabled"`:仅关闭根动画
* `"disable-all"`:关闭所有动画(含子级)
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ------------------------ | ---------------------------------------- | --------------------------- | --------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | 关闭动画的同时仍允许自定义属性 |
| `entering.value` | `EntryOrExitLayoutType` | `FadeIn` | 自定义进入动画 |
| `exiting.value` | `EntryOrExitLayoutType` | `FadeOut` | 自定义退出动画 |
| `shimmer.duration` | `number` | `1500` | 动画时长(毫秒) |
| `shimmer.speed` | `number` | `1` | 速度倍率 |
| `shimmer.highlightColor` | `string` | - | 微光高光色 |
| `shimmer.easing` | `EasingFunction` | `Easing.linear` | 缓动函数 |
| `pulse.duration` | `number` | `1000` | 动画时长(毫秒) |
| `pulse.minOpacity` | `number` | `0.5` | 最小不透明度 |
| `pulse.maxOpacity` | `number` | `1` | 最大不透明度 |
| `pulse.easing` | `EasingFunction` | `Easing.inOut(Easing.ease)` | 缓动函数 |
# Spinner 加载指示器
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/spinner
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(feedback)/spinner.mdx
> 展示旋转加载动画。
## 导入
```tsx
import { Spinner } from 'heroui-native';
```
## 结构
```tsx
...
```
* **Spinner**:主容器,控制加载状态、尺寸与颜色。未提供子节点时渲染默认动画指示器。
* **Spinner.Indicator**:可选子组件,用于自定义动画配置与图标外观;可传入自定义子节点替换默认图标。
## 用法
### 基础用法
展示旋转加载指示器。
```tsx
```
### 尺寸
使用 `size` 控制大小。
```tsx
```
### 颜色
使用预设色或自定义颜色字符串。
```tsx
```
### 加载状态
使用 `isLoading` 控制是否显示。
```tsx
```
### 动画速度
在 `Indicator` 上使用 `animation` 自定义旋转速度。
```tsx
```
### 自定义图标
用自定义内容替换默认图标。
```tsx
const themeColorForeground = useThemeColor('foreground')
⏳
```
## 示例
```tsx
import { Spinner } from 'heroui-native';
import { Ionicons } from '@expo/vector-icons';
import React from 'react';
import { Text, TouchableOpacity, View } from 'react-native';
export default function SpinnerExample() {
const [isLoading, setIsLoading] = React.useState(true);
return (
正在加载内容…
处理中…
setIsLoading(!isLoading)}>
{isLoading ? '点击停止' : '点击开始'}
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/spinner.tsx)。
## API 参考
### Spinner
| prop | type | default | description |
| -------------- | ----------------------------------------------------------- | ----------- | ---------------------------- |
| `children` | `React.ReactNode` | `undefined` | 旋转器内部内容 |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 尺寸 |
| `color` | `'default' \| 'success' \| 'warning' \| 'danger' \| string` | `'default'` | 颜色主题或自定义色值 |
| `isLoading` | `boolean` | `true` | 是否处于加载中(显示动画) |
| `className` | `string` | `undefined` | 自定义 class |
| `animation` | `SpinnerRootAnimation` | - | 根级动画配置 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
#### SpinnerRootAnimation
Spinner 根组件的动画配置,可为:
* `false` 或 `"disabled"`:仅禁用根级动画
* `"disable-all"`:禁用全部动画(含子级)
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ---------------- | ---------------------------------------- | ---------------------------------------------------------------------- | ----------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | 在自定义属性时禁用动画 |
| `entering.value` | `EntryOrExitLayoutType` | `FadeIn` `.duration(200)` `.easing(Easing.out(Easing.ease))` | 自定义进入动画 |
| `exiting.value` | `EntryOrExitLayoutType` | `FadeOut` `.duration(100)` | 自定义退出动画 |
### Spinner.Indicator
| prop | type | default | description |
| ----------------------- | --------------------------- | ----------- | ----------------------------------- |
| `children` | `React.ReactNode` | `undefined` | 指示器内部内容 |
| `iconProps` | `SpinnerIconProps` | `undefined` | 默认图标的属性 |
| `className` | `string` | `undefined` | 指示器元素的 class |
| `animation` | `SpinnerIndicatorAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | 支持 Reanimated `Animated.View` 的全部属性 |
#### SpinnerIndicatorAnimation
`Spinner.Indicator` 的动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ----------------- | ---------------------------- | --------------- | ----------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时禁用动画 |
| `rotation.speed` | `number` | `1.1` | 旋转速度倍率 |
| `rotation.easing` | `WithTimingConfig['easing']` | `Easing.linear` | 动画缓动配置 |
### SpinnerIconProps
| prop | type | default | description |
| -------- | ------------------ | ---------------- | ----------- |
| `width` | `number \| string` | `24` | 图标宽度 |
| `height` | `number \| string` | `24` | 图标高度 |
| `color` | `string` | `'currentColor'` | 图标颜色 |
# Checkbox 复选框
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/checkbox
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(forms)/checkbox.mdx
> 可在选中与未选中之间切换的可选控件。
## 导入
```tsx
import { Checkbox } from 'heroui-native';
```
## 结构
```tsx
...
```
* **Checkbox**:主容器,处理选中状态与用户交互。未提供子节点时渲染带动画对勾的默认指示器;自动识别是否在 Surface 上以便样式正确;支持可定制或关闭的按压缩放动画;子节点可为渲染函数以访问 `isSelected`、`isInvalid`、`isDisabled`。
* **Checkbox.Indicator**:可选对勾容器,选中时默认带滑动、缩放、透明度与圆角动画;无子节点时渲染带动画路径的 SVG 对勾;各动画可单独配置或关闭;子节点可为渲染函数以访问状态。
## 用法
### 基础用法
未提供子节点时,Checkbox 使用默认动画指示器,并自动检测是否在 Surface 背景上。
```tsx
```
### 自定义指示器
在 Indicator 中使用渲染函数,按状态显示/隐藏自定义图标。
```tsx
{({ isSelected }) => (isSelected ? : null)}
```
### 非法状态
使用 `isInvalid` 表示校验错误并应用危险色样式。
```tsx
```
### 自定义动画
为根与指示器分别自定义或关闭动画。
```tsx
{
/* 关闭所有动画(根与指示器) */
}
;
{
/* 仅关闭根动画 */
}
;
{
/* 仅关闭指示器动画 */
}
;
{
/* 自定义动画配置 */
}
;
```
## 示例
```tsx
import {
Checkbox,
Description,
ControlField,
Label,
Separator,
Surface,
} from "heroui-native";
import React from 'react';
import { View, Text } from 'react-native';
interface CheckboxFieldProps {
isSelected: boolean;
onSelectedChange: (value: boolean) => void;
title: string;
description: string;
}
const CheckboxField: React.FC = ({
isSelected,
onSelectedChange,
title,
description,
}) => {
return (
{title}
{description}
);
};
export default function BasicUsage() {
const [fields, setFields] = React.useState({
newsletter: true,
marketing: false,
terms: false,
});
const fieldConfigs: Record<
keyof typeof fields,
{ title: string; description: string }
> = {
newsletter: {
title: 'Subscribe to newsletter',
description: 'Get weekly updates about new features and tips',
},
marketing: {
title: 'Marketing communications',
description: 'Receive promotional emails and special offers',
},
terms: {
title: 'Accept terms and conditions',
description: 'Agree to our Terms of Service and Privacy Policy',
},
};
const handleFieldChange = (key: keyof typeof fields) => (value: boolean) => {
setFields((prev) => ({ ...prev, [key]: value }));
};
const fieldKeys = Object.keys(fields) as Array;
return (
{fieldKeys.map((key, index) => (
{index > 0 && }
))}
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/checkbox.tsx)。
## API 参考
### Checkbox
| prop | type | default | description |
| ----------------------- | ---------------------------------------------------------------------- | ----------- | ----------------------------------------------- |
| `children` | `React.ReactNode \| ((props: CheckboxRenderProps) => React.ReactNode)` | `undefined` | 子元素或用于自定义的渲染函数 |
| `isSelected` | `boolean` | `undefined` | 是否选中 |
| `onSelectedChange` | `(isSelected: boolean) => void` | `undefined` | 选中状态变化时回调 |
| `isDisabled` | `boolean` | `false` | 是否禁用、不可交互 |
| `isInvalid` | `boolean` | `false` | 是否非法(危险色样式) |
| `variant` | `'primary' \| 'secondary'` | `'primary'` | 视觉变体 |
| `hitSlop` | `number` | `6` | 可点区域扩展(hit slop) |
| `animation` | `CheckboxRootAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `className` | `string` | `undefined` | 额外 class |
| `...PressableProps` | `PressableProps` | - | 支持 React Native `Pressable` 标准属性(`disabled` 除外) |
#### CheckboxRenderProps
| prop | type | description |
| ------------ | --------- | ----------- |
| `isSelected` | `boolean` | 是否选中 |
| `isInvalid` | `boolean` | 是否非法 |
| `isDisabled` | `boolean` | 是否禁用 |
#### CheckboxRootAnimation
复选框根组件动画配置,可为:
* `false` 或 `"disabled"`:仅关闭根动画
* `"disable-all"`:关闭所有动画(含子级)
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| -------------------- | ---------------------------------------- | ------------------- | --------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | 关闭动画的同时仍允许自定义属性 |
| `scale.value` | `[number, number]` | `[1, 0.96]` | 缩放值 \[未按压, 按压] |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | 动画时间配置 |
### Checkbox.Indicator
| prop | type | default | description |
| ----------------------- | ---------------------------------------------------------------------- | ----------- | ---------------------------------- |
| `children` | `React.ReactNode \| ((props: CheckboxRenderProps) => React.ReactNode)` | `undefined` | 指示器内容或渲染函数 |
| `className` | `string` | `undefined` | 指示器额外 class |
| `iconProps` | `CheckboxIndicatorIconProps` | `undefined` | 默认动画对勾图标的自定义属性 |
| `animation` | `CheckboxIndicatorAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `...AnimatedViewProps` | `AnimatedProps` | - | 支持 React Native Animated View 标准属性 |
#### CheckboxIndicatorIconProps
用于自定义默认动画对勾图标。
| prop | type | description |
| --------------- | -------- | ----------------------------- |
| `size` | `number` | 图标尺寸 |
| `strokeWidth` | `number` | 描边宽度 |
| `color` | `string` | 图标颜色(默认为主题 accent-foreground) |
| `enterDuration` | `number` | 出现动画时长(对勾显示) |
| `exitDuration` | `number` | 消失动画时长(对勾隐藏) |
#### CheckboxIndicatorAnimation
指示器动画配置,可为:
* `false` 或 `"disabled"`:关闭全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| --------------------------- | ----------------------- | ------------------- | --------------- |
| `state` | `'disabled' \| boolean` | - | 关闭动画的同时仍允许自定义属性 |
| `opacity.value` | `[number, number]` | `[0, 1]` | 透明度 \[未选中, 选中] |
| `opacity.timingConfig` | `WithTimingConfig` | `{ duration: 100 }` | 透明度动画时间配置 |
| `borderRadius.value` | `[number, number]` | `[8, 0]` | 圆角 \[未选中, 选中] |
| `borderRadius.timingConfig` | `WithTimingConfig` | `{ duration: 50 }` | 圆角动画时间配置 |
| `translateX.value` | `[number, number]` | `[-4, 0]` | X 位移 \[未选中, 选中] |
| `translateX.timingConfig` | `WithTimingConfig` | `{ duration: 100 }` | 位移动画时间配置 |
| `scale.value` | `[number, number]` | `[0.8, 1]` | 缩放 \[未选中, 选中] |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 100 }` | 缩放动画时间配置 |
## Hooks
### useCheckbox
在自定义或复合结构内访问复选框上下文。
```tsx
import { useCheckbox } from 'heroui-native';
const CustomIndicator = () => {
const { isSelected, isInvalid, isDisabled } = useCheckbox();
// ... your implementation
};
```
**返回值:** `UseCheckboxReturn`
| property | type | description |
| ------------------ | ---------------------------------------------- | --------------- |
| `isSelected` | `boolean \| undefined` | 是否选中 |
| `onSelectedChange` | `((isSelected: boolean) => void) \| undefined` | 修改选中状态的回调函数 |
| `isDisabled` | `boolean` | 是否禁用、不可交互 |
| `isInvalid` | `boolean` | 是否非法(危险色) |
| `nativeID` | `string \| undefined` | 复选框元素 native ID |
**注意:** 必须在 `Checkbox` 内使用;在上下文外调用会抛错。
# ControlField 控件字段
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/control-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(forms)/control-field.mdx
> 将标签、说明(或其他内容)与控件(Switch 或 Checkbox)组合为单一可按压区域的字段组件。
## 导入
```tsx
import { ControlField } from 'heroui-native';
```
## 结构
```tsx
...
...
...
...
```
* **ControlField**:根容器,管理布局与状态向下传递
* **Label**:主标签(来自 [Label](./label))
* **Description**:辅助说明(来自 [Description](./description))
* **ControlField.Indicator**:表单控件容器([Switch](./switch)、[Checkbox](./checkbox)、[Radio](./radio))
* **FieldError**:校验错误展示(来自 [FieldError](./field-error))
## 用法
### 基础用法
ControlField 包裹控件,提供一致布局与状态管理。
```tsx
Label text
```
### 带说明
在标签下使用 Description 添加辅助说明。
```tsx
Enable notifications
Receive push notifications about your account activity
```
### 带错误信息
使用 FieldError 展示校验错误。
```tsx
I agree to the terms
By checking this box, you agree to our Terms of Service
This field is required
```
### 禁用态
使用 `isDisabled` 控制是否可交互。
```tsx
Disabled field
This field is disabled
```
### 关闭所有动画
使用 `"disable-all"` 关闭根及子级全部动画。
```tsx
Label text
Description text
```
## 示例
```tsx
import {
Checkbox,
Description,
FieldError,
ControlField,
Label,
Switch,
} from 'heroui-native';
import React from 'react';
import { ScrollView, View } from 'react-native';
export default function ControlFieldExample() {
const [notifications, setNotifications] = React.useState(false);
const [terms, setTerms] = React.useState(false);
const [newsletter, setNewsletter] = React.useState(true);
return (
Enable notifications
Receive push notifications about your account activity
I agree to the terms and conditions
By checking this box, you agree to our Terms of Service
This field is required
Subscribe to newsletter
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/control-field.tsx)。
## API 参考
### ControlField
| prop | type | default | description |
| ----------------- | -------------------------------------------------------------------------- | ----------- | -------------------------------- |
| children | `React.ReactNode \| ((props: ControlFieldRenderProps) => React.ReactNode)` | - | 字段内部内容或渲染函数 |
| isSelected | `boolean` | `undefined` | 是否选中/勾选 |
| isDisabled | `boolean` | `false` | 是否禁用 |
| isInvalid | `boolean` | `false` | 是否非法 |
| isRequired | `boolean` | `false` | 是否必填 |
| className | `string` | - | 根元素自定义 class |
| onSelectedChange | `(isSelected: boolean) => void` | - | 选中状态变化时回调 |
| animation | `"disable-all" \| undefined` | `undefined` | 动画配置;`"disable-all"` 时关闭根及子级全部动画 |
| ...PressableProps | `PressableProps` | - | 支持 React Native Pressable 全部属性 |
### Label
`Label` 会自动消费 ControlField 上下文中的表单状态(`isDisabled`、`isInvalid`)。
**说明**:完整属性见 [Label 组件文档](./label)。
### Description
`Description` 会自动消费 ControlField 上下文中的表单状态(`isDisabled`、`isInvalid`)。
**说明**:完整属性见 [Description 组件文档](./description)。
### ControlField.Indicator
| prop | type | default | description |
| ------------ | ----------------------------------- | ---------- | ----------------------------- |
| children | `React.ReactNode` | - | 要渲染的控件(Switch、Checkbox、Radio) |
| variant | `'checkbox' \| 'radio' \| 'switch'` | `'switch'` | 未提供 children 时渲染的内置变体 |
| className | `string` | - | 指示器容器自定义 class |
| ...ViewProps | `ViewProps` | - | 支持 React Native View 全部属性 |
**说明:** 提供 `children` 时,若子组件上尚未设置,会自动从 ControlField 上下文传入 `isSelected`、`onSelectedChange`、`isDisabled`、`isInvalid`。使用 `radio` 变体时,Radio 以独立模式渲染(不在 RadioGroup 内)。
### FieldError
`FieldError` 会自动消费 ControlField 上下文中的 `isInvalid`。
**说明**:完整属性见 [FieldError 组件文档](./field-error)。显隐由父级 ControlField 的 `isInvalid` 控制。
## Hooks
### useControlField
在 `ControlField` 内访问字段上下文(用于自定义子结构)。
**返回值:**
| property | type | description |
| ------------------ | ---------------------------------------------- | --------------------- |
| `isSelected` | `boolean \| undefined` | 是否选中/勾选 |
| `onSelectedChange` | `((isSelected: boolean) => void) \| undefined` | 选中状态变化回调 |
| `isDisabled` | `boolean` | 是否禁用 |
| `isInvalid` | `boolean` | 是否非法 |
| `isPressed` | `SharedValue` | Reanimated 共享值,表示按压状态 |
# Description 描述
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/description
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(forms)/description.mdx
> 用于为表单字段等提供无障碍说明与辅助文案的文本组件。
## 导入
```tsx
import { Description } from 'heroui-native';
```
## 结构
```tsx
...
```
* **Description**:以弱化样式展示说明或辅助文案;可通过 `nativeID` 与表单字段关联以支持无障碍。
## 用法
### 基础用法
使用默认弱化样式展示说明文字。
```tsx
This is a helpful description.
```
### 与表单字段组合
使用 `nativeID` 为表单字段提供可关联的说明。
```tsx
Email address
We'll never share your email with anyone else.
```
### 无障碍关联
通过 `nativeID` 与 `aria-describedby` 将说明与字段关联,便于读屏。
```tsx
Password
Use at least 8 characters with a mix of letters, numbers, and symbols.
```
### 非法态时隐藏
使用 `hideOnInvalid` 控制字段非法时是否隐藏说明。
```tsx
Email
We'll never share your email with anyone else.
Please enter a valid email address
```
当 `hideOnInvalid` 为 `true` 时,字段非法会隐藏说明;为 `false`(默认)时非法仍显示说明。
## 示例
```tsx
import { Description, Input, Label, TextField } from 'heroui-native';
import { View } from 'react-native';
export default function DescriptionExample() {
return (
Email address
We'll never share your email with anyone else.
Password
Use at least 8 characters with a mix of letters, numbers, and symbols.
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/description.tsx)。
## API 参考
### Description
| prop | type | default | description |
| --------------- | ----------------------------------- | ------- | ------------------------------------------- |
| `children` | `React.ReactNode` | - | 说明文本内容 |
| `className` | `string` | - | 额外 class |
| `nativeID` | `string` | - | 无障碍用 native ID,与 `aria-describedby` 等配合关联字段 |
| `isInvalid` | `boolean` | - | 是否处于非法态(可覆盖上下文) |
| `isDisabled` | `boolean` | - | 是否禁用态(可覆盖上下文) |
| `hideOnInvalid` | `boolean` | `false` | 非法时是否隐藏说明 |
| `animation` | `DescriptionAnimation \| undefined` | - | 说明显隐等过渡的动画配置 |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部标准属性 |
# FieldError 字段错误
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/field-error
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(forms)/field-error.mdx
> 展示校验错误信息,并带有平滑动画。
## 导入
```tsx
import { FieldError } from 'heroui-native';
```
## 结构
```tsx
错误信息内容
```
* **FieldError**:展示错误信息的主容器,带动画。字符串子节点会自动用 `Text` 包裹,也可传入自定义 React 节点。通过 `isInvalid` 控制显隐,并支持自定义进入/退出动画。
## 用法
### 基础用法
校验失败时展示错误信息。
```tsx
此字段为必填
```
### 受控显隐
使用 `isInvalid` 控制何时显示。放在 `TextField` 等表单项内时,会自动消费 form-item-state 上下文。
```tsx
const [isInvalid, setIsInvalid] = useState(false);
请输入有效的邮箱地址 ;
```
### 与表单字段配合
`FieldError` 会通过 form-item-state 上下文自动读取 `TextField` 的表单状态。
```tsx
import { FieldError, Label, TextField } from 'heroui-native';
邮箱
请输入有效的邮箱地址
```
### 自定义内容
子节点可传入自定义 React 组件而非纯字符串。
```tsx
输入无效
```
### 自定义动画
使用 `animation` 覆盖默认进入/退出动画。
```tsx
import { SlideInDown, SlideOutUp } from 'react-native-reanimated';
字段校验未通过
;
```
完全禁用动画:
```tsx
字段校验未通过
```
### 自定义样式
为容器与文字应用自定义样式。
```tsx
密码至少 8 位
```
### 自定义 Text 属性
当子节点为字符串时,可通过 `textProps` 传给内部 `Text`。
```tsx
这是一段可能很长需要截断的错误提示文案示例
```
## 示例
```tsx
import { Description, FieldError, Label, TextField } from 'heroui-native';
import { useState } from 'react';
import { View } from 'react-native';
export default function FieldErrorExample() {
const [email, setEmail] = useState('');
const [isInvalid, setIsInvalid] = useState(false);
const isValidEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
const handleBlur = () => {
setIsInvalid(email !== '' && !isValidEmail);
};
return (
邮箱地址
我们将通过此邮箱与您联系
请输入有效的邮箱地址
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/field-error.tsx)。
## API 参考
### FieldError
| prop | type | default | description |
| ---------------------- | --------------------------------------------- | ----------- | ------------------------------------------------------ |
| `children` | `React.ReactNode` | `undefined` | 错误内容;字符串子节点会用 `Text` 包裹 |
| `isInvalid` | `boolean` | `undefined` | 控制是否显示(可覆盖 form-item-state)。置于 `TextField` 内时会自动消费表单状态 |
| `animation` | `FieldErrorRootAnimation` | - | 动画配置 |
| `className` | `string` | `undefined` | 容器的额外 class |
| `classNames` | `ElementSlots` | `undefined` | 各部分的额外 class |
| `styles` | `{ container?: ViewStyle; text?: TextStyle }` | `undefined` | 容器与文字的样式 |
| `textProps` | `TextProps` | `undefined` | 子节点为字符串时传给 `Text` 的额外属性 |
| `...AnimatedViewProps` | `AnimatedProps` | - | 支持 Reanimated `Animated.View` 的全部属性 |
**classNames:** `ElementSlots` 为各部分提供类型安全的 class。可用插槽:`container`、`text`。
#### `styles`
| prop | type | description |
| ----------- | ----------- | ----------- |
| `container` | `ViewStyle` | 容器样式 |
| `text` | `TextStyle` | 文字样式 |
#### FieldErrorRootAnimation
根组件动画配置,可为:
* `false` 或 `"disabled"`:仅禁用根级动画
* `"disable-all"`:禁用全部动画(含子级)
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ---------------- | ---------------------------------------- | ----------------------------------------------------------------------- | ----------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | 在自定义属性时禁用动画 |
| `entering.value` | `EntryOrExitLayoutType` | `FadeIn` `.duration(150)` `.easing(Easing.out(Easing.ease))` | 自定义进入动画 |
| `exiting.value` | `EntryOrExitLayoutType` | `FadeOut` `.duration(100)` `.easing(Easing.out(Easing.ease))` | 自定义退出动画 |
# InputGroup 输入框组
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/input-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(forms)/input-group.mdx
> 复合布局组件,将输入框与可选的前后缀装饰组合在一起。
## 导入
```tsx
import { InputGroup } from 'heroui-native';
```
## 结构
```tsx
...
...
```
* **InputGroup**:布局容器,包裹前缀、输入与后缀;提供动画设置与测量上下文,自动将前后缀宽度应用为 `Input` 的内边距。
* **InputGroup.Prefix**:绝对定位在输入左侧;测量宽度自动作为 `InputGroup.Input` 的 `paddingLeft`。
* **InputGroup.Suffix**:绝对定位在输入右侧;测量宽度自动作为 `InputGroup.Input` 的 `paddingRight`。
* **InputGroup.Input**:透传至 `Input`,支持全部 `Input` 属性,并自动获得前后缀对应的左右内边距。
## 用法
### 基础用法
通过复合子部件为输入框附加前后缀内容。
```tsx
...
...
```
### 仅前缀
在输入前附加图标等内容。
```tsx
```
### 仅后缀
在输入后附加图标等内容。
```tsx
```
### 装饰性与可交互
在 `Prefix`/`Suffix` 上设置 `isDecorative` 时,触摸事件会穿透到 `Input`,且对读屏隐藏装饰内容;包含可交互元素时不要设置。
```tsx
```
### 禁用状态
禁用整个输入组,状态会级联到子组件。
```tsx
```
### 与 TextField 组合
与 `TextField`、`Label`、`Description` 等组合成完整表单项。
```tsx
邮箱
我们不会公开您的邮箱
```
## 示例
```tsx
import { InputGroup } from 'heroui-native';
import { Ionicons } from '@expo/vector-icons';
import { useState } from 'react';
import { Pressable, View } from 'react-native';
export default function InputGroupExample() {
const [value, setValue] = useState('');
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
return (
setIsPasswordVisible(!isPasswordVisible)}
hitSlop={20}
>
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/input-group.tsx)。
## API 参考
### InputGroup
| prop | type | default | description |
| -------------- | ------------------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 组内子节点 |
| `className` | `string` | - | 额外的 class |
| `isDisabled` | `boolean` | `false` | 是否禁用整个输入组及子级 |
| `animation` | `AnimationRootDisableAll` | - | 输入组动画配置 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
#### AnimationRootDisableAll
根组件动画配置,可为:
* `"disable-all"`:禁用全部动画(含子级,级联)
* `undefined`:使用默认动画
### InputGroup.Prefix
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 前缀区域内容 |
| `className` | `string` | - | 额外的 class |
| `isDecorative` | `boolean` | `false` | 为 true 时触摸穿透到 `Input`,且对读屏隐藏 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
### InputGroup.Suffix
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 后缀区域内容 |
| `className` | `string` | - | 额外的 class |
| `isDecorative` | `boolean` | `false` | 为 true 时触摸穿透到 `Input`,且对读屏隐藏 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
### InputGroup.Input
透传至 [Input](./input) 组件,支持其全部属性。
# InputOTP 一次性密码输入框
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/input-otp
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(forms)/input-otp.mdx
> 用于输入一次性验证码(OTP)的输入组件,支持分格、动画与校验。
## 导入
```tsx
import { InputOTP } from 'heroui-native';
```
## 结构
```tsx
```
* **InputOTP**:根容器,管理 OTP 状态与文本变更,并为子组件提供上下文;处理焦点、校验与字符输入。
* **InputOTP.Group**:将多个格子编组;用于视觉分组(例如每 3 位一组)。
* **InputOTP.Slot**:单个字符格;`index` 须在 OTP 序列中唯一且与位置对应。未提供子节点时,默认渲染 `SlotPlaceholder`、`SlotValue` 与 `SlotCaret`。
* **InputOTP.SlotPlaceholder**:空位时显示的占位字符;`Slot` 无子节点时默认使用。
* **InputOTP.SlotValue**:显示已输入字符并带动画;`Slot` 无子节点时默认使用。
* **InputOTP.SlotCaret**:动画光标,指示当前输入位置;置于 `Slot` 内以显示正在输入的位置。
* **InputOTP.Separator**:分组之间的视觉分隔符。
## 用法
### 基础用法
创建 6 位 OTP,分两组并带分隔符。
```tsx
console.log(code)}>
```
### 四位 PIN
简单的 4 位数字 PIN。
```tsx
console.log(code)}>
```
### 自定义占位
为每个格子位置提供自定义占位字符。
```tsx
console.log(code)}
>
{({ slots }) => (
<>
{slots.map((slot) => (
))}
>
)}
```
### 受控值
以编程方式控制 OTP 值。
```tsx
const [value, setValue] = useState('');
;
```
### 校验态
非法时展示校验错误样式。
```tsx
```
### 输入模式(正则)
使用正则限制可输入字符。内置:`REGEXP_ONLY_DIGITS`(0–9)、`REGEXP_ONLY_CHARS`(a–z、A–Z)、`REGEXP_ONLY_DIGITS_AND_CHARS`(数字与字母)。
```tsx
import { InputOTP, REGEXP_ONLY_CHARS } from 'heroui-native';
console.log(code)}
>
;
```
### 自定义布局
在 `Group` 上使用渲染属性以自定义格子布局。
```tsx
{({ slots, isFocused, isInvalid }) => (
<>
{slots.map((slot) => (
))}
>
)}
```
### 在底部抽屉内
在 `BottomSheet` 中渲染 `InputOTP` 时,使用 `useBottomSheetAwareHandlers` 返回的 `onFocus` / `onBlur` 传给 `InputOTP`,以正确处理键盘避让。
```tsx
import { InputOTP, useBottomSheetAwareHandlers } from 'heroui-native';
const BottomSheetOTPInput = () => {
const { onFocus, onBlur } = useBottomSheetAwareHandlers();
return (
);
};
```
## 示例
```tsx
import { InputOTP, Label, Description, type InputOTPRef } from 'heroui-native';
import { View } from 'react-native';
import { useRef } from 'react';
export default function InputOTPExample() {
const ref = useRef(null);
const onComplete = (code: string) => {
console.log('OTP completed:', code);
setTimeout(() => {
ref.current?.clear();
}, 1000);
};
return (
验证账户
我们已向 a****@gmail.com 发送验证码
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/input-otp.tsx)。
## API 参考
### InputOTP
| prop | type | default | description |
| -------------------------- | ----------------------------- | ----------- | --------------------------------------------------- |
| `maxLength` | `number` | - | OTP 最大长度(必填) |
| `value` | `string` | - | 受控值 |
| `defaultValue` | `string` | - | 非受控默认值 |
| `onChange` | `(value: string) => void` | - | 值变化回调 |
| `onComplete` | `(value: string) => void` | - | 所有格子填满时触发 |
| `isDisabled` | `boolean` | `false` | 是否禁用 |
| `isInvalid` | `boolean` | `false` | 是否处于非法状态 |
| `pattern` | `string` | - | 允许字符的正则(如 `REGEXP_ONLY_DIGITS`、`REGEXP_ONLY_CHARS`) |
| `inputMode` | `TextInputProps['inputMode']` | `'numeric'` | 输入模式 |
| `placeholder` | `string` | - | 占位字符串;每个字符对应一个格子位置 |
| `placeholderTextColor` | `string` | - | 全部格子的占位文字颜色 |
| `placeholderTextClassName` | `string` | - | 全部格子的占位文字 class |
| `pasteTransformer` | `(text: string) => string` | - | 粘贴内容转换(如去掉连字符);默认会移除非匹配字符 |
| `onFocus` | `(e: FocusEvent) => void` | - | 聚焦回调 |
| `onBlur` | `(e: BlurEvent) => void` | - | 失焦回调 |
| `textInputProps` | `Omit` | - | 透传给底层 `TextInput` 的额外属性 |
| `children` | `React.ReactNode` | - | 子节点 |
| `className` | `string` | - | 根容器额外 class |
| `style` | `PressableProps['style']` | - | 传给容器 `Pressable` 的样式 |
| `isBottomSheetAware` | `boolean` | `true` | 在 `BottomSheet` 内是否自动处理键盘相关状态;设为 `false` 可关闭 |
| `animation` | `"disable-all" \| undefined` | `undefined` | 动画配置;`"disable-all"` 可禁用自身及子级全部动画 |
### InputOTP.Group
| prop | type | default | description |
| -------------- | --------------------------------------------------------------------------- | ------- | ----------------------------- |
| `children` | `React.ReactNode \| ((props: InputOTPGroupRenderProps) => React.ReactNode)` | - | 子节点,或接收格子数据与上下文的渲染函数 |
| `className` | `string` | - | 额外 class |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
#### InputOTPGroupRenderProps
| prop | type | description |
| ------------ | ------------ | ----------- |
| `slots` | `SlotData[]` | 每个位置的格子数据数组 |
| `maxLength` | `number` | OTP 最大长度 |
| `value` | `string` | 当前 OTP 值 |
| `isFocused` | `boolean` | 是否聚焦 |
| `isDisabled` | `boolean` | 是否禁用 |
| `isInvalid` | `boolean` | 是否非法 |
### InputOTP.Slot
| prop | type | default | description |
| -------------- | ----------------- | ------- | --------------------------------------------------------- |
| `index` | `number` | - | 格子下标(必填),须为 `0` 到 `maxLength - 1` |
| `children` | `React.ReactNode` | - | 自定义格子内容;未提供时默认为 `SlotPlaceholder`、`SlotValue`、`SlotCaret` |
| `className` | `string` | - | 额外 class |
| `style` | `ViewStyle` | - | 额外样式 |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
### InputOTP.SlotPlaceholder
| prop | type | default | description |
| -------------- | ----------- | ------- | ------------------------------------ |
| `children` | `string` | - | 显示文本(可选,默认使用 `slot.placeholderChar`) |
| `className` | `string` | - | 额外 class |
| `style` | `TextStyle` | - | 额外样式 |
| `...TextProps` | `TextProps` | - | 支持全部标准 React Native `Text` 属性 |
### InputOTP.SlotValue
| prop | type | default | description |
| -------------- | ---------------------------- | ------- | ----------------------------- |
| `children` | `string` | - | 显示文本(可选,默认使用 `slot.char`) |
| `className` | `string` | - | 额外 class |
| `animation` | `InputOTPSlotValueAnimation` | - | `SlotValue` 动画配置 |
| `...TextProps` | `TextProps` | - | 支持全部标准 React Native `Text` 属性 |
#### InputOTPSlotValueAnimation
`InputOTP.SlotValue` 动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ------------------ | ----------------------- | ---------------------------------------- | ------------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时用于禁用动画 |
| `wrapper.entering` | `EntryOrExitLayoutType` | `FadeIn.duration(250)` | 包裹层进入动画 |
| `wrapper.exiting` | `EntryOrExitLayoutType` | `FadeOut.duration(100)` | 包裹层退出动画 |
| `text.entering` | `EntryOrExitLayoutType` | `FlipInXDown.duration(250).easing(...)` | 文本进入动画 |
| `text.exiting` | `EntryOrExitLayoutType` | `FlipOutXDown.duration(250).easing(...)` | 文本退出动画 |
### InputOTP.SlotCaret
| prop | type | default | description |
| ----------------------- | ---------------------------- | -------- | ---------------------------------------------- |
| `className` | `string` | - | 额外 class |
| `style` | `ViewStyle` | - | 额外样式 |
| `animation` | `InputOTPSlotCaretAnimation` | - | `SlotCaret` 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式;为 `false` 时移除内置动画样式,可自行实现 |
| `pointerEvents` | `'none' \| 'auto' \| ...` | `'none'` | 指针事件配置 |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
#### InputOTPSlotCaretAnimation
`InputOTP.SlotCaret` 动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ------------------ | ----------------------- | ---------- | ---------------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时用于禁用动画 |
| `opacity.value` | `[number, number]` | `[0, 1]` | 透明度 \[最小, 最大] |
| `opacity.duration` | `number` | `500` | 动画时长(毫秒) |
| `height.value` | `[number, number]` | `[16, 18]` | 高度 \[最小, 最大](像素) |
| `height.duration` | `number` | `500` | 动画时长(毫秒) |
### InputOTP.Separator
| prop | type | default | description |
| -------------- | ----------- | ------- | ----------------------------- |
| `className` | `string` | - | 额外 class |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
## Hooks
### useInputOTP
读取 `InputOTP` 根上下文,须在 `InputOTP` 内使用。
```tsx
const { value, maxLength, isFocused, isDisabled, isInvalid, slots } =
useInputOTP();
```
### useInputOTPSlot
读取 `InputOTP.Slot` 上下文,须在 `InputOTP.Slot` 内使用。
```tsx
const { slot, isActive, isCaretVisible } = useInputOTPSlot();
```
# Input 输入框
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/input
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(forms)/input.mdx
> 单行文本输入,带样式边框与背景,用于收集用户输入。
## 导入
```tsx
import { Input } from 'heroui-native';
```
## 用法
### 基础用法
`Input` 可单独使用,也可放在 `TextField` 内。
```tsx
import { Input } from 'heroui-native';
;
```
### 与 TextField 组合
与 `TextField` 搭配形成完整表单结构。
```tsx
import { Input, Label, TextField } from 'heroui-native';
邮箱
;
```
### 校验状态
非法时展示错误样式。
```tsx
import { FieldError, Input, Label, TextField } from 'heroui-native';
邮箱
请输入有效邮箱
;
```
### 局部覆盖非法状态
在输入上覆盖上下文中的非法状态。
```tsx
import { FieldError, Input, Label, TextField } from 'heroui-native';
邮箱
邮箱格式不正确
;
```
### 禁用状态
禁用输入,阻止交互。
```tsx
import { Input, Label, TextField } from 'heroui-native';
禁用字段
;
```
### 变体
按场景使用不同视觉变体。
```tsx
import { Input, Label, TextField } from 'heroui-native';
主要变体
次要变体
```
### 自定义样式
通过 `className` 自定义外观。
```tsx
import { Input, Label, TextField } from 'heroui-native';
自定义样式
;
```
### 在 Bottom Sheet 内
在 `BottomSheet` 中渲染 `Input` 时,使用 `useBottomSheetAwareHandlers` 连接键盘避让:将返回的 `onFocus`、`onBlur` 传给 `Input`。
```tsx
import { Input, TextField, useBottomSheetAwareHandlers } from 'heroui-native';
const BottomSheetTextInput = () => {
const { onFocus, onBlur } = useBottomSheetAwareHandlers();
return (
);
};
```
## 示例
```tsx
import { Ionicons } from '@expo/vector-icons';
import { Description, Input, Label, TextField } from 'heroui-native';
import { useState } from 'react';
import { Pressable, View } from 'react-native';
import { withUniwind } from 'uniwind';
const StyledIonicons = withUniwind(Ionicons);
export const TextInputContent = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
return (
邮箱
我们不会向他人公开您的邮箱。
新密码
setIsPasswordVisible(!isPasswordVisible)}
>
密码至少 6 位
);
};
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/input.tsx)。
## API 参考
### Input
| prop | type | default | description |
| ------------------------- | -------------------------- | --------------------- | ------------------------------------------ |
| isInvalid | `boolean` | `undefined` | 是否非法(可覆盖上下文) |
| variant | `'primary' \| 'secondary'` | `'primary'` | 输入框视觉变体 |
| className | `string` | - | 自定义 class |
| selectionColorClassName | `string` | `"accent-accent"` | 选中文本颜色的 class |
| placeholderColorClassName | `string` | `"field-placeholder"` | 占位符文字颜色的 class |
| isBottomSheetAware | `boolean` | `true` | 在 BottomSheet 内是否自动处理键盘相关逻辑;设为 `false` 可关闭 |
| animation | `AnimationRoot` | `undefined` | 输入框动画配置 |
| ...TextInputProps | `TextInputProps` | - | 支持 React Native `TextInput` 的全部属性 |
> **说明**:置于 `TextField` 内时,`Input` 会通过 form-item-state 上下文自动消费 `isDisabled`、`isInvalid` 等表单状态。
# Label 标签
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/label
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(forms)/label.mdx
> 用于标注表单字段等 UI 的文本组件,支持必填标记与校验状态。
## 导入
```tsx
import { Label } from 'heroui-native';
```
## 结构
```tsx
...
```
* **Label**:根容器,管理标签状态并为子组件提供上下文。传入字符串子节点时会自动渲染为 `Label.Text`。支持禁用、必填与非法状态。
* **Label.Text**:标签文字;在必填时自动显示星号,非法或禁用时改变颜色。
## 用法
### 基础用法
展示标签文字。字符串子节点会自动渲染为 `Label.Text`。
```tsx
用户名
```
### 与表单字段配合
将 `Label` 与表单字段组合以提供无障碍标签。
```tsx
用户名
```
### 必填字段
使用 `isRequired` 显示必填星号。
```tsx
密码
```
### 非法状态
在校验失败时使用非法样式突出标签。
```tsx
import { FieldError, Label, TextField } from 'heroui-native';
确认密码
两次密码不一致
```
### 禁用状态
禁用标签以表示字段不可交互。
```tsx
订阅方案
```
### 自定义布局
使用复合子组件自定义标签结构。
```tsx
自定义标签
```
### 自定义样式
通过 `className`、`classNames` 或 `styles` 传入样式。
```tsx
自定义样式标签
```
## 示例
```tsx
import { FieldError, Label, TextField } from 'heroui-native';
import { View } from 'react-native';
export default function LabelExample() {
return (
用户名
密码
确认密码
两次密码不一致
订阅方案
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/label.tsx)。
## API 参考
### Label
| prop | type | default | description |
| ------------------- | ---------------------------- | ----------- | --------------------------------------- |
| `children` | `React.ReactNode` | - | 标签内容。为字符串时自动渲染为 `Label.Text`;否则按原样渲染子节点 |
| `isRequired` | `boolean` | `false` | 是否必填;为 true 时显示星号 |
| `isInvalid` | `boolean` | `false` | 是否非法;为 true 时文字使用危险色 |
| `isDisabled` | `boolean` | `false` | 是否禁用;应用禁用样式并阻止交互 |
| `className` | `string` | - | 额外的 class |
| `animation` | `"disable-all" \| undefined` | `undefined` | 动画配置;`"disable-all"` 可禁用自身及子级的全部动画 |
| `...PressableProps` | `PressableProps` | - | 支持 React Native `Pressable` 的全部属性 |
### Label.Text
| prop | type | default | description |
| -------------- | ---------------------------------------- | ------- | ----------------------------------- |
| `children` | `React.ReactNode` | - | 标签文字内容 |
| `className` | `string` | - | 文本元素的额外 class |
| `classNames` | `ElementSlots` | - | 标签各部分的额外 class |
| `styles` | `Partial>` | - | 标签各部分的样式 |
| `nativeID` | `string` | - | 无障碍用原生 ID,通过 aria-labelledby 关联表单控件 |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部属性 |
#### `ElementSlots`
| prop | type | description |
| ---------- | -------- | ----------- |
| `text` | `string` | 标签文字的 class |
| `asterisk` | `string` | 星号的 class |
#### `styles`
| prop | type | description |
| ---------- | ----------- | ----------- |
| `text` | `TextStyle` | 标签文字样式 |
| `asterisk` | `TextStyle` | 星号样式 |
# RadioGroup 单选框组
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/radio-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(forms)/radio-group.mdx
> 单选按钮组,同一时间只能选中一个选项。
## 导入
```tsx
import { RadioGroup } from 'heroui-native';
```
## 结构
```tsx
...
...
...
```
* **RadioGroup**:管理单选项选中状态的容器,支持横向与纵向布局。
* **RadioGroup.Item**:组内单个选项,必须放在 `RadioGroup` 内。处理选中状态;在仅提供文本子节点时会渲染默认 ` ` 指示器。支持渲染函数子节点以访问状态(`isSelected`、`isInvalid`、`isDisabled`)。
* **Label**:可选的可点击文字标签,与单选项关联以提升无障碍。请直接使用 [Label](./label) 组件。
* **Description**:标签下方的可选说明文字。请直接使用 [Description](./description) 组件。
* **Radio**:置于 `RadioGroup.Item` 内的 [Radio](./radio) 组件,用于渲染单选指示器。会自动识别 `RadioGroupItem` 上下文并从中获取 `isSelected`、`isDisabled`、`isInvalid` 与 `variant`。
* **Radio.Indicator**:单选圆环的可选容器;无子节点时渲染默认拇指样式,管理选中视觉。完整 API 见 [Radio](./radio)。
* **Radio.IndicatorThumb**:选中时显示的可选内圆,随选中状态缩放动画;可替换为自定义内容。见 [Radio](./radio)。
* **FieldError**:在组无效时显示的错误信息,带动画显示在组内容下方。请直接使用 [FieldError](./field-error) 组件。
## 用法
### 基础用法
使用简单字符串子节点时,会自动渲染标题与指示器。
```tsx
选项 1
选项 2
选项 3
```
### 带说明文字
在每个选项下方添加描述以补充上下文。
```tsx
import { RadioGroup, Radio, Label, Description } from 'heroui-native';
import { View } from 'react-native';
标准配送
5–7 个工作日送达
加急配送
2–3 个工作日送达
;
```
### 自定义指示器
使用 `Radio` 子组件将默认拇指替换为自定义内容。
```tsx
import { RadioGroup, Radio, Label } from 'heroui-native';
{({ isSelected }) => (
<>
自定义选项
{isSelected && (
)}
>
)}
;
```
### 使用渲染函数
在 `RadioGroup.Item` 上使用渲染函数以访问状态并自定义整块内容。
```tsx
import { RadioGroup, Radio, Label } from 'heroui-native';
{({ isSelected, isInvalid, isDisabled }) => (
<>
选项 1
{isSelected && }
>
)}
;
```
### 显示错误信息
在单选组下方展示校验错误。
```tsx
import { RadioGroup, FieldError } from 'heroui-native';
function RadioGroupWithError() {
const [value, setValue] = React.useState(undefined);
return (
我同意条款
我不同意
请选择一项以继续
);
}
```
## 示例
```tsx
import {
Description,
Label,
Radio,
RadioGroup,
Separator,
Surface,
} from 'heroui-native';
import React from 'react';
import { View } from 'react-native';
export default function RadioGroupExample() {
const [selection, setSelection] = React.useState('desc1');
return (
标准配送
5–7 个工作日送达
加急配送
2–3 个工作日送达
次日达
下一个工作日送达
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/radio-group.tsx)。
## API 参考
### RadioGroup
| prop | type | default | description |
| --------------- | ---------------------------- | ----------- | --------------------------------------- |
| `children` | `React.ReactNode` | `undefined` | 单选组内容 |
| `value` | `string \| undefined` | `undefined` | 当前选中值 |
| `onValueChange` | `(val: string) => void` | `undefined` | 选中值变化时的回调 |
| `isDisabled` | `boolean` | `false` | 是否禁用整个单选组 |
| `isInvalid` | `boolean` | `false` | 组是否处于无效状态 |
| `variant` | `'primary' \| 'secondary'` | `undefined` | 单选组样式变体(子项未单独设置时继承) |
| `animation` | `"disable-all" \| undefined` | `undefined` | 动画配置。使用 `"disable-all"` 可关闭包含子节点在内的全部动画 |
| `className` | `string` | `undefined` | 自定义 className |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native View 属性 |
### RadioGroup.Item
| prop | type | default | description |
| ------------------- | ---------------------------------------------------------------------------- | ----------- | -------------------------------- |
| `children` | `React.ReactNode \| ((props: RadioGroupItemRenderProps) => React.ReactNode)` | `undefined` | 选项内容,或用于自定义项的渲染函数 |
| `value` | `string` | `undefined` | 该选项关联的值 |
| `isDisabled` | `boolean` | `false` | 是否禁用该选项 |
| `isInvalid` | `boolean` | `false` | 该选项是否无效 |
| `variant` | `'primary' \| 'secondary'` | `'primary'` | 该选项的样式变体 |
| `hitSlop` | `number` | `6` | 可点击区域的热区扩展 |
| `className` | `string` | `undefined` | 自定义 className |
| `...PressableProps` | `PressableProps` | - | 支持全部标准 Pressable 属性(不含 disabled) |
#### RadioGroupItemRenderProps
| prop | type | description |
| ------------ | --------- | ----------- |
| `isSelected` | `boolean` | 该选项是否选中 |
| `isInvalid` | `boolean` | 该选项是否无效 |
| `isDisabled` | `boolean` | 该选项是否禁用 |
### Radio(位于 RadioGroup.Item 内)
`Radio` 放在 `RadioGroup.Item` 内用于渲染单选指示器。此时会自动识别 `RadioGroupItem` 上下文并从中获取 `isSelected`、`isDisabled`、`isInvalid` 与 `variant`,无需手动传参。
使用 ` ` 获得默认指示器,或组合 `Radio.Indicator` 与 `Radio.IndicatorThumb` 自定义样式。
| prop | type | default | description |
| ------------------- | ------------------------------------------------------------------- | ----------- | -------------------------------- |
| `children` | `React.ReactNode \| ((props: RadioRenderProps) => React.ReactNode)` | `undefined` | 子元素或渲染函数以自定义单选 |
| `variant` | `'primary' \| 'secondary'` | `'primary'` | 单选视觉变体 |
| `isSelected` | `boolean` | `undefined` | 是否选中 |
| `isDisabled` | `boolean` | `undefined` | 是否禁用且不可交互 |
| `isInvalid` | `boolean` | `false` | 是否无效(危险色) |
| `className` | `string` | `undefined` | 额外 CSS 类 |
| `animation` | `RadioRootAnimation` | - | 单选根动画配置 |
| `onSelectedChange` | `(isSelected: boolean) => void` | `undefined` | 选中状态变化时的回调 |
| `...PressableProps` | `PressableProps` | - | 支持全部标准 Pressable 属性(不含 disabled) |
#### RadioRenderProps
| prop | type | description |
| ------------ | --------- | ----------- |
| `isSelected` | `boolean` | 是否选中 |
| `isDisabled` | `boolean` | 是否禁用 |
| `isInvalid` | `boolean` | 是否无效 |
#### RadioRootAnimation
单选根组件的动画配置,可为:
* `"disable-all"`:关闭包含子节点(Indicator、IndicatorThumb)在内的全部动画
* `undefined`:使用默认动画
### Radio.Indicator
| prop | type | default | description |
| ---------------------- | -------------------------- | ----------- | -------------------------------- |
| `children` | `React.ReactNode` | `undefined` | 指示器内容 |
| `className` | `string` | `undefined` | 指示器额外 CSS 类 |
| `...AnimatedViewProps` | `AnimatedProps` | - | 支持全部 Reanimated Animated.View 属性 |
### Radio.IndicatorThumb
| prop | type | default | description |
| ----------------------- | ------------------------------ | ----------- | -------------------------------- |
| `className` | `string` | `undefined` | 拇指区域额外 CSS 类 |
| `animation` | `RadioIndicatorThumbAnimation` | - | 拇指动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `...AnimatedViewProps` | `AnimatedProps` | - | 支持全部 Reanimated Animated.View 属性 |
#### RadioIndicatorThumbAnimation
单选指示器拇指的动画配置,可为:
* `false` 或 `"disabled"`:关闭全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| -------------------- | ----------------------- | ---------------------------------------------------- | --------------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时用于禁用动画 |
| `scale.value` | `[number, number]` | `[1.5, 1]` | 缩放值 \[未选中, 已选中] |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 300, easing: Easing.out(Easing.ease) }` | 动画时间配置 |
**说明:** 标签、说明与错误信息请直接使用基础组件:
* 标签使用 [Label](../label/label.md)
* 说明使用 [Description](../description/description.md)
* 错误使用 [FieldError](../field-error/field-error.md)
## Hooks
### useRadioGroup
#### 返回值
| 属性 | 类型 | 描述 |
| --------------- | -------------------------- | -------- |
| `value` | `string \| undefined` | 当前选中值 |
| `isDisabled` | `boolean` | 单选组是否禁用 |
| `isInvalid` | `boolean` | 单选组是否无效 |
| `variant` | `'primary' \| 'secondary'` | 单选组样式变体 |
| `onValueChange` | `(value: string) => void` | 修改选中值的函数 |
### useRadioGroupItem
#### 返回值
| 属性 | 类型 | 描述 |
| ------------------ | ---------------------------------------------- | ------------------ |
| `isSelected` | `boolean` | 该选项是否选中 |
| `isDisabled` | `boolean \| undefined` | 该选项是否禁用 |
| `isInvalid` | `boolean \| undefined` | 该选项是否无效 |
| `variant` | `'primary' \| 'secondary' \| undefined` | 该选项的样式变体 |
| `onSelectedChange` | `((isSelected: boolean) => void) \| undefined` | 修改选中状态的回调(在组内选中该项) |
# SearchField 搜索框
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/search-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(forms)/search-field.mdx
> 用于筛选与查询的复合搜索输入框。
## 导入
```tsx
import { SearchField } from 'heroui-native';
```
## 结构
```tsx
```
* **SearchField**:根容器,接收 `value` 与 `onChange` 并通过上下文下发;同时提供 `isDisabled`、`isInvalid`、`isRequired` 与动画设置。
* **SearchField.Group**:横向 `flex-row` 容器,排列搜索图标、输入与清除按钮。
* **SearchField.SearchIcon**:默认放大镜图标,绝对定位在输入左侧;可传入子节点替换默认图标。
* **SearchField.Input**:包装 `Input` 并应用搜索相关默认行为;自动从上下文读取 `value` 与 `onChangeText`。
* **SearchField.ClearButton**:清除输入的小图标按钮;值为空时自动隐藏;按下时调用上下文的 `onChange("")`。
## 用法
### 基础用法
在根上传入 `value` 与 `onChange`;`Input` 与 `ClearButton` 通过上下文消费。
```tsx
```
### 标签与说明
在 `Group` 外放置 `Label`、`Description` 以补充语义。
```tsx
查找商品
按名称、分类或 SKU 搜索
```
### 校验
在根上使用 `isInvalid`、`isRequired`,并配合 `FieldError` 展示错误。
```tsx
搜索用户
至少输入 3 个字符再搜索
未找到结果,请尝试其他关键词。
```
### 自定义搜索图标
向 `SearchField.SearchIcon` 传入子节点替换默认图标。
```tsx
🔍
```
### 禁用
根上设置 `isDisabled`,通过上下文禁用子级。
```tsx
已禁用的搜索
搜索暂时不可用
```
## 示例
```tsx
import { Description, Label, SearchField } from 'heroui-native';
import { useState } from 'react';
import { View } from 'react-native';
export default function SearchFieldExample() {
const [searchValue, setSearchValue] = useState('');
return (
查找商品
按名称、分类或 SKU 搜索
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/search-field.tsx)。
## API 参考
### SearchField
| prop | type | default | description |
| -------------- | ------------------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 搜索字段内的子节点 |
| `value` | `string` | - | 受控搜索文本 |
| `onChange` | `(value: string) => void` | - | 文本变化回调 |
| `isDisabled` | `boolean` | `false` | 是否禁用 |
| `isInvalid` | `boolean` | `false` | 是否非法 |
| `isRequired` | `boolean` | `false` | 是否必填 |
| `className` | `string` | - | 额外的 class |
| `animation` | `AnimationRootDisableAll` | - | 搜索字段动画配置 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
#### AnimationRootDisableAll
根动画配置,可为:
* `"disable-all"`:禁用全部动画(含子级,级联)
* `undefined`:使用默认动画
### SearchField.Group
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 组内子节点 |
| `className` | `string` | - | 额外的 class |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
### SearchField.SearchIcon
| prop | type | default | description |
| -------------- | -------------------------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 自定义内容,替换默认搜索图标 |
| `className` | `string` | - | 额外的 class |
| `iconProps` | `SearchFieldSearchIconIconProps` | - | 自定义默认搜索图标(提供 `children` 时忽略) |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
#### SearchFieldSearchIconIconProps
| prop | type | default | description |
| ------- | -------- | ------------- | ----------- |
| `size` | `number` | `16` | 图标尺寸 |
| `color` | `string` | 主题的 `muted` 色 | 图标颜色 |
### SearchField.Input
在 [Input](./input) 属性之上带有搜索默认值(`placeholder="Search..."`、`returnKeyType="search"`、`accessibilityRole="search"`)。不提供 `value` 与 `onChangeText`,由 `SearchField` 上下文提供。
### SearchField.ClearButton
受控 `value` 为空字符串时自动隐藏;按下时调用上下文的 `onChange("")`。若额外传入 `onPress`,会在清空后调用。
| prop | type | default | description |
| ---------------- | --------------------------------- | ------- | ---------------- |
| `children` | `React.ReactNode` | - | 自定义内容,替换默认关闭图标 |
| `iconProps` | `SearchFieldClearButtonIconProps` | - | 清除按钮图标属性 |
| `className` | `string` | - | 额外的 class |
| `...ButtonProps` | `ButtonRootProps` | - | 支持 Button 根级全部属性 |
#### SearchFieldClearButtonIconProps
| prop | type | default | description |
| ------- | -------- | ------------- | ----------- |
| `size` | `number` | `14` | 图标尺寸 |
| `color` | `string` | 主题的 `muted` 色 | 图标颜色 |
## Hooks
### useSearchField
访问搜索字段上下文,必须在 `SearchField` 内使用。
```tsx
import { useSearchField } from 'heroui-native';
const { value, onChange, isDisabled, isInvalid, isRequired } = useSearchField();
```
#### 返回值
| property | type | description |
| ------------ | ---------------------------------------- | ----------- |
| `value` | `string \| undefined` | 当前受控搜索文本 |
| `onChange` | `((value: string) => void) \| undefined` | 更新搜索文本的回调 |
| `isDisabled` | `boolean` | 是否禁用 |
| `isInvalid` | `boolean` | 是否非法 |
| `isRequired` | `boolean` | 是否必填 |
# Select 选择器
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/select
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(forms)/select.mdx
> 通过按钮触发,展示可选列表供用户选择。
## 导入
```tsx
import { Select } from 'heroui-native';
```
## 结构
```tsx
...
...
```
* **Select**:根容器,管理打开/关闭、选中值,并向子组件提供上下文。
* **Select.Trigger**:可点击的触发器,用于切换选择器显示。为任意子元素包裹按压处理,支持 `variant`(`'default'` 或 `'unstyled'`)。
* **Select.Value**:显示当前选中值或占位符;选中变化时自动更新,样式随是否有选中值变化。
* **Select.TriggerIndicator**:可选的视觉指示器,表示开/关状态;默认渲染带动画的双角标,随打开/关闭旋转。
* **Select.Portal**:在Portal层渲染内容,保证正确的层级与定位。
* **Select.Overlay**:可选的背景遮罩,可透明或半透明,用于捕获外部点击。
* **Select.Content**:内容容器,支持三种呈现:气泡(浮动定位)、底部抽屉或对话框。
* **Select.Close**:关闭按钮;可传入自定义子节点,否则使用默认关闭图标。
* **Select.ListLabel**:列表标题,使用预设排版样式。
* **Select.Item**:可选中的选项,处理选中态与按压。
* **Select.ItemLabel**:选项主文案。
* **Select.ItemDescription**:可选的说明文字,弱化样式。
* **Select.ItemIndicator**:选中项的可选指示器,默认渲染对勾图标。
## 用法
### 基础用法
Select 通过复合子组件构建下拉选择界面。
```tsx
...
```
### 在触发器显示选中值
使用 Value 在触发器区域展示当前选中项。
```tsx
```
### 气泡(Popover)呈现
使用 `presentation="popover"` 获得带自动定位的浮动内容。
```tsx
...
```
### 宽度控制
通过 `width` 控制内容宽度;仅对气泡呈现生效。
```tsx
{
/* 固定像素宽度 */
}
...
;
{
/* 与触发器同宽 */
}
...
;
{
/* 全宽(100%) */
}
...
;
{
/* 随内容自适应(默认) */
}
...
;
```
### 底部抽屉呈现
使用底部抽屉以获得更贴近移动端的体验。
```tsx
...
```
### 对话框呈现
使用对话框呈现居中模态式选择。
```tsx
...
请选择一项
```
### 自定义选项内容
通过自定义子节点与指示器定制选项外观。
```tsx
...
🇺🇸
🇬🇧
```
### 使用渲染函数
在 `Select.Item` 上使用渲染函数,根据选中态等自定义内容。
```tsx
...
{({ isSelected, value, isDisabled }) => (
<>
🇺🇸
>
)}
{({ isSelected }) => (
<>
🇬🇧
>
)}
```
### 带选项说明
为选项添加说明以提供更多上下文。
```tsx
...
面向个人使用的必备功能
```
### 带触发器指示器
添加视觉指示器表示开/关状态;打开/关闭时会旋转。
```tsx
```
### 无样式触发器与自定义组合
使用 `unstyled` 变体,将触发器与 Button 等组件组合。
```tsx
```
### 受控模式
以编程方式控制打开状态与选中值。
```tsx
const [value, setValue] = useState();
const [isOpen, setIsOpen] = useState(false);
;
```
## 示例
```tsx
import { Select, Separator } from 'heroui-native';
import React, { useState } from 'react';
type SelectOption = {
value: string;
label: string;
};
const US_STATES: SelectOption[] = [
{ value: 'CA', label: '加利福尼亚' },
{ value: 'NY', label: '纽约' },
{ value: 'TX', label: '得克萨斯' },
{ value: 'FL', label: '佛罗里达' },
];
export default function SelectExample() {
const [value, setValue] = useState();
return (
选择州/省
{US_STATES.map((state, index) => (
{index < US_STATES.length - 1 && }
))}
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/select.tsx)。
## API 参考
### Select
| prop | type | default | description |
| --------------- | ------------------------------------------------- | ----------- | ----------------------------- |
| `children` | `ReactNode` | - | 选择器子内容 |
| `value` | `SelectOption \| SelectOption[]` | - | 当前选中值(受控) |
| `onValueChange` | `(value: SelectOption \| SelectOption[]) => void` | - | 选中值变化时的回调 |
| `defaultValue` | `SelectOption \| SelectOption[]` | - | 默认选中值(非受控) |
| `isOpen` | `boolean` | - | 是否打开(受控) |
| `isDefaultOpen` | `boolean` | - | 初始是否打开(非受控) |
| `onOpenChange` | `(isOpen: boolean) => void` | - | 打开状态变化时的回调 |
| `isDisabled` | `boolean` | `false` | 是否禁用 |
| `presentation` | `'popover' \| 'bottom-sheet' \| 'dialog'` | `'popover'` | 内容呈现方式 |
| `animation` | `SelectRootAnimation` | - | 动画配置 |
| `asChild` | `boolean` | `false` | 是否将子元素作为实际渲染节点 |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
#### SelectRootAnimation
Select 根级动画配置,可为:
* `false` 或 `"disabled"`:仅禁用根动画
* `"disable-all"`:禁用根与子级全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ---------------- | ------------------------------------------------ | ------- | ------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | 在自定义属性时用于禁用动画 |
| `entering.value` | `SpringAnimationConfig \| TimingAnimationConfig` | - | 打开时的动画配置 |
| `exiting.value` | `SpringAnimationConfig \| TimingAnimationConfig` | - | 关闭时的动画配置 |
#### SpringAnimationConfig
| prop | type | default | description |
| -------- | ------------------ | ------- | ------------------- |
| `type` | `'spring'` | - | 动画类型(须为 `'spring'`) |
| `config` | `WithSpringConfig` | - | Reanimated 弹簧动画配置 |
#### TimingAnimationConfig
| prop | type | default | description |
| -------- | ------------------ | ------- | ------------------- |
| `type` | `'timing'` | - | 动画类型(须为 `'timing'`) |
| `config` | `WithTimingConfig` | - | Reanimated 时长动画配置 |
### Select.Trigger
| prop | type | default | description |
| ------------------- | ------------------------- | ----------- | ---------------------------------------------- |
| `variant` | `'default' \| 'unstyled'` | `'default'` | 触发器变体:`'default'` 应用预设容器样式,`'unstyled'` 移除默认样式 |
| `children` | `ReactNode` | - | 触发器内容 |
| `className` | `string` | - | 触发器额外 class |
| `asChild` | `boolean` | `true` | 是否将子元素作为实际渲染节点 |
| `isDisabled` | `boolean` | - | 是否禁用触发器 |
| `...PressableProps` | `PressableProps` | - | 支持全部标准 React Native `Pressable` 属性 |
### Select.Value
| prop | type | default | description |
| -------------- | ----------- | ------- | ----------------------------- |
| `placeholder` | `string` | - | 未选中时的占位文案 |
| `className` | `string` | - | 值区域额外 class |
| `...TextProps` | `TextProps` | - | 支持全部标准 React Native `Text` 属性 |
**说明:** 值组件会根据是否有选中项自动应用不同文字颜色:
* 已选中:`text-foreground`
* 未选中(占位):`text-field-placeholder`
### Select.TriggerIndicator
| prop | type | default | description |
| ----------------------- | --------------------------------- | ------- | ----------------------------- |
| `children` | `ReactNode` | - | 自定义指示器内容;默认带动画的双角标 |
| `className` | `string` | - | 指示器额外 class |
| `style` | `ViewStyle` | - | 指示器自定义样式 |
| `iconProps` | `SelectTriggerIndicatorIconProps` | - | 双角标图标配置 |
| `animation` | `SelectTriggerIndicatorAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
**说明:** 以下样式属性由动画占用,不能通过 `className` 设置:
* `transform`(尤其是 `rotate`)— 用于开/关旋转过渡
若要自定义,请使用 `animation`。若需完全关闭动画样式并自行用 `className` 或 `style` 控制,请设置 `isAnimatedStyleActive={false}`。
#### SelectTriggerIndicatorIconProps
| prop | type | default | description |
| ------- | -------- | ------- | -------------- |
| `size` | `number` | `16` | 图标尺寸 |
| `color` | `string` | - | 图标颜色(默认同前景主题色) |
#### SelectTriggerIndicatorAnimation
`Select.TriggerIndicator` 的动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认动画(0° 到 -180° 旋转)
* `object`:自定义动画配置
| prop | type | default | description |
| ----------------------- | ----------------------- | -------------------------------------------- | ------------------ |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时用于禁用动画 |
| `rotation.value` | `[number, number]` | `[0, -180]` | 旋转角度 \[关闭, 打开],单位度 |
| `rotation.springConfig` | `WithSpringConfig` | `{ damping: 140, stiffness: 1000, mass: 4 }` | 旋转弹簧动画配置 |
### Select.Portal
| prop | type | default | description |
| -------------------------------------------- | ----------- | ------- | -------------------------------------------------------------------------------------------------- |
| `children` | `ReactNode` | - | Portal内容(必填) |
| `disableFullWindowOverlay` | `boolean` | `false` | 在 iOS 为 `true` 时使用 `View` 代替 `FullWindowOverlay`,便于元素检查器;遮罩将无法叠在原生模态之上 |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | 控制 VoiceOver 是否将遮罩窗口视为模态容器。为 `true` 时,VoiceOver 仅聚焦遮罩内元素。仅 iOS;API 不稳定,可能随 react-native-screens 变更 |
| `className` | `string` | - | Portal容器额外 class |
| `hostName` | `string` | - | Portal宿主元素的可选名称 |
| `forceMount` | `boolean` | - | 是否强制挂载到 DOM |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
### Select.Overlay
| prop | type | default | description |
| ----------------------- | ------------------------ | ------- | ----------------------------------- |
| `className` | `string` | - | 遮罩额外 class |
| `animation` | `SelectOverlayAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `closeOnPress` | `boolean` | `true` | 点击遮罩是否关闭选择器 |
| `forceMount` | `boolean` | - | 是否强制挂载到 DOM |
| `asChild` | `boolean` | `false` | 是否将子元素作为实际渲染节点 |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | 支持 Reanimated `Animated.View` 的全部属性 |
#### SelectOverlayAnimation
`Select.Overlay` 的动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认动画(底部抽屉/对话框为基于进度的透明度;气泡为关键帧动画)
* `object`:自定义动画配置
| prop | type | default | description |
| --------------- | -------------------------- | ----------- | ------------------------------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时用于禁用动画 |
| `opacity.value` | `[number, number, number]` | `[0, 1, 0]` | 透明度 \[空闲, 打开, 关闭](用于底部抽屉/对话框呈现) |
| `entering` | `EntryOrExitLayoutType` | - | 进入过渡自定义关键帧(用于气泡呈现) |
| `exiting` | `EntryOrExitLayoutType` | - | 退出过渡自定义关键帧(用于气泡呈现) |
### Select.Content(气泡呈现)
| prop | type | default | description |
| ----------------------- | ------------------------------------------------ | --------------- | ----------------------------------- |
| `children` | `ReactNode` | - | 选择器内容 |
| `width` | `number \| 'trigger' \| 'content-fit' \| 'full'` | `'content-fit'` | 内容宽度策略 |
| `presentation` | `'popover'` | `'popover'` | 呈现模式 |
| `placement` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | 相对触发器的方位 |
| `align` | `'start' \| 'center' \| 'end'` | `'center'` | 沿放置轴的对齐方式 |
| `avoidCollisions` | `boolean` | `true` | 靠近视口边缘时是否翻转 placement |
| `offset` | `number` | `8` | 与触发器的间距(像素) |
| `alignOffset` | `number` | `0` | 沿对齐轴的偏移(像素) |
| `className` | `string` | - | 内容容器额外 class |
| `animation` | `SelectContentPopoverAnimation` | - | 动画配置 |
| `forceMount` | `boolean` | - | 是否强制挂载到 DOM |
| `insets` | `Insets` | - | 定位时需遵守的屏幕边距 |
| `asChild` | `boolean` | `false` | 是否将子元素作为实际渲染节点 |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | 支持 Reanimated `Animated.View` 的全部属性 |
#### SelectContentPopoverAnimation
`Select.Content`(气泡呈现)的动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认关键帧(按 placement 的 translateY/translateX、scale、opacity)
* `object`:自定义 `entering` 和/或 `exiting` 关键帧
| prop | type | default | description |
| ---------- | ----------------------- | ------- | ------------------------------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时用于禁用动画 |
| `entering` | `EntryOrExitLayoutType` | - | 进入过渡关键帧(默认:按 placement 的 translateY/translateX、scale、opacity,200ms) |
| `exiting` | `EntryOrExitLayoutType` | - | 退出过渡关键帧(默认:与进入镜像,150ms) |
### Select.Content(底部抽屉呈现)
| prop | type | default | description |
| --------------------------- | ------------------ | ------- | ------------------------------- |
| `children` | `ReactNode` | - | 底部抽屉内容 |
| `presentation` | `'bottom-sheet'` | - | 呈现模式 |
| `contentContainerClassName` | `string` | - | 内容容器额外 class |
| `...BottomSheetProps` | `BottomSheetProps` | - | 支持 `@gorhom/bottom-sheet` 的全部属性 |
### Select.Content(对话框呈现)
| prop | type | default | description |
| -------------- | -------------------------------------------------------- | ------- | ----------------------------- |
| `children` | `ReactNode` | - | 对话框内容 |
| `presentation` | `'dialog'` | - | 呈现模式 |
| `classNames` | `{ wrapper?: string; content?: string }` | - | 包裹层与内容区额外 class |
| `styles` | `Partial>` | - | 对话框各部分的样式 |
| `animation` | `SelectContentAnimation` | - | 动画配置 |
| `isSwipeable` | `boolean` | `true` | 是否允许滑动关闭 |
| `forceMount` | `boolean` | - | 是否强制挂载到 DOM |
| `asChild` | `boolean` | `false` | 是否将子元素作为实际渲染节点 |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
#### `styles`
| prop | type | description |
| --------- | ----------- | ----------- |
| `wrapper` | `ViewStyle` | 外层包裹容器样式 |
| `content` | `ViewStyle` | 对话框内容区样式 |
#### SelectContentAnimation
`Select.Content`(对话框呈现)的动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认关键帧(scale 与 opacity)
* `object`:自定义 `entering` 和/或 `exiting` 关键帧
| prop | type | default | description |
| ---------- | ----------------------- | ------- | --------------------------------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时用于禁用动画 |
| `entering` | `EntryOrExitLayoutType` | - | 进入过渡关键帧(默认:scale 与 opacity,200ms) |
| `exiting` | `EntryOrExitLayoutType` | - | 退出过渡关键帧(默认:与进入镜像,150ms) |
### Select.Close
`Select.Close` 继承 [CloseButton](./close-button),按下时自动关闭选择器。
### Select.ListLabel
| prop | type | default | description |
| -------------- | ----------- | ------- | ----------------------------- |
| `children` | `ReactNode` | - | 列表标题文案 |
| `className` | `string` | - | 列表标题额外 class |
| `...TextProps` | `TextProps` | - | 支持全部标准 React Native `Text` 属性 |
### Select.Item
| prop | type | default | description |
| ------------------- | ------------------------------------------------------------ | ------- | ---------------------------------- |
| `children` | `ReactNode \| ((props: SelectItemRenderProps) => ReactNode)` | - | 自定义选项内容;默认可为标签+指示器,或渲染函数 |
| `value` | `any` | - | 选项关联的值(必填) |
| `label` | `string` | - | 选项标签文案(必填) |
| `isDisabled` | `boolean` | `false` | 是否禁用该选项 |
| `className` | `string` | - | 选项额外 class |
| `...PressableProps` | `PressableProps` | - | 支持全部标准 React Native `Pressable` 属性 |
#### SelectItemRenderProps
使用渲染函数作为 `children` 时,会传入以下属性:
| property | type | description |
| ------------ | --------- | ----------- |
| `isSelected` | `boolean` | 当前项是否选中 |
| `value` | `string` | 当前项的值 |
| `isDisabled` | `boolean` | 当前项是否禁用 |
### Select.ItemLabel
| prop | type | default | description |
| -------------- | ----------- | ------- | ----------------------------- |
| `className` | `string` | - | 选项标签额外 class |
| `...TextProps` | `TextProps` | - | 支持全部标准 React Native `Text` 属性 |
### Select.ItemDescription
| prop | type | default | description |
| -------------- | ----------- | ------- | ----------------------------- |
| `children` | `ReactNode` | - | 说明文案 |
| `className` | `string` | - | 说明额外 class |
| `...TextProps` | `TextProps` | - | 支持全部标准 React Native `Text` 属性 |
### Select.ItemIndicator
| prop | type | default | description |
| -------------- | ------------------------------ | ------- | ----------------------------- |
| `children` | `ReactNode` | - | 自定义指示器;默认对勾图标 |
| `className` | `string` | - | 指示器额外 class |
| `iconProps` | `SelectItemIndicatorIconProps` | - | 对勾图标配置 |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
#### SelectItemIndicatorIconProps
| prop | type | default | description |
| ------- | -------- | ---------------- | ----------- |
| `size` | `number` | `16` | 图标尺寸 |
| `color` | `string` | `--colors-muted` | 图标颜色 |
## Hooks
### useSelect
用于读取 Select 根上下文,返回状态与控制方法。
```tsx
import { useSelect } from 'heroui-native';
const {
isOpen,
onOpenChange,
isDefaultOpen,
isDisabled,
presentation,
triggerPosition,
setTriggerPosition,
contentLayout,
setContentLayout,
nativeID,
value,
onValueChange,
} = useSelect();
```
#### 返回值
| property | type | description |
| -------------------- | -------------------------------------------------- | ----------- |
| `isOpen` | `boolean` | 当前是否打开 |
| `onOpenChange` | `(open: boolean) => void` | 修改打开状态的回调 |
| `isDefaultOpen` | `boolean \| undefined` | 默认是否打开(非受控) |
| `isDisabled` | `boolean \| undefined` | 是否禁用 |
| `presentation` | `'popover' \| 'bottom-sheet' \| 'dialog'` | 内容呈现方式 |
| `triggerPosition` | `LayoutPosition \| null` | 触发器相对视口的位置 |
| `setTriggerPosition` | `(position: LayoutPosition \| null) => void` | 更新触发器位置 |
| `contentLayout` | `LayoutRectangle \| null` | 选择器内容的布局测量 |
| `setContentLayout` | `(layout: LayoutRectangle \| null) => void` | 更新内容布局测量 |
| `nativeID` | `string` | 当前实例的唯一标识 |
| `value` | `SelectOption \| SelectOption[]` | 当前选中项 |
| `onValueChange` | `(option: SelectOption \| SelectOption[]) => void` | 选中值变化时的回调 |
**说明:** 必须在 `Select` 内使用;在上下文外调用将抛错。
### useSelectAnimation
用于在自定义或复合子组件中读取 Select 动画相关共享值。
```tsx
import { useSelectAnimation } from 'heroui-native';
const { selectState, progress, isDragging, isGestureReleaseAnimationRunning } =
useSelectAnimation();
```
#### 返回值
| property | type | description |
| ---------------------------------- | ---------------------- | -------------------- |
| `progress` | `SharedValue` | 动画进度(0=空闲,1=打开,2=关闭) |
| `isDragging` | `SharedValue` | 内容是否正在被拖拽 |
| `isGestureReleaseAnimationRunning` | `SharedValue` | 手势释放后的动画是否正在运行 |
**说明:** 必须在 `Select` 内使用;在动画上下文外调用将抛错。
#### SelectOption
| property | type | description |
| -------- | -------- | ----------- |
| `value` | `string` | 选项值 |
| `label` | `string` | 选项显示标签 |
### useSelectItem
用于读取 Select Item 上下文,返回当前项的值与标签。
```tsx
import { useSelectItem } from 'heroui-native';
const { itemValue, label } = useSelectItem();
```
#### 返回值
| property | type | description |
| ----------- | -------- | ----------- |
| `itemValue` | `string` | 当前项的值 |
| `label` | `string` | 当前项的标签文案 |
## 特别说明
### 元素检查器(iOS)
Select 在 iOS 上使用 `FullWindowOverlay`。开发时若需启用 React Native 元素检查器,请在 `Select.Portal` 上设置 `disableFullWindowOverlay={true}`。代价是下拉层将无法叠在原生模态之上。
### 原生模态(iOS)
当 `Select` 位于以原生模态形式呈现的页面内时(`presentation: 'modal' | 'formSheet' | 'pageSheet'`),下拉层可能会向上偏移渲染。在新架构(Fabric)中,`react-native-screens` 将 `RNSModalScreen` 标记为 Fabric 根节点,因此触发器的坐标是相对于模态原点上报的,而 `FullWindowOverlay`(下拉层挂载点)锚定在 iOS 应用窗口上。可通过将 `safeAreaInsets.top` 加到 `offset` 来补偿:
```tsx
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const insets = useSafeAreaInsets();
...
;
```
# TextArea 多行文本框
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/text-area
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(forms)/text-area.mdx
> 多行文本输入,带样式边框与背景,用于收集较长内容。
## 导入
```tsx
import { TextArea } from 'heroui-native';
```
## 用法
### 基础用法
`TextArea` 可单独使用,也可放在 `TextField` 内。
```tsx
import { TextArea } from 'heroui-native';
```
### 与 TextField 组合
与 `TextField` 搭配形成完整表单结构。
```tsx
import { Description, Label, TextArea, TextField } from 'heroui-native';
留言
请尽量提供详细信息。
```
### 校验状态
非法时展示错误样式。
```tsx
import { FieldError, Label, TextArea, TextField } from 'heroui-native';
留言
请输入有效留言
```
### 禁用状态
禁用后不可编辑。
```tsx
import { Label, TextArea, TextField } from 'heroui-native';
禁用字段
```
### 变体
按场景使用不同视觉变体。
```tsx
import { Label, TextArea, TextField } from 'heroui-native';
主要变体
次要变体
```
### 自定义样式
通过 `className` 自定义外观。
```tsx
import { Label, TextArea, TextField } from 'heroui-native';
自定义样式
```
## 示例
```tsx
import { Description, FieldError, Label, TextArea, TextField } from 'heroui-native';
import { View } from 'react-native';
export default function TextAreaExample() {
return (
主要变体
默认变体,主要样式
次要变体
用于表面上的次要变体
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/text-area.tsx)。
## API 参考
`TextArea` 继承 [Input](./input) 的全部属性。区别仅为默认值:`multiline` 默认为 `true`,`textAlignVertical` 默认为 `'top'`。
# TextField 文本输入框
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/text-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(forms)/text-field.mdx
> 带标签、说明与错误处理的文本输入,用于收集用户输入。
## 导入
```tsx
import { TextField } from 'heroui-native';
```
## 结构
```tsx
...
...
...
```
* **TextField**:根容器,负责间距与状态管理
* **Label**:标签,必填时可显示星号(见 [Label](./label))
* **Input**:带动画边框与背景的输入(见 [Input](./input))
* **Description**:辅助说明文字(见 [Description](./description))
* **FieldError**:校验错误展示(见 [FieldError](./field-error))
## 用法
### 基础用法
提供带标签与说明的完整输入结构。
```tsx
邮箱
我们不会公开您的邮箱
```
### 必填
在必填字段的标签上显示星号。
```tsx
用户名
```
### 校验
非法时展示错误信息。
```tsx
import { FieldError, Input, Label, TextField } from 'heroui-native';
邮箱
请输入有效邮箱
;
```
### 局部覆盖非法状态
为单个部件覆盖上下文的非法状态。
```tsx
import {
Description,
FieldError,
Input,
Label,
TextField,
} from 'heroui-native';
邮箱
尽管输入非法,此说明仍可显示
邮箱格式不正确
;
```
### 多行输入
用于较长内容。
```tsx
留言
最多 500 字
```
### 禁用状态
禁用整个字段。
```tsx
禁用字段
```
### 变体
按场景切换输入样式。
```tsx
主要变体
次要变体
```
### 自定义样式
通过 `className` 自定义输入外观。
```tsx
自定义样式
```
## 示例
```tsx
import { Ionicons } from '@expo/vector-icons';
import { Description, Input, Label, TextField } from 'heroui-native';
import { useState } from 'react';
import { Pressable, View } from 'react-native';
import { withUniwind } from 'uniwind';
const StyledIonicons = withUniwind(Ionicons);
export const TextInputContent = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
return (
邮箱
我们不会向他人公开您的邮箱。
新密码
setIsPasswordVisible(!isPasswordVisible)}
>
密码至少 6 位
);
};
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/text-field.tsx)。
## API 参考
### TextField
| prop | type | default | description |
| ------------ | ---------------------------- | ----------- | ---------------------------------- |
| children | `React.ReactNode` | - | 文本字段内的子节点 |
| isDisabled | `boolean` | `false` | 是否禁用整个字段 |
| isInvalid | `boolean` | `false` | 是否处于非法状态 |
| isRequired | `boolean` | `false` | 是否必填(显示星号) |
| className | `string` | - | 根元素自定义 class |
| animation | `"disable-all" \| undefined` | `undefined` | 动画配置;`"disable-all"` 可禁用自身及子级的全部动画 |
| ...ViewProps | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
> **说明**:`Label`、`Input`、`Description`、`FieldError` 的详细 API 见各自文档:
>
> * [Label](./label)
> * [Input](./input)
> * [Description](./description)
> * [FieldError](./field-error)
>
> 这些组件会通过 form-item-state 上下文自动消费 `TextField` 的表单状态。
## Hooks
### useTextField
访问 `TextField` 上下文,必须在 `TextField` 内使用。
```tsx
import { TextField, useTextField } from 'heroui-native';
function CustomComponent() {
const { isDisabled, isInvalid, isRequired } = useTextField();
// 使用上下文值…
}
```
#### 返回值
| property | type | description |
| ---------- | --------- | ----------- |
| isDisabled | `boolean` | 是否禁用整个字段 |
| isInvalid | `boolean` | 是否处于非法状态 |
| isRequired | `boolean` | 是否必填 |
# Card 卡片
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/card
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(layout)/card.mdx
> 卡片容器,提供灵活分区以结构化展示内容。
## 导入
```tsx
import { Card } from 'heroui-native';
```
## 结构
```tsx
...
...
...
...
```
* **Card**:主容器,继承 `Surface`;提供可配置的表面变体与整体布局。
* **Card.Header**:顶部区域,可放图标、徽章等。
* **Card.Body**:主内容区,`flex-1` 填充 `Header` 与 `Footer` 之间的空间。
* **Card.Title**:标题,前景色与中等字重。
* **Card.Description**:描述,弱化色与较小字号。
* **Card.Footer**:底部区域,可放按钮等操作。
## 用法
### 基础用法
使用内置分区组织内容。
```tsx
...
```
### 标题与描述
组合标题与描述以结构化展示文字。
```tsx
...
...
```
### 页头与页脚
增加顶部与底部区域放置图标、徽章或操作。
```tsx
...
...
...
```
### 变体
通过变体控制卡片背景外观。
```tsx
...
...
...
...
```
### 横向布局
使用 `flex-row` 等样式创建横向卡片。
```tsx
```
### 背景图
使用绝对定位图片作为背景。
```tsx
...
```
## 示例
```tsx
import { Button, Card } from 'heroui-native';
import { Ionicons } from '@expo/vector-icons';
import { View } from 'react-native';
export default function CardExample() {
return (
¥450
客厅沙发 • 2025 系列
这款沙发适合现代热带风、巴洛克灵感等空间。
立即购买
加入购物车
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/card.tsx)。
## API 参考
### Card
| prop | type | default | description |
| -------------- | --------------------------------------------------------- | ----------- | ---------------------------------- |
| `children` | `React.ReactNode` | - | 卡片内内容 |
| `variant` | `'default' \| 'secondary' \| 'tertiary' \| 'transparent'` | `'default'` | 卡片表面视觉变体 |
| `className` | `string` | - | 额外的 class |
| `animation` | `"disable-all" \| undefined` | `undefined` | 动画配置;`"disable-all"` 可禁用自身及子级的全部动画 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
### Card.Header
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 页头内子节点 |
| `className` | `string` | - | 额外的 class |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
### Card.Body
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 主内容区子节点 |
| `className` | `string` | - | 额外的 class |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
### Card.Footer
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 页脚内子节点 |
| `className` | `string` | - | 额外的 class |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
### Card.Title
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 标题文字 |
| `className` | `string` | - | 额外的 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部属性 |
### Card.Description
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 描述文字 |
| `className` | `string` | - | 额外的 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部属性 |
# Separator 分隔符
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/separator
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(layout)/separator.mdx
> 用于在视觉上分隔内容的简单线条。
## 导入
```tsx
import { Separator } from "heroui-native";
```
## 结构
```tsx
```
* **Separator**:简单的分隔线组件,可水平或垂直排列,并支持自定义粗细与变体样式。
## 用法
### 基础用法
在内容区块之间创建视觉分隔。
```tsx
```
### 方向
使用 `orientation` 控制分隔线方向。
```tsx
水平分隔线
下方内容
左侧
右侧
```
### 变体
在细线与粗线之间选择,以强调程度区分。
```tsx
```
### 自定义粗细
使用数值精确控制线条粗细(像素)。
```tsx
```
## 示例
```tsx
import { Separator, Surface } from 'heroui-native';
import { Text, View } from 'react-native';
export default function SeparatorExample() {
return (
HeroUI Native
现代化的 React Native 组件库。
组件
主题
示例
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/separator.tsx)。
## API 参考
### Separator
| prop | type | default | description |
| -------------- | ---------------------------- | -------------- | ---------------------------- |
| `variant` | `'thin' \| 'thick'` | `'thin'` | 分隔线样式变体 |
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | 分隔线方向 |
| `thickness` | `number` | `undefined` | 自定义粗细(像素);水平时控制高度,垂直时控制宽度 |
| `className` | `string` | `undefined` | 额外的 class |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
# Surface 表面
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/surface
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(layout)/surface.mdx
> 提供层级与背景样式的容器组件。
## 导入
```tsx
import { Surface } from 'heroui-native';
```
## 结构
Surface 是提供层级与背景样式的容器,可包裹子内容,并通过变体与样式属性定制外观。
```tsx
...
```
* **Surface**:主容器,通过变体提供一致的内边距、背景与层级感。
## 用法
### 基础用法
Surface 用于创建具有一致内边距与样式的容器。
```tsx
...
```
### 变体
通过不同层级控制视觉外观。
```tsx
...
...
...
```
### 嵌套 Surface
使用不同变体嵌套,形成视觉层级。
```tsx
...
...
...
```
### 自定义样式
通过 `className` 或 `style` 传入自定义样式。
```tsx
...
...
```
### 禁用全部动画
将 `animation` 设为 `"disable-all"` 可禁用自身及子级的全部动画。
```tsx
{
/* 禁用自身及子级的全部动画 */
}
无动画 ;
```
## 示例
```tsx
import { Surface } from 'heroui-native';
import { Text, View } from 'react-native';
export default function SurfaceExample() {
return (
表面内容
默认表面变体,使用 bg-surface 样式。
表面内容
次要表面变体,使用 bg-surface-secondary 样式。
表面内容
第三级表面变体,使用 bg-surface-tertiary 样式。
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/surface.tsx)。
## API 参考
### Surface
| prop | type | default | description |
| -------------- | --------------------------------------------------------- | ----------- | ---------------------------------- |
| `variant` | `'default' \| 'secondary' \| 'tertiary' \| 'transparent'` | `'default'` | 视觉变体,控制背景色与边框 |
| `children` | `React.ReactNode` | - | 渲染在表面内的内容 |
| `className` | `string` | - | 额外的 class |
| `animation` | `"disable-all" \| undefined` | `undefined` | 动画配置;`"disable-all"` 可禁用自身及子级的全部动画 |
| `asChild` | `boolean` | `false` | 是否以子元素方式渲染 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
# Avatar 头像
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/avatar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(media)/avatar.mdx
> 展示用户头像,支持图片、文字首字母或回退图标。
## 导入
```tsx
import { Avatar } from 'heroui-native';
```
## 结构
```tsx
```
* **Avatar**:主容器,管理头像展示状态,向子组件提供尺寸与颜色上下文;可通过动画配置统一控制子级动画。
* **Avatar.Image**:可选图片组件,展示头像;自动处理加载与错误,并以不透明度淡入。
* **Avatar.Fallback**:图片加载失败或不可用时显示;无子节点时显示默认人像图标;支持可配置的进入动画与延迟。
## 用法
### 基础用法
未提供图片或文字时,显示默认人像图标。
```tsx
```
### 使用图片
展示头像图片并自动处理回退。
```tsx
JD
```
### 文字首字母
使用首字母作为头像内容。
```tsx
AB
```
### 自定义图标
以自定义图标作为回退内容。
```tsx
```
### 尺寸
使用 `size` 控制头像大小。
```tsx
```
### 变体
使用 `variant` 切换视觉风格。
```tsx
DF
SF
```
### 颜色
应用不同颜色变体。
```tsx
DF
AC
SC
WR
DG
```
### 延迟显示回退
延迟显示回退,避免图片加载时的闪烁。
```tsx
NA
```
### 自定义图片组件
配合 `asChild` 使用自定义图片组件。
```tsx
import { Image } from 'expo-image';
EI
;
```
### 动画控制
在 Avatar 不同层级控制动画。
#### 禁用全部动画
在根组件禁用自身及子级的全部动画:
```tsx
JD
```
#### 自定义图片动画
自定义图片不透明度动画:
```tsx
JD
```
#### 自定义回退动画
自定义回退进入动画:
```tsx
import { FadeInDown } from 'react-native-reanimated';
JD
;
```
#### 单独禁用动画
对指定子组件禁用动画:
```tsx
JD
```
## 示例
```tsx
import { Avatar } from 'heroui-native';
import { View } from 'react-native';
export default function AvatarExample() {
const users = [
{ id: 1, image: 'https://example.com/user1.jpg', name: '张 三' },
{ id: 2, image: 'https://example.com/user2.jpg', name: '李 四' },
{ id: 3, image: 'https://example.com/user3.jpg', name: '王 五' },
];
return (
{users.map((user) => (
{user.name
.split(' ')
.map((n) => n[0])
.join('')}
))}
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/avatar.tsx)。
## API 参考
### Avatar
| prop | type | default | description |
| -------------- | ------------------------------------------------------------- | ----------- | ---------------------------------- |
| `children` | `React.ReactNode` | - | 头像内容(`Image` 与/或 `Fallback`) |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 尺寸 |
| `variant` | `'default' \| 'soft'` | `'default'` | 视觉变体 |
| `color` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | `'accent'` | 颜色变体 |
| `className` | `string` | - | 额外的 class |
| `animation` | `"disable-all"` \| `undefined` | `undefined` | 动画配置;`"disable-all"` 可禁用自身及子级的全部动画 |
| `alt` | `string` | - | 无障碍替代文本描述 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
### Avatar.Image
根据 `asChild` 扩展不同的基础类型:
* `asChild={false}`(默认):扩展自 React Native Reanimated 的 `AnimatedProps`
* `asChild={true}`:扩展自定义图片组件的原语图片属性
**说明:** `asChild={true}` 时,取决于自定义组件实现,`className` 可能不会生效;请确保自定义组件正确处理样式 props。
| prop | type | default | description |
| ----------------------- | ---------------------------------------------- | ------- | -------------------------- |
| `source` | `ImageSourcePropType` | - | 图片源(`asChild={false}` 时必填) |
| `asChild` | `boolean` | `false` | 是否使用自定义图片子组件 |
| `className` | `string` | - | 额外的 class |
| `animation` | `AvatarImageAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `...AnimatedProps` | `AnimatedProps` or primitive props | - | 随 `asChild` 变化的额外属性 |
#### AvatarImageAnimation
图片动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ---------------------- | ----------------------- | --------------------------------------------------- | --------------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时禁用动画 |
| `opacity.value` | `[number, number]` | `[0, 1]` | 不透明度 \[初始, 已加载] |
| `opacity.timingConfig` | `WithTimingConfig` | `{ duration: 200, easing: Easing.in(Easing.ease) }` | 时间曲线配置 |
**说明:** `asChild={true}` 时动画会自动禁用。
### Avatar.Fallback
| prop | type | default | description |
| ----------------------- | ------------------------------------------------------------- | --------------------- | ----------------------------------- |
| `children` | `React.ReactNode` | - | 回退内容(文字、图标或自定义节点) |
| `delayMs` | `number` | `0` | 显示回退前的延迟(毫秒),作用于进入动画 |
| `color` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | inherited from parent | 回退颜色变体 |
| `className` | `string` | - | 容器额外 class |
| `classNames` | `ElementSlots` | - | 各部分额外 class |
| `styles` | `{ container?: ViewStyle; text?: TextStyle }` | - | 回退各部分的样式 |
| `textProps` | `TextProps` | - | 子节点为字符串时传给 `Text` 的属性 |
| `iconProps` | `PersonIconProps` | - | 自定义默认人像图标的属性 |
| `animation` | `AvatarFallbackAnimation` | - | 动画配置 |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | 支持 Reanimated `Animated.View` 的全部属性 |
**classNames:** `ElementSlots` 提供类型安全的 class。可用插槽:`container`、`text`。
#### `styles`
| prop | type | description |
| ----------- | ----------- | ----------- |
| `container` | `ViewStyle` | 容器样式 |
| `text` | `TextStyle` | 文字样式 |
#### AvatarFallbackAnimation
回退动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ---------------- | ----------------------- | -------------------------------------------------------------------------------------- | ----------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时禁用动画 |
| `entering.value` | `EntryOrExitLayoutType` | `FadeIn` `.duration(200)` `.easing(Easing.in(Easing.ease))` `.delay(0)` | 自定义进入动画 |
#### PersonIconProps
| prop | type | description |
| ------- | -------- | ----------- |
| `size` | `number` | 图标尺寸(可选) |
| `color` | `string` | 图标颜色(可选) |
## Hooks
### useAvatar
访问 Avatar 根上下文,获取头像状态。
**说明:** `status` 常用于在图片加载时显示骨架屏。
```tsx
import { Avatar, useAvatar, Skeleton } from 'heroui-native';
function AvatarWithSkeleton() {
return (
JD
);
}
function AvatarContent() {
const { status } = useAvatar();
if (status === 'loading') {
return ;
}
return null;
}
```
| property | type | description |
| ----------- | ---------------------------------------------------- | ------------ |
| `status` | `'loading' \| 'loaded' \| 'error'` | 当前图片加载状态 |
| `setStatus` | `(status: 'loading' \| 'loaded' \| 'error') => void` | 手动设置状态(高级用法) |
**状态含义:**
* `'loading'`:图片加载中,可显示骨架屏
* `'loaded'`:图片加载成功
* `'error'`:加载失败或资源无效,会自动显示回退
# Accordion 手风琴
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/accordion
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(navigation)/accordion.mdx
> 可折叠内容面板,在紧凑空间内组织信息
## 导入
```tsx
import { Accordion } from 'heroui-native';
```
## 结构
```tsx
...
...
...
```
* **Accordion**:主容器,管理手风琴状态与行为;控制各条目的展开/收起,支持单选或多选展开模式,并提供 `default` 或 `surface` 等视觉变体。
* **Accordion.Item**:单个条目的容器,包裹触发器与内容,并管理该条目的展开状态。
* **Accordion.Trigger**:用于切换条目展开的可交互区域,基于 Header 与 Trigger 原语构建。
* **Accordion.Indicator**:可选的视觉指示器,展示展开状态;默认使用随状态旋转的动画 chevron 图标。
* **Accordion.Content**:可展开内容的容器,配合布局过渡动画实现平滑展开/收起。
## 用法
### 基础用法
Accordion 通过复合子组件创建可展开的内容区块。
```tsx
...
...
```
### 单选模式
同一时间只允许展开一个条目。
```tsx
...
...
...
...
```
### 多选模式
允许多个条目同时展开。
```tsx
...
...
...
...
...
...
```
### Surface 变体
为手风琴应用表面容器样式。
```tsx
...
...
```
### 自定义指示器
用自定义内容替换默认 chevron 指示器。
```tsx
...
...
```
### 无分隔线
隐藏条目之间的分隔线。
```tsx
...
...
...
...
```
### 自定义样式
通过 `className`、`classNames` 或 `styles` 传入自定义样式。
```tsx
...
...
```
### 配合 PressableFeedback
对 `Accordion.Trigger` 使用 `asChild`,并用 `PressableFeedback` 包裹内容以添加按压反馈动画。
```tsx
import { Accordion, PressableFeedback } from 'heroui-native';
import { View } from 'react-native';
条目标题
...
;
```
## 示例
```tsx
import { Accordion, useThemeColor } from 'heroui-native';
import { Ionicons } from '@expo/vector-icons';
import { View, Text } from 'react-native';
export default function AccordionExample() {
const themeColorMuted = useThemeColor('muted');
const accordionData = [
{
id: '1',
title: '如何下单?',
icon: ,
content:
'这是一段示例说明文字,用于演示折叠面板中的正文内容展示效果。',
},
{
id: '2',
title: '支持哪些支付方式?',
icon: ,
content:
'这是一段示例说明文字,用于演示折叠面板中的正文内容展示效果。',
},
{
id: '3',
title: '运费如何计算?',
icon: ,
content:
'这是一段示例说明文字,用于演示折叠面板中的正文内容展示效果。',
},
];
return (
{accordionData.map((item) => (
{item.icon}
{item.title}
{item.content}
))}
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/accordion.tsx)。
## API 参考
### Accordion
| prop | type | default | description |
| ----------------------- | -------------------------------------------------- | ----------- | ----------------------------------- |
| `children` | `React.ReactNode` | - | 渲染在手风琴内的子节点 |
| `selectionMode` | `'single' \| 'multiple'` | - | 允许单条或多条同时展开 |
| `variant` | `'default' \| 'surface'` | `'default'` | 手风琴视觉变体 |
| `hideSeparator` | `boolean` | `false` | 是否隐藏条目之间的分隔线 |
| `defaultValue` | `string \| string[] \| undefined` | - | 非受控模式下的默认展开项 |
| `value` | `string \| string[] \| undefined` | - | 受控模式下的当前展开项 |
| `isDisabled` | `boolean` | - | 是否禁用全部条目 |
| `isCollapsible` | `boolean` | `true` | 已展开条目是否可再次收起 |
| `animation` | `AccordionRootAnimation` | - | 根级动画配置 |
| `className` | `string` | - | 容器的额外 class |
| `classNames` | `ElementSlots` | - | 各插槽的额外 class |
| `styles` | `Partial>` | - | 根组件各部分的样式 |
| `onValueChange` | `(value: string \| string[] \| undefined) => void` | - | 展开项变化时的回调 |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | 支持 Reanimated `Animated.View` 的全部属性 |
#### `ElementSlots`
| prop | type | description |
| ----------- | -------- | ----------------- |
| `container` | `string` | 手风琴容器的自定义 class |
| `separator` | `string` | 条目之间分隔线的自定义 class |
#### `styles`
| prop | type | description |
| ----------- | ----------- | ----------- |
| `container` | `ViewStyle` | 手风琴容器样式 |
| `separator` | `ViewStyle` | 条目之间分隔线样式 |
#### AccordionRootAnimation
手风琴根组件的动画配置,可为:
* `false` 或 `"disabled"`:仅禁用根级动画
* `"disable-all"`:禁用全部动画(含子级)
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| -------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | 在自定义属性时禁用动画 |
| `layout.value` | `LayoutTransition` | `LinearTransition` `.springify()` `.damping(140)` `.stiffness(1600)` `.mass(4)` | 手风琴过渡的自定义布局动画 |
### Accordion.Item
| prop | type | default | description |
| ----------------------- | --------------------------------------------------------------------------- | ------- | ----------------------------------- |
| `children` | `React.ReactNode \| ((props: AccordionItemRenderProps) => React.ReactNode)` | - | 条目内的子节点,或渲染函数 |
| `value` | `string` | - | 唯一标识该条目的值 |
| `isDisabled` | `boolean` | - | 是否禁用该条目 |
| `className` | `string` | - | 额外的 class |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | 支持 Reanimated `Animated.View` 的全部属性 |
#### AccordionItemRenderProps
| prop | type | description |
| ------------ | --------- | ----------- |
| `isExpanded` | `boolean` | 当前条目是否展开 |
| `value` | `string` | 该条目的唯一值 |
### Accordion.Trigger
| prop | type | default | description |
| ------------------- | ----------------- | ------- | ------------------------------- |
| `children` | `React.ReactNode` | - | 触发器内的子节点 |
| `className` | `string` | - | 额外的 class |
| `isDisabled` | `boolean` | - | 是否禁用触发器 |
| `...PressableProps` | `PressableProps` | - | 支持 React Native `Pressable` 的属性 |
### Accordion.Indicator
| prop | type | default | description |
| ----------------------- | ----------------------------- | ------- | ----------------------------------- |
| `children` | `React.ReactNode` | - | 自定义指示器内容;未提供时默认为带动画的 chevron |
| `className` | `string` | - | 额外的 class |
| `iconProps` | `AccordionIndicatorIconProps` | - | 图标配置 |
| `animation` | `AccordionIndicatorAnimation` | - | 指示器动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | 支持 Reanimated `Animated.View` 的全部属性 |
#### AccordionIndicatorIconProps
| prop | type | default | description |
| ------- | -------- | ------------ | ----------- |
| `size` | `number` | `16` | 图标尺寸 |
| `color` | `string` | `foreground` | 图标颜色 |
#### AccordionIndicatorAnimation
指示器动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ----------------------- | ----------------------- | -------------------------------------------- | ------------------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时禁用动画 |
| `rotation.value` | `[number, number]` | `[0, -180]` | 旋转角度 \[收起, 展开],单位为度 |
| `rotation.springConfig` | `WithSpringConfig` | `{ damping: 140, stiffness: 1000, mass: 4 }` | 旋转弹簧动画配置 |
### Accordion.Content
| prop | type | default | description |
| -------------- | --------------------------- | ------- | -------------------------- |
| `children` | `React.ReactNode` | - | 内容区域内的子节点 |
| `className` | `string` | - | 额外的 class |
| `animation` | `AccordionContentAnimation` | - | 内容动画配置 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的属性 |
#### AccordionContentAnimation
内容区动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ---------------- | ----------------------- | ---------------------------------------------------------------------- | ----------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时禁用动画 |
| `entering.value` | `EntryOrExitLayoutType` | `FadeIn` `.duration(200)` `.easing(Easing.out(Easing.ease))` | 自定义进入动画 |
| `exiting.value` | `EntryOrExitLayoutType` | `FadeOut` `.duration(200)` `.easing(Easing.in(Easing.ease))` | 自定义退出动画 |
## Hooks
### useAccordion
访问手风琴根上下文,必须在 `Accordion` 内使用。
```tsx
import { useAccordion } from 'heroui-native';
const { value, onValueChange, selectionMode, isCollapsible, isDisabled } =
useAccordion();
```
#### 返回值
| property | type | description |
| --------------- | --------------------------------------------------------------------- | ------------------ |
| `selectionMode` | `'single' \| 'multiple' \| undefined` | 单选或多选展开模式 |
| `value` | `(string \| undefined) \| string[]` | 当前展开项:单选为字符串,多选为数组 |
| `onValueChange` | `(value: string \| undefined) => void \| ((value: string[]) => void)` | 更新展开项的回调 |
| `isCollapsible` | `boolean` | 已展开项是否可收起 |
| `isDisabled` | `boolean \| undefined` | 是否禁用全部条目 |
### useAccordionItem
访问单条条目上下文,必须在 `Accordion.Item` 内使用。
```tsx
import { useAccordionItem } from 'heroui-native';
const { value, isExpanded, isDisabled, nativeID } = useAccordionItem();
```
#### 返回值
| property | type | description |
| ------------ | ---------------------- | ------------------ |
| `value` | `string` | 该条目的唯一值 |
| `isExpanded` | `boolean` | 当前是否展开 |
| `isDisabled` | `boolean \| undefined` | 该条目是否禁用 |
| `nativeID` | `string` | 无障碍与 ARIA 使用的原生 ID |
## 特别说明
当 Accordion 与同屏其他组件一起使用时,请为这些组件导入并应用 `AccordionLayoutTransition`,以保证整屏布局动画一致、顺滑。
```jsx
import { Accordion, AccordionLayoutTransition } from 'heroui-native';
import Animated from 'react-native-reanimated';
{/* 其他内容 */}
{/* 手风琴条目 */}
;
```
这样在展开或收起时,屏幕上各组件会使用相同的时长与缓动,体验更统一。
# ListGroup 列表组
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/list-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(navigation)/list-group.mdx
> 基于 Surface 的容器,用于分组展示列表项并保持一致的布局与间距。
## 导入
```tsx
import { ListGroup } from 'heroui-native';
```
## 结构
```tsx
...
...
...
```
* **ListGroup**:基于 Surface 的根容器,用于分组列表项;支持全部 Surface 变体(`default`、`secondary`、`tertiary`、`transparent`)。
* **ListGroup.Item**:可按压的水平 `flex-row` 行容器,提供统一的间距与对齐。
* **ListGroup.ItemPrefix**:可选前导槽,用于图标、头像等。
* **ListGroup.ItemContent**:`flex-1` 包裹标题与说明,占据剩余横向空间。
* **ListGroup.ItemTitle**:主标题,前景色与中等字重。
* **ListGroup.ItemDescription**:次要说明,弱化颜色与较小字号。
* **ListGroup.ItemSuffix**:可选尾部槽;默认渲染右箭头;传入子节点可覆盖默认图标。
## 用法
### 基础用法
通过组合子部件创建带标题与说明的分组列表。
```tsx
个人信息
姓名、邮箱、手机号
支付方式
Visa 尾号 4829
```
### 带图标
使用 `ListGroup.ItemPrefix` 放置前置图标。
```tsx
个人资料
姓名、照片、简介
安全
密码、双重验证
```
### 仅标题
省略 `ListGroup.ItemDescription` 以展示仅标题行。
```tsx
Wi-Fi
蓝牙
```
### Surface 变体
为根容器应用不同的视觉变体。
```tsx
Wi-Fi
```
### 自定义尾部
向 `ListGroup.ItemSuffix` 传入子节点以覆盖默认箭头。
```tsx
语言
简体中文
通知
7
```
### 自定义尾部图标属性
通过 `iconProps` 调整默认箭头尺寸与颜色。
```tsx
存储空间
已用 12.4 GB / 共 50 GB
```
### 配合 PressableFeedback
用 `PressableFeedback` 包裹列表项以添加缩放与水波纹反馈。此模式下将 `onPress` 放在 `PressableFeedback` 上,并对 `ListGroup.Item` 使用 `disabled`。
```tsx
import { ListGroup, PressableFeedback, Separator } from 'heroui-native';
{}}>
外观
主题、字号、显示
{}}>
通知
提醒、声音、角标
```
## 示例
```tsx
import { Ionicons } from '@expo/vector-icons';
import { ListGroup, Separator, useThemeColor } from 'heroui-native';
import { View, Text } from 'react-native';
import { withUniwind } from 'uniwind';
const StyledIonicons = withUniwind(Ionicons);
export default function ListGroupExample() {
const mutedColor = useThemeColor('muted');
return (
账户
个人信息
姓名、邮箱、手机号
支付方式
Visa 尾号 4829
偏好设置
外观
主题、字号、显示
通知
提醒、声音、角标
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/list-group.tsx)。
## API 参考
### ListGroup
| prop | type | default | description |
| -------------- | --------------------------------------------------------- | ----------- | ----------------------------- |
| `children` | `React.ReactNode` | - | 分组内的子节点 |
| `variant` | `'default' \| 'secondary' \| 'tertiary' \| 'transparent'` | `'default'` | 底层 Surface 容器的视觉变体 |
| `className` | `string` | - | 根容器额外 class |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
### ListGroup.Item
| prop | type | default | description |
| ------------------- | ----------------- | ------- | ---------------------------------- |
| `children` | `React.ReactNode` | - | 列表行内的子节点 |
| `className` | `string` | - | 列表行额外 class |
| `...PressableProps` | `PressableProps` | - | 支持全部标准 React Native `Pressable` 属性 |
### ListGroup.ItemPrefix
| prop | type | default | description |
| -------------- | ----------------- | ------- | ----------------------------- |
| `children` | `React.ReactNode` | - | 前导内容,如图标或头像 |
| `className` | `string` | - | 前导区域额外 class |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
### ListGroup.ItemContent
| prop | type | default | description |
| -------------- | ----------------- | ------- | ----------------------------- |
| `children` | `React.ReactNode` | - | 内容区,通常为标题与说明 |
| `className` | `string` | - | 内容区额外 class |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
### ListGroup.ItemTitle
| prop | type | default | description |
| -------------- | ----------------- | ------- | ----------------------------- |
| `children` | `React.ReactNode` | - | 标题文本或自定义内容 |
| `className` | `string` | - | 标题额外 class |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
### ListGroup.ItemDescription
| prop | type | default | description |
| -------------- | ----------------- | ------- | ----------------------------- |
| `children` | `React.ReactNode` | - | 说明文本或自定义内容 |
| `className` | `string` | - | 说明额外 class |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
### ListGroup.ItemSuffix
| prop | type | default | description |
| -------------- | -------------------- | ------- | ----------------------------- |
| `children` | `React.ReactNode` | - | 自定义尾部内容;提供时将覆盖默认右箭头图标 |
| `className` | `string` | - | 尾部额外 class |
| `iconProps` | `ListGroupIconProps` | - | 自定义默认右箭头图标;仅在无 `children` 时生效 |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
#### ListGroupIconProps
| prop | type | default | description |
| ------- | -------- | -------------- | ----------- |
| `size` | `number` | `16` | 箭头图标尺寸(像素) |
| `color` | `string` | 主题的 `muted` 颜色 | 箭头图标颜色 |
# Tabs 标签页
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/tabs
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(navigation)/tabs.mdx
> 使用选项卡视图组织内容,支持动画过渡与指示器。
## 导入
```tsx
import { Tabs } from 'heroui-native';
```
## 结构
```tsx
...
...
...
```
* **Tabs**:管理选项卡状态与选中的根容器。控制当前激活项、处理值变化,并向子组件提供上下文。
* **Tabs.List**:放置选项卡触发器的容器。将多个触发器组合在一起,并支持 `primary` 或 `secondary` 等样式变体。
* **Tabs.ScrollView**:可选的横向滚动容器。当标签溢出时可横向滚动,并可在选中时自动居中。
* **Tabs.Trigger**:每个选项卡的交互触发器。处理按压以切换激活项,并测量位置以驱动指示器动画。
* **Tabs.Label**:触发器上的文字标签,用于展示选项卡标题及对应样式。
* **Tabs.Indicator**:当前激活项的可视指示器,可在选项卡之间以弹簧或时长动画平滑过渡。
* **Tabs.Separator**:选项卡之间的分隔线。当当前值不在 `betweenValues` 数组中时显示,并带有透明度过渡动画。
* **Tabs.Content**:面板内容容器。当其 `value` 与当前激活项一致时显示对应内容。
## 用法
### 基础用法
Tabs 使用复合子组件,将内容划分为可切换的区域。
```tsx
标签一
标签二
...
...
```
### 主样式(primary)
默认圆角主样式,选中项背后为填充指示器。
```tsx
设置
个人资料
...
...
```
### 次样式(secondary)
下划线指示器,视觉更轻量。
```tsx
概览
分析
...
...
```
### 可滚动标签
标签较多时通过横向滚动容纳。
```tsx
第一个
第二个
第三个
第四个
第五个
...
...
...
...
...
```
### 禁用标签
使用 `isDisabled` 禁止与特定标签交互。
```tsx
可用
已禁用
其他
...
...
```
### 与图标组合
图标与文字并用,信息更直观。
```tsx
首页
搜索
...
...
```
### 使用渲染函数
在 `Tabs.Trigger` 上使用渲染函数,可读取选中状态并按需自定义内容。
```tsx
{({ isSelected, value, isDisabled }) => (
设置
)}
{({ isSelected }) => (
<>
个人资料
>
)}
...
...
```
### 与分隔线配合
在标签之间添加分隔线;可见性由 `betweenValues` 与当前激活项共同决定(详见下方 API)。
```tsx
通用
通知
个人资料
...
...
...
```
## 示例
```tsx
import {
Button,
Checkbox,
Description,
ControlField,
Label,
Tabs,
TextField,
} from 'heroui-native';
import { useState } from 'react';
import { View, Text } from 'react-native';
import Animated, {
FadeIn,
FadeOut,
LinearTransition,
} from 'react-native-reanimated';
const AnimatedContentContainer = ({
children,
}: {
children: React.ReactNode;
}) => (
{children}
);
export default function TabsExample() {
const [activeTab, setActiveTab] = useState('general');
const [showSidebar, setShowSidebar] = useState(true);
const [accountActivity, setAccountActivity] = useState(true);
const [name, setName] = useState('');
return (
通用
通知
个人资料
显示侧边栏
显示侧边导航面板
账户动态
接收与账户活动相关的通知
姓名
更新资料
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/tabs.tsx)。
## API 参考
### Tabs
| prop | type | 默认值 | 描述 |
| --------------- | ---------------------------- | ----------- | ------------------------------------ |
| `children` | `React.ReactNode` | - | 渲染在 Tabs 内的子元素 |
| `value` | `string` | - | 当前激活的标签值 |
| `variant` | `'primary' \| 'secondary'` | `'primary'` | 视觉变体 |
| `className` | `string` | - | 根容器额外 className |
| `animation` | `"disable-all" \| undefined` | `undefined` | 动画配置。设为 `"disable-all"` 可关闭全部动画(含子树) |
| `onValueChange` | `(value: string) => void` | - | 激活标签变化时的回调 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
### Tabs.List
| prop | type | 默认值 | 描述 |
| -------------- | ----------------- | --- | ------------------------------ |
| `children` | `React.ReactNode` | - | 渲染在列表内的子元素 |
| `className` | `string` | - | 额外 className |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
### Tabs.ScrollView
| prop | type | 默认值 | 描述 |
| --------------------------- | ---------------------------------------- | ---------- | ------------------------------------ |
| `children` | `React.ReactNode` | - | 渲染在滚动视图内的子元素 |
| `scrollAlign` | `'start' \| 'center' \| 'end' \| 'none'` | `'center'` | 选中项的滚动对齐方式 |
| `className` | `string` | - | 滚动容器额外 className |
| `contentContainerClassName` | `string` | - | 内容容器额外 className |
| `...ScrollViewProps` | `ScrollViewProps` | - | 支持 React Native `ScrollView` 的全部标准属性 |
### Tabs.Trigger
| prop | type | 默认值 | 描述 |
| ------------------- | ------------------------------------------------------------------------- | ------- | -------------------------------------- |
| `children` | `React.ReactNode \| ((props: TabsTriggerRenderProps) => React.ReactNode)` | - | 子节点,或接收 `TabsTriggerRenderProps` 的渲染函数 |
| `value` | `string` | - | 唯一标识该标签的值 |
| `isDisabled` | `boolean` | `false` | 是否禁用该触发器 |
| `className` | `string` | - | 额外 className |
| `...PressableProps` | `PressableProps` | - | 支持 React Native `Pressable` 的全部标准属性 |
#### TabsTriggerRenderProps
使用渲染函数作为 `children` 时,会传入以下属性:
| property | type | 描述 |
| ------------ | --------- | ---------- |
| `isSelected` | `boolean` | 当前触发器是否被选中 |
| `value` | `string` | 该触发器的值 |
| `isDisabled` | `boolean` | 该触发器是否禁用 |
### Tabs.Label
| prop | type | 默认值 | 描述 |
| -------------- | ----------------- | --- | ------------------------------ |
| `children` | `React.ReactNode` | - | 作为标签渲染的文本内容 |
| `className` | `string` | - | 额外 className |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部标准属性 |
### Tabs.Indicator
| prop | type | 默认值 | 描述 |
| ----------------------- | ------------------------ | ------ | ----------------------------------- |
| `children` | `React.ReactNode` | - | 自定义指示器内容 |
| `className` | `string` | - | 额外 className |
| `animation` | `TabsIndicatorAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | 支持 Reanimated `Animated.View` 的全部属性 |
#### TabsIndicatorAnimation
`Tabs.Indicator` 的动画配置,可为:
* `false` 或 `"disabled"`:关闭所有动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | 默认值 | 描述 |
| ------------------- | -------------------------------------- | ------------------------------------------------------------------------ | --------------- |
| `state` | `'disabled' \| boolean` | - | 自定义属性时用于禁用动画 |
| `width.type` | `'spring' \| 'timing'` | `'spring'` | 宽度动画类型 |
| `width.config` | `WithSpringConfig \| WithTimingConfig` | `{ stiffness: 1200, damping: 120 }`(spring)或 `{ duration: 200 }`(timing) | Reanimated 动画配置 |
| `height.type` | `'spring' \| 'timing'` | `'spring'` | 高度动画类型 |
| `height.config` | `WithSpringConfig \| WithTimingConfig` | 同上 | Reanimated 动画配置 |
| `translateX.type` | `'spring' \| 'timing'` | `'spring'` | 水平位移动画类型 |
| `translateX.config` | `WithSpringConfig \| WithTimingConfig` | 同上 | Reanimated 动画配置 |
### Tabs.Separator
| prop | type | 默认值 | 描述 |
| ----------------------- | ------------------------ | ------- | --------------------------------------------------- |
| `betweenValues` | `string[]` | - | 分隔线两侧对应的标签值数组。当**当前**标签值**不在**该数组中时,分隔线可见(与可见性动画联动) |
| `isAlwaysVisible` | `boolean` | `false` | 为 `true` 时透明度恒为 1,不受当前标签影响 |
| `className` | `string` | - | 额外 className |
| `animation` | `TabsSeparatorAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `children` | `React.ReactNode` | - | 自定义分隔线内容 |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | 支持 Reanimated `Animated.View` 的全部属性 |
**说明:** 以下样式属性由动画占用,不能仅通过 `className` 覆盖:
* `opacity`:用于分隔线显隐过渡(当前标签在 `betweenValues` 内时为 0,否则为 1)
若要调整这些属性,请使用 `animation`。若需完全关闭动画样式、改用自己的 `className` 或 `style`,请设置 `isAnimatedStyleActive={false}`。
#### TabsSeparatorAnimation
`Tabs.Separator` 的动画配置,可为:
* `false` 或 `"disabled"`:关闭所有动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | 默认值 | 描述 |
| ---------------------- | ----------------------- | ------------------- | --------------- |
| `state` | `'disabled' \| boolean` | - | 自定义属性时用于禁用动画 |
| `opacity.value` | `[number, number]` | `[0, 1]` | 透明度区间 \[隐藏, 显示] |
| `opacity.timingConfig` | `WithTimingConfig` | `{ duration: 200 }` | 时长类动画配置 |
### Tabs.Content
| prop | type | 默认值 | 描述 |
| -------------- | ----------------- | --- | ------------------------------ |
| `children` | `React.ReactNode` | - | 渲染在面板内的子元素 |
| `value` | `string` | - | 该内容与哪个标签值对应 |
| `className` | `string` | - | 额外 className |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
## Hooks
### useTabs
在自定义组件或复合子组件中读取 Tabs 根上下文。
```tsx
import { useTabs } from 'heroui-native';
const CustomComponent = () => {
const { value, onValueChange, nativeID } = useTabs();
// ...你的实现
};
```
#### 返回值
类型:`UseTabsReturn`
| property | type | 描述 |
| --------------- | ------------------------- | -------------- |
| `value` | `string` | 当前激活的标签值 |
| `onValueChange` | `(value: string) => void` | 用于切换激活标签的回调 |
| `nativeID` | `string` | 该 Tabs 实例的唯一标识 |
**说明:** 必须在 `Tabs` 内使用;在上下文外调用会抛错。
### useTabsMeasurements
读取标签测量上下文,用于管理各触发器的位置与尺寸。
```tsx
import { useTabsMeasurements } from 'heroui-native';
const CustomIndicator = () => {
const { measurements, variant } = useTabsMeasurements();
// ...你的实现
};
```
#### 返回值
类型:`UseTabsMeasurementsReturn`
| property | type | 描述 |
| ----------------- | ------------------------------------------------------- | ------------- |
| `measurements` | `Record` | 各标签触发器的测量数据 |
| `setMeasurements` | `(key: string, measurements: ItemMeasurements) => void` | 更新指定触发器的测量数据 |
| `variant` | `'primary' \| 'secondary'` | 当前 Tabs 的视觉变体 |
#### ItemMeasurements
| property | type | 描述 |
| -------- | -------- | --------- |
| `width` | `number` | 触发器宽度(像素) |
| `height` | `number` | 触发器高度(像素) |
| `x` | `number` | 触发器的 x 坐标 |
**说明:** 必须在 `Tabs` 内使用;在上下文外调用会抛错。
### useTabsTrigger
在自定义组件或复合子组件中读取单个 `Tabs.Trigger` 的上下文。
```tsx
import { useTabsTrigger } from 'heroui-native';
const CustomLabel = () => {
const { value, isSelected, nativeID } = useTabsTrigger();
// ...你的实现
};
```
#### 返回值
类型:`UseTabsTriggerReturn`
| property | type | 描述 |
| ------------ | --------- | ---------- |
| `value` | `string` | 该触发器的值 |
| `nativeID` | `string` | 该触发器的唯一标识 |
| `isSelected` | `boolean` | 当前触发器是否被选中 |
**说明:** 必须在 `Tabs.Trigger` 内使用;在上下文外调用会抛错。
# BottomSheet 底部弹层
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/bottom-sheet
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(overlays)/bottom-sheet.mdx
> 自底部滑入的底部表单,带动画与下滑关闭手势。
## 导入
```tsx
import { BottomSheet } from 'heroui-native';
```
## 结构
```tsx
...
...
...
...
```
* **BottomSheet**:根组件,管理开关状态并向子级提供上下文。
* **BottomSheet.Trigger**:按下后打开底部表单的可按压区域。
* **BottomSheet.Portal**:在 Portal 中渲染,使用全屏覆盖层。
* **BottomSheet.Overlay**:覆盖全屏的背景层,按下通常可关闭。
* **BottomSheet.Content**:主容器,基于 @gorhom/bottom-sheet 渲染并支持手势。
* **BottomSheet.Close**:关闭按钮;可自定义子节点或使用默认关闭图标。
* **BottomSheet.Title**:标题,语义标题角色并关联无障碍。
* **BottomSheet.Description**:说明文字,并关联无障碍。
## 用法
### 基础底部表单
包含标题、描述与关闭按钮的简单示例。
```tsx
打开底部表单
...
...
```
### 悬浮(Detached)
与底边留出间距的悬浮样式。
```tsx
...
...
```
### 多停靠点与滚动
多档高度与可滚动内容。
```tsx
...
...
```
### 自定义遮罩
用模糊等自定义内容替换默认遮罩。
```tsx
import { useBottomSheet, useBottomSheetAnimation } from 'heroui-native';
import { StyleSheet, Pressable } from 'react-native';
import { interpolate, useDerivedValue } from 'react-native-reanimated';
import { AnimatedBlurView } from './animated-blur-view';
import { useUniwind } from 'uniwind';
export const BottomSheetBlurOverlay = () => {
const { theme } = useUniwind();
const { onOpenChange } = useBottomSheet();
const { progress } = useBottomSheetAnimation();
const blurIntensity = useDerivedValue(() => {
return interpolate(progress.get(), [0, 1, 2], [0, 40, 0]);
});
return (
onOpenChange(false)}
>
);
};
```
```tsx
...
...
```
## 示例
```tsx
import { BottomSheet, Button } from 'heroui-native';
import { useState } from 'react';
import { View } from 'react-native';
import { withUniwind } from 'uniwind';
import Ionicons from '@expo/vector-icons/Ionicons';
const StyledIonicons = withUniwind(Ionicons);
export default function BottomSheetExample() {
const [isOpen, setIsOpen] = useState(false);
return (
打开底部表单
保持安全
将软件更新到最新版本,以获得更好的安全性与性能。
setIsOpen(false)}>立即更新
setIsOpen(false)}>
稍后
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/bottom-sheet.tsx)。
## API 参考
### BottomSheet
| prop | type | default | description |
| --------------- | -------------------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 触发器与底部表单内容 |
| `isOpen` | `boolean` | - | 受控开关状态 |
| `isDefaultOpen` | `boolean` | `false` | 非受控初始是否打开 |
| `animation` | `AnimationRootDisableAll` | - | 动画配置 |
| `onOpenChange` | `(value: boolean) => void` | - | 开关状态变化回调 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
#### 动画配置
根动画配置,可为:
* `"disable-all"`:禁用全部动画(含子级)
* `undefined`:使用默认动画
### BottomSheet.Trigger
| prop | type | default | description |
| -------------------------- | ----------------------- | ------- | ---------------------------------------- |
| `children` | `React.ReactNode` | - | 触发器内容 |
| `asChild` | `boolean` | - | 是否无包裹渲染为子元素 |
| `...TouchableOpacityProps` | `TouchableOpacityProps` | - | 支持 React Native `TouchableOpacity` 的全部属性 |
### BottomSheet.Portal
| prop | type | default | description |
| -------------------------------------------- | ---------------------- | ------- | ----------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Portal 内容(遮罩与底部表单) |
| `disableFullWindowOverlay` | `boolean` | `false` | iOS 为 true 时使用普通 `View` 替代 `FullWindowOverlay`,便于检查器;遮罩不再叠在原生模态之上 |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | 是否将覆盖窗口视为模态容器(VoiceOver)。仅 iOS;可能随 react-native-screens 变化 |
| `className` | `string` | - | Portal 容器额外 class |
| `style` | `StyleProp` | - | Portal 容器额外样式 |
| `hostName` | `string` | - | 可选 Portal 宿主名 |
| `forceMount` | `boolean` | - | 关闭时仍挂载以配合动画 |
### BottomSheet.Overlay
| prop | type | default | description |
| ----------------------- | ------------------------------------------------------ | ------- | --------------------------------- |
| `children` | `React.ReactNode` | - | 自定义遮罩内容 |
| `className` | `string` | - | 遮罩额外 class |
| `style` | `ViewStyle` | - | 遮罩容器样式 |
| `animation` | `Omit` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `isCloseOnPress` | `boolean` | `true` | 按下遮罩是否关闭 |
| `...PressableProps` | `PressableProps` | - | 支持 React Native `Pressable` 的全部属性 |
#### 动画配置
遮罩动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置(不含 `entering` / `exiting`)
| prop | type | default | description |
| --------------- | -------------------------- | ----------- | ------------------ |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时禁用动画 |
| `opacity.value` | `[number, number, number]` | `[0, 1, 0]` | 不透明度 \[空闲, 打开, 关闭] |
### BottomSheet.Content
| prop | type | default | description |
| --------------------------- | ---------------------------------------- | ------- | -------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | 底部表单内容 |
| `className` | `string` | - | 容器额外 class |
| `containerClassName` | `string` | - | 外层容器 class |
| `contentContainerClassName` | `string` | - | 内容区 class |
| `backgroundClassName` | `string` | - | 背景 class |
| `handleClassName` | `string` | - | 拖动手柄区域 class |
| `handleIndicatorClassName` | `string` | - | 手柄指示条 class |
| `contentContainerProps` | `Omit` | - | 内容容器 props |
| `animation` | `AnimationDisabled` | - | 动画配置 |
| `...GorhomBottomSheetProps` | `Partial` | - | 支持 [@gorhom/bottom-sheet 全部 props](https://gorhom.dev/react-native-bottom-sheet/props) |
**说明:** 内容区内可使用 [@gorhom/bottom-sheet 组件](https://gorhom.dev/react-native-bottom-sheet/components/bottomsheetview),如 `BottomSheetView`、`BottomSheetScrollView`、`BottomSheetFlatList` 等。
### BottomSheet.Close
`BottomSheet.Close` 继承 [CloseButton](./close-button),按下时自动关闭底部表单。
### BottomSheet.Title
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 标题内容 |
| `className` | `string` | - | 标题额外 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部属性 |
### BottomSheet.Description
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 描述内容 |
| `className` | `string` | - | 描述额外 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部属性 |
## Hooks
### useBottomSheet
访问底部表单原语上下文。
```tsx
const { isOpen, onOpenChange } = useBottomSheet();
```
| property | type | description |
| -------------- | -------------------------- | ----------- |
| `isOpen` | `boolean` | 当前是否打开 |
| `onOpenChange` | `(value: boolean) => void` | 修改开关状态 |
### useBottomSheetAnimation
访问底部表单动画上下文。
```tsx
const { progress } = useBottomSheetAnimation();
```
| property | type | description |
| ---------- | --------------------- | -------------------- |
| `progress` | `SharedValue` | 动画进度(0=空闲,1=打开,2=关闭) |
## 特别说明
### 元素检查器(iOS)
`BottomSheet` 在 iOS 使用 `FullWindowOverlay`,位于独立原生窗口,会破坏 React Native 元素检查器。开发时可在 `BottomSheet.Portal` 设置 `disableFullWindowOverlay={true}`。代价:底部表单将无法叠在原生系统模态之上。
### 关闭回调
建议使用 `BottomSheet` 的 `onOpenChange` 处理关闭逻辑,可在所有关闭场景可靠触发(下滑、点遮罩、点关闭、程序化关闭等)。
```tsx
{
setIsOpen(value);
if (!value) {
// 任意方式关闭时都会执行
yourCallbackOnClose();
}
}}
>
...
```
**说明:** `@gorhom/bottom-sheet` 在 `BottomSheet.Content` 上的 `onClose` 仅在下滑关闭时触发,点遮罩、关闭按钮或程序化关闭不会触发。需要可靠关闭回调时请使用根组件的 `onOpenChange`。
# Dialog 对话框
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/dialog
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(overlays)/dialog.mdx
> 模态浮层,带动画过渡并支持手势关闭。
## 导入
```tsx
import { Dialog } from 'heroui-native';
```
## 结构
```tsx
...
...
...
...
...
```
* **Dialog**:根组件,管理开关状态并向子级提供上下文。
* **Dialog.Trigger**:按下后打开对话框的可按压区域。
* **Dialog.Portal**:在 Portal 中渲染内容,居中布局并控制动画。
* **Dialog.Overlay**:内容背后的遮罩,按下通常可关闭对话框。
* **Dialog.Content**:主容器,支持拖拽关闭等手势。
* **Dialog.Close**:关闭按钮;可自定义子节点或使用默认关闭图标。
* **Dialog.Title**:标题,语义为标题角色。
* **Dialog.Description**:补充说明文字。
## 用法
### 基础对话框
包含标题、描述与关闭按钮的简单对话框。
```tsx
打开对话框
...
...
```
### 可滚动内容
长内容使用滚动容器承载。
```tsx
...
...
...
```
### 表单对话框
包含输入与键盘避让的对话框。
```tsx
...
...
...
提交
```
## 示例
```tsx
import { Button, Dialog } from 'heroui-native';
import { View } from 'react-native';
import { useState } from 'react';
export default function DialogExample() {
const [isOpen, setIsOpen] = useState(false);
return (
打开对话框
确认操作
确定要继续吗?此操作无法撤销。
setIsOpen(false)}>
取消
确认
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/dialog.tsx)。
## API 参考
### Dialog
| prop | type | default | description |
| --------------- | -------------------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 触发器与对话框内容 |
| `isOpen` | `boolean` | - | 受控开关状态 |
| `isDefaultOpen` | `boolean` | `false` | 非受控初始是否打开 |
| `animation` | `AnimationRootDisableAll` | - | 动画配置 |
| `onOpenChange` | `(value: boolean) => void` | - | 开关状态变化回调 |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
#### AnimationRootDisableAll
根动画配置,可为:
* `false` 或 `"disabled"`:仅禁用根级动画
* `"disable-all"`:禁用全部动画(含子级)
* `true` 或 `undefined`:使用默认动画
### Dialog.Trigger
| prop | type | default | description |
| -------------------------- | ----------------------- | ------- | ---------------------------------------- |
| `children` | `React.ReactNode` | - | 触发器内容 |
| `asChild` | `boolean` | - | 是否无包裹渲染为子元素 |
| `...TouchableOpacityProps` | `TouchableOpacityProps` | - | 支持 React Native `TouchableOpacity` 的全部属性 |
### Dialog.Portal
| prop | type | default | description |
| -------------------------------------------- | ---------------------- | ------- | ----------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Portal 内容(遮罩与对话框) |
| `disableFullWindowOverlay` | `boolean` | `false` | iOS 为 true 时使用普通 `View` 替代 `FullWindowOverlay`,便于检查器;遮罩不再叠在原生模态之上 |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | 是否将覆盖窗口视为模态容器(VoiceOver)。仅 iOS;可能随 react-native-screens 变化 |
| `className` | `string` | - | Portal 容器额外 class |
| `style` | `StyleProp` | - | Portal 容器额外样式 |
| `hostName` | `string` | - | 可选 Portal 宿主名 |
| `forceMount` | `boolean` | - | 关闭时仍挂载以配合动画 |
### Dialog.Overlay
| prop | type | default | description |
| ----------------------- | ------------------------ | ------- | --------------------------------- |
| `children` | `React.ReactNode` | - | 自定义遮罩内容 |
| `className` | `string` | - | 遮罩额外 class |
| `style` | `ViewStyle` | - | 遮罩容器样式 |
| `animation` | `DialogOverlayAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `isCloseOnPress` | `boolean` | `true` | 按下遮罩是否关闭 |
| `forceMount` | `boolean` | - | 关闭时仍挂载以配合动画 |
| `...PressableProps` | `PressableProps` | - | 支持 React Native `Pressable` 的全部属性 |
#### DialogOverlayAnimation
遮罩动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| --------------- | -------------------------- | ----------------------- | ----------------------------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时禁用动画 |
| `opacity.value` | `[number, number, number]` | `[0, 1, 0]` | 不透明度 \[空闲, 打开, 关闭](基于进度,用于呈现) |
| `entering` | `EntryOrExitLayoutType` | `FadeIn.duration(200)` | 自定义进入动画(Popover 呈现用) |
| `exiting` | `EntryOrExitLayoutType` | `FadeOut.duration(150)` | 自定义退出动画(Popover 呈现用) |
### Dialog.Content
| prop | type | default | description |
| ----------------------- | ------------------------ | ------- | ----------------------------------- |
| `children` | `React.ReactNode` | - | 对话框内容 |
| `className` | `string` | - | 内容容器额外 class |
| `style` | `StyleProp` | - | 内容容器额外样式 |
| `animation` | `DialogContentAnimation` | - | 动画配置 |
| `isSwipeable` | `boolean` | `true` | 是否可滑动关闭 |
| `forceMount` | `boolean` | - | 关闭时仍挂载以配合动画 |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | 支持 Reanimated `Animated.View` 的全部属性 |
#### DialogContentAnimation
内容动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ---------- | ----------------------- | ------------------------------------------------------------------------ | ----------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时禁用动画 |
| `entering` | `EntryOrExitLayoutType` | 关键帧 `scale: 0.96→1` 与 `opacity: 0→1`(200ms,缓动 `Easing.out(Easing.ease)`) | 自定义进入动画 |
| `exiting` | `EntryOrExitLayoutType` | 关键帧 `scale: 1→0.96` 与 `opacity: 1→0`(150ms,缓动 `Easing.in(Easing.ease)`) | 自定义退出动画 |
### Dialog.Close
`Dialog.Close` 继承 [CloseButton](./close-button),按下时自动关闭对话框。
### Dialog.Title
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 标题内容 |
| `className` | `string` | - | 标题额外 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部属性 |
### Dialog.Description
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 描述内容 |
| `className` | `string` | - | 描述额外 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部属性 |
## Hooks
### useDialog
访问对话框原语上下文。
```tsx
const { isOpen, onOpenChange } = useDialog();
```
| property | type | description |
| -------------- | -------------------------- | ----------- |
| `isOpen` | `boolean` | 当前是否打开 |
| `onOpenChange` | `(value: boolean) => void` | 修改开关状态 |
### useDialogAnimation
访问对话框动画上下文,用于高级定制。
```tsx
const { progress, isDragging, isGestureReleaseAnimationRunning } =
useDialogAnimation();
```
| property | type | description |
| ---------------------------------- | ---------------------- | -------------------- |
| `progress` | `SharedValue` | 动画进度(0=空闲,1=打开,2=关闭) |
| `isDragging` | `SharedValue` | 是否正在拖拽 |
| `isGestureReleaseAnimationRunning` | `SharedValue` | 手势释放动画是否进行中 |
## 特别说明
### 元素检查器(iOS)
`Dialog` 在 iOS 使用 `FullWindowOverlay`。开发时若需启用 React Native 元素检查器,可在 `Dialog.Portal` 设置 `disableFullWindowOverlay={true}`。代价:对话框将无法叠在原生系统模态之上。
# Popover 弹出框
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/popover
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(overlays)/popover.mdx
> 锚定在触发器上的浮动内容面板,支持方位与对齐选项。
## 导入
```tsx
import { Popover } from 'heroui-native';
```
## 结构
```tsx
...
...
...
```
* **Popover**:根容器,管理展开/收起、定位,并为子组件提供上下文。
* **Popover.Trigger**:可点击的触发器,切换浮层可见性;为子元素包裹按压处理。
* **Popover.Portal**:在Portal层渲染内容,保证层级与定位正确。
* **Popover.Overlay**:可选背景遮罩;可透明或半透明,用于捕获外部点击。
* **Popover.Content**:内容容器,含定位、样式与碰撞检测;支持 `popover` 与底部抽屉呈现。
* **Popover.Arrow**:可选箭头,指向触发器;随 `placement` 自动定位。
* **Popover.Close**:关闭按钮;可自定义子节点,默认关闭图标。
* **Popover.Title**:可选标题,使用预设排版。
* **Popover.Description**:可选说明文字,弱化样式。
## 用法
### 基础用法
通过组合子部件创建浮动内容面板。
```tsx
...
...
```
### 标题与说明
使用标题与说明组织内容层级。
```tsx
...
...
...
```
### 带箭头
添加指向触发器的箭头以增强视觉关联。
```tsx
...
...
```
> **说明:** 使用 ` ` 时,需要为 `Popover.Content` 添加边框,例如 `border border-border`,以便箭头与内容边框视觉衔接。
### 宽度控制
通过 `width` 控制浮层内容宽度。
```tsx
{
/* 固定像素宽度 */
}
...
...
;
{
/* 与触发器同宽 */
}
...
...
;
{
/* 全宽(100%) */
}
...
...
;
{
/* 随内容自适应(默认) */
}
...
...
;
```
### 底部抽屉呈现
在移动端使用底部抽屉交互。
> **重要:** `Popover.Content` 的 `presentation` 必须与 `Popover` 根上的 `presentation` 一致。开发模式下不一致会抛错。
```tsx
...
...
...
关闭
```
### 方位选项
控制浮层相对触发器出现的位置。
```tsx
...
...
```
### 对齐选项
沿放置轴微调内容对齐。
```tsx
...
...
```
### 自定义动画
在 `Popover` 根上使用 `animation` 配置展开/收起过渡。
```tsx
...
...
```
### 编程式控制
```tsx
// 通过 ref 编程式打开/关闭
const popoverRef = useRef(null);
// 打开
popoverRef.current?.open();
// 关闭
popoverRef.current?.close();
// 完整示例
触发器
内容
popoverRef.current?.close()}>关闭
;
```
## 示例
```tsx
import { Ionicons } from '@expo/vector-icons';
import { Button, Popover, useThemeColor } from 'heroui-native';
import { Text, View } from 'react-native';
export default function PopoverExample() {
const themeColorMuted = useThemeColor('muted');
return (
查看说明
说明
此浮层包含标题与描述,用于向用户提供更有层次的信息。
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/popover.tsx)。
## API 参考
### Popover
| prop | type | default | description |
| --------------- | ----------------------------- | ----------- | ----------------------------------------------------------------- |
| `children` | `ReactNode` | - | 浮层内的子节点 |
| `isOpen` | `boolean` | - | 是否展开(受控) |
| `isDefaultOpen` | `boolean` | - | 初始是否展开(非受控) |
| `onOpenChange` | `(isOpen: boolean) => void` | - | 展开状态变化时的回调 |
| `animation` | `AnimationRootDisableAll` | - | 动画配置,可为 `false`、`"disabled"`、`"disable-all"`、`true` 或 `undefined` |
| `presentation` | `'popover' \| 'bottom-sheet'` | `'popover'` | 内容呈现方式 |
| `asChild` | `boolean` | `false` | 是否将子元素作为实际渲染节点 |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
#### AnimationRootDisableAll
根级动画配置,可为:
* `false` 或 `"disabled"`:仅禁用根动画
* `"disable-all"`:禁用根与子级全部动画
* `true` 或 `undefined`:使用默认动画
### Popover.Trigger
| prop | type | default | description |
| ------------------- | ---------------- | ------- | ---------------------------------- |
| `children` | `ReactNode` | - | 触发器内容 |
| `className` | `string` | - | 触发器额外 class |
| `asChild` | `boolean` | `true` | 是否将子元素作为实际渲染节点 |
| `...PressableProps` | `PressableProps` | - | 支持全部标准 React Native `Pressable` 属性 |
### Popover.Portal
| prop | type | default | description |
| -------------------------------------------- | ----------- | ------- | -------------------------------------------------------------------------------------------------- |
| `children` | `ReactNode` | - | Portal内容(必填) |
| `disableFullWindowOverlay` | `boolean` | `false` | 在 iOS 为 `true` 时使用 `View` 代替 `FullWindowOverlay`,便于元素检查器;遮罩将无法叠在原生模态之上 |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | 控制 VoiceOver 是否将遮罩窗口视为模态容器。为 `true` 时,VoiceOver 仅聚焦遮罩内元素。仅 iOS;API 不稳定,可能随 react-native-screens 变更 |
| `hostName` | `string` | - | Portal宿主元素的可选名称 |
| `forceMount` | `boolean` | - | 是否强制挂载 |
| `className` | `string` | - | Portal容器额外 class |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
### Popover.Overlay
| prop | type | default | description |
| ----------------------- | ------------------------- | ------- | ----------------------------------- |
| `className` | `string` | - | 遮罩额外 class |
| `closeOnPress` | `boolean` | `true` | 点击遮罩是否关闭 |
| `forceMount` | `boolean` | - | 是否强制挂载 |
| `animation` | `PopoverOverlayAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `asChild` | `boolean` | `false` | 是否将子元素作为实际渲染节点 |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | 支持 Reanimated `Animated.View` 的全部属性 |
#### PopoverOverlayAnimation
遮罩动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| --------------- | -------------------------- | ----------- | --------------------------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时用于禁用动画 |
| `opacity.value` | `[number, number, number]` | `[0, 1, 0]` | 透明度 \[空闲, 打开, 关闭],用于底部抽屉等呈现 |
| `entering` | `EntryOrExitLayoutType` | 默认淡入 200ms | 自定义进入关键帧,用于 `popover` 呈现 |
| `exiting` | `EntryOrExitLayoutType` | 默认淡出 150ms | 自定义退出关键帧,用于 `popover` 呈现 |
### Popover.Content(Popover 呈现)
| prop | type | default | description |
| ------------------------- | ------------------------------------------------ | --------------- | -------------------------------------- |
| `children` | `ReactNode` | - | 浮层内容 |
| `presentation` | `'popover'` | `'popover'` | 呈现模式,须与 `Popover` 根一致;未传时默认为 `popover` |
| `width` | `number \| 'trigger' \| 'content-fit' \| 'full'` | `'content-fit'` | 内容宽度策略 |
| `placement` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | 相对触发器的方位 |
| `align` | `'start' \| 'center' \| 'end'` | `'center'` | 沿放置轴的对齐 |
| `avoidCollisions` | `boolean` | `true` | 靠近视口边缘时是否翻转 placement |
| `offset` | `number` | `9` | 与触发器的间距(像素) |
| `alignOffset` | `number` | `0` | 沿对齐轴的偏移(像素) |
| `disablePositioningStyle` | `boolean` | `false` | 是否禁用自动定位样式 |
| `forceMount` | `boolean` | - | 是否强制挂载 |
| `insets` | `Insets` | - | 定位时需遵守的屏幕边距 |
| `className` | `string` | - | 内容容器额外 class |
| `animation` | `PopupPopoverContentAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `asChild` | `boolean` | `false` | 是否将子元素作为实际渲染节点 |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | 支持 Reanimated `Animated.View` 的全部属性 |
### Popover.Content(底部抽屉呈现)
| prop | type | default | description |
| --------------------------- | ---------------------- | ------- | -------------------------------- |
| `children` | `ReactNode` | - | 底部抽屉内容 |
| `presentation` | `'bottom-sheet'` | - | 呈现模式,须为 `bottom-sheet` 并与根一致(必填) |
| `contentContainerClassName` | `string` | - | 内容容器额外 class |
| `contentContainerProps` | `BottomSheetViewProps` | - | 内容容器属性 |
| `enablePanDownToClose` | `boolean` | `true` | 是否允许下滑关闭 |
| `backgroundStyle` | `ViewStyle` | - | 底部抽屉背景样式 |
| `handleIndicatorStyle` | `ViewStyle` | - | 把手指示器样式 |
| `...BottomSheetProps` | `BottomSheetProps` | - | 支持 `@gorhom/bottom-sheet` 的全部属性 |
#### PopupPopoverContentAnimation
内容(`popover` 呈现)动画配置,可为:
* `false` 或 `"disabled"`:禁用全部动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ---------- | ----------------------- | ------------------------------------------------ | ------------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时用于禁用动画 |
| `entering` | `EntryOrExitLayoutType` | 默认关键帧:translateY/translateX、scale、opacity(200ms) | 自定义进入关键帧 |
| `exiting` | `EntryOrExitLayoutType` | 默认与进入镜像(150ms) | 自定义退出关键帧 |
### Popover.Arrow
| prop | type | default | description |
| --------------------- | ---------------------------------------- | ------- | ----------------------------- |
| `className` | `string` | - | 箭头额外 class |
| `height` | `number` | `12` | 箭头高度(像素) |
| `width` | `number` | `20` | 箭头宽度(像素) |
| `fill` | `string` | - | 填充色(默认与内容背景一致) |
| `stroke` | `string` | - | 描边色(默认与内容边框色一致) |
| `strokeWidth` | `number` | `1` | 描边宽度(像素) |
| `strokeBaselineInset` | `number` | `1` | 描边基线内缩(像素) |
| `placement` | `'top' \| 'bottom' \| 'left' \| 'right'` | - | 浮层方位(自内容继承) |
| `children` | `ReactNode` | - | 自定义箭头内容(替换默认 SVG) |
| `style` | `StyleProp` | - | 箭头容器额外样式 |
| `...ViewProps` | `ViewProps` | - | 支持全部标准 React Native `View` 属性 |
### Popover.Close
`Popover.Close` 继承 [CloseButton](./close-button),按下时自动关闭浮层。
### Popover.Title
| prop | type | default | description |
| -------------- | ----------- | ------- | ----------------------------- |
| `children` | `ReactNode` | - | 标题文案 |
| `className` | `string` | - | 标题额外 class |
| `...TextProps` | `TextProps` | - | 支持全部标准 React Native `Text` 属性 |
### Popover.Description
| prop | type | default | description |
| -------------- | ----------- | ------- | ----------------------------- |
| `children` | `ReactNode` | - | 说明文案 |
| `className` | `string` | - | 说明额外 class |
| `...TextProps` | `TextProps` | - | 支持全部标准 React Native `Text` 属性 |
## Hooks
### usePopover
在自定义或复合子组件中读取浮层上下文。
```tsx
import { usePopover } from 'heroui-native';
const CustomContent = () => {
const { isOpen, onOpenChange, triggerPosition } = usePopover();
// …实现
};
```
#### 返回值
| property | type | description |
| -------------------- | --------------------------------------------------- | ----------- |
| `isOpen` | `boolean` | 当前是否打开 |
| `onOpenChange` | `(open: boolean) => void` | 修改展开状态的回调 |
| `isDefaultOpen` | `boolean \| undefined` | 默认是否打开(非受控) |
| `isDisabled` | `boolean \| undefined` | 是否禁用 |
| `triggerPosition` | `LayoutPosition \| null` | 触发器相对视口的位置 |
| `setTriggerPosition` | `(triggerPosition: LayoutPosition \| null) => void` | 更新触发器位置 |
| `contentLayout` | `LayoutRectangle \| null` | 浮层内容的布局测量 |
| `setContentLayout` | `(contentLayout: LayoutRectangle \| null) => void` | 更新内容布局测量 |
| `nativeID` | `string` | 当前实例唯一标识 |
**说明:** 必须在 `Popover` 内使用;在上下文外调用将抛错。
### usePopoverAnimation
在自定义或复合子组件中读取浮层动画共享值。
```tsx
import { usePopoverAnimation } from 'heroui-native';
const CustomContent = () => {
const { progress, isDragging } = usePopoverAnimation();
// …实现
};
```
#### 返回值
| property | type | description |
| ------------ | ---------------------- | -------------------- |
| `progress` | `SharedValue` | 动画进度(0=空闲,1=打开,2=关闭) |
| `isDragging` | `SharedValue` | 是否正在拖拽 |
**说明:** 必须在 `Popover` 内使用;在动画上下文外调用将抛错。
## 特别说明
### 元素检查器(iOS)
`Popover` 在 iOS 使用 `FullWindowOverlay`。开发时若需启用 React Native 元素检查器,可在 `Popover.Portal` 设置 `disableFullWindowOverlay={true}`。代价:浮层将无法叠在原生系统模态之上。
### 原生模态(iOS)
当 `Popover` 位于以原生模态形式呈现的页面内时(`presentation: 'modal' | 'formSheet' | 'pageSheet'`),浮层内容可能会向上偏移渲染。在新架构(Fabric)中,`react-native-screens` 将 `RNSModalScreen` 标记为 Fabric 根节点,因此触发器的坐标是相对于模态原点上报的,而 `FullWindowOverlay`(浮层挂载点)锚定在 iOS 应用窗口上。可通过将 `safeAreaInsets.top` 加到 `offset` 来补偿:
```tsx
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const insets = useSafeAreaInsets();
...
;
```
# Toast 轻提示
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/toast
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(overlays)/toast.mdx
> 在屏幕顶部或底部展示的临时通知消息。
## 导入
```tsx
import { Toast, useToast } from 'heroui-native';
```
## 结构
```tsx
...
...
...
```
* **Toast**:主容器,负责定位、动画与滑动手势。
* **Toast.Title**:标题文字,继承父级 Toast 的变体样式。
* **Toast.Description**:标题下方的描述文字。
* **Toast.Action**:操作按钮;按钮变体默认随 Toast 变体推断,也可覆盖。
* **Toast.Close**:关闭按钮,图标按钮样式,按下时调用隐藏。
## 用法
### 用法一:简单字符串
使用纯字符串快速展示 Toast。
```tsx
const { toast } = useToast();
toast.show('这是一条 Toast 消息');
```
### 用法二:配置对象
通过配置对象传入标题、描述、变体与操作按钮等。
```tsx
const { toast } = useToast();
toast.show({
variant: 'success',
label: '套餐已升级',
description: '可继续使用 HeroUI Chat',
icon: ,
actionLabel: '关闭',
onActionPress: ({ hide }) => hide(),
});
```
### 用法三:自定义组件
使用完全自定义的组件以自由控制样式与布局。
```tsx
const { toast } = useToast();
toast.show({
component: (props) => (
自定义 Toast
这是一个自定义 Toast 组件
),
});
```
**说明**:Toast 条目会做性能相关的 memo。若需把外部状态(如加载中)传入自定义 Toast,不会自动随状态重渲染。请使用 React Context、全局状态或 ref 等方式让状态能传递到 Toast 内。
### 禁用全部动画
使用 `"disable-all"` 可禁用自身及子级(如 `Toast.Action` 内的 `Button`)的全部动画。
```tsx
const { toast } = useToast();
toast.show({
variant: 'success',
label: '操作完成',
description: '已禁用全部动画',
animation: 'disable-all',
});
```
自定义组件示例:
```tsx
const { toast } = useToast();
toast.show({
component: (props) => (
无动画
此 Toast 已禁用全部动画
操作
),
});
```
## 示例
```tsx
import { Button, Toast, useToast, useThemeColor } from 'heroui-native';
import { View } from 'react-native';
export default function ToastExample() {
const { toast } = useToast();
const themeColorForeground = useThemeColor('foreground');
return (
toast.show({
variant: 'success',
label: '套餐已升级',
description: '可继续使用 HeroUI Chat',
actionLabel: '关闭',
onActionPress: ({ hide }) => hide(),
})
}
>
显示成功 Toast
toast.show({
component: (props) => (
自定义 Toast
使用自定义组件渲染
props.hide()}>撤销
),
})
}
>
显示自定义 Toast
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/toast.tsx)。
## 全局配置
通过 `HeroUINativeProvider` 的 `config` 全局配置 Toast;本地调用可覆盖默认值。
> **说明**:Provider 的完整配置见 [Provider 文档](/docs/native/getting-started/handbook/provider#toast-configuration)。
### 边距(Insets)
控制 Toast 与屏幕边缘的距离,会在安全区内边距基础上叠加。例如四边距屏幕 20px:
```tsx
{children}
```
### 使用 KeyboardAvoidingView 包裹内容
用 `KeyboardAvoidingView` 包裹 Toast 内容,键盘弹出时自动避让:
```tsx
import {
KeyboardAvoidingView,
KeyboardProvider,
} from 'react-native-keyboard-controller';
import { HeroUINativeProvider } from 'heroui-native';
import { useCallback } from 'react';
function AppContent() {
const contentWrapper = useCallback(
(children: React.ReactNode) => (
{children}
),
[]
);
return (
{children}
);
}
```
### 默认属性
全局设置变体、位置、动画与滑动等默认值:
```tsx
{children}
```
## API 参考
### Toast
| prop | type | default | description |
| ----------------------- | ------------------------------------------------------------- | ----------- | ---------------------------- |
| `variant` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | `'default'` | 视觉变体 |
| `placement` | `'top' \| 'bottom'` | `'top'` | 在屏幕上的位置 |
| `isSwipeable` | `boolean` | `true` | 是否可滑动关闭并带橡皮筋拖拽效果 |
| `animation` | `ToastRootAnimation` | - | 动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `className` | `string` | - | Toast 容器额外 class |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部属性 |
#### ToastRootAnimation
Toast 根动画配置,可为:
* `false` 或 `"disabled"`:仅禁用根级动画
* `"disable-all"`:禁用全部动画(含子级)
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -------------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | 在自定义属性时禁用动画 |
| `opacity.value` | `[number, number]` | `[1, 0]` | Toast 移出可视堆叠时的透明度插值 |
| `opacity.timingConfig` | `WithTimingConfig` | `{ duration: 300 }` | 透明度过渡的时间配置 |
| `translateY.value` | `[number, number]` | `[0, 10]` | 堆叠 Toast 微位移效果的 Y 插值 |
| `translateY.timingConfig` | `WithTimingConfig` | `{ duration: 300 }` | translateY 过渡的时间配置 |
| `scale.value` | `[number, number]` | `[1, 0.97]` | 堆叠 Toast 景深缩放的插值 |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 300 }` | 缩放过渡的时间配置 |
| `entering.top` | `EntryOrExitLayoutType` | `FadeInUp` `.springify()` `.withInitialValues({ opacity: 1, transform: [{ translateY: -100 }] })` `.mass(3)` | 顶部放置时的进入动画 |
| `entering.bottom` | `EntryOrExitLayoutType` | `FadeInDown` `.springify()` `.withInitialValues({ opacity: 1, transform: [{ translateY: 100 }] })` `.mass(3)` | 底部放置时的进入动画 |
| `exiting.top` | `EntryOrExitLayoutType` | 关键帧动画 `translateY: -100, scale: 0.97, opacity: 0.5` | 顶部放置时的退出动画 |
| `exiting.bottom` | `EntryOrExitLayoutType` | 关键帧动画 `translateY: 100, scale: 0.97, opacity: 0.5` | 底部放置时的退出动画 |
### Toast.Title
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 标题内容 |
| `className` | `string` | - | 额外的 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部属性 |
### Toast.Description
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------- |
| `children` | `React.ReactNode` | - | 描述内容 |
| `className` | `string` | - | 额外的 class |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部属性 |
### Toast.Action
`Toast.Action` 继承 [Button](button) 的全部属性。按钮变体默认由 Toast 变体推断,也可覆盖。
| prop | type | default | description |
| ----------- | ---------------------- | ------- | ----------------------- |
| `children` | `React.ReactNode` | - | 操作按钮文字 |
| `variant` | `ButtonVariant` | - | 按钮变体;未提供时由 Toast 变体自动决定 |
| `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` | 操作按钮尺寸 |
| `className` | `string` | - | 额外的 class |
`onPress`、`isDisabled` 等其余属性见 [Button API 参考](button#api-reference)。
### Toast.Close
`Toast.Close` 继承 [Button](button) 的全部属性。
| prop | type | default | description |
| ----------- | ----------------------------------- | ------- | ---------------------- |
| `children` | `React.ReactNode` | - | 自定义关闭图标;默认使用 CloseIcon |
| `iconProps` | `{ size?: number; color?: string }` | - | 默认关闭图标的属性 |
| `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` | 关闭按钮尺寸 |
| `className` | `string` | - | 额外的 class |
| `onPress` | `(event: any) => void` | - | 自定义按下处理;默认隐藏 Toast |
其余继承属性见 [Button API 参考](button#api-reference)。
### ToastProviderProps
通过 `HeroUINativeProvider` 的 `config.toast` 进行全局配置时可用的属性。
| prop | type | default | description |
| -------------------------------------------- | --------------------------------------------------- | ------- | ---------------------------------------------------------------------------- |
| `defaultProps` | `ToastGlobalConfig` | - | 全局默认配置,可被单次调用覆盖 |
| `disableFullWindowOverlay` | `boolean` | `false` | iOS 上为 true 时使用普通 `View` 替代 `FullWindowOverlay`,便于元素检查器;Toast 将不再叠在原生模态之上 |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | 是否将覆盖窗口视为模态容器(VoiceOver)。为 true 时焦点限制在覆盖层内。仅 iOS;可能随 react-native-screens 变化 |
| `insets` | `ToastInsets` | - | 相对屏幕边缘的内边距(与安全区内边距相加) |
| `maxVisibleToasts` | `number` | `3` | 最大可见条数,超过后开始降低透明度 |
| `contentWrapper` | `(children: React.ReactNode) => React.ReactElement` | - | 自定义包裹 Toast 内容的函数 |
| `children` | `React.ReactNode` | - | 子节点 |
#### ToastGlobalConfig
全局默认,可被单次调用覆盖。
| prop | type | description |
| ------------- | ------------------------------------------------------------- | -------------- |
| `variant` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | 视觉变体 |
| `placement` | `'top' \| 'bottom'` | 位置 |
| `isSwipeable` | `boolean` | 是否可滑动关闭并带橡皮筋效果 |
| `animation` | `ToastRootAnimation` | Toast 动画配置 |
#### ToastInsets
相对屏幕边缘的间距,会与安全区内边距相加。
| prop | type | default | description |
| -------- | -------- | ------- | ---------------------------------- |
| `top` | `number` | - | 距顶部像素(叠加安全区)。平台默认:iOS 0,Android 12 |
| `bottom` | `number` | - | 距底部像素(叠加安全区)。平台默认:iOS 6,Android 12 |
| `left` | `number` | - | 距左侧像素(叠加安全区)。默认 12 |
| `right` | `number` | - | 距右侧像素(叠加安全区)。默认 12 |
## Hooks
### useToast
访问 Toast 能力,必须在 `ToastProvider` 内使用(由 `HeroUINativeProvider` 提供)。
| 返回值 | type | description |
| ---------------- | -------------- | ------------------------------ |
| `toast` | `ToastManager` | 含 `show`、`hide` 等方法的 Toast 管理器 |
| `isToastVisible` | `boolean` | 当前是否有 Toast 可见 |
#### ToastManager
| method | type | description |
| ------ | ------------------------------------------------- | ------------------------------------------------ |
| `show` | `(options: string \| ToastShowOptions) => string` | 显示 Toast,返回 ID。支持字符串、配置对象或自定义组件三种形式 |
| `hide` | `(ids?: string \| string[] \| 'all') => void` | 隐藏一条或多条。无参隐藏最后一条;`'all'` 隐藏全部;传入 ID 或 ID 数组隐藏指定项 |
#### ToastShowOptions
展示选项:默认样式的配置对象,或自定义组件。
**使用配置对象(无 `component`)时:**
| prop | type | default | description |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------ |
| `variant` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | - | 视觉变体 |
| `placement` | `'top' \| 'bottom'` | - | 位置 |
| `isSwipeable` | `boolean` | - | 是否可滑动关闭 |
| `animation` | `ToastRootAnimation \| false \| "disabled" \| "disable-all"` | - | 动画配置 |
| `duration` | `number \| 'persistent'` | `4000` | 自动隐藏毫秒数;`'persistent'` 表示不自动隐藏 |
| `id` | `string` | - | 可选 ID;未提供则自动生成 |
| `label` | `string` | - | 标题文字 |
| `description` | `string` | - | 描述文字 |
| `actionLabel` | `string` | - | 操作按钮文案 |
| `onActionPress` | `(helpers: { show: (options: string \| ToastShowOptions) => string; hide: (ids?: string \| string[] \| 'all') => void }) => void` | - | 操作按钮按下回调 |
| `icon` | `React.ReactNode` | - | 左侧图标 |
| `onShow` | `() => void` | - | 显示时回调 |
| `onHide` | `() => void` | - | 隐藏时回调 |
**使用自定义组件时:**
| prop | type | default | description |
| ----------- | ---------------------------------------------------- | ------- | ------------------------------ |
| `id` | `string` | - | 可选 ID;未提供则自动生成 |
| `component` | `(props: ToastComponentProps) => React.ReactElement` | - | 接收 Toast props 并返回 React 元素的函数 |
| `duration` | `number \| 'persistent'` | `4000` | 自动隐藏毫秒数;`'persistent'` 表示不自动隐藏 |
| `onShow` | `() => void` | - | 显示时回调 |
| `onHide` | `() => void` | - | 隐藏时回调 |
## 特别说明
### 元素检查器(iOS)
Toast 在 iOS 上使用 `FullWindowOverlay`。开发时若需使用 React Native 元素检查器,可在 `HeroUINativeProvider` 的 `config.toast` 中设置 `disableFullWindowOverlay={true}`。代价:Toast 将无法叠在原生系统模态之上。
# Typography 文本
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/text
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(typography)/text.mdx
> 用于渲染带语义类型变体的样式化文本的排版基元组件。
## 导入
```tsx
import { Typography } from 'heroui-native';
```
## 结构
```tsx
...
{/* 子组件 */}
...
...
...
```
* **Typography**:文本根元素。通过 `type` 选择排版预设,并提供互不耦合的 `align`、`color`、`weight`、`truncate` 属性。
* **Typography.Heading**:限定为标题类型(`h1`–`h6`)的便捷包装组件,会自动添加 `accessibilityRole="header"`。
* **Typography.Paragraph**:限定为正文类型(`body`、`body-sm`、`body-xs`)的便捷包装组件。
* **Typography.Code**:以 chip 样式呈现的等宽内联文本,采用平台合适的等宽字体。
## 用法
### 基础用法
`Typography` 默认渲染正文文本。
```tsx
Hello, world!
```
### 类型变体
使用 `type` 属性选择语义化排版预设。
```tsx
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Body text
Small body text
Extra-small body text
Code snippet
```
### 标题
使用 `Typography.Heading` 渲染标题文本,自动具备 header 无障碍角色。
```tsx
Page Title
Section Title
Subsection Title
```
### 段落
使用 `Typography.Paragraph` 渲染正文文本。
```tsx
这是一个使用默认尺寸渲染的正文段落。
这是较小的正文文本。
```
### 代码
使用 `Typography.Code`(或等价的 ``)渲染内联代码片段。两者都会呈现为 chip 样式的等宽内联元素,带有低饱和背景、圆角,并采用 `self-start` 布局以避免在 flex 容器中被拉伸。平台相关的等宽 `fontFamily` 在 `Typography` 根元素上应用,因此两种写法可互换。
```tsx
console.log('hello')
console.log('hello')
```
### 对齐
使用 `align` 属性控制水平对齐。`start` 与 `end` 是 RTL 感知的(在从右到左布局下会翻转)。
```tsx
Start-aligned
Center-aligned
End-aligned
Justified text spreads across the line.
```
> **说明:** `text-justify` 在 React Native 中仅 iOS 生效;Android 会回退为左对齐。
### 颜色
使用 `color` 属性应用语义化前景色预设。
```tsx
Default foreground
Muted secondary text
```
如需其他主题色,可通过 `className` 传入对应工具类(如 `className="text-accent"`、`className="text-danger"`)。
### 字重
使用 `weight` 属性覆盖由 `type` 推导出的字重。该覆盖通过 `tailwind-merge` 合并,因此始终优先于 type 变体的默认字重。
```tsx
Bold H1
Medium body
Semibold body
```
### 截断
使用布尔属性 `truncate` 将文本限制为单行并以省略号结尾。它映射到 React Native 的 `numberOfLines={1}`。如显式提供 `numberOfLines`,则以后者为准。
```tsx
当内容溢出容器时,这一长行文本会被截断并显示省略号。
;
{
/* 通过底层 RN 属性实现多行截断 */
}
通过 React Native 标准的 `numberOfLines` 属性可实现多行截断。
;
```
## 示例
```tsx
import { Typography } from 'heroui-native';
import { View } from 'react-native';
export default function TypographyExample() {
return (
欢迎
快速开始
这是使用 Typography 组件渲染的正文段落。
用于注释或脚注的较小辅助文本。
npm install heroui-native
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/text.tsx)。
## API 参考
### Typography
`Typography` 继承 React Native 的全部 `TextProps`,并新增排版相关属性。
| prop | type | default | description |
| -------------- | -------------------------------------------------------------------------------------------- | ----------- | --------------------------------------------------------------- |
| `type` | `'h1' \| 'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6' \| 'body' \| 'body-sm' \| 'body-xs' \| 'code'` | `'body'` | 语义化排版变体(字号、默认字重、行高) |
| `align` | `'start' \| 'center' \| 'end' \| 'justify'` | `'start'` | 水平对齐方式。`start` 与 `end` 为 RTL 感知;`justify` 仅 iOS 生效 |
| `color` | `'default' \| 'muted'` | `'default'` | 语义化前景色预设 |
| `weight` | `'normal' \| 'medium' \| 'semibold' \| 'bold'` | - | 字重覆盖。设置后会覆盖 `type` 暗含的字重 |
| `truncate` | `boolean` | `false` | 将文本截断为单行并显示省略号(即设 `numberOfLines={1}`)。显式 `numberOfLines` 优先级更高 |
| `children` | `React.ReactNode` | - | 渲染内容 |
| `className` | `string` | - | 额外 CSS 类 |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部标准属性 |
### Typography.Heading
继承 `Typography` 根元素的全部属性(`align`、`color`、`weight`、`truncate`、`className` 及 React Native `TextProps`)。自动设置 `accessibilityRole="header"`,并将 `type` 收窄为标题变体。
| prop | type | default | description |
| -------------- | ---------------------------------------------- | ------- | ------------------------------ |
| `type` | `'h1' \| 'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6'` | `'h1'` | 标题级别 |
| `children` | `React.ReactNode` | - | 渲染内容 |
| `className` | `string` | - | 额外 CSS 类 |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部标准属性 |
### Typography.Paragraph
继承 `Typography` 根元素的全部属性(`align`、`color`、`weight`、`truncate`、`className` 及 React Native `TextProps`)。将 `type` 收窄为正文变体。
| prop | type | default | description |
| -------------- | ---------------------------------- | -------- | ------------------------------ |
| `type` | `'body' \| 'body-sm' \| 'body-xs'` | `'body'` | 段落文本字号 |
| `children` | `React.ReactNode` | - | 渲染内容 |
| `className` | `string` | - | 额外 CSS 类 |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部标准属性 |
### Typography.Code
继承 `Typography` 根元素的全部属性(`align`、`color`、`weight`、`truncate`、`className`、`style` 及 React Native `TextProps`)。它是一个强制 `type="code"` 的轻量包装;平台相关的等宽 `fontFamily` 在 `Typography` 根元素上合并,因此 `` 与 `` 渲染效果完全一致。
| prop | type | default | description |
| -------------- | ----------------- | ------- | ------------------------------ |
| `children` | `React.ReactNode` | - | 渲染内容 |
| `className` | `string` | - | 额外 CSS 类 |
| `...TextProps` | `TextProps` | - | 支持 React Native `Text` 的全部标准属性 |
# PressableFeedback 按压反馈
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/pressable-feedback
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(utilities)/pressable-feedback.mdx
> 为按压交互提供视觉反馈的容器组件,内置缩放动画。
## 导入
```tsx
import { PressableFeedback } from 'heroui-native';
```
## 结构
```tsx
...
```
* **PressableFeedback**:内置缩放动画的可按压容器;管理按压状态与容器尺寸,并通过上下文提供给子复合部件。使用 `PressableFeedback.Scale` 时可将 `animation={false}` 关闭根级内置缩放。
* **PressableFeedback.Scale**:对特定子元素应用缩放的包装层;需要精确控制哪个元素缩放,或要在缩放层上直接应用 `className` / `style` 时使用。
* **PressableFeedback.Highlight**:iOS 风格的高亮遮罩,绝对定位,在按压时淡入。
* **PressableFeedback.Ripple**:Android 风格的涟漪,从触点扩展的径向渐变圆。
## 用法
### 基础
默认提供按下缩放反馈,多数场景推荐直接使用。
```tsx
...
```
### 配合 Highlight
在默认缩放之外叠加 iOS 风格高亮。
```tsx
...
```
### 配合 Ripple
在默认缩放之外叠加 Android 风格涟漪。
```tsx
...
```
### 自定义缩放动画
通过根组件 `animation.scale` 配置,支持 `value`、`timingConfig`、`ignoreScaleCoefficient`。
```tsx
...
```
### 自定义 Highlight 动画
配置高亮层的不透明度与背景色。
```tsx
...
```
### 自定义 Ripple 动画
配置涟漪颜色、不透明度与时长。
```tsx
...
```
### 对指定子元素缩放(PressableFeedback.Scale)
需要对容器内某一子元素而非根节点缩放时,将根组件设为 `animation={false}` 关闭内置缩放,再使用 `PressableFeedback.Scale`,以便在缩放层上直接应用 `className` / `style`。
```tsx
...
```
可与 `Highlight` 或 `Ripple` 组合在 `Scale` 内:
```tsx
...
```
### 禁用全部动画
根上设置 `animation="disable-all"` 可级联禁用内置缩放及子复合部件(Scale、Highlight、Ripple)的动画。
```tsx
...
```
也可在保留缩放配置的同时禁用动画(例如运行时切换):
```tsx
...
```
## 示例
```tsx
import { PressableFeedback, Card, Button } from 'heroui-native';
import { Image } from 'expo-image';
import { LinearGradient } from 'expo-linear-gradient';
import { StyleSheet, View, Text } from 'react-native';
export default function PressableFeedbackExample() {
return (
Neo
家用机器人
即将开售
订阅通知
通知我
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/pressable-feedback.tsx)。
## API 参考
### PressableFeedback
| prop | type | default | description |
| ----------------------- | -------------------------------- | ------- | --------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | 需要包裹按压反馈的内容 |
| `isDisabled` | `boolean` | `false` | 是否禁用 |
| `className` | `string` | - | 额外的 class |
| `animation` | `PressableFeedbackRootAnimation` | - | 通过 `{ scale: ... }` 自定义缩放;`false` 关闭根级缩放;`'disable-all'` 级联禁用全部 |
| `isAnimatedStyleActive` | `boolean` | `true` | 根内置动画样式是否启用 |
| `asChild` | `boolean` | `false` | 是否以子元素方式渲染 |
| `...rest` | `AnimatedProps` | - | 支持 Reanimated `Animated` `Pressable` 的属性 |
#### PressableFeedbackRootAnimation
根 `animation` 遵循标准 `AnimationRoot` 控制流:
* `true` 或 `undefined`:使用默认内置缩放
* `false` 或 `"disabled"`:关闭根内置缩放(改用 `PressableFeedback.Scale` 时)
* `"disable-all"`:级联禁用全部动画(含内置缩放与子级 Scale、Highlight、Ripple)
* `object`:自定义内置缩放
| prop | type | default | description |
| ------- | ---------------------------------------- | ------- | ----------------------------- |
| `scale` | `PressableFeedbackScaleAnimation` | - | 自定义内置缩放(value、timingConfig 等) |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | 在保留配置的同时控制动画状态(例如运行时开关) |
### PressableFeedback.Scale
对容器内指定子元素应用缩放时使用;根上设 `animation={false}` 以关闭其内置缩放。
| prop | type | default | description |
| ----------------------- | --------------------------------- | ------- | --------------------------------- |
| `className` | `string` | - | 额外的 class |
| `animation` | `PressableFeedbackScaleAnimation` | - | 缩放动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `style` | `ViewStyle` | - | 额外样式 |
| `...AnimatedProps` | `AnimatedProps` | - | 支持 Reanimated `Animated.View` 的属性 |
#### PressableFeedbackScaleAnimation
缩放动画配置,可为:
* `false` 或 `"disabled"`:禁用缩放动画
* `true` 或 `undefined`:使用默认缩放动画
* `object`:自定义缩放配置
| prop | type | default | description |
| ------------------------ | ----------------------- | ---------------------------------------------------- | ----------------------------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时禁用动画 |
| `value` | `number` | `0.985` | 按下时的缩放值(会随容器宽度自动调整) |
| `timingConfig` | `WithTimingConfig` | `{ duration: 300, easing: Easing.out(Easing.ease) }` | 时间曲线配置 |
| `ignoreScaleCoefficient` | `boolean` | `false` | 为 true 时忽略自动缩放系数,直接使用 `value` |
### PressableFeedback.Highlight
| prop | type | default | description |
| ----------------------- | ------------------------------------- | ------- | --------------------------------- |
| `className` | `string` | - | 额外的 class |
| `animation` | `PressableFeedbackHighlightAnimation` | - | 高亮层动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `style` | `ViewStyle` | - | 额外样式 |
| `...AnimatedProps` | `AnimatedProps` | - | 支持 Reanimated `Animated.View` 的属性 |
#### PressableFeedbackHighlightAnimation
高亮层动画配置,可为:
* `false` 或 `"disabled"`:禁用高亮动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ----------------------- | ----------------------- | ------------------- | --------------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时禁用动画 |
| `opacity.value` | `[number, number]` | `[0, 0.1]` | 不透明度 \[未按下, 按下] |
| `opacity.timingConfig` | `WithTimingConfig` | `{ duration: 200 }` | 时间曲线配置 |
| `backgroundColor.value` | `string` | 随主题灰色 | 高亮层背景色 |
### PressableFeedback.Ripple
| prop | type | default | description |
| ----------------------- | ----------------------------------------- | ------- | --------------------------- |
| `className` | `string` | - | 容器插槽的 class |
| `classNames` | `ElementSlots` | - | 各插槽 class(container、ripple) |
| `styles` | `Partial>` | - | 涟漪遮罩各部分的样式 |
| `animation` | `PressableFeedbackRippleAnimation` | - | 涟漪动画配置 |
| `isAnimatedStyleActive` | `boolean` | `true` | 是否启用 Reanimated 动画样式 |
| `...ViewProps` | `Omit` | - | 支持 `View` 属性(不含 `style`) |
#### `styles`
| prop | type | description |
| ----------- | ----------- | ----------- |
| `container` | `ViewStyle` | 容器插槽样式 |
| `ripple` | `ViewStyle` | 涟漪插槽样式 |
#### PressableFeedbackRippleAnimation
涟漪动画配置,可为:
* `false` 或 `"disabled"`:禁用涟漪动画
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| ------------------------------------ | -------------------------- | ------------------- | ------------------------- |
| `state` | `'disabled' \| boolean` | - | 在自定义属性时禁用动画 |
| `backgroundColor.value` | `string` | 随主题计算 | 涟漪背景色 |
| `progress.baseDuration` | `number` | `1000` | 涟漪进度基准时长(会按对角线自动调整) |
| `progress.minBaseDuration` | `number` | `750` | 进度动画最小时长 |
| `progress.ignoreDurationCoefficient` | `boolean` | `false` | 为 true 时忽略自动时长系数,直接使用基准时长 |
| `opacity.value` | `[number, number, number]` | `[0, 0.1, 0]` | 不透明度 \[起始, 峰值, 结束] |
| `opacity.timingConfig` | `WithTimingConfig` | `{ duration: 200 }` | 时间曲线配置 |
| `scale.value` | `[number, number, number]` | `[0, 1, 1]` | 缩放 \[起始, 峰值, 结束] |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 200 }` | 时间曲线配置 |
#### `ElementSlots`
涟漪各插槽的额外 class:
| slot | description |
| ----------- | ----------------------------------------------------------------------- |
| `container` | 外层容器(`absolute inset-0`),可通过 class 完全定制样式 |
| `ripple` | 内层涟漪(`absolute top-0 left-0 rounded-full`),带动画属性,不宜用 className 覆盖动画相关表现 |
# ScrollShadow 滚动阴影
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/components/scroll-shadow
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/components/(utilities)/scroll-shadow.mdx
> 根据滚动位置与溢出情况,为可滚动内容添加动态渐变边缘阴影。
## 导入
```tsx
import { ScrollShadow } from 'heroui-native';
```
## 结构
```tsx
...
```
* **ScrollShadow**:包裹可滚动组件,按滚动位置与内容溢出在边缘显示动态渐变阴影;自动识别横向/纵向滚动并管理阴影显隐。
* **LinearGradientComponent**:必填,传入兼容库的 `LinearGradient`(如 expo-linear-gradient、react-native-linear-gradient)以绘制渐变阴影。
## 用法
### 基础用法
包裹任意可滚动组件,自动在边缘添加阴影。
```tsx
...
```
### 横向滚动
根据子组件的 `horizontal` 属性自动识别横向滚动。
```tsx
```
### 自定义阴影尺寸
用 `size` 控制渐变阴影的高度或宽度(像素)。
```tsx
...
```
### 显隐控制
用 `visibility` 指定显示哪些边的阴影。
```tsx
...
...
...
```
### 自定义阴影颜色
覆盖默认使用主题背景的阴影颜色。
```tsx
...
```
### 自定义滚动处理
**重要:** ScrollShadow 内部会将子节点转为 Reanimated 动画组件。若需使用 `onScroll`,必须使用 `react-native-reanimated` 的 `useAnimatedScrollHandler`,而不能使用普通的 `onScroll`。
```tsx
import { LinearGradient } from 'expo-linear-gradient';
import Animated, { useAnimatedScrollHandler } from 'react-native-reanimated';
const scrollHandler = useAnimatedScrollHandler({
onScroll: (event) => {
console.log(event.contentOffset.y);
},
});
...
;
```
## 示例
```tsx
import { ScrollShadow, Surface } from 'heroui-native';
import { LinearGradient } from 'expo-linear-gradient';
import { FlatList, ScrollView, Text, View } from 'react-native';
export default function ScrollShadowExample() {
const horizontalData = Array.from({ length: 10 }, (_, i) => ({
id: i,
title: `Card ${i + 1}`,
}));
return (
Horizontal List
(
{item.title}
)}
showsHorizontalScrollIndicator={false}
contentContainerClassName="p-5 gap-4"
/>
Vertical Content
Long Content
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.
Sed ut perspiciatis unde omnis iste natus error sit voluptatem
accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
quae ab illo inventore veritatis et quasi architecto beatae vitae.
);
}
```
更多示例见 [GitHub 仓库](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/scroll-shadow.tsx)。
## API 参考
### ScrollShadow
| prop | type | default | description |
| ------------------------- | ---------------------------------------------------------------------- | -------- | ------------------------------------------------- |
| `children` | `React.ReactElement` | - | 需要增强阴影的可滚动组件,须为单一 React 元素(ScrollView、FlatList 等) |
| `LinearGradientComponent` | `ComponentType<` `LinearGradientProps>` | **必填** | 来自任意兼容库的 LinearGradient 组件 |
| `size` | `number` | `50` | 渐变阴影高度或宽度(像素) |
| `orientation` | `'horizontal' \| 'vertical'` | 自动检测 | 阴影方向;未提供时根据子组件 `horizontal` 自动检测 |
| `visibility` | `'auto' \| 'top' \| 'bottom' \| 'left' \| 'right' \| 'both' \| 'none'` | `'auto'` | 阴影显隐模式;`auto` 根据滚动位置与溢出自动显示 |
| `color` | `string` | 主题色 | 渐变阴影自定义颜色;未提供时使用主题背景色 |
| `isEnabled` | `boolean` | `true` | 是否启用阴影效果 |
| `animation` | `ScrollShadowRootAnimation` | - | 动画配置 |
| `className` | `string` | - | 容器额外 class |
| `...ViewProps` | `ViewProps` | - | 支持 React Native `View` 的全部标准属性 |
#### ScrollShadowRootAnimation
ScrollShadow 动画配置,可为:
* `false` 或 `"disabled"`:仅关闭根动画
* `"disable-all"`:关闭所有动画(含子级)
* `true` 或 `undefined`:使用默认动画
* `object`:自定义动画配置
| prop | type | default | description |
| --------------- | ---------------------------------------- | -------- | --------------------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | 关闭动画的同时仍允许自定义属性 |
| `opacity.value` | `[number, number]` | `[0, 1]` | 不透明度 \[初始, 激活];底部/右侧阴影时顺序相反 |
### LinearGradientProps
`LinearGradientComponent` 应接受以下属性:
| prop | type | description |
| ----------- | -------------------------- | ----------------------- |
| `colors` | `any` | 渐变颜色数组 |
| `locations` | `any`(可选) | 各颜色停靠位置 |
| `start` | `any`(可选) | 渐变起点,如 `{ x: 0, y: 0 }` |
| `end` | `any`(可选) | 渐变终点,如 `{ x: 1, y: 0 }` |
| `style` | `StyleProp`(可选) | 应用于渐变视图的样式 |
## 特别说明
**重要:** ScrollShadow 内部会将子节点转为 Reanimated 动画组件。若需在可滚动组件上使用滚动回调,必须使用 `react-native-reanimated` 的 `useAnimatedScrollHandler`,不能使用标准 `onScroll`。
# 动画
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/getting-started/animation
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/getting-started/(handbook)/animation.mdx
> 为 HeroUI Native 组件添加流畅动画与过渡
HeroUI Native 的动画基于 [react-native-reanimated](https://docs.swmansion.com/react-native-reanimated/),手势由 [react-native-gesture-handler](https://docs.swmansion.com/react-native-gesture-handler/) 处理。若需更细粒度控制,建议先熟悉二者。
## `animation` 属性
每个带动画的组件都暴露统一的 `animation` 属性,用于控制该组件上的全部动画:可调整数值、时间配置、布局动画,或完全关闭动画。
**做法:** 处理动画时,先查看目标组件是否提供 `animation` 属性。
## 修改动画
向 `animation` 传入对象即可定制。不同组件暴露的可调属性不同。若只想微调内置动画,可使用我们提供的各项配置;若要完全自定义动画逻辑,通常需要自行编写带动画的自定义组件。
### 示例 1:简单数值调整
调整缩放、透明度或颜色等数值:
```tsx
import {Switch} from 'heroui-native';
;
```
### 示例 2:时间曲线配置
自定义时长与缓动:
```tsx
import {Accordion} from 'heroui-native';
;
```
### 示例 3:布局动画(进入 / 退出)
使用 Reanimated 的布局动画 API:
```tsx
import {Accordion} from 'heroui-native';
import {FadeInRight, FadeInLeft, ZoomIn} from 'react-native-reanimated';
import {Easing} from 'react-native-reanimated';
Content here
;
```
### 示例 4:使用 `state` 精细控制
`state` 可在关闭动画的同时仍保留属性配置,便于微调而不真正播放动画:
```tsx
import {Switch} from 'heroui-native';
```
`state` 可取:
* `'disabled'`:关闭动画,但仍可配置属性值
* `'disable-all'`:关闭自身及子级全部动画(仅在根级可用)
* `boolean`:简单开关(`true` 启用,`false` 禁用)
这样既能精细控制动画行为,又能在不启用动画的情况下自定义属性值。
## 关闭动画
可通过 `animation` 在不同层级关闭动画。
### 关闭选项
* `animation={false}` 或 `animation="disabled"`:仅关闭当前组件动画
* `animation="disable-all"`:关闭根级及其子级全部动画(仅根级可用)
* `animation={true}` 或 `animation={undefined}`:使用默认动画
### 组件级关闭
仅关闭某个子部件的动画:
```tsx
```
### 根级 `disable-all`
`"disable-all"` 仅在复合组件根级可用,会向下级联,**包括**树中的独立组件(如 `Button`、`Spinner` 等),不仅限于复合子部件:
```tsx
// Disables all animations including Button components inside Card
$450
Living room Sofa
Buy now
Add to cart
```
**注意:** `"disable-all"` 会级联到所有子组件,包括独立的 `Button`、`Spinner` 等。
## 全局动画配置
在 `HeroUINativeProvider` 上统一关闭应用内全部 HeroUI Native 动画:
```tsx
import {HeroUINativeProvider} from 'heroui-native';
;
```
这会覆盖各组件自身的 `animation` 设置,全局禁用动画。
## 无障碍
系统「减少动态效果」会在底层自动处理:当用户开启该无障碍选项时,库会通过 `GlobalAnimationSettingsProvider` 与 Reanimated 的 `useReducedMotion()` 全局关闭动画。
你通常无需额外编码,库会尊重系统无障碍偏好。
## 动画状态管理
内部会统一管理禁用态,避免关闭动画后出现卡顿或跳变:禁用时组件会直接落到终态,而不是播放到一半。
## 子级渲染函数
许多组件支持将 `children` 设为函数,便于根据 `isSelected` 等状态渲染:
```tsx
import {Switch} from 'heroui-native';
{({isSelected, isDisabled}) => (
{isSelected ? : }
)}
;
```
该模式便于根据选中、禁用等状态构建动态界面。
## 下一步
* [样式](/docs/native/getting-started/styling)
* [主题](/docs/native/getting-started/theming)
* [颜色](/docs/native/getting-started/colors)
# 颜色
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/getting-started/colors
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/getting-started/(handbook)/colors.mdx
> HeroUI Native 的调色板与主题系统
import {ColorSectionSideBySide, ColorSectionStacked, ColorSectionFormField, ColorSectionPrimitive} from "@/components/color-section";
HeroUI Native 的颜色体系围绕语义意图构建,而非堆砌视觉色板。系统不会暴露庞大的原始色表,而是定义一小套有意义的色彩角色,覆盖绝大多数界面需求。
系统中的多数颜色会由少量基础值自动派生。这样 HeroUI 能在保持对比度、层级与主题行为一致的同时,让整套体系易于理解与修改。
颜色应首先传达用途与状态;视觉变化来自尺度、强调与上下文。
## 强调色
强调色代表品牌或产品的主识别色,用于吸引对关键操作、高亮与重点时刻的注意。
强调色应有意识地节制使用。滥用会削弱其冲击力,并破坏视觉层级。多数情况下,组件会从基础强调色自动派生悬停、柔和背景与聚焦等相关取值。
## 默认(中性色)
默认色构成系统的中性骨架,用于大多数非强调的界面元素。
## 成功
成功色传达积极结果、确认与完成状态,常见于反馈组件、状态指示与校验通过等场景。
## 警告
警告色表示需谨慎、存在风险,或需要留意但非破坏性的操作,常用于提示、消息以及用户应暂停或复核信息的过渡状态。
## 危险
危险色表示破坏性、不可逆或关键的操作与状态,应一眼可辨,并稳定用于错误、危险按钮与严重告警。
## 前景色
前景色用于正文级内容,如文字与图标。这些颜色针对可读性与无障碍优化,并会随背景与表面上下文自动适配。请勿在组件内硬编码前景色。
## 背景色
背景色定义界面的基底画布,在保持视觉克制的前提下建立整体对比与氛围。
## 表面色
表面色叠在背景之上,用于卡片、面板、模态与下拉等容器。表面通过抬升、对比与分层形成区隔与层级,而非依赖强烈的色相跳跃。
## 表单字段
表单字段色是面向输入、控件与可交互字段的专用令牌,覆盖默认、聚焦与悬停等多种状态。将其独立出来,可让表单元素在视觉上与按钮及界面其余部分保持清晰区分。
## 分隔线
分隔线色用于分割线、描边与轻量边界,用来组织内容、引导视线而不增加噪点。分隔线色应保持低对比、不抢眼。
## 其他
其他颜色在界面中承担特定工具性角色,用于组织内容、引导视线而不增加噪点。
## 基础色
基础色是与模式无关的底层取值,作为语义色令牌的根基,在明暗主题之间不会改变。
## 如何使用颜色
**在组件中:**
```tsx
import { View, Text } from 'react-native';
内容
点击我
;
```
**在 CSS 文件中:**
```css title="global.css"
/* 直接使用 CSS 变量 */
.container {
flex: 1;
background-color: var(--accent);
width: 50px;
height: 50px;
border-radius: var(--radius);
}
```
## 默认主题
完整主题定义见仓库中的 [variables.css](https://github.com/heroui-inc/heroui-native/blob/main/src/styles/variables.css)。该主题通过 [Uniwind 主题系统](https://docs.uniwind.dev/theming/basics) 在明暗模式间自动切换,并支持跟随系统与在代码中切换主题。
```css
@theme {
/* 基础色(在明暗模式间保持不变) */
--white: oklch(100% 0 0);
--black: oklch(0% 0 0);
--snow: oklch(0.9911 0 0);
--eclipse: oklch(0.2103 0.0059 285.89);
/* 边框 */
--border-width: 1px;
--field-border-width: 0px;
/* 基础圆角 */
--radius: 0.5rem;
--field-radius: calc(var(--radius) * 1.5);
/* 不透明度 */
--opacity-disabled: 0.5;
}
@layer theme {
:root {
@variant light {
/* 基础颜色 */
--background: oklch(0.9702 0 0);
--foreground: var(--eclipse);
/* 表面 */
--surface: var(--white);
--surface-foreground: var(--foreground);
--surface-secondary: oklch(0.9524 0.0013 286.37);
--surface-secondary-foreground: var(--foreground);
--surface-tertiary: oklch(0.9373 0.0013 286.37);
--surface-tertiary-foreground: var(--foreground);
/* 覆盖层 */
--overlay: var(--white);
--overlay-foreground: var(--foreground);
--backdrop: oklch(0% 0 0 / 20%);
--muted: oklch(0.5517 0.0138 285.94);
--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(90% 0.004 286.32);
--separator: oklch(74% 0.004 286.32);
--focus: var(--accent);
--link: var(--foreground);
}
@variant dark {
/* 基础颜色 */
--background: oklch(12% 0.005 285.823);
--foreground: var(--snow);
/* 表面 */
--surface: oklch(0.2103 0.0059 285.89);
--surface-foreground: var(--foreground);
--surface-secondary: oklch(0.257 0.0037 286.14);
--surface-secondary-foreground: var(--foreground);
--surface-tertiary: oklch(0.2721 0.0024 247.91);
--surface-tertiary-foreground: var(--foreground);
/* 覆盖层 */
--overlay: oklch(0.2103 0.0059 285.89);
--overlay-foreground: var(--foreground);
--backdrop: oklch(0% 0 0 / 20%);
--muted: oklch(70.5% 0.015 286.067);
--default: oklch(27.4% 0.006 286.033);
--default-foreground: var(--snow);
--accent: oklch(0.6204 0.195 253.83);
--accent-foreground: var(--snow);
/* 表单字段 */
--field-background: oklch(0.2103 0.0059 285.89);
--field-foreground: var(--foreground);
--field-placeholder: var(--muted);
--field-border: transparent;
/* 状态色 */
--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);
/* 组件颜色 */
--segment: oklch(0.3964 0.01 285.93);
--segment-foreground: var(--foreground);
/* 杂项颜色 */
--border: oklch(28% 0.006 286.033);
--separator: oklch(40% 0.006 286.033);
--focus: var(--accent);
--link: var(--foreground);
}
}
}
```
## 自定义颜色
**覆盖已有颜色:**
```css
@layer theme {
@variant light {
/* 覆盖默认颜色 */
--accent: oklch(0.65 0.25 270); /* 自定义靛蓝强调色 */
--success: oklch(0.65 0.15 155);
}
@variant dark {
/* 覆盖深色主题颜色 */
--accent: oklch(0.65 0.25 270);
--success: oklch(0.75 0.12 155);
}
}
```
**提示:** 可在 [oklch.com](https://oklch.com) 转换颜色。
**添加自定义颜色:**
```css
@layer theme {
@variant light {
--info: oklch(0.6 0.15 210);
--info-foreground: oklch(0.98 0 0);
}
@variant dark {
--info: oklch(0.7 0.12 210);
--info-foreground: oklch(0.15 0 0);
}
}
@theme inline {
--color-info: var(--info);
--color-info-foreground: var(--info-foreground);
}
```
随后即可使用:
```tsx
import { View, Text } from 'react-native';
提示信息
;
```
> **注意:** 若要进一步了解主题变量及其在 Tailwind CSS v4 中的行为,请参阅 [Tailwind CSS 主题文档](https://tailwindcss.com/docs/theme)。
## useThemeColor 钩子
`useThemeColor` 钩子已增强,支持一次选取多种颜色,在复杂主题场景下更灵活。
**一次选取多种颜色:**
现在可以同时选取多种颜色,在需要一并处理相关色值时很有用:
```tsx
import { useThemeColor } from 'heroui-native';
// 一次选取多种颜色
const [accent, accentForeground, success, danger] = useThemeColor([
'accent',
'accentForeground',
'success',
'danger',
]);
// 使用所选颜色
强调色文字
;
```
该改进在需要同时选取并应用多种颜色时,可提升性能,也便于管理复杂的主题组合。
# 组合
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/getting-started/composition
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/getting-started/(handbook)/composition.mdx
> 用组件组合模式搭建灵活 UI
HeroUI Native 通过组合模式实现灵活、可定制的组件:可更换实际渲染元素、将多个部件拼在一起,并完全掌控结构。
## 复合组件
HeroUI Native 采用点记法的复合组件——子组件作为属性导出(如 `Button.Label`、`Dialog.Trigger`、`Accordion.Item`),共同构成完整界面。
```tsx
import { Button, Dialog } from 'heroui-native';
function DialogExample() {
return (
打开对话框
对话框标题
对话框说明
);
}
```
## asChild 属性
`asChild` 用于改变组件实际渲染的元素。为 `true` 时,HeroUI Native 会克隆子元素并合并属性,而不是渲染默认包裹节点。
```tsx
import { Button, Dialog } from 'heroui-native';
function DialogExample() {
return (
{/* asChild:Button 直接作为触发器,无额外包裹 */}
打开对话框
{/* Dialog.Close 也可使用 asChild */}
取消
对话框标题
对话框说明
);
}
```
## 自定义组件
通过组合 HeroUI Native 原语封装自己的组件:
```tsx
import { Button, Card, Popover } from 'heroui-native';
import { View } from 'react-native';
// 商品卡片
function ProductCard({ title, description, price, onBuy, ...props }) {
return (
{price}
{title}
{description}
立即购买
);
}
// 带 Popover 的按钮
function PopoverButton({ children, popoverContent, ...props }) {
return (
{children}
{popoverContent}
);
}
// 用法
console.log('购买')}
/>
说明
更多详情见此处
}>
显示说明
```
## 自定义变体
可用 `tailwind-variants` 扩展样式。**文字颜色类须加在 `Button.Label` 上,而不是父级 `Button`:**
```tsx
import { Button } from 'heroui-native';
import type { ButtonRootProps } from 'heroui-native';
import { tv, type VariantProps } from 'tailwind-variants';
const customButtonVariants = tv({
base: 'font-semibold rounded-lg',
variants: {
intent: {
primary: 'bg-blue-500',
secondary: 'bg-gray-200',
danger: 'bg-red-500',
},
},
defaultVariants: {
intent: 'primary',
},
});
const customLabelVariants = tv({
base: '',
variants: {
intent: {
primary: 'text-white',
secondary: 'text-gray-800',
danger: 'text-white',
},
},
defaultVariants: {
intent: 'primary',
},
});
type CustomButtonVariants = VariantProps;
interface CustomButtonProps
extends Omit,
CustomButtonVariants {
className?: string;
labelClassName?: string;
}
export function CustomButton({
intent,
className,
labelClassName,
children,
...props
}: CustomButtonProps) {
return (
{children}
);
}
```
## 下一步
* 了解 [样式](/docs/native/getting-started/styling) 体系
* 阅读 [主题](/docs/native/getting-started/theming) 文档
* 探索 [动画](/docs/native/getting-started/animation) 选项
# Portal
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/getting-started/portal
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/getting-started/(handbook)/portal.mdx
Portal 可将子节点渲染到应用中的其他位置,特别适用于需要浮在其他内容之上的组件,例如模态框、覆盖层与弹出层。
## 默认配置
默认情况下,`PortalHost` 已包含在 `HeroUINativeProvider` 中,无需手动添加。Provider 会自动为所有使用 Portal 的组件设置好Portal系统。
## 进阶用法
如需自定义Portal实现,可直接从 `heroui-native` 导入 `Portal` 与 `PortalHost`:
```tsx
import { Portal, PortalHost } from "heroui-native";
import { View, Text } from "react-native";
function AppLayout() {
return (
Header Content
Main Content Area
{/* Portal host positioned at the top of the screen */}
);
}
function CustomNotification() {
return (
This notification appears at the top via Portal
);
}
```
本例中,`CustomNotification` 组件通过 `Portal` 将内容渲染到位于屏幕顶部的 `PortalHost` 处,使通知浮于所有其他内容之上,无论它在组件树中的实际定义位置在哪。
## 状态管理注意事项
父组件中的状态变化可能导致Portal内组件出现意外问题。例如,将文本输入框直接放入Portal内时,若父组件触发重渲染,可能会重置输入框的自动建议,或导致其他界面异常。
为避免该问题,请将交互组件(如输入框)的状态保留在Portal内部:把Portal内容拆分为独立组件,从而隔离父组件重渲染带来的影响。
### 示例模式
```tsx
// ❌ 问题:父级状态导致重渲染,影响Portal内内容
function ParentComponent() {
const [dialogOpen, setDialogOpen] = useState(false);
const [inputValue, setInputValue] = useState(""); // State in parent
return (
Open Dialog
);
}
// ✅ 正确:在Portal内的独立组件中管理状态
function ParentComponent() {
const [dialogOpen, setDialogOpen] = useState(false);
return (
Open Dialog
setDialogOpen(false)}
// Form state isolated from parent
/>
);
}
function DialogFormContent({ onClose }: { onClose: () => void }) {
const [inputValue, setInputValue] = useState(""); // State inside portal
const [error, setError] = useState("");
return (
{error}
Close
);
}
```
在正确示例中,`DialogFormContent` 独立于父组件管理自身状态。这样即便父组件因 `dialogOpen` 等变化而重渲染,也不会影响输入框的内部状态,从而保留自动建议等输入行为。
## API 参考
### PortalHost
默认情况下,所有 `Portal` 组件的子内容都会作为该 `PortalHost` 的子节点进行渲染。
| prop | type | description |
| ---- | -------- | ---------------- |
| name | `string` | 作为自定义宿主使用时提供(可选) |
### Portal
| prop | type | description |
| -------- | ----------------- | -------------------------- |
| name\* | `string` | 唯一值;同名 Portal 会替换原有 Portal |
| hostName | `string` | 子内容需要渲染到自定义宿主时提供(可选) |
| children | `React.ReactNode` | 要渲染到Portal中的内容 |
\* 必填属性
## 相关链接
* [快速开始](/docs/native/getting-started/quick-start) — 基础搭建指南
* 查阅 [Provider](/docs/native/getting-started/provider) 文档
# Provider
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/getting-started/provider
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/getting-started/(handbook)/provider.mdx
> 配置 HeroUI Native 的文本、动画与 Toast 等全局能力
`HeroUINativeProvider` 是根级 Provider,用于在 React Native 应用中初始化并配置 HeroUI Native,提供全局配置与Portal管理。
## 概览
Provider 作为 HeroUI Native 的主入口,为应用包裹必要的上下文与配置:
* **安全区内边距**:通过 `SafeAreaListener` 自动同步安全区变化,并写入 Uniwind,便于在 Tailwind 中使用(如 `pb-safe-offset-3`)
* **文本配置**:全局 Text 相关设置,保证各 HeroUI 组件文本表现一致
* **动画配置**:全局控制是否关闭应用内全部动画
* **Toast 配置**:全局 Toast 系统(边距、默认属性、内容包裹层等)
* **Portal管理**:处理叠层、模态等需要浮在应用层级之上的组件
## 基础用法
在应用根节点使用 Provider:
```tsx
import { HeroUINativeProvider } from 'heroui-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
export default function App() {
return (
{/* Your app content */}
);
}
```
## 配置项
Provider 接受 `config` 属性,常用分组如下。
### 文本组件配置
面向 HeroUI Native 内所有 Text 的全局设置。仅包含适合「全局统一」调整的属性:
```tsx
import { HeroUINativeProvider } from 'heroui-native';
import type { HeroUINativeConfig } from 'heroui-native';
const config: HeroUINativeConfig = {
textProps: {
// Disable font scaling for accessibility
allowFontScaling: false,
// Auto-adjust font size to fit container
adjustsFontSizeToFit: false,
// Maximum font size multiplier when scaling
maxFontSizeMultiplier: 1.5,
// Minimum font scale (iOS only, 0.01-1.0)
minimumFontScale: 0.5,
},
};
export default function App() {
return (
{/* Your app content */}
);
}
```
### 动画配置
应用级动画开关:
```tsx
const config: HeroUINativeConfig = {
// Disable all animations across the application (cascades to all children)
animation: 'disable-all',
};
```
**说明:** 设为 `'disable-all'` 后,应用内全部动画将被关闭,可用于无障碍或性能优化场景。
### 开发者信息配置
控制开发环境下控制台中的提示信息:
```tsx
const config: HeroUINativeConfig = {
devInfo: {
// Disable styling principles information message
stylingPrinciples: false,
},
};
```
**说明:** 默认会输出信息类日志。将 `stylingPrinciples: false` 可关闭开发时关于样式原则的提示。
### Toast 配置
配置全局 Toast(边距、默认属性、包裹层等),也可完全关闭 Toast:
**方式一:关闭 Toast Provider**
```tsx
const config: HeroUINativeConfig = {
// Disable toast provider entirely
toast: false,
// or
toast: 'disabled',
};
```
**说明:** 当 `toast` 为 `false` 或 `'disabled'` 时,不会渲染 `ToastProvider`,应用内无法使用 Toast。
**方式二:配置 Toast Provider**
```tsx
import { KeyboardAvoidingView } from 'react-native';
const config: HeroUINativeConfig = {
toast: {
// Global toast configuration (used as defaults for all toasts)
defaultProps: {
variant: 'default',
placement: 'top',
isSwipeable: true,
animation: true,
},
// Insets for spacing from screen edges (added to safe area insets)
insets: {
top: 0, // Default: iOS = 0, Android = 12
bottom: 6, // Default: iOS = 6, Android = 12
left: 12, // Default: 12
right: 12, // Default: 12
},
// Maximum number of visible toasts before opacity starts fading
maxVisibleToasts: 3,
// Custom wrapper function to wrap the toast content
contentWrapper: (children) => (
{children}
),
},
};
```
## 完整示例
综合展示各项配置:
```tsx
import { HeroUINativeProvider } from 'heroui-native';
import type { HeroUINativeConfig } from 'heroui-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
const config: HeroUINativeConfig = {
// Global text configuration
textProps: {
minimumFontScale: 0.5,
maxFontSizeMultiplier: 1.5,
allowFontScaling: true,
adjustsFontSizeToFit: false,
},
// Global animation configuration
animation: 'disable-all', // Optional: disable all animations
// Developer information messages configuration
devInfo: {
stylingPrinciples: true, // Optional: disable styling principles message
},
// Global toast configuration
// Option 1: Configure toast with custom settings
toast: {
defaultProps: {
variant: 'default',
placement: 'top',
},
insets: {
top: 0,
bottom: 6,
left: 12,
right: 12,
},
maxVisibleToasts: 3,
},
// Option 2: Disable toast entirely
// toast: false,
// or
// toast: 'disabled',
};
export default function App() {
return (
);
}
```
## 与 Expo Router 集成
使用 Expo Router 时,在根布局中包裹:
```tsx
// app/_layout.tsx
import { HeroUINativeProvider } from 'heroui-native';
import type { HeroUINativeConfig } from 'heroui-native';
import { Stack } from 'expo-router';
const config: HeroUINativeConfig = {
textProps: {
minimumFontScale: 0.5,
maxFontSizeMultiplier: 1.5,
},
};
export default function RootLayout() {
return (
);
}
```
## 架构
### Provider 层级
`HeroUINativeProvider` 内部由多层 Provider 组合而成:
```
HeroUINativeProvider
├── SafeAreaListener (handles safe area insets updates)
│ └── GlobalAnimationSettingsProvider (animation configuration)
│ └── TextComponentProvider (text configuration)
│ └── ToastProvider (toast configuration, conditionally rendered)
│ └── Your App
│ └── PortalHost (for overlays)
```
**说明:** `ToastProvider` 是否渲染取决于 `toast` 配置。当 `toast` 为 `false` 或 `'disabled'` 时,不会渲染 `ToastProvider`,应用内容与 `PortalHost` 将直接挂在 `TextComponentProvider` 之下。
### 安全区内边距处理
Provider 会自动使用 `react-native-safe-area-context` 的 [`SafeAreaListener`](https://appandflow.github.io/react-native-safe-area-context/api/safe-area-listener) 包裹应用。该组件监听安全区与帧的变化但不会触发重渲染,并通过 `onChange` 回调将最新的 insets 同步给 Uniwind。
## 轻量 Provider(Raw)
`HeroUINativeProviderRaw` 是面向包体优化的精简版,不包含 `ToastProvider` 与 `PortalHost`,仅保留最小起点,按需自行组合能力。
### 何时使用
当你希望精确控制打包进应用的依赖时,可使用 Raw Provider。从 `heroui-native/provider-raw` 导入后,下列依赖仅在用到对应组件时才需要:
* **react-native-screens** — 浮层类组件(Popover、Dialog)需要
* **@gorhom/bottom-sheet** — BottomSheet 需要
* **react-native-svg** — 使用图标的组件(Accordion、Alert、Checkbox 等)需要
### 接入
```tsx
import {
HeroUINativeProviderRaw,
type HeroUINativeConfigRaw,
} from 'heroui-native/provider-raw';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
const config: HeroUINativeConfigRaw = {
textProps: {
maxFontSizeMultiplier: 1.5,
},
};
export default function App() {
return (
{/* Your app content */}
);
}
```
### 手动添加 Toast 与 Portal
使用 Raw Provider 且仍需要 Toast 或 Portal 时,请自行组合:
```tsx
import { HeroUINativeProviderRaw } from 'heroui-native/provider-raw';
import { PortalHost } from 'heroui-native/portal';
import { ToastProvider } from 'heroui-native/toast';
export default function App() {
return (
{/* Your app content */}
);
}
```
### Raw 的层级
```
HeroUINativeProviderRaw
├── SafeAreaListener (handles safe area insets updates)
│ └── GlobalAnimationSettingsProvider (animation configuration)
│ └── TextComponentProvider (text configuration)
│ └── Your App
```
## 最佳实践
### 1. 单一 Provider 实例
始终在应用根使用**一个** `HeroUINativeProvider`,不要嵌套多个:
```tsx
// ❌ Bad
{/* Don't do this */}
// ✅ Good
```
### 2. 配置对象外置
将 `config` 定义在组件外,避免每次渲染重新创建:
```tsx
// ❌ Bad
function App() {
return (
{/* ... */}
);
}
// ✅ Good
const config: HeroUINativeConfig = {
textProps: {
maxFontSizeMultiplier: 1.5,
},
};
function App() {
return (
{/* ... */}
);
}
```
### 3. 文本与无障碍
配置文本属性时请兼顾无障碍,例如允许系统字体缩放但限制上限:
```tsx
const config: HeroUINativeConfig = {
textProps: {
// Allow font scaling for accessibility
allowFontScaling: true,
// But limit maximum scale
maxFontSizeMultiplier: 1.5,
},
};
```
## TypeScript 支持
Provider 具备完整类型,可导入类型以获得更好的 IDE 体验:
```tsx
import { HeroUINativeProvider, type HeroUINativeConfig } from 'heroui-native';
const config: HeroUINativeConfig = {
// Full type safety and autocomplete
textProps: {
allowFontScaling: true,
maxFontSizeMultiplier: 1.5,
},
animation: 'disable-all', // Optional: disable all animations
devInfo: {
stylingPrinciples: true, // Optional: disable styling principles message
},
// Toast configuration options:
// - false or 'disabled': Disable toast provider
// - ToastProviderProps object: Configure toast settings
toast: {
defaultProps: {
variant: 'default',
placement: 'top',
},
insets: {
top: 0,
bottom: 6,
left: 12,
right: 12,
},
},
};
```
## 相关链接
* [快速开始](/docs/native/getting-started/quick-start) — 基础搭建指南
* [主题](/docs/native/getting-started/theming) — 颜色与主题定制
* [样式](/docs/native/getting-started/styling) — 使用 Tailwind 编写样式
# 样式
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/getting-started/styling
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/getting-started/(handbook)/styling.mdx
> 使用 Tailwind 或 StyleSheet API 为 HeroUI Native 组件编写样式
HeroUI Native 提供灵活的样式方案:Tailwind CSS 工具类、StyleSheet API,以及用于动态样式的 render props。
## 样式原则
HeroUI Native 以 `className` 作为主要样式入口,所有组件均可通过 `className` 使用 Tailwind 类。
**StyleSheet 优先级:** 同时传入 `style`(StyleSheet)与 `className` 时,`style` 优先级更高,可在需要时覆盖 Tailwind。
**动画样式:** 部分样式属性由 `react-native-reanimated` 驱动,与 StyleSheet 类似,其优先级也高于 `className`。要确认哪些属性受动画占用、无法仅靠 `className` 设置:
* **在 IDE 中悬停 `className`** — TypeScript 定义会提示可用属性
* **查阅组件文档** — 组件页顶部通常有样式源码链接,其中会标注动画相关限制
**自定义动画样式:** 若某属性被动画占用,可在支持 `animation` 属性的组件上通过 `animation` 进行调整。
## 基础样式
**使用 className:** 所有 HeroUI Native 组件都支持 `className`:
```tsx
import { Button } from 'heroui-native';
Custom Button
;
```
**使用 style:** 也可通过 `style` 传入内联样式:
```tsx
import { Button } from 'heroui-native';
Styled Button
;
```
## Render Props
使用渲染函数读取组件状态并动态定制内容:
```tsx
import { RadioGroup, Label, cn } from 'heroui-native';
{({ isSelected, isInvalid, isDisabled }) => (
<>
Option 1
{isSelected && }
>
)}
;
```
## 封装可复用组件
结合 [tailwind-variants](https://tailwind-variants.org/)(Tailwind 优先的变体 API)封装自定义组件:
```tsx
import { Button } from 'heroui-native';
import type { ButtonRootProps } from 'heroui-native';
import { tv, type VariantProps } from 'tailwind-variants';
const customButtonVariants = tv({
base: 'font-semibold rounded-lg',
variants: {
intent: {
primary: 'bg-blue-500',
secondary: 'bg-gray-200',
danger: 'bg-red-500',
},
},
defaultVariants: {
intent: 'primary',
},
});
const customLabelVariants = tv({
base: '',
variants: {
intent: {
primary: 'text-white',
secondary: 'text-gray-800',
danger: 'text-white',
},
},
defaultVariants: {
intent: 'primary',
},
});
type CustomButtonVariants = VariantProps;
interface CustomButtonProps
extends Omit,
CustomButtonVariants {
className?: string;
labelClassName?: string;
}
export function CustomButton({
intent,
className,
labelClassName,
children,
...props
}: CustomButtonProps) {
return (
{children}
);
}
```
## 使用组件自带的 classNames
每个 HeroUI Native 组件都会导出与内部一致的 `classNames` 工具对象,便于让自定义组件在视觉上与库内组件保持一致。
例如,让自定义 `Link` 看起来像 `Button`:
```tsx
import { buttonClassNames, cn } from 'heroui-native';
import { Pressable, Text } from 'react-native';
interface LinkProps {
href: string;
variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
size?: 'sm' | 'md' | 'lg';
children: React.ReactNode;
className?: string;
}
export function Link({
href,
variant = 'primary',
size = 'md',
children,
className,
}: LinkProps) {
return (
{
// Handle navigation
}}
>
{children}
);
}
```
**可用的 classNames 导出:**
每个组件都会导出对应的 `classNames` 对象,例如:
* `buttonClassNames` — 包含 `root` 与 `label` 函数
* `cardClassNames` — 包含 `root`、`header`、`body`、`footer`、`label` 与 `description` 函数
* `chipClassNames` — 包含 `root` 与 `label` 函数
* 其他组件同理……
**典型用法:**
```tsx
import { buttonClassNames } from 'heroui-native';
// Use with variant and size options
const rootClasses = buttonClassNames.root({
variant: 'primary',
size: 'md',
className: 'custom-class', // Optional: merge with your own classes
});
const labelClasses = buttonClassNames.label({
variant: 'primary',
size: 'md',
});
```
`classNames` 函数的变体参数与对应组件一致,便于在自定义组件与 HeroUI 组件之间保持视觉一致。
## 响应式设计
HeroUI Native 通过 [Uniwind](https://docs.uniwind.dev/breakpoints) 支持 Tailwind 的响应式断点前缀,如 `sm:`、`md:`、`lg:`、`xl:`,按屏幕宽度条件应用样式。
**移动优先:** 先写无前缀的小屏样式,再用断点为大屏增强。
### 响应式排版与间距
```tsx
import { Button } from 'heroui-native';
import { View, Text } from 'react-native';
Responsive Heading
Responsive Button
;
```
### 响应式布局
```tsx
import { View, Text } from 'react-native';
{/* Mobile: 1 column, Tablet: 2 columns, Desktop: 3 columns */}
Item 1
;
```
**默认断点:**
* `sm`:640px
* `md`:768px
* `lg`:1024px
* `xl`:1280px
* `2xl`:1536px
自定义断点与更多说明见 [Uniwind 断点文档](https://docs.uniwind.dev/breakpoints)。
## 工具函数
HeroUI Native 提供一些样式相关的工具。
### cn
`cn` 用于合并 Tailwind 类并处理冲突,适合条件类或与 props 传入的类合并:
```tsx
import { cn } from 'heroui-native';
import { View } from 'react-native';
function MyComponent({ className, isActive }) {
return (
);
}
```
`cn` 基于 `tailwind-variants`,具备:
* 自动合并 Tailwind 类(`twMerge: true`)
* 自定义透明度分组等能力
* 合理的冲突解决(靠后的类覆盖靠前的类)
**冲突示例:**
```tsx
// 'bg-accent' overrides 'bg-background'
cn('bg-background p-4', 'bg-accent');
// Result: 'p-4 bg-accent'
```
### useThemeColor
从 CSS 变量读取主题色,支持单色或一次读取多种颜色(后者更高效)。
**单色:**
```tsx
import { useThemeColor } from 'heroui-native';
function MyComponent() {
const accentColor = useThemeColor('accent');
const dangerColor = useThemeColor('danger');
return (
Error message
);
}
```
**多色(推荐在需要多个 token 时使用):**
```tsx
import { useThemeColor } from 'heroui-native';
function MyComponent() {
const [accentColor, backgroundColor, dangerColor] = useThemeColor([
'accent',
'background',
'danger',
]);
return (
Error message
);
}
```
**类型签名:**
```tsx
// Single color
useThemeColor(themeColor: ThemeColor): string
// Multiple colors (with type inference for tuples)
useThemeColor(
themeColor: T
): CreateStringTuple
// Multiple colors (array)
useThemeColor(themeColor: ThemeColor[]): string[]
```
可用主题色包括:`background`、`foreground`、`surface`、`accent`、`default`、`success`、`warning`、`danger` 及其 hover、soft、foreground 等变体,以及 `muted`、`border`、`separator`、`field`、`overlay` 等语义色。
## 下一步
* [动画](/docs/native/getting-started/animation)
* [主题](/docs/native/getting-started/theming)
* [颜色](/docs/native/getting-started/colors)
# 主题
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/getting-started/theming
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/getting-started/(handbook)/theming.mdx
> 使用 CSS 变量与全局样式定制 HeroUI Native 设计系统
HeroUI Native 使用 CSS 变量实现主题化,可用标准 CSS 覆盖从颜色到组件样式的一切。
## 工作原理
主题系统建立在 [Tailwind CSS v4](https://tailwindcss.com/docs/theme) 的主题能力之上,并通过 [Uniwind](https://uniwind.dev/) 接入 React Native。导入 `heroui-native/styles` 后,会使用 Tailwind 内置色板并映射到语义变量,自动在明暗主题间切换,并借助 CSS 层与 `@theme` 进行组织。
**命名约定:**
* 无后缀的颜色一般用作背景(如 `--accent`)
* 带 `-foreground` 后缀的用于该背景上的文字(如 `--accent-foreground`)
## 快速开始
**在组件中应用颜色:**
```tsx
import { View, Text } from 'react-native';
Your app content
;
```
**切换主题:**
通过 [Uniwind](https://docs.uniwind.dev/theming/basics),HeroUI Native 自动支持深色模式;可跟随系统,也可手动切换明暗变体:
```tsx
import { Uniwind, useUniwind } from 'uniwind';
import { Button } from 'heroui-native';
function ThemeToggle() {
const { theme } = useUniwind();
return (
Uniwind.setTheme(theme === 'light' ? 'dark' : 'light')}
>
Toggle {theme === 'light' ? 'Dark' : 'Light'} Mode
);
}
```
**覆盖颜色:**
```css
/* global.css */
@layer theme {
@variant light {
/* Override any color variable */
--accent: oklch(0.65 0.25 270); /* Custom indigo accent */
--success: oklch(0.65 0.15 155);
}
@variant dark {
--accent: oklch(0.65 0.25 270);
--success: oklch(0.75 0.12 155);
}
}
```
> **说明:** 完整色板与可视化参考见 [颜色](/docs/native/getting-started/colors)。
**创建自定义主题:**
可借助 Uniwind 的变体系统定义多套主题。完整自定义主题文档见 [Uniwind 自定义主题指南](https://docs.uniwind.dev/theming/custom-themes)。
**重要:** 所有主题必须定义**相同**的变量集合。必填变量清单见 [默认主题](/docs/native/getting-started/colors#default-theme)。
```css
/* global.css */
@layer theme {
:root {
@variant ocean-light {
/* Base Colors */
--background: oklch(0.95 0.02 230);
--foreground: oklch(0.25 0.04 230);
/* Surface: Used for non-overlay components (cards, accordions, disclosure groups) */
--surface: oklch(0.98 0.01 230);
--surface-foreground: oklch(0.3 0.045 230);
--surface-secondary: oklch(0.96 0.012 230);
--surface-secondary-foreground: oklch(0.3 0.045 230);
--surface-tertiary: oklch(0.94 0.015 230);
--surface-tertiary-foreground: oklch(0.3 0.045 230);
/* Overlay: Used for floating/overlay components (dialogs, popovers, modals, menus) */
--overlay: oklch(0.998 0.003 230);
--overlay-foreground: oklch(0.3 0.045 230);
--backdrop: oklch(0% 0 0 / 20%);
--muted: oklch(0.55 0.035 230);
--default: oklch(0.94 0.018 230);
--default-foreground: oklch(0.4 0.05 230);
/* Accent */
--accent: oklch(0.6 0.2 230);
--accent-foreground: oklch(0.98 0.005 230);
/* Form Field Defaults - Colors */
--field-background: oklch(0.98 0.01 230);
--field-foreground: oklch(0.25 0.04 230);
--field-placeholder: var(--muted);
--field-border: transparent;
/* Status Colors */
--success: oklch(0.72 0.14 165);
--success-foreground: oklch(0.25 0.08 165);
--warning: oklch(0.78 0.12 85);
--warning-foreground: oklch(0.3 0.08 85);
--danger: oklch(0.68 0.18 15);
--danger-foreground: oklch(0.98 0.005 15);
/* Component Colors */
--segment: oklch(0.98 0.01 230);
--segment-foreground: oklch(0.25 0.04 230);
/* Misc Colors */
--border: oklch(0 0 0 / 0%);
--separator: oklch(0.91 0.015 230);
--focus: var(--accent);
--link: oklch(0.62 0.17 230);
/* 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 2px 8px 0 rgba(0, 0, 0, 0.02), 0 -6px 12px 0 rgba(0, 0, 0, 0.01),
0 14px 28px 0 rgba(0, 0, 0, 0.03);
--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);
}
@variant ocean-dark {
/* Base Colors */
--background: oklch(0.15 0.04 230);
--foreground: oklch(0.94 0.01 230);
/* Surface: Used for non-overlay components (cards, accordions, disclosure groups) */
--surface: oklch(0.2 0.048 230);
--surface-foreground: oklch(0.9 0.015 230);
--surface-secondary: oklch(0.24 0.046 230);
--surface-secondary-foreground: oklch(0.9 0.015 230);
--surface-tertiary: oklch(0.27 0.044 230);
--surface-tertiary-foreground: oklch(0.9 0.015 230);
/* Overlay: Used for floating/overlay components (dialogs, popovers, modals, menus) */
--overlay: oklch(0.23 0.045 230);
--overlay-foreground: oklch(0.9 0.015 230);
--backdrop: oklch(0% 0 0 / 20%);
--muted: oklch(0.5 0.04 230);
--default: oklch(0.25 0.05 230);
--default-foreground: oklch(0.88 0.018 230);
/* Accent */
--accent: oklch(0.72 0.21 230);
--accent-foreground: oklch(0.15 0.04 230);
/* Form Field Defaults - Colors */
--field-background: var(--default);
--field-foreground: var(--foreground);
--field-placeholder: var(--muted);
--field-border: transparent;
/* Status Colors */
--success: oklch(0.68 0.16 165);
--success-foreground: oklch(0.95 0.008 165);
--warning: oklch(0.75 0.14 90);
--warning-foreground: oklch(0.2 0.04 90);
--danger: oklch(0.65 0.2 20);
--danger-foreground: oklch(0.95 0.008 20);
/* Component Colors */
--segment: oklch(0.22 0.046 230);
--segment-foreground: oklch(0.9 0.015 230);
/* Misc Colors */
--border: oklch(0 0 0 / 0%);
--separator: oklch(0.28 0.045 230);
--focus: var(--accent);
--link: oklch(0.75 0.18 230);
/* Shadows */
--surface-shadow: 0 0 0 0 transparent inset; /* No shadow on dark mode */
--overlay-shadow: 0 0 1px 0 rgba(255, 255, 255, 0.3) inset;
--field-shadow: 0 0 0 0 transparent inset; /* Transparent shadow to allow ring utilities to work */
}
}
}
```
**重要:** 添加自定义主题后,必须在 Metro 配置中注册:
```js
// metro.config.js
const { withUniwindConfig } = require('uniwind/metro');
const {
wrapWithReanimatedMetroConfig,
} = require('react-native-reanimated/metro-config');
const config = {
// ... your existing config
};
module.exports = withUniwindConfig(wrapWithReanimatedMetroConfig(config), {
cssEntryFile: './global.css',
dtsFile: './src/uniwind.d.ts',
extraThemes: ['ocean-light', 'ocean-dark'],
});
```
在应用中切换主题:
```tsx
import { Uniwind } from 'uniwind';
import { Button } from 'heroui-native';
function App() {
return (
Uniwind.setTheme('ocean-light')}>
Ocean Theme
);
}
```
## 添加自定义颜色
在主题中加入自定义语义色:
```css
@layer theme {
@variant light {
--info: oklch(0.6 0.15 210);
--info-foreground: oklch(0.98 0 0);
}
@variant 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
import { View, Text } from 'react-native';
Info message
;
```
## 自定义字体
要在应用中使用自定义字体,需要先加载字体,再覆盖字体相关的 CSS 变量。
### 1. 在应用中加载字体
先加载字体(例如使用 Expo 的 `useFonts`):
```tsx
import { useFonts } from 'expo-font';
import { HeroUINativeProvider } from 'heroui-native';
import {
YourFont_400Regular,
YourFont_500Medium,
YourFont_600SemiBold,
} from '@expo-google-fonts/your-font';
export default function App() {
const [fontsLoaded] = useFonts({
YourFont_400Regular,
YourFont_500Medium,
YourFont_600SemiBold,
});
if (!fontsLoaded) {
return null; // Or return a loading screen
}
return {/* Your app content */} ;
}
```
### 2. 配置字体 CSS 变量
加载完成后,在 `global.css` 中覆盖字体变量:
```css
@theme {
--font-normal: 'YourFont-400Regular';
--font-medium: 'YourFont-500Medium';
--font-semibold: 'YourFont-600SemiBold';
}
```
**说明:** CSS 变量中的字体名应与已加载字体的 PostScript 名称一致。请查阅字体包文档,或直接使用 `useFonts` 中出现的名称。
所有 HeroUI Native 组件会自动使用这些字体变量,以保持排版一致。
## 变量参考
HeroUI 定义三类变量:
1. **基础变量** — 如 `--white`、`--black` 等不随主题切换的值
2. **主题变量** — 随明暗主题切换的颜色
3. **计算变量** — 自动生成的按压态(hover)与尺寸变体等
完整参考:[颜色文档](/docs/native/getting-started/colors)、[默认主题变量](https://github.com/heroui-inc/heroui-native/blob/main/src/styles/variables.css)、[共享主题工具](https://github.com/heroui-inc/heroui-native/blob/main/src/styles/theme.css)
**计算变量(Tailwind):**
我们通过 Tailwind 的 `@theme` 指令自动生成按压态与圆角等计算变量,定义见 [theme.css](https://github.com/heroui-inc/heroui-native/blob/main/src/styles/theme.css):
```css
@theme inline static {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-surface: var(--surface);
--color-surface-foreground: var(--surface-foreground);
--color-surface-hover: color-mix(in oklab, var(--surface) 92%, var(--surface-foreground) 8%);
--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-backdrop: var(--backdrop);
--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);
/* Form Field Tokens */
--color-field: var(--field-background, var(--default));
--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, var(--radius-xl));
--border-width-field: var(--field-border-width, var(--border-width));
--shadow-surface: var(--surface-shadow);
--shadow-overlay: var(--overlay-shadow);
--shadow-field: var(--field-shadow);
/* Calculated Variables */
/* Colors */
/* --- background shades --- */
--color-background-secondary: color-mix(in oklab, var(--background) 96%, var(--foreground) 4%);
--color-background-tertiary: color-mix(in oklab, var(--background) 92%, var(--foreground) 8%);
--color-background-inverse: var(--foreground);
/* ------------------------- */
--color-default-hover: color-mix(in oklab, var(--default) 96%, var(--default-foreground) 4%);
--color-accent-hover: color-mix(in oklab, var(--accent) 90%, var(--accent-foreground) 10%);
--color-success-hover: color-mix(in oklab, var(--success) 90%, var(--success-foreground) 10%);
--color-warning-hover: color-mix(in oklab, var(--warning) 90%, var(--warning-foreground) 10%);
--color-danger-hover: color-mix(in oklab, var(--danger) 90%, var(--danger-foreground) 10%);
/* Form Field Colors */
--color-field-hover: color-mix(in oklab, var(--field-background, var(--default)) 90%, var(--field-foreground, var(--foreground)) 2%);
--color-field-focus: var(--field-background, var(--default));
--color-field-border-hover: color-mix(in oklab, var(--field-border, var(--border)) 88%, var(--field-foreground, var(--foreground)) 10%);
--color-field-border-focus: color-mix(in oklab, var(--field-border, var(--border)) 74%, var(--field-foreground, var(--foreground)) 22%);
/* Soft Colors */
--color-accent-soft: color-mix(in oklab, var(--accent) 15%, transparent);
--color-accent-soft-foreground: var(--accent);
--color-accent-soft-hover: color-mix(in oklab, var(--accent) 20%, transparent);
--color-danger-soft: color-mix(in oklab, var(--danger) 15%, transparent);
--color-danger-soft-foreground: var(--danger);
--color-danger-soft-hover: color-mix(in oklab, var(--danger) 20%, transparent);
--color-warning-soft: color-mix(in oklab, var(--warning) 15%, transparent);
--color-warning-soft-foreground: var(--warning);
--color-warning-soft-hover: color-mix(in oklab, var(--warning) 20%, transparent);
--color-success-soft: color-mix(in oklab, var(--success) 15%, transparent);
--color-success-soft-foreground: var(--success);
--color-success-soft-hover: color-mix(in oklab, var(--success) 20%, transparent);
/* Separator Colors - Levels */
--color-separator-secondary: color-mix(in oklab, var(--surface) 85%, var(--surface-foreground) 15%);
--color-separator-tertiary: color-mix(in oklab, var(--surface) 81%, var(--surface-foreground) 19%);
/* Border Colors - Levels (progressive contrast: default → secondary → tertiary) */
/* Light mode: lighter → darker | Dark mode: darker → lighter */
--color-border-secondary: color-mix(in oklab, var(--surface) 78%, var(--surface-foreground) 22%);
--color-border-tertiary: color-mix(in oklab, var(--surface) 66%, var(--surface-foreground) 34%);
/* Radius and default sizes - defaults can change by just changing the --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) */
}
```
表单控件依赖 `--field-*` 变量及其计算出的 hover/focus 变体。在主题中调整它们即可重塑输入框、复选框、单选与 OTP 等,而不会影响按钮、卡片等 Surface 组件的观感。
## 鲜亮配色
默认情况下,HeroUI Native 使用可读性更好的柔和前景色,即将语义色与前景色按比例混合,以在 soft 背景上获得更佳对比度。如果你偏好饱和度更高、更鲜亮的柔和前景色,可以在引入基础样式之后,额外导入可选的 `heroui-native/styles/vibrant` 样式:
```css
/* global.css */
@import "heroui-native/styles";
@import "heroui-native/styles/vibrant"; /* [!code highlight] */
```
这会将所有 `*-soft-foreground` 变量(accent、success、warning、danger)切换为「语义色 92% + 前景色 8%」的混合配方——更贴近原始色调,但仍带有轻微的对比度增强。[Alert](/docs/native/components/alert)、[Avatar](/docs/native/components/avatar)、[Button](/docs/native/components/button)、[Chip](/docs/native/components/chip)、[Toast](/docs/native/components/toast) 等组件会自动在其 soft 变体上使用新的柔和前景色——无需修改任何组件属性。
| 模式 | 可读性优先(默认) | 鲜亮 |
| ------- | -------------------------------------------- | ------------------------------------- |
| Soft 前景 | `color-mix(color 70-80%, foreground 30-40%)` | `color-mix(color 92%, foreground 8%)` |
鲜亮配色优先考虑视觉饱和度而非对比度。对某些颜色组合(尤其是更浅的强调色),它可能不满足 WCAG 无障碍准则。
可选的鲜亮配色自 [v1.0.4](/docs/native/releases/v1-0-4) 起提供。
## 相关资源
* [颜色](/docs/native/getting-started/colors)
* [样式指南](/docs/native/getting-started/styling)
* [Tailwind CSS v4 主题](https://tailwindcss.com/docs/theme)
* [OKLCH 调色工具](https://oklch.com)
# 设计原则
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/getting-started/design-principles
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/getting-started/(overview)/design-principles.mdx
> 指导 HeroUI Native 设计与开发的核心理念
HeroUI Native 遵循 9 条核心原则,强调清晰、无障碍、可定制与开发者体验。
## 核心原则
### 1. 语义意图优先于视觉样式
使用语义化命名(primary、secondary、tertiary)而非纯视觉描述(solid、flat、bordered)。受 [Uber Base 设计系统](https://base.uber.com/6d2425e9f/p/756216-button) 启发,变体形成清晰层级:
```tsx
// ✅ 语义变体传达层级
Save
Edit
Cancel
```
| 变体 | 用途 | 使用建议 |
| ------------- | --------- | ----------- |
| **Primary** | 推进流程的主操作 | 同一上下文通常 1 个 |
| **Secondary** | 备选操作 | 可多个 |
| **Tertiary** | 取消、跳过等弱操作 | 谨慎使用 |
| **Danger** | 破坏性操作 | 按需使用 |
### 2. 无障碍作为基础
遵循移动端无障碍最佳实践,内置合理的触控可达性、焦点管理与读屏支持。所有组件提供适当的无障碍标签与语义结构,以支持 VoiceOver(iOS)与 TalkBack(Android)。
```tsx
import { Tabs } from 'heroui-native';
Profile
Security
Content
Content
```
### 3. 组合优于配置
复合组件可按需重排、定制或省略子部件,通过点记法精确拼装。
```tsx
// 组合子部件得到所需结构
import { Accordion } from 'heroui-native';
Question Text
Answer content
```
### 4. 渐进式披露
从简单开始,仅在需要时增加复杂度。组件在最少配置下即可工作,并随需求增长而扩展。
```tsx
import { Button, Spinner } from 'heroui-native';
import { Feather } from '@expo/vector-icons';
// Level 1: Minimal
Click me
// Level 2: Enhanced
Submit
// Level 3: Advanced
{isLoading ? (
<>
Loading...
>
) : (
Submit
)}
```
### 5. 可预测的行为
跨组件保持一致:`sm` / `md` / `lg` 尺寸、变体体系与 `className` 支持。API 一致,行为一致。
```tsx
import { Button, Chip, Avatar } from 'heroui-native';
// All components follow the same patterns
Click me
Success
JD
```
### 6. 类型安全优先
完整的 TypeScript 支持:智能提示、自动补全与编译期检查。可为自定义组件扩展类型。
```tsx
import type { ButtonRootProps } from 'heroui-native';
// Type-safe props and event handlers
{ // Properly typed press handler
console.log('Button pressed');
}}
>
Click me
// Extend types for custom components
interface CustomButtonProps extends Omit {
intent: 'save' | 'cancel' | 'delete';
}
```
### 7. 卓越的开发者体验
清晰的 API、可读的错误信息、智能提示,以及对 AI 友好的 Markdown 文档。
### 8. 完整可定制
默认即美观;也可通过 CSS 变量与 [Uniwind 主题系统](https://docs.uniwind.dev/theming/basics) 整体换肤。每个插槽都可定制。
```css
/* Custom colors using Uniwind's theme layer */
@layer theme {
@variant light {
--accent: oklch(0.65 0.25 270); /* Custom indigo accent */
--background: oklch(0.98 0 0); /* Custom background */
}
@variant dark {
--accent: oklch(0.65 0.25 270);
--background: oklch(0.15 0 0);
}
}
/* Radius customization */
@theme {
--radius: 0.75rem; /* Increase for rounder components */
}
```
### 9. 开放与可扩展
可包装、扩展与定制组件以匹配产品需求;也可用 `className` 应用自定义样式。
```tsx
import { Button } from 'heroui-native';
import type { ButtonRootProps } from 'heroui-native';
// Custom wrapper component
interface CTAButtonProps extends Omit {
intent?: 'primary-cta' | 'secondary-cta' | 'minimal';
}
const CTAButton = ({
intent = 'primary-cta',
children,
...props
}: CTAButtonProps) => {
const variantMap = {
'primary-cta': 'primary',
'secondary-cta': 'secondary',
'minimal': 'ghost'
} as const;
return (
{children}
);
};
// Usage
Get Started
Learn More
```
**结合 tailwind-variants 扩展:**
```tsx
import { Button } from 'heroui-native';
import { tv } from 'tailwind-variants';
// Extend button styles with custom variants
const myButtonVariants = tv({
base: 'px-4 py-2 rounded-lg',
variants: {
variant: {
'primary-cta': 'bg-accent px-8 py-4 shadow-lg',
'secondary-cta': 'border-2 border-accent px-6 py-3',
}
},
defaultVariants: {
variant: 'primary-cta',
}
});
// Label variants for text colors (must be applied to Button.Label)
const myLabelVariants = tv({
base: '',
variants: {
variant: {
'primary-cta': 'text-accent-foreground',
'secondary-cta': 'text-accent',
}
},
defaultVariants: {
variant: 'primary-cta',
}
});
// Use the custom variants
function CustomButton({ variant, className, labelClassName, children, ...props }) {
return (
{children}
);
}
// Usage
Get Started
Learn More
```
# 快速开始
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/getting-started/quick-start
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/getting-started/(overview)/quick-start.mdx
> 几分钟内上手 HeroUI Native
请根据项目情况选择适合的方案:
* **方案 1** —— 通过我们的 CLI 一键创建预先配置好的新项目,无需手动设置。
* **方案 2** —— 将 HeroUI Native 添加到已有的 React Native 或 Expo 项目。
## 方案 1:创建新项目
最快捷的启动方式。CLI 会生成一个 Expo 项目,内置 HeroUI Native、所有必需的 peer 依赖、Uniwind / Tailwind CSS、全局样式以及已配置好的 `HeroUINativeProvider`,让你可以直接进入开发。
```bash
npx create-heroui-native-app@latest
```
```bash
pnpm create heroui-native-app@latest
```
```bash
yarn create heroui-native-app
```
```bash
bun create heroui-native-app@latest
```
按照交互提示完成配置,然后启动开发服务器:
```bash
cd my-app
npm run start
```
到此即可开始开发。可直接跳到[使用第一个组件](#use-your-first-component),或[浏览组件](/docs/native/components)。
生成的项目自带 Expo + TypeScript、预配置的 Uniwind + Tailwind CSS、含必要导入的 `global.css`,并已在应用入口包裹 `GestureHandlerRootView` 与 `HeroUINativeProvider`。
## 方案 2:添加到已有项目
如果你已经有一个 React Native 或 Expo 应用,请按以下步骤手动安装与配置 HeroUI Native。
**更喜欢让 AI 助手代劳?** 在编辑器中安装 [HeroUI Native MCP 服务器](/docs/native/getting-started/mcp-server),将上方提示词粘贴给你的 AI 助手 —— 它会分析你的项目并完成整套配置。
### 1. 安装 HeroUI Native
```bash
npm install heroui-native
```
```bash
pnpm add heroui-native
```
```bash
yarn add heroui-native
```
```bash
bun add heroui-native
```
### 2. 安装必需的 peer 依赖
```bash
npm install react-native-reanimated@^4.1.1 react-native-gesture-handler@^2.28.0 react-native-worklets@^0.5.1 react-native-safe-area-context@^5.6.0 react-native-svg@^15.12.1 tailwind-variants@^3.2.2 tailwind-merge@^3.4.0
```
```bash
pnpm add react-native-reanimated@^4.1.1 react-native-gesture-handler@^2.28.0 react-native-worklets@^0.5.1 react-native-safe-area-context@^5.6.0 react-native-svg@^15.12.1 tailwind-variants@^3.2.2 tailwind-merge@^3.4.0
```
```bash
yarn add react-native-reanimated@^4.1.1 react-native-gesture-handler@^2.28.0 react-native-worklets@^0.5.1 react-native-safe-area-context@^5.6.0 react-native-svg@^15.12.1 tailwind-variants@^3.2.2 tailwind-merge@^3.4.0
```
```bash
bun add react-native-reanimated@^4.1.1 react-native-gesture-handler@^2.28.0 react-native-worklets@^0.5.1 react-native-safe-area-context@^5.6.0 react-native-svg@^15.12.1 tailwind-variants@^3.2.2 tailwind-merge@^3.4.0
```
建议使用上文列出的确切版本,以避免兼容性问题。版本不一致可能导致难以预期的缺陷。
### 3. 可选依赖
仅在用到对应组件或能力时需要安装:
| Package | Version | 用途 |
| ---------------------- | --------- | ---------------------------------------------------------------------- |
| `react-native-screens` | `^4.16.0` | BottomSheet、Dialog、Menu、Popover、Select、Toast |
| `@gorhom/bottom-sheet` | `^5.2.9` | BottomSheet;Menu / Popover / Select 使用 `presentation="bottom-sheet"` 时 |
### 4. 配置 Uniwind
请按 [Uniwind 安装指南](https://docs.uniwind.dev/quickstart) 为 React Native 接入 Tailwind CSS。
若从 NativeWind 迁移,参见 [迁移指南](https://docs.uniwind.dev/migration-from-nativewind)。
### 5. 配置 global.css
在 `global.css` 中加入以下导入:
```css
@import 'tailwindcss';
@import 'uniwind';
@import 'heroui-native/styles';
/* Path to the heroui-native lib inside node_modules relative to global.css */
/* Examples:
* - If global.css is at project root: ./node_modules/heroui-native/lib
* - If global.css is in app/: ../node_modules/heroui-native/lib
* - If global.css is in src/styles/: ../../node_modules/heroui-native/lib
*/
@source './node_modules/heroui-native/lib';
```
### 6. 使用 Provider 包裹应用
使用 `HeroUINativeProvider` 包裹应用,并务必外层再包一层 `GestureHandlerRootView`:
```tsx
import { HeroUINativeProvider } from 'heroui-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
export default function App() {
return (
{/* Your app content */}
);
}
```
> **说明:** 关于文本属性、动画、Toast 等高级配置,请参阅 [Provider 文档](/docs/native/getting-started/provider)。
## 使用第一个组件
```tsx
import { Button } from 'heroui-native';
import { View } from 'react-native';
export default function MyComponent() {
return (
console.log('Pressed!')}>Get Started
);
}
```
## 通过细粒度导出减小包体
若希望减小包体、仅引入用到的组件,可使用细粒度导出:
```tsx
// Granular imports - use when you need only a few components
import { HeroUINativeProvider } from "heroui-native/provider";
import { Button } from "heroui-native/button";
import { Card } from "heroui-native/card";
// General import - imports the whole library, use when you're using many components
import { Button, Card } from "heroui-native";
```
细粒度导入适合只用少量组件的场景,有助于控制包体。从 `heroui-native` 整体导入会包含完整库,适合在应用中广泛使用多组件时采用。
**可用的细粒度入口:**
* `heroui-native/provider` — Provider
* `heroui-native/provider-raw` — 轻量 Provider(最小依赖起点)
* `heroui-native/[component-name]` — 单个组件
* `heroui-native/portal` — Portal 工具
* `heroui-native/toast` — Toast Provider 与工具
* `heroui-native/utils` — 工具函数
* `heroui-native/hooks` — 自定义 Hooks
**重要:** 为控制包体,请**始终**坚持使用细粒度导入策略。只要存在一处从 `heroui-native` 的整体导入,就可能使上述优化失效。
> **提示:** 若需要更强控制,可使用 [`HeroUINativeProviderRaw`](/docs/native/getting-started/provider#raw-provider) — 轻量 Provider,不包含 `ToastProvider` 与 `PortalHost`。
## 接下来
* [HeroUI Native Provider](/docs/native/getting-started/provider)
* [样式指南](/docs/native/getting-started/styling)
* [主题文档](/docs/native/getting-started/theming)
## 在 Web(Expo)上运行
HeroUI Native **目前不建议用于 Web**。我们优先聚焦 iOS 与 Android。Web 开发请使用 [HeroUI React](/docs/react/getting-started/quick-start)。
# Agent Skills
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/getting-started/agent-skills
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/getting-started/(ui-for-agents)/agent-skills.mdx
> 让 AI 助手使用 HeroUI Native 组件构建移动端界面
HeroUI Native Skills 为 AI 助手提供组件、模式与 React Native 最佳实践等系统化知识。
### 安装
```bash
curl -fsSL https://heroui.com/install | bash -s heroui-native
```
或使用 skills 包:
```bash
npx skills add heroui-inc/heroui
```
支持 Claude Code、Cursor、OpenCode 等。
### 用法
Skills 会被 AI **自动发现**,也可通过 `/heroui-native` 显式调用。
你可以让助手:
* 使用 HeroUI Native 搭建移动组件
* 用 HeroUI Native 创建页面
* 定制主题与样式
* 查阅组件文档
更复杂的场景可配合 [MCP 服务器](/docs/native/getting-started/mcp-server),以实时访问组件文档与源码。
### 包含内容
* HeroUI Native 安装指南
* 全部组件的 props、示例与用法模式
* 基于 Uniwind 的主题与样式指南
* 设计原则与组合模式
### 目录结构
```
skills/heroui-native/
├── SKILL.md # 主说明
├── LICENSE.txt # Apache License 2.0
└── scripts/ # 工具脚本
├── list_components.mjs
├── get_component_docs.mjs
├── get_theme.mjs
└── get_docs.mjs
```
### 相关文档
* [Agent Skills 规范](https://agentskills.io/home)
* [Claude Agent Skills](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview)
* [Cursor Skills](https://cursor.com/docs/context/skills)
* [OpenCode Skills](https://opencode.ai/docs/skills)
# AGENTS.md
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/getting-started/agents-md
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/getting-started/(ui-for-agents)/agents-md.mdx
> 将 HeroUI Native 文档下载到项目中,供 AI 编程助手引用
可将 HeroUI Native 文档直接下载到项目内,供 AI 助手本地引用。
**说明:** `agents-md` 命令专用于 **HeroUI React v3** 与 **HeroUI Native**。其他 CLI 命令(如 `add`、`init`、`upgrade` 等)目前仍面向 HeroUI v2。
### 用法
```bash
npx heroui-cli@latest agents-md --native
```
指定输出文件:
```bash
npx heroui-cli@latest agents-md --native --output AGENTS.md
```
### 作用
* 将最新 HeroUI Native 文档下载到 `.heroui-docs/native/`
* 在 `AGENTS.md` 或 `CLAUDE.md` 中生成索引
* 自动将 `.heroui-docs/` 加入 `.gitignore`
### 选项
* `--native` — 仅下载 Native 文档
* `--output ` — 输出文件(如 `AGENTS.md` 或 `AGENTS.md CLAUDE.md`)
* `--ssh` — 使用 SSH 进行 git clone
### 要求
* Tailwind CSS >= v4(通过 Uniwind)
### 相关文档
* [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 vs Skills](https://vercel.com/blog/agents-md-outperforms-skills-in-our-agent-evals) — 评测相关讨论
# LLMs.txt
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/getting-started/llms-txt
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/getting-started/(ui-for-agents)/llms-txt.mdx
> 让 Claude、Cursor、Windsurf 等 AI 助手理解 HeroUI Native 文档
我们提供 [LLMs.txt](https://llmstxt.org/) 文件,便于 AI 编程助手读取 HeroUI Native 文档。
## 可用文件
**核心文档:**
* [/native/llms.txt](/native/llms.txt) — Native 文档快速索引
* [/native/llms-full.txt](/native/llms-full.txt) — HeroUI Native 完整文档
**上下文窗口较小时:**
* [/native/llms-components.txt](/native/llms-components.txt) — 仅组件文档
* [/native/llms-patterns.txt](/native/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 Native documentation from https://heroui.com/native/llms.txt
```
或在项目的 `.claude` 中配置以自动加载。
**Cursor:** 使用 `@Docs`:
```
@Docs https://heroui.com/native/llms-full.txt
```
[了解更多](https://docs.cursor.com/context/@-symbols/@-docs)
**Windsurf:** 写入 `.windsurfrules`:
```
#docs https://heroui.com/native/llms-full.txt
```
[了解更多](https://docs.codeium.com/windsurf/memories#memories-and-rules)
**其他 AI 工具:** 多数助手支持通过 URL 引用文档,直接提供:
```
https://heroui.com/native/llms.txt
```
**仅组件文档:**
```
https://heroui.com/native/llms-components.txt
```
**模式与最佳实践:**
```
https://heroui.com/native/llms-patterns.txt
```
## 参与改进
若 AI 生成代码有问题,欢迎在 [GitHub](https://github.com/heroui-inc/heroui) 上协助改进 LLMs.txt 文件。
# MCP 服务器
**Category**: native
**URL**: https://v3.heroui.com/cn/docs/native/getting-started/mcp-server
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/cn/native/getting-started/(ui-for-agents)/mcp-server.mdx
> 在 AI 助手中直接访问 HeroUI Native 文档
HeroUI MCP 服务器让 AI 助手直接读取 HeroUI Native 组件文档,便于在 AI 驱动的开发流程中使用 HeroUI。
当前 MCP 服务器支持 **heroui-native** 与 [stdio 传输](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#stdio)。npm 包名为 `@heroui/native-mcp`。源码见 [GitHub](https://github.com/heroui-inc/heroui-mcp)。
随着 HeroUI Native 组件增加,MCP 中的可用内容也会同步扩展。
## 快速配置
**Cursor:**
或手动添加到 **Cursor 设置** → **Tools** → **MCP Servers**:
```json title=".cursor/mcp.json"
{
"mcpServers": {
"heroui-native": {
"command": "npx",
"args": ["-y", "@heroui/native-mcp@latest"]
}
}
}
```
也可将以下内容加入 `~/.cursor/mcp.json`。详见 [Cursor 文档](https://cursor.com/docs/context/mcp)。
**Claude Code:** 在终端执行:
```bash
claude mcp add heroui-native -- npx -y @heroui/native-mcp@latest
```
或手动写入项目的 `.mcp.json`:
```json title=".mcp.json"
{
"mcpServers": {
"heroui-native": {
"command": "npx",
"args": ["-y", "@heroui/native-mcp@latest"]
}
}
}
```
添加配置后重启 Claude Code,运行 `/mcp` 查看列表;若显示 **Connected** 即可使用。
更多说明见 [Claude Code MCP 文档](https://docs.claude.com/en/docs/claude-code/mcp)。
**Windsurf:** 在项目 `.windsurf/mcp.json` 中加入:
```json title=".windsurf/mcp.json"
{
"mcpServers": {
"heroui-native": {
"command": "npx",
"args": ["-y", "@heroui/native-mcp@latest"]
}
}
}
```
保存后重启 Windsurf 以生效。
详见 [Windsurf MCP 文档](https://docs.windsurf.com/windsurf/cascade/mcp)。
**Zed:** 在 `settings.json` 中配置(命令面板 `zed: open settings` 或 `Cmd-,` / `Ctrl-,`):
```json title="settings.json"
{
"context_servers": {
"heroui-native": {
"command": "npx",
"args": ["-y", "@heroui/native-mcp@latest"],
"env": {}
}
}
}
```
重启 Zed 后,在 Agent 面板设置中确认 heroui-native 旁指示点为绿色,提示为「Server is active」。
详见 [Zed MCP 文档](https://zed.dev/docs/ai/mcp)。
**VS Code:** 若配合 GitHub Copilot 使用 MCP,在项目 `.vscode/mcp.json` 中添加:
```json title=".vscode/mcp.json"
{
"servers": {
"heroui-native": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@heroui/native-mcp@latest"]
}
}
}
```
打开 `.vscode/mcp.json`,在 heroui-native 旁点击 **Start**。
详见 [VS Code MCP 文档](https://code.visualstudio.com/docs/copilot/customization/mcp-servers)。
**Codex:** 写入 `~/.codex/config.toml` 或项目内 `.codex/config.toml`:
```toml title="config.toml"
[mcp_servers.heroui-native]
command = "npx"
args = ["-y", "@heroui/native-mcp@latest"]
```
重启 Codex,在 TUI 中运行 `/mcp` 校验服务器已激活。
详见 [Codex MCP 文档](https://developers.openai.com/codex/mcp)。
**OpenCode:** 在项目 `opencode.json` 中添加:
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"heroui-native": {
"type": "local",
"command": ["npx", "-y", "@heroui/native-mcp@latest"]
}
}
}
```
重启 OpenCode 后生效。
详见 [OpenCode MCP 文档](https://open-code.ai/docs/en/mcp-servers)。
## 用法
配置完成后,可向 AI 提问,例如:
* "Help me install HeroUI Native in my Expo app"
* "Show me all HeroUI Native components"
* "What props does the Button component have?"
* "Give me an example of using the Card component"
* "What are the theme variables for dark mode?"
### 自动升级
MCP 也可协助将 HeroUI Native 升级到最新版本,例如:
```bash
"Hey Cursor, update HeroUI Native to the latest version"
```
助手通常会:
* 对比当前版本与最新发布
* 阅读变更日志中的破坏性变更
* 在项目中应用必要的代码更新
适用于升级到最新稳定版或预发布版本。
## 可用工具
MCP 向 AI 暴露以下工具:
| 工具 | 说明 |
| --------------------- | ---------------------------------------------------------------------------------- |
| `list_components` | 列出全部 HeroUI Native 组件 |
| `get_component_docs` | 获取一个或多个组件的完整文档(结构、props、示例与用法模式) |
| `get_theme_variables` | 读取颜色、排版、间距等主题变量,支持明暗模式 |
| `get_docs` | 浏览 HeroUI Native 全部文档(指南与原则等);安装说明可使用路径 `/docs/native/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/native-mcp)
* [GitHub 仓库](https://github.com/heroui-inc/heroui-mcp)
* [贡献指南](https://github.com/heroui-inc/heroui-mcp/blob/main/CONTRIBUTING.md)
# 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)