`)
* `.table__cell` - Data cell (``)
* `.table__footer` - Footer container (outside table)
#### Advanced Classes
* `.table__column-resizer` - Drag handle for column resizing
* `.table__resizable-container` - Wrapper enabling column resizing
* `.table__load-more` - Sentinel row for infinite scrolling
* `.table__load-more-content` - Styled container for the loading indicator
* `.table__sortable-column-header` - Sortable column label + indicator wrapper
* `.table__sortable-column-indicator` - Sort direction chevron (rotates via `[data-direction="descending"]`)
#### Variant Classes
* `.table-root--primary` - Gray background container with card-style body (default)
* `.table-root--secondary` - No background, standalone rounded headers
### Interactive States
The Table supports both CSS pseudo-classes and data attributes for flexibility:
* **Hover**: `:hover` or `[data-hovered="true"]` (row background change)
* **Selected**: `[data-selected="true"]` (row highlight)
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]` (inset focus ring on rows, columns, and cells)
* **Disabled**: `:disabled` or `[aria-disabled="true"]` (reduced opacity)
* **Sortable**: `[data-allows-sorting="true"]` (interactive cursor on columns)
* **Dragging**: `[data-dragging="true"]` (reduced opacity)
* **Drop Target**: `[data-drop-target="true"]` (accent background)
## API Reference
### Table Props
| Prop | Type | Default | Description |
| ----------- | -------------------------- | ----------- | ------------------------------------------------------------------------------------------------- |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant. Primary has a gray background container; secondary is flat with transparent rows. |
| `className` | `string` | - | Additional CSS classes for the root container |
| `children` | `React.ReactNode` | - | Table content (ScrollContainer, Footer, etc.) |
### Table.ScrollContainer Props
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Table.Content element |
### Table.Content Props
Inherits from [React Aria Table](https://react-spectrum.adobe.com/react-aria/Table.html).
| Prop | Type | Default | Description |
| ------------------- | -------------------------------------- | -------- | ------------------------------ |
| `aria-label` | `string` | - | Accessible label for the table |
| `selectionMode` | `"none" \| "single" \| "multiple"` | `"none"` | Selection behavior |
| `selectedKeys` | `Selection` | - | Controlled selected keys |
| `onSelectionChange` | `(keys: Selection) => void` | - | Selection change handler |
| `sortDescriptor` | `SortDescriptor` | - | Current sort state |
| `onSortChange` | `(descriptor: SortDescriptor) => void` | - | Sort change handler |
| `className` | `string` | - | Additional CSS classes |
### Table.Header Props
Inherits from [React Aria TableHeader](https://react-spectrum.adobe.com/react-aria/Table.html#tableheader).
| Prop | Type | Default | Description |
| ---------- | --------------------------------------------------- | ------- | ------------------------------------------- |
| `columns` | `T[]` | - | Dynamic column data for render prop pattern |
| `children` | `React.ReactNode \| (column: T) => React.ReactNode` | - | Static columns or render prop |
### Table.Column Props
Inherits from [React Aria Column](https://react-spectrum.adobe.com/react-aria/Table.html#column).
| Prop | Type | Default | Description |
| --------------- | ------------------------------------------------------------------- | ------- | ------------------------------------------------- |
| `id` | `string` | - | Column identifier |
| `allowsSorting` | `boolean` | `false` | Whether the column is sortable |
| `isRowHeader` | `boolean` | `false` | Whether this column is a row header |
| `defaultWidth` | `string \| number` | - | Default width for resizable columns |
| `minWidth` | `number` | - | Minimum width for resizable columns |
| `children` | `React.ReactNode \| (values: ColumnRenderProps) => React.ReactNode` | - | Column content or render prop with sort direction |
### Table.Body Props
Inherits from [React Aria TableBody](https://react-spectrum.adobe.com/react-aria/Table.html#tablebody).
| Prop | Type | Default | Description |
| ------------------ | ------------------------------------------------- | ------- | ------------------------------------------ |
| `items` | `T[]` | - | Dynamic row data for render prop pattern |
| `renderEmptyState` | `() => React.ReactNode` | - | Content to display when the table is empty |
| `children` | `React.ReactNode \| (item: T) => React.ReactNode` | - | Static rows or render prop |
### Table.Row Props
Inherits from [React Aria Row](https://react-spectrum.adobe.com/react-aria/Table.html#row).
| Prop | Type | Default | Description |
| ----------- | ------------------ | ------- | ---------------------- |
| `id` | `string \| number` | - | Row identifier |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Row cells |
### Table.Cell Props
Inherits from [React Aria Cell](https://react-spectrum.adobe.com/react-aria/Table.html#cell).
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Cell content |
### Table.SortableColumnHeader Props
Renders a sortable column label with an ascending/descending indicator. Use it inside a `Table.Column` render-prop callback and forward the `sortDirection` value.
| Prop | Type | Default | Description |
| --------------- | ----------------------------- | ------- | -------------------------------------------------------------------------------------------------- |
| `sortDirection` | `"ascending" \| "descending"` | - | Current sort direction. Pass through from the `Table.Column` render prop. |
| `showIndicator` | `boolean` | `true` | Whether to render the sort indicator icon when a direction is set. |
| `indicator` | `React.ReactNode` | - | Custom indicator element. Overrides the default chevron and receives a `data-direction` attribute. |
| `className` | `string` | - | Additional CSS classes for the wrapper. |
| `children` | `React.ReactNode` | - | Column label content. |
### Table.Footer Props
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | --------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Footer content (e.g., pagination) |
### Table.ColumnResizer Props
Inherits from [React Aria ColumnResizer](https://react-spectrum.adobe.com/react-aria/Table.html#columnresizer).
| Prop | Type | Default | Description |
| ----------- | -------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
### Table.ResizableContainer Props
Inherits from [React Aria ResizableTableContainer](https://react-spectrum.adobe.com/react-aria/Table.html#resizabletablecontainer).
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Table.Content element |
### Table.LoadMore Props
Inherits from [React Aria TableLoadMoreItem](https://react-spectrum.adobe.com/react-aria/Table.html).
| Prop | Type | Default | Description |
| ------------ | ----------------- | ------- | ----------------------------------------------- |
| `isLoading` | `boolean` | `false` | Whether data is currently loading |
| `onLoadMore` | `() => void` | - | Handler called when the sentinel row is visible |
| `children` | `React.ReactNode` | - | Loading indicator content |
### Table.LoadMoreContent Props
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ----------------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Loading indicator content (e.g., Spinner) |
### Table.Collection Props
Re-exported from React Aria `Collection`. Used to render dynamic cells within rows alongside static cells (e.g., checkboxes).
| Prop | Type | Default | Description |
| ---------- | ------------------------------ | ------- | ------------------------- |
| `items` | `T[]` | - | Collection items |
| `children` | `(item: T) => React.ReactNode` | - | Render prop for each item |
### TableLayout
| Name | Type | Default | Description |
| ------------------------ | --------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `rowHeight` | `number \| undefined` | 48 | The fixed height of a row in px. |
| `estimatedRowHeight` | `number \| undefined` | — | The estimated height of a row, when row heights are variable. |
| `headingHeight` | `number \| undefined` | 48 | The fixed height of a section header in px. |
| `estimatedHeadingHeight` | `number \| undefined` | — | The estimated height of a section header, when the height is variable. |
| `loaderHeight` | `number \| undefined` | 48 | The fixed height of a loader element in px. This loader is specifically for "load more" elements rendered when loading more rows at the root level or inside nested row/sections. |
| `dropIndicatorThickness` | `number \| undefined` | 2 | The thickness of the drop indicator. |
| `gap` | `number \| undefined` | 0 | The gap between items. |
| `padding` | `number \| undefined` | 0 | The padding around the list. |
# Calendar
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/calendar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(date-and-time)/calendar.mdx
> Composable date picker with month grid, navigation, and year picker support built on React Aria Calendar
## Import
```tsx
import { Calendar } from '@heroui/react';
```
### Usage
```tsx
"use client";
import {Calendar} from "@heroui/react";
export function Basic() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### Anatomy
```tsx
import {Calendar} from '@heroui/react';
export default () => (
{(day) => {day} }
{(date) => }
)
```
### Year Picker
`Calendar.YearPickerTrigger`, `Calendar.YearPickerGrid`, and their body/cell subcomponents provide an integrated year navigation pattern.
```tsx
"use client";
import {Calendar} from "@heroui/react";
export function YearPicker() {
return (
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### Default Value
```tsx
"use client";
import {Calendar} from "@heroui/react";
import {parseDate} from "@internationalized/date";
export function DefaultValue() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### Controlled
Use controlled `value` and `focusedValue` for external state coordination and custom shortcuts.
```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);
}}
>
Today
{
const nextWeekStart = startOfWeek(today(getLocalTimeZone()), locale);
setValue(nextWeekStart);
setFocusedDate(nextWeekStart);
}}
>
Week
{
const nextMonthStart = startOfMonth(today(getLocalTimeZone()));
setValue(nextMonthStart);
setFocusedDate(nextMonthStart);
}}
>
Month
{(day) => {day} }
{(date) => }
Selected date: {value ? value.toString() : "(none)"}
{
const todayDate = today(getLocalTimeZone());
setValue(todayDate);
setFocusedDate(todayDate);
}}
>
Set Today
{
const christmasDate = parseDate("2025-12-25");
setValue(christmasDate);
setFocusedDate(christmasDate);
}}
>
Set Christmas
setValue(null)}>
Clear
);
}
```
### Min and Max Dates
```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) => }
Select a date between today and {maxDate.toString()}
);
}
```
### Unavailable Dates
Use `isDateUnavailable` to block dates such as weekends, holidays, or booked slots.
```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) => }
Weekends are unavailable
);
}
```
### Weeks in Month
Set `weeksInMonth` to a fixed value (for example, `6`) to keep the grid height stable when navigating between months. Use with care in non-Gregorian locales, similar to `firstDayOfWeek`.
```tsx
"use client";
import {Calendar, Description} from "@heroui/react";
export function WeeksInMonth() {
return (
{(day) => {day} }
{(date) => }
Always shows 6 weeks per month to avoid layout shift when navigating
);
}
```
### Week View
Set `visibleDuration={{ weeks: n }}` to show one or more weeks at a time. Navigation advances by the visible week range. Use `pageBehavior="single"` to move one week at a time when showing multiple weeks.
```tsx
"use client";
import {Calendar, Label, ListBox, Select} from "@heroui/react";
import {useState} from "react";
const weekOptions = [
{id: "1", name: "1 week"},
{id: "2", name: "2 weeks"},
{id: "3", name: "3 weeks"},
{id: "4", name: "4 weeks"},
{id: "5", name: "5 weeks"},
{id: "6", name: "6 weeks"},
{id: "8", name: "8 weeks"},
] as const;
export function WeekView() {
const [weeks, setWeeks] = useState(1);
return (
value && setWeeks(Number(value))}
>
Visible weeks
{weekOptions.map((option) => (
{option.name}
))}
{(day) => {day} }
{(date) => }
);
}
```
### Day View
Set `visibleDuration={{ days: n }}` to show a rolling window of consecutive days. Navigation advances by the visible day range. Use `pageBehavior="single"` to move one day at a time when showing multiple days.
```tsx
"use client";
import {Calendar, Label, ListBox, Select} from "@heroui/react";
import {useState} from "react";
const dayOptions = [
{id: "1", name: "1 day"},
{id: "5", name: "5 days"},
{id: "7", name: "7 days"},
{id: "8", name: "8 days"},
{id: "10", name: "10 days"},
{id: "14", name: "14 days"},
{id: "21", name: "21 days"},
] as const;
export function DayView() {
const [days, setDays] = useState(5);
return (
value && setDays(Number(value))}
>
Visible days
{dayOptions.map((option) => (
{option.name}
))}
{(day) => {day} }
{(date) => }
);
}
```
### Multiple Selection
Set `selectionMode="multiple"` to let users select several dates. `value`, `defaultValue`, and `onChange` use an array of dates.
```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} date(s) selected` : "Select multiple dates"}
);
}
```
### Disabled
```tsx
"use client";
import {Calendar, Description} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function Disabled() {
return (
{(day) => {day} }
{(date) => }
Calendar is disabled
);
}
```
### Read Only
```tsx
"use client";
import {Calendar, Description} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function ReadOnly() {
return (
{(day) => {day} }
{(date) => }
Calendar is read-only
);
}
```
### Focused Value
Programmatically control which date is focused using `focusedValue` and `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) => }
Focused: {focusedDate.toString()}
setFocusedDate(parseDate("2025-01-01"))}
>
Go to Jan
setFocusedDate(parseDate("2025-06-15"))}
>
Go to Jun
setFocusedDate(parseDate("2025-12-25"))}
>
Go to Christmas
);
}
```
### Cell Indicators
You can customize `Calendar.Cell` children and use `Calendar.CellIndicator` to display metadata like events.
```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)) && (
)}
>
)}
)}
);
}
```
### Multiple Months
Render multiple grids with `visibleDuration` and `offset` for booking and planning experiences. Use `Calendar.Heading` with an `offset` (for example, `offset={{ months: 1 }}`) in each column header to label that month.
```tsx
"use client";
import {Calendar} from "@heroui/react";
export function MultipleMonths() {
return (
{(day) => {day} }
{(date) => }
{(day) => {day} }
{(date) => }
);
}
```
### International Calendars
By default, Calendar displays dates using the calendar system for the user's locale. You can override this by wrapping your Calendar with `I18nProvider` and setting the [Unicode calendar locale extension](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string).
The example below shows the Indian calendar system:
```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}) => }
);
}
```
**Note:** The `onChange` event always returns a date in the same calendar system as the `value` or `defaultValue` (Gregorian if no value is provided), regardless of the displayed locale. This ensures your application logic works consistently with a single calendar system while still displaying dates in the user's preferred format.
### Custom Navigation Icons
Pass children to `Calendar.NavButton` to replace the default chevron icons.
```tsx
"use client";
import {Calendar} from "@heroui/react";
export function CustomIcons() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### Real-World Example
```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) && }
>
)}
)}
Has bookings
Weekend/Unavailable
{selectedDate ? (
Book {selectedDate.toString()}
) : null}
);
}
```
### Custom Styles
```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
## Styling
### Passing Tailwind CSS classes
```tsx
import {Calendar} from '@heroui/react';
function CustomCalendar() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### Customizing the component classes
```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 Classes
Calendar uses these classes in `packages/styles/components/calendar.css` and `packages/styles/components/calendar-year-picker.css`:
* `.calendar` - Root container.
* `.calendar__header` - Header row containing nav buttons and heading.
* `.calendar__heading` - Current month label.
* `.calendar__nav-button` - Previous/next navigation controls.
* `.calendar__grid` - Main day grid.
* `.calendar__grid-header` - Weekday header row wrapper.
* `.calendar__grid-body` - Date rows wrapper.
* `.calendar__header-cell` - Weekday header cell.
* `.calendar__cell` - Interactive day cell.
* `.calendar__cell-indicator` - Dot indicator inside a day cell.
* `.calendar-year-picker__trigger` - Year picker toggle button.
* `.calendar-year-picker__trigger-heading` - Heading text inside year picker trigger.
* `.calendar-year-picker__trigger-indicator` - Indicator icon inside year picker trigger.
* `.calendar-year-picker__year-grid` - Overlay grid of selectable years.
* `.calendar-year-picker__year-cell` - Individual year option.
### Interactive States
Calendar supports both pseudo-classes and React Aria data attributes:
* **Selected**: `[data-selected="true"]`
* **Today**: `[data-today="true"]`
* **Unavailable**: `[data-unavailable="true"]`
* **Outside month**: `[data-outside-month="true"]`
* **Hovered**: `:hover` or `[data-hovered="true"]`
* **Pressed**: `:active` or `[data-pressed="true"]`
* **Focus visible**: `:focus-visible` or `[data-focus-visible="true"]`
* **Disabled**: `:disabled` or `[data-disabled="true"]`
## API Reference
### Calendar Props
Calendar inherits all props from React Aria [Calendar](https://react-spectrum.adobe.com/react-aria/Calendar.html).
| Prop | Type | Default | Description |
| ------------------------ | ---------------------------------------------------------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `selectionMode` | `'single' \| 'multiple'` | `'single'` | Whether one or many dates can be selected. |
| `value` | `DateValue \| null` or `DateValue[] \| null` | - | Controlled selected date(s). Use an array when `selectionMode` is `multiple`. |
| `defaultValue` | `DateValue \| null` or `DateValue[] \| null` | - | Initial selected date(s) (uncontrolled). |
| `onChange` | `(value: DateValue \| null)` or `(value: DateValue[] \| null) => void` | - | Called when selection changes. |
| `focusedValue` | `DateValue` | - | Controlled focused date. |
| `onFocusChange` | `(value: DateValue) => void` | - | Called when focus moves to another date. |
| `minValue` | `DateValue` | Calendar-aware `1900-01-01` | Earliest selectable date. |
| `maxValue` | `DateValue` | Calendar-aware `2099-12-31` | Latest selectable date. |
| `weeksInMonth` | `number` | - | The number of weeks in a month. This overrides the default set by the locale. |
| `isDateUnavailable` | `(date: DateValue) => boolean` | - | Marks dates as unavailable. |
| `firstDayOfWeek` | `'sun' \| 'mon' \| 'tue' \| 'wed' \| 'thu' \| 'fri' \| 'sat'` | - | Overrides the locale default for the first day of the week. |
| `pageBehavior` | `'visible' \| 'single'` | `'visible'` | Whether paging advances by the visible duration or one unit at a time. |
| `selectionAlignment` | `'start' \| 'center' \| 'end'` | `'center'` | Aligns the visible range to the selection on initial render. |
| `isDisabled` | `boolean` | `false` | Disables interaction and selection. |
| `isReadOnly` | `boolean` | `false` | Keeps content readable but prevents selection changes. |
| `isInvalid` | `boolean` | `false` | Marks the calendar as invalid for validation UI. |
| `visibleDuration` | `{months?: number; weeks?: number; days?: number}` | `{months: 1}` | Visible time range. Use `{ months: n }` for month view, `{ weeks: n }` for week view, or `{ days: n }` for day view. |
| `defaultYearPickerOpen` | `boolean` | `false` | Initial open state of internal year picker. |
| `isYearPickerOpen` | `boolean` | - | Controlled year picker open state. |
| `onYearPickerOpenChange` | `(isOpen: boolean) => void` | - | Called when year picker open state changes. |
### Composition Parts
| Component | Description |
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `Calendar.Header` | Header container for navigation and heading. |
| `Calendar.Heading` | Formatted heading for the visible range. Supports `offset` (for multi-month layouts) and `format` (month/year/day options). |
| `Calendar.NavButton` | Previous/next navigation control (`slot=\"previous\"` or `slot=\"next\"`). |
| `Calendar.Grid` | Day grid for one month (`offset` supported for multi-month layouts). |
| `Calendar.GridHeader` | Weekday header container. |
| `Calendar.GridBody` | Date cell body container. |
| `Calendar.HeaderCell` | Weekday label cell. |
| `Calendar.Cell` | Individual date cell. |
| `Calendar.CellIndicator` | Optional indicator element for custom metadata. |
| `Calendar.YearPickerTrigger` | Trigger to toggle year-picker mode. |
| `Calendar.YearPickerTriggerHeading` | Localized heading content inside the year-picker trigger. |
| `Calendar.YearPickerTriggerIndicator` | Toggle icon inside the year-picker trigger. |
| `Calendar.YearPickerGrid` | Overlay year selection grid container. |
| `Calendar.YearPickerGridBody` | Body renderer for year grid cells. |
| `Calendar.YearPickerCell` | Individual year option cell. |
### Year Picker Parts
Year picker subcomponents inherit formatting props from React Aria [`useCalendarHeading`](https://react-aria.adobe.com/useCalendar#usecalendarheading) and [`useCalendarYearPicker`](https://react-aria.adobe.com/useCalendar#usecalendaryearpicker).
| Component | Prop | Type | Default | Description |
| ----------------------------------- | -------------- | ---------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `Calendar.YearPickerTriggerHeading` | `format` | `DateFormatterOptions` | - | Customize month/year label (e.g. `{month: 'short'}`). |
| `Calendar.YearPickerTriggerHeading` | `offset` | `{months?: number}` | - | Shift the heading relative to the focused date (multi-month layouts). |
| `Calendar.YearPickerGrid` | `format` | `DateFormatterOptions` | `{year: 'numeric'}` | Customize year cell labels (era, calendar system, etc.). |
| `Calendar.YearPickerGrid` | `visibleYears` | `number` | min–max span or `20` | Number of years shown in the sliding window. Defaults to the full range between `minValue` and `maxValue` when both are set. |
### Calendar.Cell Render Props
When `Calendar.Cell` children is a function, React Aria render props are available:
| Prop | Type | Description |
| ---------------- | --------- | ------------------------------------------- |
| `formattedDate` | `string` | Localized day label for the cell. |
| `isSelected` | `boolean` | Whether the date is selected. |
| `isUnavailable` | `boolean` | Whether the date is unavailable. |
| `isDisabled` | `boolean` | Whether the cell is disabled. |
| `isOutsideMonth` | `boolean` | Whether the date belongs to adjacent month. |
For a complete list of supported calendar systems and their identifiers, see:
* [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/) — date types (`CalendarDate`, `CalendarDateTime`, `ZonedDateTime`) and utilities used by all date components
* [`I18nProvider`](https://react-aria.adobe.com/I18nProvider) — override locale for a subtree
* [`useLocale`](https://react-aria.adobe.com/useLocale) — read the current locale and layout direction
# DateField
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/date-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(date-and-time)/date-field.mdx
> Date input field with labels, descriptions, and validation built on React Aria DateField
## Import
```tsx
import { DateField } from '@heroui/react';
```
### Usage
```tsx
"use client";
import {DateField, Label} from "@heroui/react";
export function Basic() {
return (
Date
{(segment) => }
);
}
```
### Anatomy
```tsx
import {DateField, Label, Description, FieldError} from '@heroui/react';
export default () => (
{(segment) => }
)
```
> **DateField** combines label, date input, description, and error into a single accessible component.
### With Description
```tsx
"use client";
import {DateField, Description, Label} from "@heroui/react";
export function WithDescription() {
return (
Birth date
{(segment) => }
Enter your date of birth
Appointment date
{(segment) => }
Enter a date for your appointment
);
}
```
### Required Field
```tsx
"use client";
import {DateField, Description, Label} from "@heroui/react";
export function Required() {
return (
Date
{(segment) => }
Start date
{(segment) => }
Required field
);
}
```
### Validation
Use `isInvalid` together with `FieldError` to surface validation messages.
```tsx
"use client";
import {DateField, FieldError, Label} from "@heroui/react";
export function Invalid() {
return (
Date
{(segment) => }
Please enter a valid date
Date
{(segment) => }
Date must be in the future
);
}
```
### With Validation
DateField supports validation with `minValue`, `maxValue`, and custom validation logic.
```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 (
Date
{(segment) => }
{isInvalid ? (
Date must be today or in the future
) : (
Enter a date from today onwards
)}
);
}
```
### Granularity
```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: "Day"},
{id: "hour", label: "Hour"},
{id: "minute", label: "Minute"},
{id: "second", label: "Second"},
] 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 (
Appointment Date
{(segment) => }
Granularity
Determines the smallest unit displayed in the date picker. By default, this is "day"
for dates, and "minute" for times.
setGranularity(value as typeof granularity)}
>
{granularityOptions.map((option) => (
{option.label}
))}
);
}
```
### Controlled
Control the value to synchronize with other components or state management.
```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 (
Date
{(segment) => }
Current value: {value ? value.toString() : "(empty)"}
setValue(today(getLocalTimeZone()))}>
Set today
setValue(null)}>
Clear
);
}
```
### Disabled State
```tsx
"use client";
import {DateField, Description, Label} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function Disabled() {
return (
Date
{(segment) => }
This date field is disabled
Date
{(segment) => }
This date field is disabled
);
}
```
### With Icons
Add prefix or suffix icons to enhance the date field.
```tsx
"use client";
import {Calendar} from "@gravity-ui/icons";
import {DateField, Label} from "@heroui/react";
export function WithPrefixIcon() {
return (
Date
{(segment) => }
);
}
```
```tsx
"use client";
import {Calendar} from "@gravity-ui/icons";
import {DateField, Label} from "@heroui/react";
export function WithSuffixIcon() {
return (
Date
{(segment) => }
);
}
```
```tsx
"use client";
import {Calendar, ChevronDown} from "@gravity-ui/icons";
import {DateField, Description, Label} from "@heroui/react";
export function WithPrefixAndSuffix() {
return (
Date
{(segment) => }
Enter a date
);
}
```
### Full Width
```tsx
"use client";
import {Calendar, ChevronDown} from "@gravity-ui/icons";
import {DateField, Label} from "@heroui/react";
export function FullWidth() {
return (
Date
{(segment) => }
Date
{(segment) => }
);
}
```
### Variants
The DateField.Group component supports two visual variants:
* **`primary`** (default) - Standard styling with shadow, suitable for most use cases
* **`secondary`** - Lower emphasis variant without shadow, suitable for use in Surface components
```tsx
"use client";
import {DateField, Label} from "@heroui/react";
export function Variants() {
return (
Primary variant
{(segment) => }
Secondary variant
{(segment) => }
);
}
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` on DateField.Group to apply the lower emphasis variant suitable for surface backgrounds.
```tsx
"use client";
import {Calendar} from "@gravity-ui/icons";
import {DateField, Description, Label, Surface} from "@heroui/react";
export function OnSurface() {
return (
Date
{(segment) => }
Enter a date
Appointment date
{(segment) => }
Enter a date for your appointment
);
}
```
### Form Example
Complete form example with validation and submission handling.
```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 submitted:", {date: value});
setValue(null);
setIsSubmitting(false);
}, 1500);
};
return (
);
}
```
## 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
### Custom Render Function
```tsx
"use client";
import {DateField, Label} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
}>Date
}>
}>
{(segment) => }
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import {DateField, Label, Description} from '@heroui/react';
function CustomDateField() {
return (
Appointment date
{(segment) => }
Select a date for your appointment.
);
}
```
### Customizing the component classes
DateField has minimal default styling. Override the `.date-field` class to customize the container styling.
```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 Classes
* `.date-field` – Root container with minimal styling (`flex flex-col gap-1`)
> **Note:** Child components ([Label](/docs/components/label), [Description](/docs/components/description), [FieldError](/docs/components/field-error)) have their own CSS classes and styling. See their respective documentation for customization options. DateField.Group styling is documented below in the API Reference section.
### Interactive States
DateField automatically manages these data attributes based on its state:
* **Invalid**: `[data-invalid="true"]` or `[aria-invalid="true"]` - Automatically hides the description slot when invalid
* **Required**: `[data-required="true"]` - Applied when `isRequired` is true
* **Disabled**: `[data-disabled="true"]` - Applied when `isDisabled` is true
* **Focus Within**: `[data-focus-within="true"]` - Applied when any child input is focused
## API Reference
### DateField Props
DateField inherits all props from React Aria's [DateField](https://react-aria.adobe.com/DateField.md) component.
#### Base Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------------------------ | ------- | ------------------------------------------------------------------- |
| `children` | `React.ReactNode \| (values: DateFieldRenderProps) => React.ReactNode` | - | Child components (Label, DateField.Group, etc.) or render function. |
| `className` | `string \| (values: DateFieldRenderProps) => string` | - | CSS classes for styling, supports render props. |
| `style` | `React.CSSProperties \| (values: DateFieldRenderProps) => React.CSSProperties` | - | Inline styles, supports render props. |
| `fullWidth` | `boolean` | `false` | Whether the date field should take full width of its container |
| `id` | `string` | - | The element's unique identifier. |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
#### Value Props
| Prop | Type | Default | Description |
| ------------------ | ------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------- |
| `value` | `DateValue \| null` | - | Current value (controlled). Uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) types. |
| `defaultValue` | `DateValue \| null` | - | Default value (uncontrolled). Uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) types. |
| `onChange` | `(value: DateValue \| null) => void` | - | Handler called when the value changes. |
| `placeholderValue` | `DateValue \| null` | - | Placeholder date that influences the format of the placeholder. |
#### Validation Props
| Prop | Type | Default | Description |
| -------------------- | -------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `isRequired` | `boolean` | `false` | Whether user input is required before form submission. |
| `isInvalid` | `boolean` | - | Whether the value is invalid. |
| `minValue` | `DateValue \| null` | - | The minimum allowed date that a user may select. Uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) types. |
| `maxValue` | `DateValue \| null` | - | The maximum allowed date that a user may select. Uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) types. |
| `isDateUnavailable` | `(date: DateValue) => boolean` | - | Callback that is called for each date. If it returns true, the date is unavailable. |
| `validate` | `(value: DateValue) => ValidationError \| true \| null \| undefined` | - | Custom validation function. |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | Whether to use native HTML form validation or ARIA attributes. |
#### Format Props
| Prop | Type | Default | Description |
| ------------------------- | ------------- | ------- | -------------------------------------------------------------------------------------------- |
| `granularity` | `Granularity` | - | Determines the smallest unit displayed. Defaults to `"day"` for dates, `"minute"` for times. |
| `hourCycle` | `12 \| 24` | - | Whether to display time in 12 or 24 hour format. By default, determined by locale. |
| `hideTimeZone` | `boolean` | `false` | Whether to hide the time zone abbreviation. |
| `shouldForceLeadingZeros` | `boolean` | - | Whether to always show leading zeros in month, day, and hour fields. |
#### State Props
| Prop | Type | Default | Description |
| ------------ | --------- | ------- | -------------------------------------------------- |
| `isDisabled` | `boolean` | - | Whether the input is disabled. |
| `isReadOnly` | `boolean` | - | Whether the input can be selected but not changed. |
#### Form Props
| Prop | Type | Default | Description |
| -------------- | --------- | ------- | -------------------------------------------------------------------------------- |
| `name` | `string` | - | Name of the input element, for HTML form submission. Submits as ISO 8601 string. |
| `autoFocus` | `boolean` | - | Whether the element should receive focus on render. |
| `autoComplete` | `string` | - | Type of autocomplete functionality the input should provide. |
#### Accessibility Props
| Prop | Type | Default | Description |
| ------------------ | -------- | ------- | ----------------------------------------------------- |
| `aria-label` | `string` | - | Accessibility label when no visible label is present. |
| `aria-labelledby` | `string` | - | ID of elements that label this field. |
| `aria-describedby` | `string` | - | ID of elements that describe this field. |
| `aria-details` | `string` | - | ID of elements with additional details. |
### Composition Components
DateField works with these separate components that should be imported and used directly:
* **Label** - Field label component from `@heroui/react`
* **DateField.Group** - Date input group component (documented below)
* **DateField.Input** - Input component with segmented editing from `@heroui/react`
* **DateField.InputContainer** - Scrollable container for grouping multiple inputs (e.g. start/end range inputs) with horizontal overflow
* **DateField.Segment** - Individual date segment (year, month, day, etc.)
* **DateField.Prefix** / **DateField.Suffix** - Prefix and suffix slots for the input group
* **Description** - Helper text component from `@heroui/react`
* **FieldError** - Validation error message from `@heroui/react`
Each of these components has its own props API. Use them directly within DateField for composition:
```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 Types
DateField uses types from [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/):
* `CalendarDate` - Date without time or timezone
* `CalendarDateTime` - Date with time but no timezone
* `ZonedDateTime` - Date with time and timezone
* `Time` - Time only
Example:
```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
{/* ... */}
```
> **Note:** DateField uses the [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) package for date manipulation, parsing, and type definitions. See the [Internationalized Date documentation](https://react-aria.adobe.com/internationalized/date/) for more information about available types and functions.
### DateFieldRenderProps
When using render props with `className`, `style`, or `children`, these values are available:
| Prop | Type | Description |
| ---------------- | --------- | ----------------------------------------------- |
| `isDisabled` | `boolean` | Whether the field is disabled. |
| `isInvalid` | `boolean` | Whether the field is currently invalid. |
| `isReadOnly` | `boolean` | Whether the field is read-only. |
| `isRequired` | `boolean` | Whether the field is required. |
| `isFocused` | `boolean` | Whether the field is currently focused. |
| `isFocusWithin` | `boolean` | Whether any child element is focused. |
| `isFocusVisible` | `boolean` | Whether focus is visible (keyboard navigation). |
### DateField.Group Props
DateField.Group accepts all props from React Aria's `Group` component plus the following:
| Prop | Type | Default | Description |
| ----------- | -------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `className` | `string` | - | Tailwind classes merged with the component styles. |
| `fullWidth` | `boolean` | `false` | Whether the date input group should take full width of its container |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
### DateField.Input Props
DateField.Input accepts all props from React Aria's `DateInput` component plus the following:
| Prop | Type | Default | Description |
| ----------- | -------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `className` | `string` | - | Tailwind classes merged with the component styles. |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the input. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
The `DateField.Input` component accepts a render prop function that receives date segments. Each segment represents a part of the date (year, month, day, etc.).
### DateField.Segment Props
DateField.Segment accepts all props from React Aria's `DateSegment` component:
| Prop | Type | Default | Description |
| ----------- | ------------- | ------- | ------------------------------------------------------------- |
| `segment` | `DateSegment` | - | The date segment object from the DateField.Input render prop. |
| `className` | `string` | - | Tailwind classes merged with the component styles. |
### DateField.InputContainer Props
DateField.InputContainer accepts standard HTML `div` attributes:
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ----------------------------------------------------------------------------------------------------- |
| `className` | `string` | - | Tailwind classes merged with the component styles. |
| `children` | `ReactNode` | - | Content to display inside the scrollable container (typically multiple `DateField.Input` components). |
### DateField.Prefix Props
DateField.Prefix accepts standard HTML `div` attributes:
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | -------------------------------------------------- |
| `className` | `string` | - | Tailwind classes merged with the component styles. |
| `children` | `ReactNode` | - | Content to display in the prefix slot. |
### DateField.Suffix Props
DateField.Suffix accepts standard HTML `div` attributes:
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | -------------------------------------------------- |
| `className` | `string` | - | Tailwind classes merged with the component styles. |
| `children` | `ReactNode` | - | Content to display in the suffix slot. |
## DateField.Group Styling
### Customizing the component classes
The base classes power every instance. Override them once with `@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 Classes
* `.date-input-group` – Root container styling
* `.date-input-group__input` – Input wrapper styling
* `.date-input-group__input-container` – Scrollable container for grouping multiple inputs
* `.date-input-group__segment` – Individual date segment styling
* `.date-input-group__prefix` – Prefix element styling
* `.date-input-group__suffix` – Suffix element styling
### DateField.Group Interactive States
* **Hover**: `:hover` or `[data-hovered="true"]`
* **Focus Within**: `[data-focus-within="true"]` or `:focus-within`
* **Invalid**: `[data-invalid="true"]` (also syncs with `aria-invalid`)
* **Disabled**: `[data-disabled="true"]` or `[aria-disabled="true"]`
* **Segment Focus**: `:focus` or `[data-focused="true"]` on segment elements
* **Segment Placeholder**: `[data-placeholder="true"]` on segment elements
# DatePicker
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/date-picker
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(date-and-time)/date-picker.mdx
> Composable date picker built on React Aria DatePicker with DateField and Calendar composition
## Import
```tsx
import { DatePicker, DateField, Calendar, Label } from '@heroui/react';
```
### Usage
```tsx
"use client";
import {Calendar, DateField, DatePicker, Label} from "@heroui/react";
export function Basic() {
return (
Date
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### Anatomy
`DatePicker` follows a composition-first API. Compose `DateField` and `Calendar` explicitly to control structure and styling.
```tsx
import {Calendar, DateField, DatePicker, Label} from '@heroui/react';
export default () => (
{(segment) => }
{(day) => {day} }
{(date) => }
)
```
### Controlled
```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 (
Date
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
Current value: {value ? value.toString() : "(empty)"}
setValue(today(getLocalTimeZone()))}>
Set today
setValue(null)}>
Clear
);
}
```
### Validation
```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 (
Appointment date
{(segment) => }
Date must be today or in the future.
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### Format Options
Control how DatePicker values are displayed with props such as `granularity`, `hourCycle`, `hideTimeZone`, and `shouldForceLeadingZeros`.
```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: "Day", value: "day"},
{label: "Hour", value: "hour"},
{label: "Minute", value: "minute"},
{label: "Second", value: "second"},
];
const hourCycleOptions: {label: string; value: HourCycle}[] = [
{label: "12-hour", value: 12},
{label: "24-hour", 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}) => (
<>
Date and time
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
{!!showTimeField && (
Time
state.setTimeValue(v as TimeValue)}
>
{(segment) => }
)}
>
)}
setGranularity(value as Granularity)}
>
Granularity
{granularityOptions.map((option) => (
{option.label}
))}
setHourCycle(Number(value) as HourCycle)}
>
Hour cycle
{hourCycleOptions.map((option) => (
{option.label}
))}
Hide timezone
Force leading zeros
);
}
```
### Disabled
```tsx
"use client";
import {Calendar, DateField, DatePicker, Description, Label} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function Disabled() {
return (
Date
{(segment) => }
This date picker is disabled.
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### Custom Indicator
`DatePicker.TriggerIndicator` renders the default `IconCalendar` when no children are provided. Pass children to replace it.
```tsx
"use client";
import {Calendar, DateField, DatePicker, Description, Label} from "@heroui/react";
import {Icon} from "@iconify/react";
export function WithCustomIndicator() {
return (
Date
{(segment) => }
Replace the default calendar icon by passing custom children.
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### Form Example
```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 (
);
}
```
### International Calendar
By default, DatePicker displays dates using the calendar system for the user's locale. You can override this by wrapping your DatePicker with `I18nProvider` and setting the [Unicode calendar locale extension](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string).
The example below shows the Indian calendar system:
```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 (
Event date
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
**Note:** The `onChange` event always returns a date in the same calendar system as the `value` or `defaultValue` (Gregorian if no value is provided), regardless of the displayed locale. This ensures your application logic works consistently with a single calendar system while still displaying dates in the user's preferred format.
For a complete list of supported calendar systems and their identifiers, see:
* [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)
### Custom Render Function
```tsx
"use client";
import {Calendar, DateField, DatePicker, Label} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
}>Date
}
>
}>
{(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
## Styling
### Passing Tailwind CSS classes
You can style each composition part independently:
```tsx
import {Calendar, DateField, DatePicker, Label} from '@heroui/react';
function CustomDatePicker() {
return (
Date
{(segment) => }
{/* Calendar parts */}
);
}
```
### Customizing the component classes
To customize DatePicker base classes, use `@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 follows [BEM](https://getbem.com/) naming for reusable customization.
### CSS Classes
DatePicker uses these classes in `packages/styles/components/date-picker.css`:
* `.date-picker` - Root wrapper.
* `.date-picker__trigger` - Trigger part that opens the popover.
* `.date-picker__trigger-indicator` - Default/custom indicator slot.
* `.date-picker__popover` - Popover content wrapper.
### Interactive States
DatePicker supports React Aria data attributes and pseudo states:
* **Open**: `[data-open="true"]` on trigger.
* **Disabled**: `[data-disabled="true"]` or `[aria-disabled="true"]` on trigger.
* **Focus visible**: `:focus-visible` or `[data-focus-visible="true"]` on trigger.
* **Hover**: `:hover` or `[data-hovered="true"]` on trigger.
## API Reference
### DatePicker Props
DatePicker inherits all props from React Aria [DatePicker](https://react-aria.adobe.com/DatePicker.md).
| Prop | Type | Default | Description |
| -------------- | ----------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- |
| `value` | `DateValue \| null` | - | Controlled selected date value. |
| `defaultValue` | `DateValue \| null` | - | Default selected value in uncontrolled mode. |
| `onChange` | `(value: DateValue \| null) => void` | - | Called when selected date changes. |
| `isOpen` | `boolean` | - | Controlled popover open state. |
| `defaultOpen` | `boolean` | `false` | Initial popover open state. |
| `onOpenChange` | `(isOpen: boolean) => void` | - | Called when popover open state changes. |
| `isDisabled` | `boolean` | `false` | Disables date selection and trigger interactions. |
| `isInvalid` | `boolean` | - | Marks the field as invalid for validation state. |
| `minValue` | `DateValue` | - | Minimum selectable date. |
| `maxValue` | `DateValue` | - | Maximum selectable date. |
| `name` | `string` | - | Name used for HTML form submission. |
| `children` | `ReactNode \| (values: DatePickerRenderProps) => ReactNode` | - | Composed content or render function. |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Composition Parts
| Component | Description |
| ----------------------------- | ----------------------------------------------------------- |
| `DatePicker.Root` | Root date picker container and state owner. |
| `DatePicker.Trigger` | Trigger button, usually rendered inside `DateField.Suffix`. |
| `DatePicker.TriggerIndicator` | Indicator slot with default calendar icon. |
| `DatePicker.Popover` | Popover wrapper for `Calendar` content. |
### Related packages
* [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) — date types (`CalendarDate`, `CalendarDateTime`, `ZonedDateTime`) and utilities used by all date components
* [`I18nProvider`](https://react-aria.adobe.com/I18nProvider) — override locale for a subtree
* [`useLocale`](https://react-aria.adobe.com/useLocale) — read the current locale and layout direction
# DateRangePicker
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/date-range-picker
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(date-and-time)/date-range-picker.mdx
> Composable date range picker built on React Aria DateRangePicker with DateField and RangeCalendar composition
## Import
```tsx
import { DateField, DateRangePicker, Label, RangeCalendar } from '@heroui/react';
```
### Usage
```tsx
"use client";
import {DateField, DateRangePicker, Label, RangeCalendar} from "@heroui/react";
export function Basic() {
return (
Trip dates
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### Anatomy
`DateRangePicker` follows a composition-first API. Compose `DateField` and `RangeCalendar` explicitly to control structure and styling.
```tsx
import {DateField, DateRangePicker, Label, RangeCalendar} from '@heroui/react';
export default () => (
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
)
```
### Controlled
```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 (
Trip dates
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
Current value: {value ? `${value.start.toString()} -> ${value.end.toString()}` : "(empty)"}
{
const nextStart = today(getLocalTimeZone());
setValue({end: nextStart.add({days: 6}), start: nextStart});
}}
>
Set week
setValue(null)}>
Clear
);
}
```
### Validation
```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 (
Booking period
{(segment) => }
{(segment) => }
Select a valid range starting today or later.
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### Format Options
Control how DateRangePicker values are displayed with props such as `granularity`, `hourCycle`, `hideTimeZone`, and `shouldForceLeadingZeros`.
```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: "Day", value: "day"},
{label: "Hour", value: "hour"},
{label: "Minute", value: "minute"},
{label: "Second", value: "second"},
];
const hourCycleOptions: {label: string; value: HourCycle}[] = [
{label: "12-hour", value: 12},
{label: "24-hour", 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}) => (
<>
Date range
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
{!!showTimeField && (
Start Time
state.setTimeRange({
end: state.timeRange?.end as TimeValue,
start: v as TimeValue,
})
}
>
{(segment) => }
End Time
state.setTimeRange({
end: v as TimeValue,
start: state.timeRange?.start as TimeValue,
})
}
>
{(segment) => }
)}
Selected:{" "}
{state.value && state.value.start && state.value.end
? formatDate({end: state.value.end, start: state.value.start})
: "No date selected"}
>
)}
Format Options
setGranularity(value as Granularity)}
>
Granularity
{granularityOptions.map((option) => (
{option.label}
))}
setHourCycle(Number(value) as HourCycle)}
>
Hour cycle
{hourCycleOptions.map((option) => (
{option.label}
))}
Hide timezone
Force leading zeros
);
}
```
### Disabled
```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 (
Trip dates
{(segment) => }
{(segment) => }
This date range picker is disabled.
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### Custom Indicator
`DateRangePicker.TriggerIndicator` renders the default `IconCalendar` when no children are provided. Pass children to replace it.
```tsx
"use client";
import {DateField, DateRangePicker, Description, Label, RangeCalendar} from "@heroui/react";
import {Icon} from "@iconify/react";
export function WithCustomIndicator() {
return (
Trip dates
{(segment) => }
{(segment) => }
Replace the default calendar icon by passing custom children.
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### Form Example
```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 (
);
}
```
### International Calendar
By default, DateRangePicker displays dates using the calendar system for the user's locale. You can override this by wrapping your DateRangePicker with `I18nProvider` and setting the [Unicode calendar locale extension](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string).
The example below shows the Indian calendar system:
```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 (
Trip dates
{(segment) => }
{(segment) => }
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
**Note:** The `onChange` event always returns dates in the same calendar system as the `value` or `defaultValue` (Gregorian if no value is provided), regardless of the displayed locale.
For a complete list of supported calendar systems and their identifiers, see:
* [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)
### Custom Render Function
```tsx
"use client";
import {DateField, DateRangePicker, Label, RangeCalendar} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
startName="startDate"
>
Trip dates
{(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
## Styling
### Passing Tailwind CSS classes
You can style each composition part independently:
```tsx
import {DateField, DateRangePicker, Label, RangeCalendar} from '@heroui/react';
function CustomDateRangePicker() {
return (
Trip dates
{(segment) => }
{(segment) => }
{/* RangeCalendar parts */}
);
}
```
### Customizing the component classes
To customize DateRangePicker base classes, use `@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 follows [BEM](https://getbem.com/) naming for reusable customization.
### CSS Classes
DateRangePicker uses these classes in `packages/styles/components/date-range-picker.css`:
* `.date-range-picker` - Root wrapper.
* `.date-range-picker__trigger` - Trigger part that opens the popover.
* `.date-range-picker__trigger-indicator` - Default/custom indicator slot.
* `.date-range-picker__range-separator` - Separator between start and end date inputs.
* `.date-range-picker__popover` - Popover content wrapper.
### Interactive States
DateRangePicker supports React Aria data attributes and pseudo states:
* **Open**: `[data-open="true"]` on trigger.
* **Disabled**: `[data-disabled="true"]` or `[aria-disabled="true"]` on trigger.
* **Focus visible**: `:focus-visible` or `[data-focus-visible="true"]` on trigger.
* **Hover**: `:hover` or `[data-hovered="true"]` on trigger.
## API Reference
### DateRangePicker Props
DateRangePicker inherits all props from React Aria [DateRangePicker](https://react-aria.adobe.com/DateRangePicker).
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- |
| `value` | `{ start: DateValue; end: DateValue } \| null` | - | Controlled selected date range value. |
| `defaultValue` | `{ start: DateValue; end: DateValue } \| null` | - | Default selected range in uncontrolled mode. |
| `onChange` | `(value: { start: DateValue; end: DateValue } \| null) => void` | - | Called when selected range changes. |
| `isOpen` | `boolean` | - | Controlled popover open state. |
| `defaultOpen` | `boolean` | `false` | Initial popover open state. |
| `onOpenChange` | `(isOpen: boolean) => void` | - | Called when popover open state changes. |
| `isDisabled` | `boolean` | `false` | Disables range selection and trigger interactions. |
| `isInvalid` | `boolean` | - | Marks the field as invalid for validation state. |
| `minValue` | `DateValue` | - | Minimum selectable date. |
| `maxValue` | `DateValue` | - | Maximum selectable date. |
| `startName` | `string` | - | Name used for the start date in HTML form submission. |
| `endName` | `string` | - | Name used for the end date in HTML form submission. |
| `children` | `ReactNode \| (values: DateRangePickerRenderProps) => ReactNode` | - | Composed content or render function. |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Composition Parts
| Component | Description |
| ---------------------------------- | ----------------------------------------------------------- |
| `DateRangePicker.Root` | Root date range picker container and state owner. |
| `DateRangePicker.Trigger` | Trigger button, usually rendered inside `DateField.Suffix`. |
| `DateRangePicker.TriggerIndicator` | Indicator slot with default calendar icon. |
| `DateRangePicker.RangeSeparator` | Separator part between start and end date inputs. |
| `DateRangePicker.Popover` | Popover wrapper for `RangeCalendar` content. |
### Related packages
* [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) — date types (`CalendarDate`, `CalendarDateTime`, `ZonedDateTime`) and utilities used by all date components
* [`I18nProvider`](https://react-aria.adobe.com/I18nProvider) — override locale for a subtree
* [`useLocale`](https://react-aria.adobe.com/useLocale) — read the current locale and layout direction
# RangeCalendar
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/range-calendar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(date-and-time)/range-calendar.mdx
> Composable date range picker with month grid, navigation, and year picker support built on React Aria RangeCalendar
## Import
```tsx
import { RangeCalendar } from '@heroui/react';
```
### Usage
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
export function Basic() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### Anatomy
```tsx
import {RangeCalendar} from '@heroui/react';
export default () => (
{(day) => {day} }
{(date) => }
)
```
### Year Picker
`RangeCalendar.YearPickerTrigger`, `RangeCalendar.YearPickerGrid`, and their body/cell subcomponents provide an integrated year navigation pattern.
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
export function YearPicker() {
return (
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
### Default Value
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
import {parseDate} from "@internationalized/date";
export function DefaultValue() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### Controlled
```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);
}}
>
This week
{
const nextWeekStart = startOfWeek(today(getLocalTimeZone()).add({weeks: 1}), locale);
setFocusedDate(nextWeekStart);
}}
>
Next week
{
const nextMonthStart = startOfMonth(today(getLocalTimeZone()).add({months: 1}));
setFocusedDate(nextMonthStart);
}}
>
Next month
{(day) => {day} }
{(date) => }
Selected range: {value ? `${value.start.toString()} -> ${value.end.toString()}` : "(none)"}
{
const start = today(getLocalTimeZone());
setValue({end: start.add({days: 6}), start});
setFocusedDate(start);
}}
>
Set 1 week
{
const start = parseDate("2025-12-20");
setValue({end: parseDate("2025-12-31"), start});
setFocusedDate(start);
}}
>
Set Holidays
setValue(null)}>
Clear
);
}
```
### Min and Max Dates
```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) => }
Select dates between today and {maxDate.toString()}
);
}
```
### Unavailable Dates
Use `isDateUnavailable` to block dates such as weekends, holidays, or booked slots.
```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) => }
Some days are unavailable
);
}
```
### Anchor-Based Unavailable Dates
When selecting a range, `isDateUnavailable` receives a second argument, `anchorDate`, set to the first selected date. Use it to limit which end dates are valid (for example, within 7 days of the start).
```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) => }
After selecting a start date, only dates within 7 days are available
);
}
```
### Weeks in Month
Set `weeksInMonth` to a fixed value (for example, `6`) to keep the grid height stable when navigating between months.
```tsx
"use client";
import {Description, RangeCalendar} from "@heroui/react";
export function WeeksInMonth() {
return (
{(day) => {day} }
{(date) => }
Always shows 6 weeks per month to avoid layout shift when navigating
);
}
```
### Week View
Set `visibleDuration={{ weeks: n }}` to show one or more weeks at a time. Navigation advances by the visible week range. Use `pageBehavior="single"` to move one week at a time when showing multiple weeks.
```tsx
"use client";
import {Label, ListBox, RangeCalendar, Select} from "@heroui/react";
import {useState} from "react";
const weekOptions = [
{id: "1", name: "1 week"},
{id: "2", name: "2 weeks"},
{id: "3", name: "3 weeks"},
{id: "4", name: "4 weeks"},
{id: "5", name: "5 weeks"},
{id: "6", name: "6 weeks"},
{id: "8", name: "8 weeks"},
] as const;
export function WeekView() {
const [weeks, setWeeks] = useState(1);
return (
value && setWeeks(Number(value))}
>
Visible weeks
{weekOptions.map((option) => (
{option.name}
))}
{(day) => {day} }
{(date) => }
);
}
```
### Day View
Set `visibleDuration={{ days: n }}` to show a rolling window of consecutive days. Navigation advances by the visible day range. Use `pageBehavior="single"` to move one day at a time when showing multiple days.
```tsx
"use client";
import {Label, ListBox, RangeCalendar, Select} from "@heroui/react";
import {useState} from "react";
const dayOptions = [
{id: "1", name: "1 day"},
{id: "5", name: "5 days"},
{id: "7", name: "7 days"},
{id: "8", name: "8 days"},
{id: "10", name: "10 days"},
{id: "14", name: "14 days"},
{id: "21", name: "21 days"},
] as const;
export function DayView() {
const [days, setDays] = useState(5);
return (
value && setDays(Number(value))}
>
Visible days
{dayOptions.map((option) => (
{option.name}
))}
{(day) => {day} }
{(date) => }
);
}
```
### Allows Non-Contiguous Ranges
Enable `allowsNonContiguousRanges` to allow selection across unavailable dates.
```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) => }
Non-contiguous ranges are allowed across unavailable dates
);
}
```
### Disabled
```tsx
"use client";
import {Description, RangeCalendar} from "@heroui/react";
export function Disabled() {
return (
{(day) => {day} }
{(date) => }
Range calendar is disabled
);
}
```
### Read Only
```tsx
"use client";
import {Description, RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";
export function ReadOnly() {
return (
{(day) => {day} }
{(date) => }
Range calendar is read-only
);
}
```
### Invalid
```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 ? (
Maximum stay duration is 1 week
) : (
Select a stay of up to 7 days
)}
);
}
```
### Focused Value
```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) => }
Focused: {focusedDate.toString()}
setFocusedDate(parseDate("2025-01-01"))}
>
Go to Jan
setFocusedDate(parseDate("2025-06-15"))}
>
Go to Jun
setFocusedDate(parseDate("2025-12-25"))}
>
Go to Christmas
);
}
```
### Cell Indicators
You can customize `RangeCalendar.Cell` children and use `RangeCalendar.CellIndicator` to display metadata like events.
```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)) && (
)}
>
)}
)}
);
}
```
### Multiple Months
Render multiple grids with `visibleDuration` and `offset` for booking and planning experiences. Use `RangeCalendar.Heading` with an `offset` (for example, `offset={{ months: 1 }}`) in each column header to label that month.
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
export function MultipleMonths() {
return (
{(day) => {day} }
{(date) => }
{(day) => {day} }
{(date) => }
);
}
```
### International Calendars
By default, RangeCalendar displays dates using the calendar system for the user's locale. You can override this by wrapping your RangeCalendar with `I18nProvider` and setting the [Unicode calendar locale extension](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string).
The example below shows the Indian calendar system:
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
import {I18nProvider} from "react-aria-components";
export function InternationalCalendar() {
return (
{(day) => {day} }
{(date) => }
{({year}) => }
);
}
```
**Note:** The `onChange` event always returns a date in the same calendar system as the `value` or `defaultValue` (Gregorian if no value is provided), regardless of the displayed locale.
### Real-World Example
```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) && }
>
)}
)}
Blocked dates
Weekend/Unavailable
{selectedRange ? (
Book {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
## Styling
### Passing Tailwind CSS classes
```tsx
import {RangeCalendar} from '@heroui/react';
function CustomRangeCalendar() {
return (
{(day) => {day} }
{(date) => }
);
}
```
### Customizing the component classes
```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 Classes
RangeCalendar uses these classes in `packages/styles/components/range-calendar.css` and `packages/styles/components/calendar-year-picker.css`:
* `.range-calendar` - Root container.
* `.range-calendar__header` - Header row containing nav buttons and heading.
* `.range-calendar__heading` - Current month label.
* `.range-calendar__nav-button` - Previous/next navigation controls.
* `.range-calendar__grid` - Main day grid.
* `.range-calendar__grid-header` - Weekday header row wrapper.
* `.range-calendar__grid-body` - Date rows wrapper.
* `.range-calendar__header-cell` - Weekday header cell.
* `.range-calendar__cell` - Interactive day cell wrapper.
* `.range-calendar__cell-button` - Interactive day button inside each cell.
* `.range-calendar__cell-indicator` - Dot indicator inside a day cell.
* `.calendar-year-picker__trigger` - Year picker toggle button.
* `.calendar-year-picker__trigger-heading` - Heading text inside year picker trigger.
* `.calendar-year-picker__trigger-indicator` - Indicator icon inside year picker trigger.
* `.calendar-year-picker__year-grid` - Overlay grid of selectable years.
* `.calendar-year-picker__year-cell` - Individual year option.
### Interactive States
RangeCalendar supports both pseudo-classes and React Aria data attributes:
* **Selected**: `[data-selected="true"]`
* **Selection start**: `[data-selection-start="true"]`
* **Selection end**: `[data-selection-end="true"]`
* **Range middle**: `[data-selection-in-range="true"]`
* **Today**: `[data-today="true"]`
* **Unavailable**: `[data-unavailable="true"]`
* **Outside month**: `[data-outside-month="true"]`
* **Hovered**: `:hover` or `[data-hovered="true"]`
* **Pressed**: `:active` or `[data-pressed="true"]`
* **Focus visible**: `:focus-visible` or `[data-focus-visible="true"]`
* **Disabled**: `:disabled` or `[data-disabled="true"]`
## API Reference
### RangeCalendar Props
RangeCalendar inherits all props from React Aria [RangeCalendar](https://react-spectrum.adobe.com/react-aria/RangeCalendar.html).
| Prop | Type | Default | Description |
| --------------------------- | ---------------------------------------------------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `value` | `RangeValue \| null` | - | Controlled selected range. |
| `defaultValue` | `RangeValue \| null` | - | Initial selected range (uncontrolled). |
| `onChange` | `(value: RangeValue) => void` | - | Called when selection changes. |
| `focusedValue` | `DateValue` | - | Controlled focused date. |
| `onFocusChange` | `(value: DateValue) => void` | - | Called when focus moves to another date. |
| `minValue` | `DateValue` | Calendar-aware `1900-01-01` | Earliest selectable date. |
| `maxValue` | `DateValue` | Calendar-aware `2099-12-31` | Latest selectable date. |
| `weeksInMonth` | `number` | - | The number of weeks in a month. This overrides the default set by the locale. |
| `isDateUnavailable` | `(date: DateValue, anchorDate: CalendarDate \| null) => boolean` | - | Marks dates as unavailable. When `anchorDate` is set, it is the first date the user selected in the current range gesture. |
| `firstDayOfWeek` | `'sun' \| 'mon' \| 'tue' \| 'wed' \| 'thu' \| 'fri' \| 'sat'` | - | Overrides the locale default for the first day of the week. |
| `pageBehavior` | `'visible' \| 'single'` | `'visible'` | Whether paging advances by the visible duration or one unit at a time. |
| `selectionAlignment` | `'start' \| 'center' \| 'end'` | `'center'` | Aligns the visible range to the selection on initial render. |
| `allowsNonContiguousRanges` | `boolean` | `false` | Allows ranges that span unavailable dates. |
| `isDisabled` | `boolean` | `false` | Disables interaction and selection. |
| `isReadOnly` | `boolean` | `false` | Keeps content readable but prevents selection changes. |
| `isInvalid` | `boolean` | `false` | Marks the calendar as invalid for validation UI. |
| `visibleDuration` | `{months?: number; weeks?: number; days?: number}` | `{months: 1}` | Visible time range. Use `{ months: n }` for month view, `{ weeks: n }` for week view, or `{ days: n }` for day view. |
| `defaultYearPickerOpen` | `boolean` | `false` | Initial open state of internal year picker. |
| `isYearPickerOpen` | `boolean` | - | Controlled year picker open state. |
| `onYearPickerOpenChange` | `(isOpen: boolean) => void` | - | Called when year picker open state changes. |
### Composition Parts
| Component | Description |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- |
| `RangeCalendar.Header` | Header container for navigation and heading. |
| `RangeCalendar.Heading` | Formatted heading for the visible range. Supports `offset` (for multi-month layouts) and `format` (month/year/day options). |
| `RangeCalendar.NavButton` | Previous/next navigation control (`slot="previous"` or `slot="next"`). |
| `RangeCalendar.Grid` | Day grid for one month (`offset` supported for multi-month layouts). |
| `RangeCalendar.GridHeader` | Weekday header container. |
| `RangeCalendar.GridBody` | Date cell body container. |
| `RangeCalendar.HeaderCell` | Weekday label cell. |
| `RangeCalendar.Cell` | Individual date cell. |
| `RangeCalendar.CellIndicator` | Optional indicator element for custom metadata. |
| `RangeCalendar.YearPickerTrigger` | Trigger to toggle year-picker mode. |
| `RangeCalendar.YearPickerTriggerHeading` | Localized heading content inside the year-picker trigger. |
| `RangeCalendar.YearPickerTriggerIndicator` | Toggle icon inside the year-picker trigger. |
| `RangeCalendar.YearPickerGrid` | Overlay year selection grid container. |
| `RangeCalendar.YearPickerGridBody` | Body renderer for year grid cells. |
| `RangeCalendar.YearPickerCell` | Individual year option cell. |
### Year Picker Parts
Year picker subcomponents inherit formatting props from React Aria [`useCalendarHeading`](https://react-aria.adobe.com/useCalendar#usecalendarheading) and [`useCalendarYearPicker`](https://react-aria.adobe.com/useCalendar#usecalendaryearpicker).
| Component | Prop | Type | Default | Description |
| ---------------------------------------- | -------------- | ---------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `RangeCalendar.YearPickerTriggerHeading` | `format` | `DateFormatterOptions` | - | Customize month/year label (e.g. `{month: 'short'}`). |
| `RangeCalendar.YearPickerTriggerHeading` | `offset` | `{months?: number}` | - | Shift the heading relative to the focused date (multi-month layouts). |
| `RangeCalendar.YearPickerGrid` | `format` | `DateFormatterOptions` | `{year: 'numeric'}` | Customize year cell labels (era, calendar system, etc.). |
| `RangeCalendar.YearPickerGrid` | `visibleYears` | `number` | min–max span or `20` | Number of years shown in the sliding window. Defaults to the full range between `minValue` and `maxValue` when both are set. |
### RangeCalendar.Cell Render Props
When `RangeCalendar.Cell` children is a function, React Aria render props are available:
| Prop | Type | Description |
| ------------------ | --------- | ---------------------------------------------------- |
| `formattedDate` | `string` | Localized day label for the cell. |
| `isSelected` | `boolean` | Whether the date is selected. |
| `isSelectionStart` | `boolean` | Whether the date is the start of the selected range. |
| `isSelectionEnd` | `boolean` | Whether the date is the end of the selected range. |
| `isUnavailable` | `boolean` | Whether the date is unavailable. |
| `isDisabled` | `boolean` | Whether the cell is disabled. |
| `isOutsideMonth` | `boolean` | Whether the date belongs to adjacent month. |
For a complete list of supported calendar systems and their identifiers, see:
* [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/) — date types (`CalendarDate`, `CalendarDateTime`, `ZonedDateTime`) and utilities used by all date components
* [`I18nProvider`](https://react-aria.adobe.com/I18nProvider) — override locale for a subtree
* [`useLocale`](https://react-aria.adobe.com/useLocale) — read the current locale and layout direction
# TimeField
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/time-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(date-and-time)/time-field.mdx
> Time input field with labels, descriptions, and validation built on React Aria TimeField
## Import
```tsx
import { TimeField } from '@heroui/react';
```
### Usage
```tsx
"use client";
import {Label, TimeField} from "@heroui/react";
export function Basic() {
return (
Time
{(segment) => }
);
}
```
### Anatomy
```tsx
import {TimeField, Label, Description, FieldError} from '@heroui/react';
export default () => (
{(segment) => }
)
```
> **TimeField** combines label, time input, description, and error into a single accessible component.
### With Description
```tsx
"use client";
import {Description, Label, TimeField} from "@heroui/react";
export function WithDescription() {
return (
Start time
{(segment) => }
Enter the start time
End time
{(segment) => }
Enter the end time
);
}
```
### Required Field
```tsx
"use client";
import {Description, Label, TimeField} from "@heroui/react";
export function Required() {
return (
Time
{(segment) => }
Appointment time
{(segment) => }
Required field
);
}
```
### Validation
Use `isInvalid` together with `FieldError` to surface validation messages.
```tsx
"use client";
import {FieldError, Label, TimeField} from "@heroui/react";
export function Invalid() {
return (
Time
{(segment) => }
Please enter a valid time
Time
{(segment) => }
Time must be within business hours
);
}
```
### With Validation
TimeField supports validation with `minValue`, `maxValue`, and custom validation logic.
```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 (
Time
{(segment) => }
{isInvalid ? (
Time must be between 9:00 AM and 5:00 PM
) : (
Enter a time between 9:00 AM and 5:00 PM
)}
);
}
```
### Controlled
Control the value to synchronize with other components or state management.
```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 (
Time
{(segment) => }
Current value: {value ? value.toString() : "(empty)"}
{
const currentTime = now(getLocalTimeZone());
setValue(new Time(currentTime.hour, currentTime.minute, currentTime.second));
}}
>
Set now
setValue(null)}>
Clear
);
}
```
### Disabled State
```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 (
Time
{(segment) => }
This time field is disabled
Time
{(segment) => }
This time field is disabled
);
}
```
### With Icons
Add prefix or suffix icons to enhance the time field.
```tsx
"use client";
import {Clock} from "@gravity-ui/icons";
import {Label, TimeField} from "@heroui/react";
export function WithPrefixIcon() {
return (
Time
{(segment) => }
);
}
```
```tsx
"use client";
import {Clock} from "@gravity-ui/icons";
import {Label, TimeField} from "@heroui/react";
export function WithSuffixIcon() {
return (
Time
{(segment) => }
);
}
```
```tsx
"use client";
import {ChevronDown, Clock} from "@gravity-ui/icons";
import {Description, Label, TimeField} from "@heroui/react";
export function WithPrefixAndSuffix() {
return (
Time
{(segment) => }
Enter a time
);
}
```
### Full Width
```tsx
"use client";
import {ChevronDown, Clock} from "@gravity-ui/icons";
import {Label, TimeField} from "@heroui/react";
export function FullWidth() {
return (
Time
{(segment) => }
Time
{(segment) => }
);
}
```
### On Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` on TimeField.Group to apply the lower emphasis variant suitable for surface backgrounds.
```tsx
"use client";
import {Clock} from "@gravity-ui/icons";
import {Description, Label, Surface, TimeField} from "@heroui/react";
export function OnSurface() {
return (
Time
{(segment) => }
Enter a time
Appointment time
{(segment) => }
Enter a time for your appointment
);
}
```
### Form Example
Complete form example with validation and submission handling.
```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 (
);
}
```
## Related Components
* **Label**: Accessible label for form controls
* **FieldError**: Inline validation messages for form fields
* **Description**: Helper text for form fields
### Custom Render Function
```tsx
"use client";
import {Label, TimeField} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
Time
{(segment) => }
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import {TimeField, Label, Description} from '@heroui/react';
function CustomTimeField() {
return (
Appointment time
{(segment) => }
Select a time for your appointment.
);
}
```
### Customizing the component classes
TimeField has minimal default styling. Override the `.time-field` class to customize the container styling.
```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 Classes
* `.time-field` – Root container with minimal styling (`flex flex-col gap-1`)
> **Note:** Child components ([Label](/docs/components/label), [Description](/docs/components/description), [FieldError](/docs/components/field-error)) have their own CSS classes and styling. See their respective documentation for customization options. TimeField.Group styling is documented below in the API Reference section.
### Interactive States
TimeField automatically manages these data attributes based on its state:
* **Invalid**: `[data-invalid="true"]` or `[aria-invalid="true"]` - Automatically hides the description slot when invalid
* **Required**: `[data-required="true"]` - Applied when `isRequired` is true
* **Disabled**: `[data-disabled="true"]` - Applied when `isDisabled` is true
* **Focus Within**: `[data-focus-within="true"]` - Applied when any child input is focused
## API Reference
### TimeField Props
TimeField inherits all props from React Aria's [TimeField](https://react-aria.adobe.com/TimeField) component.
#### Base Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------------------------ | ------- | ------------------------------------------------------------------- |
| `children` | `React.ReactNode \| (values: TimeFieldRenderProps) => React.ReactNode` | - | Child components (Label, TimeField.Group, etc.) or render function. |
| `className` | `string \| (values: TimeFieldRenderProps) => string` | - | CSS classes for styling, supports render props. |
| `style` | `React.CSSProperties \| (values: TimeFieldRenderProps) => React.CSSProperties` | - | Inline styles, supports render props. |
| `fullWidth` | `boolean` | `false` | Whether the time field should take full width of its container |
| `id` | `string` | - | The element's unique identifier. |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
#### Value Props
| Prop | Type | Default | Description |
| ------------------ | ------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------- |
| `value` | `TimeValue \| null` | - | Current value (controlled). Uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) types. |
| `defaultValue` | `TimeValue \| null` | - | Default value (uncontrolled). Uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) types. |
| `onChange` | `(value: TimeValue \| null) => void` | - | Handler called when the value changes. |
| `placeholderValue` | `TimeValue \| null` | - | Placeholder time that influences the format of the placeholder. Defaults to 12:00 AM or 00:00 depending on the hour cycle. |
#### Validation Props
| Prop | Type | Default | Description |
| -------------------- | -------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `isRequired` | `boolean` | `false` | Whether user input is required before form submission. |
| `isInvalid` | `boolean` | - | Whether the value is invalid. |
| `minValue` | `TimeValue \| null` | - | The minimum allowed time that a user may select. Uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) types. |
| `maxValue` | `TimeValue \| null` | - | The maximum allowed time that a user may select. Uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) types. |
| `validate` | `(value: TimeValue) => ValidationError \| true \| null \| undefined` | - | Custom validation function. |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | Whether to use native HTML form validation or ARIA attributes. |
#### Format Props
| Prop | Type | Default | Description |
| ------------------------- | -------------------------------- | ---------- | ----------------------------------------------------------------------------------------- |
| `granularity` | `'hour' \| 'minute' \| 'second'` | `'minute'` | Determines the smallest unit displayed in the time picker. |
| `hourCycle` | `12 \| 24` | - | Whether to display time in 12 or 24 hour format. By default, determined by locale. |
| `hideTimeZone` | `boolean` | `false` | Whether to hide the time zone abbreviation. |
| `shouldForceLeadingZeros` | `boolean` | - | Whether to always show leading zeros in the hour field. By default, determined by locale. |
#### State Props
| Prop | Type | Default | Description |
| ------------ | --------- | ------- | -------------------------------------------------- |
| `isDisabled` | `boolean` | - | Whether the input is disabled. |
| `isReadOnly` | `boolean` | - | Whether the input can be selected but not changed. |
#### Form Props
| Prop | Type | Default | Description |
| ----------- | --------- | ------- | -------------------------------------------------------------------------------- |
| `name` | `string` | - | Name of the input element, for HTML form submission. Submits as ISO 8601 string. |
| `autoFocus` | `boolean` | - | Whether the element should receive focus on render. |
#### Accessibility Props
| Prop | Type | Default | Description |
| ------------------ | -------- | ------- | ----------------------------------------------------- |
| `aria-label` | `string` | - | Accessibility label when no visible label is present. |
| `aria-labelledby` | `string` | - | ID of elements that label this field. |
| `aria-describedby` | `string` | - | ID of elements that describe this field. |
| `aria-details` | `string` | - | ID of elements with additional details. |
### Composition Components
TimeField works with these separate components that should be imported and used directly:
* **Label** - Field label component from `@heroui/react`
* **TimeField.Group** - Time input group component (documented below)
* **TimeField.Input** - Input component with segmented editing from `@heroui/react`
* **TimeField.Segment** - Individual time segment (hour, minute, second, etc.)
* **TimeField.Prefix** / **TimeField.Suffix** - Prefix and suffix slots for the input group
* **Description** - Helper text component from `@heroui/react`
* **FieldError** - Validation error message from `@heroui/react`
Each of these components has its own props API. Use them directly within TimeField for composition:
```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 Types
TimeField uses types from [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/):
* `Time` - Time only (hour, minute, second)
* `CalendarDateTime` - Date with time but no timezone (TimeField displays only the time portion)
* `ZonedDateTime` - Date with time and timezone (TimeField displays only the time portion)
Example:
```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
{/* ... */}
```
> **Note:** TimeField uses the [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) package for time manipulation, parsing, and type definitions. See the [Internationalized Date documentation](https://react-aria.adobe.com/internationalized/date/) for more information about available types and functions.
### TimeFieldRenderProps
When using render props with `className`, `style`, or `children`, these values are available:
| Prop | Type | Description |
| ---------------- | --------- | ----------------------------------------------- |
| `isDisabled` | `boolean` | Whether the field is disabled. |
| `isInvalid` | `boolean` | Whether the field is currently invalid. |
| `isReadOnly` | `boolean` | Whether the field is read-only. |
| `isRequired` | `boolean` | Whether the field is required. |
| `isFocused` | `boolean` | Whether the field is currently focused. |
| `isFocusWithin` | `boolean` | Whether any child element is focused. |
| `isFocusVisible` | `boolean` | Whether focus is visible (keyboard navigation). |
### TimeField.Group Props
TimeField.Group accepts all props from React Aria's `Group` component plus the following:
| Prop | Type | Default | Description |
| ----------- | -------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `className` | `string` | - | Tailwind classes merged with the component styles. |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
### TimeField.Input Props
TimeField.Input accepts all props from React Aria's `DateInput` component plus the following:
| Prop | Type | Default | Description |
| ----------- | -------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `className` | `string` | - | Tailwind classes merged with the component styles. |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the input. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
The `TimeField.Input` component accepts a render prop function that receives date segments. Each segment represents a part of the time (hour, minute, second, etc.).
### TimeField.Segment Props
TimeField.Segment accepts all props from React Aria's `DateSegment` component:
| Prop | Type | Default | Description |
| ----------- | ------------- | ------- | ------------------------------------------------------------- |
| `segment` | `DateSegment` | - | The date segment object from the TimeField.Input render prop. |
| `className` | `string` | - | Tailwind classes merged with the component styles. |
### TimeField.Prefix Props
TimeField.Prefix accepts standard HTML `div` attributes:
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | -------------------------------------------------- |
| `className` | `string` | - | Tailwind classes merged with the component styles. |
| `children` | `ReactNode` | - | Content to display in the prefix slot. |
### TimeField.Suffix Props
TimeField.Suffix accepts standard HTML `div` attributes:
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | -------------------------------------------------- |
| `className` | `string` | - | Tailwind classes merged with the component styles. |
| `children` | `ReactNode` | - | Content to display in the suffix slot. |
## TimeField.Group Styling
### Customizing the component classes
The base classes power every instance. Override them once with `@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 Classes
* `.date-input-group` – Root container styling
* `.date-input-group__input` – Input wrapper styling
* `.date-input-group__segment` – Individual time segment styling
* `.date-input-group__prefix` – Prefix element styling
* `.date-input-group__suffix` – Suffix element styling
### TimeField.Group Interactive States
* **Hover**: `:hover` or `[data-hovered="true"]`
* **Focus Within**: `[data-focus-within="true"]` or `:focus-within`
* **Invalid**: `[data-invalid="true"]` (also syncs with `aria-invalid`)
* **Disabled**: `[data-disabled="true"]` or `[aria-disabled="true"]`
* **Segment Focus**: `:focus` or `[data-focused="true"]` on segment elements
* **Segment Placeholder**: `[data-placeholder="true"]` on segment elements
# Alert
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/alert
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(feedback)/alert.mdx
> Display important messages and notifications to users with status indicators
## Import
```tsx
import { Alert } from '@heroui/react';
```
### Usage
```tsx
import {Alert, Button, CloseButton, Spinner} from "@heroui/react";
import React from "react";
export function Basic() {
return (
{/* Default - General information */}
New features available
Check out our latest updates including dark mode support and improved accessibility
features.
{/* Accent - Important information with action */}
Update available
A new version of the application is available. Please refresh to get the latest features
and bug fixes.
Refresh
Refresh
{/* Danger - Error with detailed steps */}
Unable to connect to server
We're experiencing connection issues. Please try the following:
Check your internet connection
Refresh the page
Clear your browser cache
Retry
Retry
{/* Without description */}
Profile updated successfully
{/* Custom indicator - Loading state */}
Processing your request
Please wait while we sync your data. This may take a few moments.
{/* Without close button */}
Scheduled maintenance
Our services will be unavailable on Sunday, March 15th from 2:00 AM to 6:00 AM UTC for
scheduled maintenance.
);
}
```
### Anatomy
Import the Alert component and access all parts using dot notation.
```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
## Styling
### Passing Tailwind CSS classes
```tsx
import { Alert } from "@heroui/react";
function CustomAlert() {
return (
Custom Alert
This alert has custom styling applied
);
}
```
### Customizing the component classes
To customize the Alert component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Alert component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/alert.css)):
#### Base Classes
* `.alert` - Base alert container
* `.alert__indicator` - Icon/indicator container
* `.alert__content` - Content wrapper for title and description
* `.alert__title` - Alert title text
* `.alert__description` - Alert description text
#### Status Variant Classes
* `.alert--default` - Default gray status
* `.alert--accent` - Accent blue status
* `.alert--success` - Success green status
* `.alert--warning` - Warning yellow/orange status
* `.alert--danger` - Danger red status
### Interactive States
The Alert component is primarily informational and doesn't have interactive states on the base component. However, it can contain interactive elements like buttons or close buttons.
## API Reference
### Alert Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------- | ----------- | ------------------------------ |
| `status` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"default"` | The visual status of the alert |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | The alert content |
### Alert.Indicator Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ----------------------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Custom indicator icon (defaults to status icon) |
### Alert.Content Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ----------------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Content (typically Title and Description) |
### Alert.Title Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | The alert title text |
### Alert.Description Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | -------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | The alert description text |
# Meter
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/meter
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(feedback)/meter.mdx
> A meter represents a quantity within a known range, or a fractional value.
## Import
```tsx
import { Meter, Label } from '@heroui/react';
```
### Usage
```tsx
import {Label, Meter} from "@heroui/react";
export function Basic() {
return (
Storage
);
}
```
### Anatomy
```tsx
import { Meter, Label } from '@heroui/react';
export default () => (
Storage
);
```
### Sizes
```tsx
import {Label, Meter} from "@heroui/react";
export function Sizes() {
return (
Small
Medium
Large
);
}
```
### Colors
```tsx
import {Label, Meter} from "@heroui/react";
export function Colors() {
return (
Default
Accent
Success
Warning
Danger
);
}
```
### Custom Value Scale
Use `minValue`, `maxValue`, and `formatOptions` to customize the value range and display format.
```tsx
import {Label, Meter} from "@heroui/react";
export function CustomValue() {
return (
Revenue
);
}
```
### Without Label
When no visible label is needed, use `aria-label` for accessibility.
```tsx
import {Meter} from "@heroui/react";
export function WithoutLabel() {
return (
);
}
```
## Styling
### Passing Tailwind CSS classes
You can customize individual Meter parts:
```tsx
import { Meter, Label } from '@heroui/react';
function CustomMeter() {
return (
Storage
);
}
```
### Customizing the component classes
To customize the Meter component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Meter component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/meter.css)):
#### Base & Element Classes
* `.meter` - Base container (grid layout)
* `.meter__output` - Value text display
* `.meter__track` - Track background
* `.meter__fill` - Filled portion of the track
#### Size Classes
* `.meter--sm` - Small size variant (thinner track)
* `.meter--md` - Medium size variant (default)
* `.meter--lg` - Large size variant (thicker track)
#### Color Classes
* `.meter--default` - Default color variant
* `.meter--accent` - Accent color variant
* `.meter--success` - Success color variant
* `.meter--warning` - Warning color variant
* `.meter--danger` - Danger color variant
## API Reference
### Meter Props
Inherits from [React Aria Meter](https://react-spectrum.adobe.com/react-aria/Meter.html).
| Prop | Type | Default | Description |
| --------------- | ------------------------------------------------------------- | -------------------- | ----------------------------------- |
| `value` | `number` | `0` | The current value |
| `minValue` | `number` | `0` | The minimum value |
| `maxValue` | `number` | `100` | The maximum value |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Size of the meter track |
| `color` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"accent"` | Color of the fill bar |
| `formatOptions` | `Intl.NumberFormatOptions` | `{style: 'percent'}` | Number format for the value display |
| `valueLabel` | `ReactNode` | - | Custom value label content |
| `children` | `ReactNode \| (values: MeterRenderProps) => ReactNode` | - | Content or render prop |
### MeterRenderProps
When using the render prop pattern, these values are provided:
| Prop | Type | Description |
| ------------ | -------- | ----------------------------------- |
| `percentage` | `number` | The percentage of the meter (0-100) |
| `valueText` | `string` | The formatted value text |
# ProgressBar
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/progress-bar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(feedback)/progress-bar.mdx
> A progress bar shows either determinate or indeterminate progress of an operation over time.
## Import
```tsx
import { ProgressBar, Label } from '@heroui/react';
```
### Usage
```tsx
import {Label, ProgressBar} from "@heroui/react";
export function Basic() {
return (
Loading
);
}
```
### Anatomy
```tsx
import { ProgressBar, Label } from '@heroui/react';
export default () => (
Loading
);
```
### Sizes
```tsx
import {Label, ProgressBar} from "@heroui/react";
export function Sizes() {
return (
);
}
```
### Colors
```tsx
import {Label, ProgressBar} from "@heroui/react";
export function Colors() {
return (
Default
Accent
Success
Warning
Danger
);
}
```
### Indeterminate
Use `isIndeterminate` when progress cannot be determined.
```tsx
import {Label, ProgressBar} from "@heroui/react";
export function Indeterminate() {
return (
Loading...
);
}
```
### Custom Value Scale
Use `minValue`, `maxValue`, and `formatOptions` to customize the value range and display format.
```tsx
"use client";
import {Label, ListBox, NumberField, ProgressBar, Select, Separator} from "@heroui/react";
import {useState} from "react";
const formatStyleOptions: {label: string; value: string}[] = [
{label: "Currency", value: "currency"},
{label: "Percent", value: "percent"},
{label: "Decimal", value: "decimal"},
{label: "Unit", 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 (
Options
setValue(v)}
>
Value
{
setMinValue(v);
if (value < v) setValue(v);
}}
>
Min Value
{
setMaxValue(v);
if (value > v) setValue(v);
}}
>
Max Value
setFormat(key as string)}>
Format
{formatStyleOptions.map((option) => (
{option.label}
))}
);
}
```
### Without Label
When no visible label is needed, use `aria-label` for accessibility.
```tsx
import {ProgressBar} from "@heroui/react";
export function WithoutLabel() {
return (
);
}
```
## Styling
### Passing Tailwind CSS classes
You can customize individual ProgressBar parts:
```tsx
import { ProgressBar, Label } from '@heroui/react';
function CustomProgressBar() {
return (
Loading
);
}
```
### Customizing the component classes
To customize the ProgressBar component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The ProgressBar component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/progress-bar.css)):
#### Base & Element Classes
* `.progress-bar` - Base container (grid layout)
* `.progress-bar__output` - Value text display
* `.progress-bar__track` - Track background
* `.progress-bar__fill` - Filled portion of the track
#### Size Classes
* `.progress-bar--sm` - Small size variant (thinner track)
* `.progress-bar--md` - Medium size variant (default)
* `.progress-bar--lg` - Large size variant (thicker track)
#### Color Classes
* `.progress-bar--default` - Default color variant
* `.progress-bar--accent` - Accent color variant
* `.progress-bar--success` - Success color variant
* `.progress-bar--warning` - Warning color variant
* `.progress-bar--danger` - Danger color variant
## API Reference
### ProgressBar Props
Inherits from [React Aria ProgressBar](https://react-spectrum.adobe.com/react-aria/ProgressBar.html).
| Prop | Type | Default | Description |
| ----------------- | ------------------------------------------------------------- | -------------------- | ----------------------------------- |
| `value` | `number` | `0` | The current value |
| `minValue` | `number` | `0` | The minimum value |
| `maxValue` | `number` | `100` | The maximum value |
| `isIndeterminate` | `boolean` | `false` | Whether progress is indeterminate |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Size of the progress track |
| `color` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"accent"` | Color of the fill bar |
| `formatOptions` | `Intl.NumberFormatOptions` | `{style: 'percent'}` | Number format for the value display |
| `valueLabel` | `ReactNode` | - | Custom value label content |
| `children` | `ReactNode \| (values: ProgressBarRenderProps) => ReactNode` | - | Content or render prop |
### ProgressBarRenderProps
When using the render prop pattern, these values are provided:
| Prop | Type | Description |
| ----------------- | --------- | -------------------------------------- |
| `percentage` | `number` | The percentage of the progress (0-100) |
| `valueText` | `string` | The formatted value text |
| `isIndeterminate` | `boolean` | Whether progress is indeterminate |
# ProgressCircle
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/progress-circle
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(feedback)/progress-circle.mdx
> A circular progress indicator that shows determinate or indeterminate progress.
## Import
```tsx
import { ProgressCircle } from '@heroui/react';
```
### Usage
```tsx
import {ProgressCircle} from "@heroui/react";
export function Basic() {
return (
);
}
```
### Anatomy
```tsx
import { ProgressCircle } from '@heroui/react';
export default () => (
);
```
### Sizes
```tsx
import {ProgressCircle} from "@heroui/react";
export function Sizes() {
return (
);
}
```
### Colors
```tsx
import {ProgressCircle} from "@heroui/react";
export function Colors() {
return (
);
}
```
### Indeterminate
Use `isIndeterminate` when progress cannot be determined.
```tsx
import {ProgressCircle} from "@heroui/react";
export function Indeterminate() {
return (
);
}
```
### With Label
```tsx
import {Label, ProgressCircle} from "@heroui/react";
export function WithLabel() {
return (
);
}
```
### Custom SVG Props
Since each part is a composable component, you can override SVG attributes like `strokeWidth`, `r`, `cx`, `cy`, and `viewBox` directly.
```tsx
import {ProgressCircle} from "@heroui/react";
export function CustomSvg() {
return (
);
}
```
## Styling
### Passing Tailwind CSS classes
You can customize individual ProgressCircle parts:
```tsx
import { ProgressCircle } from '@heroui/react';
function CustomProgressCircle() {
return (
);
}
```
### Customizing the component classes
To customize the ProgressCircle component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The ProgressCircle component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/progress-circle.css)):
#### Base & Element Classes
* `.progress-circle` - Base container
* `.progress-circle__track` - SVG element
* `.progress-circle__track-circle` - Background circle
* `.progress-circle__fill-circle` - Progress arc
#### Size Classes
* `.progress-circle--sm` - Small size variant
* `.progress-circle--md` - Medium size variant (default)
* `.progress-circle--lg` - Large size variant
#### Color Classes
* `.progress-circle--default` - Default color variant
* `.progress-circle--accent` - Accent color variant
* `.progress-circle--success` - Success color variant
* `.progress-circle--warning` - Warning color variant
* `.progress-circle--danger` - Danger color variant
## API Reference
### ProgressCircle Props
Inherits from [React Aria ProgressBar](https://react-spectrum.adobe.com/react-aria/ProgressBar.html).
| Prop | Type | Default | Description |
| ----------------- | ------------------------------------------------------------- | -------------------- | ----------------------------------- |
| `value` | `number` | `0` | The current value |
| `minValue` | `number` | `0` | The minimum value |
| `maxValue` | `number` | `100` | The maximum value |
| `isIndeterminate` | `boolean` | `false` | Whether progress is indeterminate |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Size of the circle |
| `color` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"accent"` | Color of the progress arc |
| `formatOptions` | `Intl.NumberFormatOptions` | `{style: 'percent'}` | Number format for the value display |
| `children` | `ReactNode \| (values: ProgressBarRenderProps) => ReactNode` | - | Content or render prop |
### ProgressBarRenderProps
When using the render prop pattern, these values are provided:
| Prop | Type | Description |
| ----------------- | --------- | -------------------------------------- |
| `percentage` | `number` | The percentage of the progress (0-100) |
| `valueText` | `string` | The formatted value text |
| `isIndeterminate` | `boolean` | Whether progress is indeterminate |
# Skeleton
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/skeleton
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(feedback)/skeleton.mdx
> Skeleton is a placeholder to show a loading state and the expected shape of a component.
## Import
```tsx
import { Skeleton } from '@heroui/react';
```
### Usage
```tsx
import {Skeleton} from "@heroui/react";
export function Basic() {
return (
);
}
```
### Text Content
```tsx
import {Skeleton} from "@heroui/react";
export function TextContent() {
return (
);
}
```
### User Profile
```tsx
import {Skeleton} from "@heroui/react";
export function UserProfile() {
return (
);
}
```
### List Items
```tsx
import {Skeleton} from "@heroui/react";
export function List() {
return (
{Array.from({length: 3}).map((_, index) => (
))}
);
}
```
### Animation Types
```tsx
import {Skeleton} from "@heroui/react";
export function AnimationTypes() {
return (
);
}
```
### Grid
```tsx
import {Skeleton} from "@heroui/react";
export function Grid() {
return (
);
}
```
### Single Shimmer
A synchronized shimmer effect that passes over all skeleton elements at once. Apply the `skeleton--shimmer` class to a parent container and set `animationType="none"` on child skeletons.
```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
## Styling
### Global Animation Configuration
You can set a default animation type for all Skeleton components in your application by defining the `--skeleton-animation` CSS variable:
```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;
}
```
This global setting will be overridden by the `animationType` prop when specified on individual components.
### Passing Tailwind CSS classes
```tsx
import { Skeleton } from '@heroui/react';
function CustomSkeleton() {
return (
);
}
```
### Customizing the component classes
To customize the Skeleton component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Skeleton component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/skeleton.css)):
#### Base Class
`.skeleton` - Base skeleton styles with background and rounded corners
#### Animation Variant Classes
* `.skeleton--shimmer` - Adds shimmer animation with gradient effect (default)
* `.skeleton--pulse` - Adds pulse animation using Tailwind's animate-pulse
* `.skeleton--none` - No animation, static skeleton
### Animation
The Skeleton component supports three animation types, each with different visual effects:
#### Shimmer Animation
The shimmer effect creates a gradient that moves across the skeleton element:
```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-[''];
}
```
The shimmer animation is defined in the theme using:
```css
@theme inline {
--animate-skeleton: skeleton 2s linear infinite;
@keyframes skeleton {
100% {
transform: translateX(200%);
}
}
}
```
#### Pulse Animation
The pulse animation uses Tailwind's built-in `animate-pulse` utility:
```css
.skeleton--pulse {
@apply animate-pulse;
}
```
#### No Animation
For static skeletons without any animation:
```css
.skeleton--none {
/* No animation styles applied */
}
```
## API Reference
### Skeleton Props
| Prop | Type | Default | Description |
| --------------- | -------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------- |
| `animationType` | `"shimmer" \| "pulse" \| "none"` | `"shimmer"` or CSS variable | The animation type for the skeleton. Can be globally configured via `--skeleton-animation` CSS variable |
| `className` | `string` | - | Additional CSS classes |
# Spinner
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/spinner
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(feedback)/spinner.mdx
> A loading indicator component to show pending states
## Import
```tsx
import { Spinner } from '@heroui/react';
```
### Usage
```tsx
import {Spinner} from "@heroui/react";
export function SpinnerBasic() {
return (
);
}
```
### Colors
```tsx
import {Spinner} from "@heroui/react";
export function SpinnerColors() {
return (
Current
Accent
Success
Warning
Danger
);
}
```
### Sizes
```tsx
import {Spinner} from "@heroui/react";
export function SpinnerSizes() {
return (
Small
Medium
Large
Extra Large
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import {Spinner} from '@heroui/react';
function CustomSpinner() {
return (
);
}
```
### Customizing the component classes
To customize the Spinner component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.spinner {
@apply animate-spin;
}
.spinner--accent {
color: var(--accent);
}
}
```
HeroUI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Spinner component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/spinner.css)):
#### Base & Size Classes
* `.spinner` - Base spinner styles with default size
* `.spinner--sm` - Small size variant
* `.spinner--md` - Medium size variant (default)
* `.spinner--lg` - Large size variant
* `.spinner--xl` - Extra large size variant
#### Color Classes
* `.spinner--current` - Inherits current text color
* `.spinner--accent` - Accent color variant
* `.spinner--danger` - Danger color variant
* `.spinner--success` - Success color variant
* `.spinner--warning` - Warning color variant
## API Reference
### Spinner Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------- | ----------- | ---------------------------- |
| `size` | `"sm" \| "md" \| "lg" \| "xl"` | `"md"` | Size of the spinner |
| `color` | `"current" \| "accent" \| "success" \| "warning" \| "danger"` | `"current"` | Color variant of the spinner |
| `className` | `string` | - | Additional CSS classes |
# CheckboxGroup
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/checkbox-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/checkbox-group.mdx
> A checkbox group component for managing multiple checkbox selections
## Import
```tsx
import { CheckboxGroup, Checkbox, Label, Description } from '@heroui/react';
```
### Usage
```tsx
import {Checkbox, CheckboxGroup, Description, Label} from "@heroui/react";
export function Basic() {
return (
Select your interests
Choose all that apply
Coding
Love building software
Design
Enjoy creating beautiful interfaces
Writing
Passionate about content creation
);
}
```
### Anatomy
Import the CheckboxGroup component and access all parts using dot notation.
```tsx
import {CheckboxGroup, Checkbox, Label, Description, FieldError} from '@heroui/react';
export default () => (
{/* Optional */}
Label {/* plain text — the clickable label */}
{/* Optional per-checkbox help text */}
{/* Optional */}
);
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` to apply the lower emphasis variant suitable for surface backgrounds.
```tsx
import {Checkbox, CheckboxGroup, Description, Label, Surface} from "@heroui/react";
export function OnSurface() {
return (
Select your interests
Choose all that apply
Coding
Love building software
Design
Enjoy creating beautiful interfaces
Writing
Passionate about content creation
);
}
```
### With Custom Indicator
```tsx
"use client";
import {Checkbox, CheckboxGroup, Description, Label} from "@heroui/react";
export function WithCustomIndicator() {
return (
Features
Select the features you want
{({isSelected}) =>
isSelected ? (
) : null
}
Email notifications
Receive updates via email
{({isSelected}) =>
isSelected ? (
) : null
}
Newsletter
Get weekly newsletters
);
}
```
### Indeterminate
```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 : []);
}}
>
Select all
Coding
Design
Writing
);
}
```
### Controlled
```tsx
"use client";
import {Checkbox, CheckboxGroup, Label} from "@heroui/react";
import {useState} from "react";
export function Controlled() {
const [selected, setSelected] = useState(["coding", "design"]);
return (
Your skills
Coding
Design
Writing
Selected: {selected.join(", ") || "None"}
);
}
```
### Validation
```tsx
"use client";
import {Button, Checkbox, CheckboxGroup, FieldError, Form, Label} from "@heroui/react";
export function Validation() {
return (
);
}
```
### Disabled
```tsx
import {Checkbox, CheckboxGroup, Description, Label} from "@heroui/react";
export function Disabled() {
return (
Features
Feature selection is temporarily disabled
Feature 1
This feature is coming soon
Feature 2
This feature is coming soon
);
}
```
### Features and Add-ons Example
```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: "Receive updates via email",
icon: Envelope,
title: "Email Notifications",
value: "email",
},
{
description: "Get instant SMS notifications",
icon: Comment,
title: "SMS Alerts",
value: "sms",
},
{
description: "Browser and mobile push alerts",
icon: Bell,
title: "Push Notifications",
value: "push",
},
];
return (
Notification preferences
Choose how you want to receive updates
{addOns.map((addon) => (
{addon.title}
{addon.description}
))}
);
}
```
### Custom Render Function
```tsx
"use client";
import {Checkbox, CheckboxGroup, Description, Label} from "@heroui/react";
export function CustomRenderFunction() {
return (
}>
Select your interests
Choose all that apply
Coding
Love building software
Design
Enjoy creating beautiful interfaces
Writing
Passionate about content creation
);
}
```
## Related Components
* **Checkbox**: Binary choice input control
* **Label**: Accessible label for form controls
* **Fieldset**: Group related form controls with legends
## Styling
### Passing Tailwind CSS classes
You can customize the CheckboxGroup component:
```tsx
import { CheckboxGroup, Checkbox, Label } from '@heroui/react';
function CustomCheckboxGroup() {
return (
Option 1
);
}
```
### Customizing the component classes
To customize the CheckboxGroup component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.checkbox-group {
@apply flex flex-col gap-2;
}
}
```
HeroUI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The CheckboxGroup component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/checkbox-group.css)):
* `.checkbox-group` - Base checkbox group container
## API Reference
### CheckboxGroup Props
Inherits from [React Aria CheckboxGroup](https://react-spectrum.adobe.com/react-aria/CheckboxGroup.html).
| Prop | Type | Default | Description |
| -------------- | -------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------- |
| `value` | `string[]` | - | The current selected values (controlled) |
| `defaultValue` | `string[]` | - | The default selected values (uncontrolled) |
| `onChange` | `(value: string[]) => void` | - | Handler called when the selected values change |
| `isDisabled` | `boolean` | `false` | Whether the checkbox group is disabled |
| `isRequired` | `boolean` | `false` | Whether the checkbox group is required |
| `isReadOnly` | `boolean` | `false` | Whether the checkbox group is read only |
| `isInvalid` | `boolean` | `false` | Whether the checkbox group is in an invalid state |
| `name` | `string` | - | The name of the checkbox group, used when submitting an HTML form |
| `children` | `React.ReactNode \| (values: CheckboxGroupRenderProps) => React.ReactNode` | - | Checkbox group content or render prop |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### CheckboxGroupRenderProps
When using the render prop pattern, these values are provided:
| Prop | Type | Description |
| ------------ | ---------- | ------------------------------------------------- |
| `value` | `string[]` | The currently selected values |
| `isDisabled` | `boolean` | Whether the checkbox group is disabled |
| `isReadOnly` | `boolean` | Whether the checkbox group is read only |
| `isInvalid` | `boolean` | Whether the checkbox group is in an invalid state |
| `isRequired` | `boolean` | Whether the checkbox group is required |
# Checkbox
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/checkbox
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/checkbox.mdx
> Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected.
## Import
```tsx
import { Checkbox } from '@heroui/react';
```
### Usage
```tsx
import {Checkbox} from "@heroui/react";
export function Basic() {
return (
Accept terms and conditions
);
}
```
### Anatomy
Import the Checkbox component and access all parts using dot notation.
```tsx
import { Checkbox, Description, FieldError } from '@heroui/react';
export default () => (
Label {/* plain text — the clickable label + accessible name */}
{/* Optional — field-level help text */}
{/* Optional — validation message */}
);
```
### Disabled
```tsx
import {Checkbox, Description} from "@heroui/react";
export function Disabled() {
return (
Premium Feature
This feature is coming soon
);
}
```
### Default Selected
```tsx
import {Checkbox} from "@heroui/react";
export function DefaultSelected() {
return (
Enable email notifications
);
}
```
### Controlled
```tsx
"use client";
import {Checkbox} from "@heroui/react";
import {useState} from "react";
export function Controlled() {
const [isSelected, setIsSelected] = useState(true);
return (
Email notifications
Status: {isSelected ? "Enabled" : "Disabled"}
);
}
```
### Indeterminate
```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);
}}
>
Select all
Shows indeterminate state (dash icon)
);
}
```
### External Label
```tsx
import {Checkbox, Label} from "@heroui/react";
export function ExternalLabel() {
return (
Send me marketing emails
);
}
```
### With Description
```tsx
import {Checkbox, Description} from "@heroui/react";
export function WithDescription() {
return (
Email notifications
Get notified when someone mentions you in a comment
);
}
```
### Render Props
```tsx
"use client";
import {Checkbox, Description} from "@heroui/react";
export function RenderProps() {
return (
{({isSelected}) => (
<>
{isSelected ? "Terms accepted" : "Accept terms"}
{isSelected ? "Thank you for accepting" : "Please read and accept the terms"}
>
)}
);
}
```
### Form Integration
```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(
`Form submitted with:\n${Array.from(formData.entries())
.map(([key, value]) => `${key}: ${value}`)
.join("\n")}`,
);
};
return (
);
}
```
### Invalid
```tsx
import {Checkbox, FieldError} from "@heroui/react";
export function Invalid() {
return (
I agree to the terms
You must accept the terms to continue
);
}
```
### Custom Indicator
```tsx
"use client";
import {Checkbox} from "@heroui/react";
export function CustomIndicator() {
return (
{({isSelected}) =>
isSelected ? (
) : null
}
Heart
{({isSelected}) =>
isSelected ? (
) : null
}
Plus
{({isIndeterminate}) =>
isIndeterminate ? (
) : null
}
Indeterminate
);
}
```
### Full Rounded
```tsx
import {Checkbox, Label} from "@heroui/react";
export function FullRounded() {
return (
Rounded checkboxes
Small size
Default size
Large size
Extra large size
);
}
```
### Variants
The Checkbox component supports two visual variants:
* **`primary`** (default) - Standard styling with default background, suitable for most use cases
* **`secondary`** - Lower emphasis variant, suitable for use in Surface components
```tsx
import {Checkbox, Description} from "@heroui/react";
export function Variants() {
return (
Primary variant
Primary checkbox
Standard styling with default background
Secondary variant
Secondary checkbox
Lower emphasis variant for use in surfaces
);
}
```
### Custom Render Function
```tsx
"use client";
import {Checkbox, Label} from "@heroui/react";
export function CustomRenderFunction() {
return (
}>
Accept terms and conditions
);
}
```
## Related Components
* **Label**: Accessible label for form controls
* **CheckboxGroup**: Group of checkboxes with shared state
* **Description**: Helper text for form fields
## Styling
### Passing Tailwind CSS classes
You can customize individual Checkbox components:
```tsx
import { Checkbox } from '@heroui/react';
function CustomCheckbox() {
return (
Custom Checkbox
);
}
```
### Customizing the component classes
To customize the Checkbox component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Checkbox component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/checkbox.css)):
* `.checkbox` - Base checkbox container (the field)
* `.checkbox__content` - Clickable label wrapping the control and label text
* `.checkbox__control` - Checkbox control box
* `.checkbox__indicator` - Checkbox checkmark indicator
### Interactive States
The checkbox supports both CSS pseudo-classes and data attributes for flexibility:
* **Selected**: `[data-selected="true"]` or `[aria-checked="true"]` (shows checkmark and background color change)
* **Indeterminate**: `[data-indeterminate="true"]` (shows indeterminate state with dash)
* **Invalid**: `[data-invalid="true"]` or `[aria-invalid="true"]` (shows error state with danger colors)
* **Hover**: `:hover` or `[data-hovered="true"]` on `Checkbox.Control` (button)
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]` on the button (shows focus ring on control)
* **Disabled**: `[data-disabled="true"]` on the field (reduced opacity, including help text)
* **Pressed**: `:active` or `[data-pressed="true"]`
## API Reference
### Checkbox Props
Inherits from [React Aria CheckboxField](https://react-spectrum.adobe.com/react-aria/Checkbox.html).
| Prop | Type | Default | Description |
| -------------------- | -------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `isSelected` | `boolean` | `false` | Whether the checkbox is checked |
| `defaultSelected` | `boolean` | `false` | Whether the checkbox is checked by default (uncontrolled) |
| `isIndeterminate` | `boolean` | `false` | Whether the checkbox is in an indeterminate state |
| `isDisabled` | `boolean` | `false` | Whether the checkbox is disabled |
| `isInvalid` | `boolean` | `false` | Whether the checkbox is invalid |
| `isReadOnly` | `boolean` | `false` | Whether the checkbox is read only |
| `isRequired` | `boolean` | `false` | Whether the checkbox must be selected |
| `validate` | `(value: boolean) => ValidationError \| true \| null \| undefined` | - | Custom validation function |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | Whether to use native HTML form validation or ARIA |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
| `name` | `string` | - | The name of the input element, used when submitting an HTML form |
| `value` | `string` | - | The value of the input element, used when submitting an HTML form |
| `onChange` | `(isSelected: boolean) => void` | - | Handler called when the checkbox value changes |
| `children` | `React.ReactNode \| (values: CheckboxFieldRenderProps) => React.ReactNode` | - | Checkbox content or field render prop |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Checkbox.Content Props
The clickable `` that wraps the control and label text. Put `Checkbox.Control` and the `Label` inside it; keep `Description`/`FieldError` as siblings of `Checkbox.Content`. For a checkbox with no label, omit the `Label` and pass an `aria-label` on `Checkbox`.
| Prop | Type | Default | Description |
| ----------- | --------------------------------------------------------------------------- | ------- | --------------------------------------------------------- |
| `children` | `React.ReactNode \| (values: CheckboxButtonRenderProps) => React.ReactNode` | - | Button content (control + label), or a button render prop |
| `className` | `string \| (values: CheckboxButtonRenderProps) => string` | - | Classes applied to the clickable label |
### CheckboxFieldRenderProps
When using a render prop on the root `Checkbox`, these field-level values are provided:
| Prop | Type | Description |
| ----------------- | --------- | ------------------------------------------------- |
| `isSelected` | `boolean` | Whether the checkbox is currently checked |
| `isIndeterminate` | `boolean` | Whether the checkbox is in an indeterminate state |
| `isDisabled` | `boolean` | Whether the checkbox is disabled |
| `isReadOnly` | `boolean` | Whether the checkbox is read only |
| `isInvalid` | `boolean` | Whether the checkbox is invalid |
| `isRequired` | `boolean` | Whether the checkbox is required |
### CheckboxButtonRenderProps
`Checkbox.Control` and `Checkbox.Indicator` use button-level render props (`isHovered`, `isPressed`, `isFocusVisible`, etc.). Pass a function as `Checkbox.Control` children or to `Checkbox.Indicator` to access them.
# Description
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/description
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/description.mdx
> Provides supplementary text for form fields and other components
## Import
```tsx
import { Description } from '@heroui/react';
```
## Usage
```tsx
import {Description, Input, Label} from "@heroui/react";
export function Basic() {
return (
Email
We'll never share your email with anyone else.
);
}
```
## 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 | Type | Default | Description |
| ----------- | ----------- | ------- | ------------------------------ |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | The content of the description |
## Accessibility
The Description component enhances accessibility by:
* Using semantic HTML that screen readers can identify
* Providing the `slot="description"` attribute for React Aria integration
* Supporting proper text contrast ratios
## Styling
The Description component uses the following CSS classes:
* `.description` - Base description styles with `muted` text color
## Examples
### With Form Fields
```tsx
Password
Must be at least 8 characters with one uppercase letter
```
### Integration with TextField
```tsx
import {TextField, Label, Input, Description} from '@heroui/react';
Email
We'll never share your email
```
When using the [TextField](./text-field) component, accessibility attributes are automatically applied to the label and description.
# ErrorMessage
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/error-message
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/error-message.mdx
> A low-level error message component for displaying errors
## Import
```tsx
import { ErrorMessage } from '@heroui/react';
```
## Usage
`ErrorMessage` is a low-level component built on React Aria's `Text` component with an `errorMessage` slot. It's designed for displaying error messages in **non-form components** such as `TagGroup`, `Calendar`, and other collection-based components.
```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)}
>
Required Categories
News
Travel
Gaming
Shopping
Select at least one category
{!!isInvalid && <>Please select at least one category>}
);
}
```
### Anatomy
```tsx
import { TagGroup, Tag, Label, Description, ErrorMessage } from '@heroui/react';
```
## Related Components
* **TagGroup**: Focusable list of tags with selection and removal support
## When to Use
`ErrorMessage` is **not tied to forms**. It's a generic error display component for non-form contexts.
* **Recommended for** non-form components (e.g., `TagGroup`, `Calendar`, collection components)
* **For form fields**, we recommend using [`FieldError`](/docs/components/field-error) instead, which provides form-specific validation features and automatic error handling, following standardized form validation patterns.
## ErrorMessage vs FieldError
| Component | Use Case | Form Integration | Example Components |
| -------------- | ------------------------- | ---------------- | ------------------------------------ |
| `ErrorMessage` | Non-form components | No | `TagGroup`, `Calendar` |
| `FieldError` | Form fields (recommended) | Yes | `TextField`, `NumberField`, `Select` |
For form validation, we recommend using `FieldError` as it follows standardized form validation patterns and provides form-specific features. See the [FieldError documentation](/docs/components/field-error) and the [Form guide](/docs/components/form) for examples and best practices.
## API Reference
### ErrorMessage Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | The error message content |
**Note**: `ErrorMessage` is built on React Aria's `Text` component with `slot="errorMessage"`. It can be targeted using the `[slot=errorMessage]` CSS selector.
## Accessibility
The ErrorMessage component enhances accessibility by:
* Using semantic HTML that screen readers can identify
* Providing the `slot="errorMessage"` attribute for React Aria integration
* Supporting proper text contrast ratios for error states
* Following WAI-ARIA best practices for error messaging
## Styling
### Passing Tailwind CSS classes
```tsx
import { ErrorMessage } from '@heroui/react';
function CustomErrorMessage() {
return (
Custom styled error message
);
}
```
### Customizing the component classes
To customize the ErrorMessage component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.error-message {
@apply text-red-600 text-sm font-medium;
}
}
```
HeroUI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The ErrorMessage component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/error-message.css)):
#### Base Classes
* `.error-message` - Base error message styles with danger color and text truncation
#### Slot Classes
* `[slot="errorMessage"]` - ErrorMessage slot styles for React Aria integration
# FieldError
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/field-error
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/field-error.mdx
> Displays validation error messages for form fields
## Import
```tsx
import { FieldError } from '@heroui/react';
```
## Usage
The FieldError component displays validation error messages for form fields. It automatically appears when the parent field is marked as invalid and provides smooth opacity transitions.
```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 (
Username
setValue(e.target.value)}
/>
Username must be at least 3 characters
);
}
```
## 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 | Type | Default | Description |
| ----------- | ------------------------------------------------------------ | ------- | ---------------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode \| ((validation: ValidationResult) => ReactNode)` | - | Error message content or render function |
## Accessibility
The FieldError component ensures accessibility by:
* Using proper ARIA attributes for error announcement
* Supporting screen readers with semantic HTML
* Providing visual and programmatic error indication
* Automatically managing visibility based on validation state
## Styling
The FieldError component uses the following CSS classes:
* `.field-error` - Base error styles with danger color
* Only shows when the `data-visible` attribute is present
* Text is truncated with ellipsis for long messages
## Examples
### Basic Validation
```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
);
}
```
### With Dynamic Messages
```tsx
0}>
Password
{(validation) => validation.validationErrors.join(', ')}
```
### Custom Validation Logic
```tsx
function EmailField() {
const [email, setEmail] = useState('');
const isInvalid = email.length > 0 && !email.includes('@');
return (
Email
setEmail(e.target.value)}
/>
Email must include @ symbol
);
}
```
### Multiple Error Messages
```tsx
Username
{errors.map((error, i) => (
{error}
))}
```
# Fieldset
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/fieldset
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/fieldset.mdx
> Group related form controls with legends, descriptions, and actions
## Import
```tsx
import { Fieldset } from '@heroui/react';
```
### Usage
```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("Form submitted successfully!");
};
return (
);
}
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` on form controls (Input, TextArea, etc.) to apply the lower emphasis variant suitable for surface backgrounds.
```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("Form submitted successfully!");
};
return (
Profile Settings
Update your profile information.
{
if (value.length < 3) {
return "Name must be at least 3 characters";
}
return null;
}}
>
Name
Email
{
if (value.length < 10) {
return "Bio must be at least 10 characters";
}
return null;
}}
>
Bio
Minimum 10 characters
Save changes
Cancel
);
}
```
### Anatomy
Import the Fieldset component and access all parts using dot notation.
```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
## Styling
### Passing Tailwind CSS classes
```tsx
import { Fieldset, TextField, Label, Input } from '@heroui/react';
function CustomFieldset() {
return (
Team members
First name
Last name
{/* Action buttons */}
);
}
```
### Customizing the component classes
Use the `@layer components` directive to target Fieldset [BEM](https://getbem.com/)-style classes.
```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 Classes
The Fieldset compound component exposes these CSS selectors:
* `.fieldset` – Root container
* `.fieldset__legend` – Legend element
* `.fieldset__field_group` – Wrapper for grouped fields
* `.fieldset__actions` – Action bar below the fields
## API Reference
### Fieldset Props
| Prop | Type | Default | Description |
| ------------- | ------------------------------------------- | ----------------------------------------------- | --------------------------------------------------------- |
| `className` | `string` | - | Tailwind CSS classes applied to the root element. |
| `children` | `React.ReactNode` | - | Fieldset content (legend, groups, descriptions, actions). |
| `nativeProps` | `React.HTMLAttributes` | Supports native fieldset attributes and events. | |
### Fieldset.Legend Props
| Prop | Type | Default | Description |
| ------------- | ----------------------------------------- | ------- | ---------------------------------------- |
| `className` | `string` | - | Tailwind classes for the legend element. |
| `children` | `React.ReactNode` | - | Legend content, usually plain text. |
| `nativeProps` | `React.HTMLAttributes` | - | Native legend attributes. |
### Fieldset.Group Props
| Prop | Type | Default | Description |
| ------------- | -------------------------------------- | ------- | ---------------------------------------------- |
| `className` | `string` | - | Layout and spacing classes for grouped fields. |
| `children` | `React.ReactNode` | - | Form controls to group inside the fieldset. |
| `nativeProps` | `React.HTMLAttributes` | - | Native div attributes. |
### Fieldset.Actions Props
| Prop | Type | Default | Description |
| ------------- | -------------------------------------- | ------- | ------------------------------------------------- |
| `className` | `string` | - | Tailwind classes to align action buttons or text. |
| `children` | `React.ReactNode` | - | Action buttons or helper text. |
| `nativeProps` | `React.HTMLAttributes` | - | Native div attributes. |
# Form
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/form
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/form.mdx
> Wrapper component for form validation and submission handling
## Import
```tsx
import { Form } from '@heroui/react';
```
### Usage
```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(`Form submitted with: ${JSON.stringify(data, null, 2)}`);
};
return (
{
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)) {
return "Please enter a valid email address";
}
return null;
}}
>
Email
{
if (value.length < 8) {
return "Password must be at least 8 characters";
}
if (!/[A-Z]/.test(value)) {
return "Password must contain at least one uppercase letter";
}
if (!/[0-9]/.test(value)) {
return "Password must contain at least one number";
}
return null;
}}
>
Password
Must be at least 8 characters with 1 uppercase and 1 number
Submit
Reset
);
}
```
### Anatomy
Import all parts and piece them together.
```tsx
import {Form, Button} from '@heroui/react';
export default () => (
{/* Form fields go here */}
)
```
### Custom Render Function
```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(`Form submitted with: ${JSON.stringify(data, null, 2)}`);
};
return (
}
onSubmit={onSubmit}
>
{
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)) {
return "Please enter a valid email address";
}
return null;
}}
>
Email
{
if (value.length < 8) {
return "Password must be at least 8 characters";
}
if (!/[A-Z]/.test(value)) {
return "Password must contain at least one uppercase letter";
}
if (!/[0-9]/.test(value)) {
return "Password must contain at least one number";
}
return null;
}}
>
Password
Must be at least 8 characters with 1 uppercase and 1 number
Submit
Reset
);
}
```
## 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
## Styling
### Passing Tailwind CSS classes
```tsx
import {Form, TextField, Label, Input, FieldError, Button} from '@heroui/react';
function CustomForm() {
return (
Email
Submit
);
}
```
## API Reference
### Form Props
The Form component is a wrapper around React Aria's Form primitive that provides form validation and submission handling capabilities.
| Prop | Type | Default | Description |
| -------------------- | ------------------------------------------------------------------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `action` | `string \| FormHTMLAttributes['action']` | - | The URL to submit the form data to. |
| `className` | `string` | - | Tailwind CSS classes applied to the form element. |
| `children` | `React.ReactNode` | - | Form content (fields, buttons, etc.). |
| `encType` | `'application/x-www-form-urlencoded' \| 'multipart/form-data' \| 'text/plain'` | - | The encoding type for form data submission. |
| `method` | `'get' \| 'post'` | - | The HTTP method to use when submitting the form. |
| `onInvalid` | `(event: FormEvent) => void` | - | Handler called when the form validation fails. By default, the first invalid field will be focused. Use `preventDefault()` to customize focus behavior. |
| `onReset` | `(event: FormEvent) => void` | - | Handler called when the form is reset. |
| `onSubmit` | `(event: FormEvent) => void` | - | Handler called when the form is submitted. |
| `target` | `'_self' \| '_blank' \| '_parent' \| '_top'` | - | Where to display the response after submitting the form. |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | Whether to use native HTML validation or ARIA validation. 'native' blocks form submission, 'aria' displays errors in realtime. |
| `validationErrors` | `ValidationErrors` | - | Server-side validation errors mapped by field name. Displayed immediately and cleared when user modifies the field. |
| `aria-label` | `string` | - | Accessibility label for the form. |
| `aria-labelledby` | `string` | - | ID of element that labels the form. Creates a form landmark when provided. |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Form Validation
The Form component integrates with React Aria's validation system, allowing you to:
* Use built-in HTML5 validation attributes (`required`, `minLength`, `pattern`, etc.)
* Provide custom validation functions on TextField components
* Display validation errors with FieldError components
* Handle form submission with proper validation
* Provide server-side validation errors via `validationErrors` prop
#### Validation Behavior
The `validationBehavior` prop controls how validation is displayed:
* **`native`** (default): Uses native HTML validation, blocks form submission on errors
* **`aria`**: Uses ARIA attributes for validation, displays errors in realtime as user types, doesn't block submission
This behavior can be set at the form level or overridden at individual field level.
### Form Submission
Forms can be submitted in several ways:
* **Traditional submission**: Set the `action` prop to submit to a URL
* **JavaScript handling**: Use the `onSubmit` handler to process form data
* **FormData API**: Access form data using the FormData API in your submit handler
Example with FormData:
```tsx
function handleSubmit(e: FormEvent) {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const data = Object.fromEntries(formData);
console.log('Form data:', data);
}
```
### Integration with Form Fields
The Form component works seamlessly with HeroUI's form field components:
* **TextField**: For text inputs with labels and validation
* **Checkbox**: For boolean selections
* **RadioGroup**: For single selection from multiple options
* **Switch**: For toggle controls
* **Button**: For form submission and reset actions
All field components automatically integrate with the Form's validation and submission behavior when placed inside it.
### Accessibility
Forms are accessible by default when using React Aria components. Key features include:
* Native `` element semantics
* Form landmark creation with `aria-label` or `aria-labelledby`
* Automatic focus management on validation errors
* ARIA validation attributes when using `validationBehavior="aria"`
### Advanced Usage
For more advanced use cases including:
* Custom validation context
* Form context providers
* Integration with third-party libraries
* Custom focus management on validation errors
Please refer to the [React Aria Form documentation](https://react-spectrum.adobe.com/react-aria/Form.html).
# InputGroup
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/input-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/input-group.mdx
> Group related input controls with prefix and suffix elements for enhanced form fields
## Import
```tsx
import { InputGroup } from '@heroui/react';
```
### Usage
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {InputGroup, Label, TextField} from "@heroui/react";
export function Default() {
return (
Email address
);
}
```
### Anatomy
```tsx
import {InputGroup, TextField, Label} from '@heroui/react';
export default () => (
{/* Or use InputGroup.TextArea for multiline input */}
)
```
> **InputGroup** wraps an input field with optional prefix and suffix elements, creating a visually cohesive group. It's typically used within **[TextField](/docs/components/text-field)** to add icons, text, buttons, or other elements before or after the input. Use **InputGroup.Input** for single-line inputs or **InputGroup.TextArea** for multiline text inputs.
### With Prefix Icon
Add an icon before the input field.
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {Description, InputGroup, Label, TextField} from "@heroui/react";
export function WithPrefixIcon() {
return (
Email address
We'll never share this with anyone else
);
}
```
### With Suffix Icon
Add an icon after the input field.
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {Description, InputGroup, Label, TextField} from "@heroui/react";
export function WithSuffixIcon() {
return (
Email address
We don't send spam
);
}
```
### With Prefix and Suffix
Combine both prefix and suffix elements.
```tsx
"use client";
import {Description, InputGroup, Label, TextField} from "@heroui/react";
export function WithPrefixAndSuffix() {
return (
Set a price
$
USD
What customers would pay
);
}
```
### Text Prefix
Use text as a prefix, such as currency symbols or protocol prefixes.
```tsx
"use client";
import {InputGroup, Label, TextField} from "@heroui/react";
export function WithTextPrefix() {
return (
Website
https://
);
}
```
### Text Suffix
Use text as a suffix, such as domain extensions or units.
```tsx
"use client";
import {InputGroup, Label, TextField} from "@heroui/react";
export function WithTextSuffix() {
return (
Website
.com
);
}
```
### Icon Prefix and Text Suffix
Combine an icon prefix with a text suffix.
```tsx
"use client";
import {Globe} from "@gravity-ui/icons";
import {InputGroup, Label, TextField} from "@heroui/react";
export function WithIconPrefixAndTextSuffix() {
return (
Website
.com
);
}
```
### Copy Button Suffix
Add an interactive button in the suffix, such as a copy button.
```tsx
"use client";
import {Copy} from "@gravity-ui/icons";
import {Button, InputGroup, Label, TextField} from "@heroui/react";
export function WithCopySuffix() {
return (
Website
);
}
```
### Icon Prefix and Copy Button
Combine an icon prefix with an interactive button suffix.
```tsx
"use client";
import {Copy, Globe} from "@gravity-ui/icons";
import {Button, InputGroup, Label, TextField} from "@heroui/react";
export function WithIconPrefixAndCopySuffix() {
return (
Website
);
}
```
### Password Toggle
Use a button in the suffix to toggle password visibility.
```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 (
Password
setIsVisible(!isVisible)}
>
{isVisible ? : }
);
}
```
### Loading State
Show a loading spinner in the suffix to indicate processing.
```tsx
"use client";
import {InputGroup, Spinner, TextField} from "@heroui/react";
export function WithLoadingSuffix() {
return (
);
}
```
### Keyboard Shortcut
Display keyboard shortcuts using the [Kbd](/docs/components/kbd) component.
```tsx
"use client";
import {InputGroup, Kbd, TextField} from "@heroui/react";
export function WithKeyboardShortcut() {
return (
K
);
}
```
### Badge Suffix
Add a badge or chip in the suffix to show status or labels.
```tsx
"use client";
import {Chip, InputGroup, TextField} from "@heroui/react";
export function WithBadgeSuffix() {
return (
Pro
);
}
```
### Required Field
InputGroup respects the required state from its parent TextField.
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {Description, InputGroup, Label, TextField} from "@heroui/react";
export function Required() {
return (
Email address
Set a price
$
USD
What customers would pay
);
}
```
### Validation
InputGroup automatically reflects invalid state from its parent TextField.
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {FieldError, InputGroup, Label, TextField} from "@heroui/react";
export function Invalid() {
return (
Email address
Please enter a valid email address
Set a price
$
USD
Price must be greater than 0
);
}
```
### Disabled State
InputGroup respects the disabled state from its parent TextField.
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {InputGroup, Label, TextField} from "@heroui/react";
export function Disabled() {
return (
Email address
Set a price
$
USD
);
}
```
### Full Width
```tsx
import {Envelope, Eye} from "@gravity-ui/icons";
import {InputGroup, Label, TextField} from "@heroui/react";
export function FullWidth() {
return (
Email address
Password
);
}
```
### Variants
The InputGroup component supports two visual variants:
* **`primary`** (default) - Standard styling with shadow, suitable for most use cases
* **`secondary`** - Lower emphasis variant without shadow, suitable for use in Surface components
```tsx
import {Envelope} from "@gravity-ui/icons";
import {InputGroup, Label, TextField} from "@heroui/react";
export function Variants() {
return (
Primary variant
Secondary variant
);
}
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` to apply the lower emphasis variant suitable for surface backgrounds.
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {Description, InputGroup, Label, Surface, TextField} from "@heroui/react";
export function OnSurface() {
return (
Email address
We'll never share this with anyone else
);
}
```
### With TextArea
Use **InputGroup.TextArea** for multiline text inputs with prefix and suffix elements. When a textarea is present, the container automatically adjusts its height to accommodate the content and aligns prefix/suffix elements to the top.
```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 (
Add Context
setValue(event.target.value)}
/>
Add a files and more
Connect apps
Voice input
{({isPending}) => (isPending ? : )}
Send
);
}
```
## 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
## Styling
### Passing Tailwind CSS classes
```tsx
import {InputGroup, TextField, Label} from '@heroui/react';
function CustomInputGroup() {
return (
Website
https://
.com
);
}
```
### Customizing the component classes
InputGroup uses CSS classes that can be customized. Override the component classes to match your design system.
```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 Classes
* `.input-group` – Root container with border, background, and flex layout. Uses `min-h-9` for flexible height and `items-center` by default, switching to `items-start` when a textarea is present.
* `.input-group__input` – Input element with transparent background and no border. Also used as the base class for textarea elements.
* `.input-group__prefix` – Prefix container with left border radius. Aligns to top when used with textarea.
* `.input-group__suffix` – Suffix container with right border radius. Aligns to top when used with textarea.
* `.input-group--primary` – Primary variant with shadow (default)
* `.input-group--secondary` – Secondary variant without shadow, suitable for use in surfaces
**Note**: When using `InputGroup.TextArea`, the container automatically switches from `items-center` to `items-start` alignment and uses `height: auto` instead of a fixed height. Prefix and suffix elements align to the top with additional padding to match the textarea's vertical padding. The textarea uses the same `.input-group__input` base class with textarea-specific styles (minimum height and vertical resize) applied via the `[data-slot="input-group-textarea"]` attribute selector.
### Interactive States
InputGroup automatically manages these data attributes based on its state:
* **Hover**: `[data-hovered]` - Applied when hovering over the group
* **Focus Within**: `[data-focus-within]` - Applied when the input is focused
* **Invalid**: `[data-invalid]` - Applied when parent TextField is invalid
* **Disabled**: `[data-disabled]` or `[aria-disabled]` - Applied when parent TextField is disabled
## API Reference
### InputGroup Props
InputGroup inherits all props from React Aria's [Group](https://react-spectrum.adobe.com/react-aria/Group.html) component.
#### Base Props
| Prop | Type | Default | Description |
| ----------- | -------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------- |
| `children` | `React.ReactNode \| (values: GroupRenderProps) => React.ReactNode` | - | Child components (Input, TextArea, Prefix, Suffix) or render function. |
| `className` | `string \| (values: GroupRenderProps) => string` | - | CSS classes for styling, supports render props. |
| `style` | `React.CSSProperties \| (values: GroupRenderProps) => React.CSSProperties` | - | Inline styles, supports render props. |
| `fullWidth` | `boolean` | `false` | Whether the input group should take full width of its container |
| `id` | `string` | - | The element's unique identifier. |
#### Variant Props
| Prop | Type | Default | Description |
| --------- | -------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
#### Accessibility Props
| Prop | Type | Default | Description |
| ------------------ | --------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------- |
| `aria-label` | `string` | - | Accessibility label when no visible label is present. |
| `aria-labelledby` | `string` | - | ID of elements that label this group. |
| `aria-describedby` | `string` | - | ID of elements that describe this group. |
| `aria-details` | `string` | - | ID of elements with additional details. |
| `role` | `'group' \| 'region' \| 'presentation'` | `'group'` | Accessibility role for the group. Use 'region' for important content, 'presentation' for visual-only grouping. |
### Composition Components
InputGroup works with these subcomponents:
* **InputGroup.Root** - Root container (also available as `InputGroup`)
* **InputGroup.Input** - Single-line input element component
* **InputGroup.TextArea** - Multiline textarea element component
* **InputGroup.Prefix** - Prefix container component
* **InputGroup.Suffix** - Suffix container component
#### InputGroup.Input Props
InputGroup.Input inherits all props from React Aria's [Input](https://react-spectrum.adobe.com/react-aria/Input.html) component.
| Prop | Type | Default | Description |
| -------------- | -------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `className` | `string` | - | CSS classes for styling. |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the input. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
| `type` | `string` | `'text'` | Input type (text, password, email, etc.). |
| `value` | `string` | - | Current value (controlled). |
| `defaultValue` | `string` | - | Default value (uncontrolled). |
| `placeholder` | `string` | - | Placeholder text. |
| `disabled` | `boolean` | - | Whether the input is disabled. |
| `readOnly` | `boolean` | - | Whether the input is read-only. |
#### InputGroup.TextArea Props
InputGroup.TextArea inherits all props from React Aria's [TextArea](https://react-spectrum.adobe.com/react-aria/TextArea.html) component.
| Prop | Type | Default | Description |
| -------------- | -------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `className` | `string` | - | CSS classes for styling. |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the textarea. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
| `value` | `string` | - | Current value (controlled). |
| `defaultValue` | `string` | - | Default value (uncontrolled). |
| `placeholder` | `string` | - | Placeholder text. |
| `rows` | `number` | - | Number of visible text lines. |
| `disabled` | `boolean` | - | Whether the textarea is disabled. |
| `readOnly` | `boolean` | - | Whether the textarea is read-only. |
#### InputGroup.Prefix Props
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ----------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to display in the prefix (icons, text, etc.). |
| `className` | `string` | - | CSS classes for styling. |
#### InputGroup.Suffix Props
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ---------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to display in the suffix (icons, buttons, badges, etc.). |
| `className` | `string` | - | CSS classes for styling. |
### 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/en/docs/react/components/input-otp
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/input-otp.mdx
> A one-time password input component for verification codes and secure authentication
## Import
```tsx
import { InputOTP } from '@heroui/react';
```
### Usage
```tsx
import {InputOTP, Label, Link} from "@heroui/react";
export function Basic() {
return (
Verify account
We've sent a code to a****@gmail.com
Didn't receive a code?
Resend
);
}
```
### Anatomy
Import the InputOTP component and access all parts using dot notation.
```tsx
import { InputOTP } from '@heroui/react';
export default () => (
{/* ...rest of the slots */}
{/* ...rest of the slots */}
)
```
> **InputOTP** is built on top of [input-otp](https://github.com/guilhermerodz/input-otp) by [@guilherme\_rodz](https://twitter.com/guilherme_rodz), providing a flexible and accessible foundation for OTP input components.
### Four Digits
```tsx
import {InputOTP, Label} from "@heroui/react";
export function FourDigits() {
return (
Enter PIN
);
}
```
### Disabled State
```tsx
import {Description, InputOTP, Label} from "@heroui/react";
export function Disabled() {
return (
Verify account
Code verification is currently disabled
);
}
```
### With Pattern
Use the `pattern` prop to restrict input to specific characters. HeroUI exports common patterns like `REGEXP_ONLY_CHARS` and `REGEXP_ONLY_DIGITS`.
```tsx
import {Description, InputOTP, Label, REGEXP_ONLY_CHARS} from "@heroui/react";
export function WithPattern() {
return (
Enter code (letters only)
Only alphabetic characters are allowed
);
}
```
### Controlled
Control the value to synchronize with state, clear the input, or implement custom validation.
```tsx
"use client";
import {Description, InputOTP, Label} from "@heroui/react";
import React from "react";
export function Controlled() {
const [value, setValue] = React.useState("");
return (
Verify account
{value.length > 0 ? (
<>
Value: {value} ({value.length}/6) •{" "}
setValue("")}>
Clear
>
) : (
"Enter a 6-digit code"
)}
);
}
```
### With Validation
Use `isInvalid` together with validation messages to surface errors.
```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("Code verified successfully!");
};
const handleChange = (val: string) => {
setValue(val);
setIsInvalid(false);
};
return (
Verify account
Hint: The code is 123456
Invalid code. Please try again.
Submit
);
}
```
### On Complete
Use the `onComplete` callback to trigger actions when all slots are filled.
```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 (
Verify account
{
setValue(val);
setIsComplete(false);
}}
>
{isSubmitting ? (
<>
Verifying...
>
) : (
"Verify Code"
)}
);
}
```
### Form Example
A complete two-factor authentication form with validation and submission.
```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("Please enter all 6 digits");
return;
}
setIsSubmitting(true);
// Simulate API call
setTimeout(() => {
if (value === "123456") {
console.log("Code verified successfully!");
setValue("");
} else {
setError("Invalid code. Please try again.");
}
setIsSubmitting(false);
}, 1500);
};
return (
Two-factor authentication
Enter the 6-digit code from your authenticator app
{
setValue(val);
setError("");
}}
>
{error}
{isSubmitting ? (
<>
Verifying...
>
) : (
"Verify"
)}
Having trouble?
Use backup code
);
}
```
### Variants
The InputOTP component supports two visual variants:
* **`primary`** (default) - Standard styling with shadow, suitable for most use cases
* **`secondary`** - Lower emphasis variant without shadow, suitable for use in Surface components
```tsx
import {InputOTP, Label} from "@heroui/react";
export function Variants() {
return (
Primary variant
Secondary variant
);
}
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` to apply the lower emphasis variant suitable for surface backgrounds.
```tsx
import {InputOTP, Label, Link, Surface} from "@heroui/react";
export function OnSurface() {
return (
Verify account
We've sent a code to a****@gmail.com
Didn't receive a code?
Resend
);
}
```
## Related Components
* **Input**: Single-line text input built on React Aria
* **Form**: Form validation and submission handling
* **Surface**: Base container surface
## Styling
### Passing Tailwind CSS classes
```tsx
import {InputOTP, Label} from '@heroui/react';
function CustomInputOTP() {
return (
Enter verification code
);
}
```
### Customizing the component classes
To customize the InputOTP component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The InputOTP component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/input-otp.css)):
#### Base Classes
* `.input-otp` - Base container
* `.input-otp__container` - Inner container from input-otp library
* `.input-otp__group` - Group of slots
* `.input-otp__slot` - Individual input slot
* `.input-otp__slot-value` - The character inside a slot
* `.input-otp__caret` - Blinking caret indicator
* `.input-otp__separator` - Visual separator between groups
#### State Classes
* `.input-otp__slot[data-active="true"]` - Currently active slot
* `.input-otp__slot[data-filled="true"]` - Slot with a character
* `.input-otp__slot[data-disabled="true"]` - Disabled slot
* `.input-otp__slot[data-invalid="true"]` - Invalid slot
* `.input-otp__container[data-disabled="true"]` - Disabled container
### Interactive States
The component supports both CSS pseudo-classes and data attributes for flexibility:
* **Hover**: `:hover` or `[data-hovered="true"]` on slot
* **Active**: `[data-active="true"]` on slot (currently focused)
* **Filled**: `[data-filled="true"]` on slot (contains a character)
* **Disabled**: `[data-disabled="true"]` on container and slots
* **Invalid**: `[data-invalid="true"]` on slots
## API Reference
### InputOTP Props
InputOTP is built on top of the [input-otp](https://github.com/guilhermerodz/input-otp) library with additional features.
#### Base Props
| Prop | Type | Default | Description |
| -------------------- | -------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `maxLength` | `number` | - | **Required.** Number of input slots. |
| `value` | `string` | - | Controlled value (uncontrolled if not provided). |
| `onChange` | `(value: string) => void` | - | Handler called when the value changes. |
| `onComplete` | `(value: string) => void` | - | Handler called when all slots are filled. |
| `className` | `string` | - | Additional CSS classes for the container. |
| `containerClassName` | `string` | - | CSS classes for the inner container. |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
| `children` | `React.ReactNode` | - | InputOTP.Group, InputOTP.Slot, and InputOTP.Separator components. |
#### Validation Props
| Prop | Type | Default | Description |
| ------------------- | --------------- | ------- | ----------------------------------------- |
| `isDisabled` | `boolean` | `false` | Whether the input is disabled. |
| `isInvalid` | `boolean` | `false` | Whether the input is in an invalid state. |
| `validationErrors` | `string[]` | - | Server-side or custom validation errors. |
| `validationDetails` | `ValidityState` | - | HTML5 validation details. |
#### Input Props
| Prop | Type | Default | Description |
| ------------------ | --------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------ |
| `pattern` | `string` | - | Regex pattern for allowed characters (e.g., `REGEXP_ONLY_DIGITS`). |
| `textAlign` | `'left' \| 'center' \| 'right'` | `'left'` | Text alignment within slots. |
| `inputMode` | `'numeric' \| 'text' \| 'decimal' \| 'tel' \| 'search' \| 'email' \| 'url'` | `'numeric'` | Virtual keyboard type on mobile devices. |
| `placeholder` | `string` | - | Placeholder text for empty slots. |
| `pasteTransformer` | `(text: string) => string` | - | Transform pasted text (e.g., remove hyphens). |
#### Form Props
| Prop | Type | Default | Description |
| ----------- | --------- | ------- | ----------------------------------------- |
| `name` | `string` | - | Name attribute for form submission. |
| `autoFocus` | `boolean` | - | Whether to focus the first slot on mount. |
### InputOTP.Group Props
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ------------------------------------- |
| `className` | `string` | - | Additional CSS classes for the group. |
| `children` | `React.ReactNode` | - | InputOTP.Slot components. |
### InputOTP.Slot Props
| Prop | Type | Default | Description |
| ----------- | -------- | ------- | ------------------------------------------- |
| `index` | `number` | - | **Required.** Zero-based index of the slot. |
| `className` | `string` | - | Additional CSS classes for the slot. |
### InputOTP.Separator Props
| Prop | Type | Default | Description |
| ----------- | -------- | ------- | ----------------------------------------- |
| `className` | `string` | - | Additional CSS classes for the separator. |
### Exported Patterns
HeroUI re-exports common regex patterns from input-otp for convenience:
```tsx
import { REGEXP_ONLY_DIGITS, REGEXP_ONLY_CHARS, REGEXP_ONLY_DIGITS_AND_CHARS } from '@heroui/react';
// Use with pattern prop
{/* ... */}
```
* **REGEXP\_ONLY\_DIGITS** - Only numeric characters (0-9)
* **REGEXP\_ONLY\_CHARS** - Only alphabetic characters (a-z, A-Z)
* **REGEXP\_ONLY\_DIGITS\_AND\_CHARS** - Alphanumeric characters (0-9, a-z, A-Z)
# Input
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/input
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/input.mdx
> Primitive single-line text input component that accepts standard HTML attributes
## Import
```tsx
import { Input } from '@heroui/react';
```
For validation, labels, and error messages, see **[TextField](/docs/components/text-field)**.
### Usage
```tsx
import {Input} from "@heroui/react";
export function Basic() {
return ;
}
```
### Input Types
```tsx
import {Input, Label} from "@heroui/react";
export function Types() {
return (
);
}
```
### Controlled
```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 || "your-domain"}
);
}
```
### Full Width
```tsx
import {Input} from "@heroui/react";
export function FullWidth() {
return (
);
}
```
### Variants
The Input component supports two visual variants:
* **`primary`** (default) - Standard styling with shadow, suitable for most use cases
* **`secondary`** - Lower emphasis variant without shadow, suitable for use in Surface components
```tsx
import {Input} from "@heroui/react";
export function Variants() {
return (
);
}
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` to apply the lower emphasis variant suitable for surface backgrounds.
```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
## Styling
### Passing Tailwind CSS classes
```tsx
import {Input, Label} from '@heroui/react';
function CustomInput() {
return (
Project name
);
}
```
### Customizing the component classes
The base class `.input` powers every instance. Override it once with `@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 Classes
* `.input` – Native input element styling
### Interactive States
* **Hover**: `:hover` or `[data-hovered="true"]`
* **Focus Visible**: `:focus-visible` or `[data-focus-visible="true"]`
* **Invalid**: `[data-invalid="true"]` (also syncs with `aria-invalid`)
* **Disabled**: `:disabled` or `[aria-disabled="true"]`
* **Read Only**: `[aria-readonly="true"]`
## API Reference
### Input Props
Input accepts all standard HTML ` ` attributes plus the following:
| Prop | Type | Default | Description |
| -------------- | ------------------------------------------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `className` | `string` | - | Tailwind classes merged with the component styles. |
| `type` | `string` | `"text"` | Input type (text, email, password, number, etc.). |
| `value` | `string` | - | Controlled value. |
| `defaultValue` | `string` | - | Uncontrolled initial value. |
| `onChange` | `(event: React.ChangeEvent) => void` | - | Change handler. |
| `placeholder` | `string` | - | Placeholder text. |
| `disabled` | `boolean` | `false` | Disables the input. |
| `readOnly` | `boolean` | `false` | Makes the input read-only. |
| `required` | `boolean` | `false` | Marks the input as required. |
| `name` | `string` | - | Name for form submission. |
| `autoComplete` | `string` | - | Autocomplete hint for the browser. |
| `maxLength` | `number` | - | Maximum number of characters. |
| `minLength` | `number` | - | Minimum number of characters. |
| `pattern` | `string` | - | Regex pattern for validation. |
| `min` | `number \| string` | - | Minimum value (for number/date inputs). |
| `max` | `number \| string` | - | Maximum value (for number/date inputs). |
| `step` | `number \| string` | - | Stepping interval (for number inputs). |
| `fullWidth` | `boolean` | `false` | Whether the input should take full width of its container |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
> For validation props like `isInvalid`, `isRequired`, and error handling, use **[TextField](/docs/components/text-field)** with Input as a child component.
# Label
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/label
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/label.mdx
> Renders an accessible label associated with form controls
## Import
```tsx
import { Label } from '@heroui/react';
```
## Usage
```tsx
import {Input, Label} from "@heroui/react";
export function Basic() {
return (
Name
);
}
```
## 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 | Type | Default | Description |
| ------------ | ----------- | ------- | -------------------------------------------------- |
| `htmlFor` | `string` | - | The id of the element the label is associated with |
| `isRequired` | `boolean` | `false` | Whether to display a required indicator |
| `isDisabled` | `boolean` | `false` | Whether the label is in a disabled state |
| `isInvalid` | `boolean` | `false` | Whether the label is in an invalid state |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | The content of the label |
## Accessibility
The Label component is built on the native HTML `` element ([MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label)) and follows WAI-ARIA best practices:
* Associates with form controls using the `htmlFor` attribute
* Provides semantic HTML `` element
* Supports keyboard navigation when associated with form controls
* Communicates required and invalid states to screen readers
* Clicking the label focuses/activates the associated form control
## 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
## Styling
### CSS Classes
The Label component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/label.css)):
#### Base Classes
* `.label` - Base label styles with text styling
#### State Modifier Classes
* `.label--required` or `[data-required="true"] > .label` - Shows required asterisk indicator
* `.label--disabled` or `[data-disabled="true"] .label` - Disabled state styling
* `.label--invalid` or `[data-invalid="true"] .label` or `[aria-invalid="true"] .label` - Invalid state styling (danger/red text color)
**Note**: The required asterisk is smartly applied using role and data-slot detection. It excludes:
* Elements with `role="group"`, `role="radiogroup"`, or `role="checkboxgroup"`
* Elements with `data-slot="radio"` or `data-slot="checkbox"`
This prevents duplicate asterisks when using group components with required fields.
## Examples
### With Required Indicator
```tsx
Email Address
```
### With Disabled State
```tsx
Username
```
### With Invalid State
```tsx
Password
```
# NumberField
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/number-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/number-field.mdx
> Number input fields with increment/decrement buttons, validation, and internationalized formatting
## Import
```tsx
import { NumberField } from '@heroui/react';
```
### Usage
```tsx
import {Label, NumberField} from "@heroui/react";
export function Basic() {
return (
Width
);
}
```
### Anatomy
```tsx
import {NumberField, Label, Description, FieldError} from '@heroui/react';
export default () => (
)
```
> **NumberField** allows users to enter numeric values with optional increment/decrement buttons. It supports internationalized formatting, validation, and keyboard navigation.
### With Description
```tsx
import {Description, Label, NumberField} from "@heroui/react";
export function WithDescription() {
return (
Width
Enter the width in pixels
Percentage
Value must be between 0 and 100
);
}
```
### Required Field
```tsx
import {Description, Label, NumberField} from "@heroui/react";
export function Required() {
return (
Quantity
Rating
Rate from 1 to 10
);
}
```
### Validation
Use `isInvalid` together with `FieldError` to surface validation messages.
```tsx
import {FieldError, Label, NumberField} from "@heroui/react";
export function Validation() {
return (
Quantity
Quantity must be greater than or equal to 0
Percentage
Percentage must be between 0 and 100
);
}
```
### Controlled
Control the value to synchronize with other components or perform custom formatting.
```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 (
Width
Current value: {value}
setValue(0)}>
Reset to 0
setValue(2048)}>
Set to 2048
);
}
```
### With Validation
Implement custom validation logic with controlled values.
```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 (
Percentage
{isInvalid ? (
Percentage must be between 0 and 100
) : (
Enter a value between 0 and 100
)}
);
}
```
### Step Values
Configure increment/decrement step values for precise control.
```tsx
import {Description, Label, NumberField} from "@heroui/react";
export function WithStep() {
return (
Step: 1
Increments by 1
Step: 5
Increments by 5
Step: 10
Increments by 10
);
}
```
### Format Options
Format numbers as currency, percentages, decimals, or units with internationalization support.
```tsx
import {Description, Label, NumberField} from "@heroui/react";
export function WithFormatOptions() {
return (
Currency (EUR - Accounting)
Accounting format with EUR currency
Currency (USD)
Standard USD currency format
Percentage
Percentage format (0-1, where 0.5 = 50%)
Decimal (2 decimal places)
Decimal format with 2 decimal places
Unit (Kilograms)
Unit format with kilograms
);
}
```
### Custom Icons
Customize the increment and decrement button icons.
```tsx
import {Description, Label, NumberField} from "@heroui/react";
export function CustomIcons() {
return (
Width (Custom Icons)
Custom icon children
);
}
```
### With Chevrons
Use chevron icons in a vertical layout for a different visual style.
```tsx
import {Label, NumberField} from "@heroui/react";
export function WithChevrons() {
return (
Number field with chevrons
);
}
```
### Disabled State
```tsx
import {Description, Label, NumberField} from "@heroui/react";
export function Disabled() {
return (
Width
Enter the width in pixels
Percentage
Value must be between 0 and 100
);
}
```
### Full Width
```tsx
import {Label, NumberField} from "@heroui/react";
export function FullWidth() {
return (
Width
);
}
```
### Variants
The NumberField component supports two visual variants:
* **`primary`** (default) - Standard styling with shadow, suitable for most use cases
* **`secondary`** - Lower emphasis variant without shadow, suitable for use in Surface components
```tsx
import {Label, NumberField} from "@heroui/react";
export function Variants() {
return (
Primary variant
Secondary variant
);
}
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` to apply the lower emphasis variant suitable for surface backgrounds.
```tsx
import {Description, Label, NumberField, Surface} from "@heroui/react";
export function OnSurface() {
return (
Width
Enter the width in pixels
Percentage
Value must be between 0 and 100
);
}
```
### Form Example
Complete form integration with validation and submission handling.
```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 (
Order quantity
{isOutOfStock ? (
Only {STOCK_AVAILABLE} items left in stock
) : (
Only {STOCK_AVAILABLE} items available
)}
STOCK_AVAILABLE}
isPending={isSubmitting}
type="submit"
variant="primary"
>
{isSubmitting ? (
<>
Processing...
>
) : (
"Place Order"
)}
);
}
```
## Related Components
* **Label**: Accessible label for form controls
* **Description**: Helper text for form fields
* **FieldError**: Inline validation messages for form fields
### Custom Render Function
```tsx
"use client";
import {Label, NumberField} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
Width
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import {NumberField, Label} from '@heroui/react';
function CustomNumberField() {
return (
Quantity
);
}
```
### Customizing the component classes
NumberField uses CSS classes that can be customized. Override the component classes to match your design system.
```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 Classes
* `.number-field` – Root container with minimal styling (`flex flex-col gap-1`)
* `.number-field__group` – Container for input and buttons with border and background styling
* `.number-field__input` – The numeric input field
* `.number-field__increment-button` – Button to increment the value
* `.number-field__decrement-button` – Button to decrement the value
* `.number-field--primary` – Primary variant with shadow (default)
* `.number-field--secondary` – Secondary variant without shadow, suitable for use in surfaces
> **Note:** Child components ([Label](/docs/components/label), [Description](/docs/components/description), [FieldError](/docs/components/field-error)) have their own CSS classes and styling. See their respective documentation for customization options.
### Interactive States
NumberField automatically manages these data attributes based on its state:
* **Invalid**: `[data-invalid="true"]` or `[aria-invalid="true"]` - Automatically hides the description slot when invalid
* **Disabled**: `[data-disabled="true"]` - Applied when `isDisabled` is true
* **Focus Within**: `[data-focus-within="true"]` - Applied when the input or buttons are focused
* **Focus Visible**: `[data-focus-visible="true"]` - Applied when focus is visible (keyboard navigation)
* **Hovered**: `[data-hovered="true"]` - Applied when hovering over buttons
Additional attributes are available through render props (see NumberFieldRenderProps below).
## API Reference
### NumberField Props
NumberField inherits all props from React Aria's [NumberField](https://react-spectrum.adobe.com/react-aria/NumberField.html) component.
#### Base Props
| Prop | Type | Default | Description |
| ----------- | -------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode \| (values: NumberFieldRenderProps) => React.ReactNode` | - | Child components (Label, Group, Input, etc.) or render function. |
| `className` | `string \| (values: NumberFieldRenderProps) => string` | - | CSS classes for styling, supports render props. |
| `style` | `React.CSSProperties \| (values: NumberFieldRenderProps) => React.CSSProperties` | - | Inline styles, supports render props. |
| `fullWidth` | `boolean` | `false` | Whether the number field should take full width of its container |
| `id` | `string` | - | The element's unique identifier. |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
#### Value Props
| Prop | Type | Default | Description |
| -------------- | -------------------------------------- | ------- | -------------------------------------- |
| `value` | `number` | - | Current value (controlled). |
| `defaultValue` | `number` | - | Default value (uncontrolled). |
| `onChange` | `(value: number \| undefined) => void` | - | Handler called when the value changes. |
#### Formatting Props
| Prop | Type | Default | Description |
| --------------- | -------------------------- | ------- | ------------------------------------------------------------------ |
| `formatOptions` | `Intl.NumberFormatOptions` | - | Options for formatting numbers (currency, percent, decimal, unit). |
| `locale` | `string` | - | Locale for number formatting. |
#### Validation Props
| Prop | Type | Default | Description |
| -------------------- | ----------------------------------------------------------------- | ---------- | -------------------------------------------------------------- |
| `isRequired` | `boolean` | `false` | Whether user input is required before form submission. |
| `isInvalid` | `boolean` | - | Whether the value is invalid. |
| `validate` | `(value: number) => ValidationError \| true \| null \| undefined` | - | Custom validation function. |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | Whether to use native HTML form validation or ARIA attributes. |
| `validationErrors` | `string[]` | - | Server-side validation errors. |
#### Range Props
| Prop | Type | Default | Description |
| ---------- | -------- | ------- | ---------------------------------------------- |
| `minValue` | `number` | - | Minimum allowed value. |
| `maxValue` | `number` | - | Maximum allowed value. |
| `step` | `number` | `1` | Step value for increment/decrement operations. |
#### State Props
| Prop | Type | Default | Description |
| ------------ | --------- | ------- | -------------------------------------------------- |
| `isDisabled` | `boolean` | - | Whether the input is disabled. |
| `isReadOnly` | `boolean` | - | Whether the input can be selected but not changed. |
#### Form Props
| Prop | Type | Default | Description |
| ----------- | --------- | ------- | ---------------------------------------------------- |
| `name` | `string` | - | Name of the input element, for HTML form submission. |
| `autoFocus` | `boolean` | - | Whether the element should receive focus on render. |
#### Accessibility Props
| Prop | Type | Default | Description |
| ------------------ | -------- | ------- | ----------------------------------------------------- |
| `aria-label` | `string` | - | Accessibility label when no visible label is present. |
| `aria-labelledby` | `string` | - | ID of elements that label this field. |
| `aria-describedby` | `string` | - | ID of elements that describe this field. |
| `aria-details` | `string` | - | ID of elements with additional details. |
### Composition Components
NumberField works with these separate components that should be imported and used directly:
* **NumberField.Group** - Container for input and buttons
* **NumberField.Input** - The numeric input field
* **NumberField.IncrementButton** - Button to increment the value
* **NumberField.DecrementButton** - Button to decrement the value
* **Label** - Field label component from `@heroui/react`
* **Description** - Helper text component from `@heroui/react`
* **FieldError** - Validation error message from `@heroui/react`
Each of these components has its own props API. Use them directly within NumberField for composition:
```tsx
Quantity
Enter a value between 0 and 100
Value must be between 0 and 100
```
#### NumberField.Group Props
NumberField.Group inherits props from React Aria's [Group](https://react-spectrum.adobe.com/react-aria/Group.html) component.
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------------ | ------- | ----------------------------------------------------- |
| `children` | `React.ReactNode \| (values: GroupRenderProps) => React.ReactNode` | - | Child components (Input, Buttons) or render function. |
| `className` | `string \| (values: GroupRenderProps) => string` | - | CSS classes for styling. |
#### NumberField.Input Props
NumberField.Input inherits props from React Aria's [Input](https://react-spectrum.adobe.com/react-aria/Input.html) component.
| Prop | Type | Default | Description |
| ----------- | -------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `className` | `string` | - | CSS classes for styling. |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the input. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
#### NumberField.IncrementButton Props
NumberField.IncrementButton inherits props from React Aria's [Button](https://react-spectrum.adobe.com/react-aria/Button.html) component.
| Prop | Type | Default | Description |
| ----------- | ----------------- | -------------- | ------------------------------------------------------ |
| `children` | `React.ReactNode` | ` ` | Icon or content for the button. Defaults to plus icon. |
| `className` | `string` | - | CSS classes for styling. |
| `slot` | `"increment"` | `"increment"` | Must be set to "increment" (automatically set). |
#### NumberField.DecrementButton Props
NumberField.DecrementButton inherits props from React Aria's [Button](https://react-spectrum.adobe.com/react-aria/Button.html) component.
| Prop | Type | Default | Description |
| ----------- | ----------------- | --------------- | ------------------------------------------------------- |
| `children` | `React.ReactNode` | ` ` | Icon or content for the button. Defaults to minus icon. |
| `className` | `string` | - | CSS classes for styling. |
| `slot` | `"decrement"` | `"decrement"` | Must be set to "decrement" (automatically set). |
### NumberFieldRenderProps
When using render props with `className`, `style`, or `children`, these values are available:
| Prop | Type | Description |
| ---------------- | --------------------- | -------------------------------------------------------------------------- |
| `isDisabled` | `boolean` | Whether the field is disabled. |
| `isInvalid` | `boolean` | Whether the field is currently invalid. |
| `isReadOnly` | `boolean` | Whether the field is read-only. |
| `isRequired` | `boolean` | Whether the field is required. |
| `isFocused` | `boolean` | Whether the field is currently focused (DEPRECATED - use `isFocusWithin`). |
| `isFocusWithin` | `boolean` | Whether any child element is focused. |
| `isFocusVisible` | `boolean` | Whether focus is visible (keyboard navigation). |
| `value` | `number \| undefined` | Current value. |
| `minValue` | `number \| undefined` | Minimum allowed value. |
| `maxValue` | `number \| undefined` | Maximum allowed value. |
| `step` | `number` | Step value for increment/decrement. |
# RadioGroup
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/radio-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/radio-group.mdx
> Radio group for selecting a single option from a list
## Import
```tsx
import { RadioGroup, Radio } from '@heroui/react';
```
### Usage
```tsx
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Basic() {
return (
Plan selection
Choose the plan that suits you best
Basic Plan
Includes 100 messages per month
Premium Plan
Includes 200 messages per month
Business Plan
Unlimited messages
);
}
```
### Anatomy
Import the RadioGroup component and access all parts using dot notation.
```tsx
import {RadioGroup, Radio, Label, Description, FieldError} from '@heroui/react';
export default () => (
{/* The clickable area: control + label */}
✓ {/* Custom indicator (optional) */}
Label {/* plain text — the clickable label */}
{/* Sibling — stays outside the button (announced via aria-describedby) */}
{/* Optional — per-radio validation message */}
{/* Optional — group-level validation */}
)
```
### Custom Indicator
```tsx
"use client";
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function CustomIndicator() {
return (
Plan selection
Choose the plan that suits you best
{({isSelected}) =>
isSelected ? ✓ : null
}
Basic Plan
Includes 100 messages per month
{({isSelected}) =>
isSelected ? ✓ : null
}
Premium Plan
Includes 200 messages per month
{({isSelected}) =>
isSelected ? ✓ : null
}
Business Plan
Unlimited messages
);
}
```
### Horizontal Orientation
```tsx
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Horizontal() {
return (
Subscription plan
Starter
For side projects
Pro
Advanced reporting
Teams
Up to 10 teammates
);
}
```
### Controlled
```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 (
Subscription plan
Starter
For side projects and small teams
Pro
Advanced reporting and analytics
Teams
Share access with up to 10 teammates
Selected plan: {value}
);
}
```
### Uncontrolled
Combine `defaultValue` with `onChange` when you only need to react to updates.
```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)}
>
Subscription plan
Starter
For side projects and small teams
Pro
Advanced reporting and analytics
Teams
Share access with up to 10 teammates
Last chosen plan: {selection}
);
}
```
### Validation
```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(`Your chosen plan is: ${value}`);
}}
>
Subscription plan
Starter
For side projects and small teams
Pro
Advanced reporting and analytics
Teams
Share access with up to 10 teammates
Choose a subscription before continuing.
Submit
{!!message && {message}
}
);
}
```
### Disabled
```tsx
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Disabled() {
return (
Subscription plan
Plan changes are temporarily paused while we roll out updates.
Starter
For side projects and small teams
Pro
Advanced reporting and analytics
Teams
Share access with up to 10 teammates
);
}
```
### Variants
The RadioGroup component supports two visual variants:
* **`primary`** (default) - Standard styling with default background, suitable for most use cases
* **`secondary`** - Lower emphasis variant, suitable for use in Surface components
```tsx
import {Description, Radio, RadioGroup} from "@heroui/react";
export function Variants() {
return (
Primary variant
Option 1
Standard styling with default background
Option 2
Another option with primary styling
Secondary variant
Option 1
Lower emphasis variant for use in surfaces
Option 2
Another option with secondary styling
);
}
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` to apply the lower emphasis variant suitable for surface backgrounds.
```tsx
import {Description, Label, Radio, RadioGroup, Surface} from "@heroui/react";
export function OnSurface() {
return (
Plan selection
Choose the plan that suits you best
Basic Plan
Includes 100 messages per month
Premium Plan
Includes 200 messages per month
Business Plan
Unlimited messages
);
}
```
### Delivery & Payment
## Related Components
* **Fieldset**: Group related form controls with legends
* **Surface**: Base container surface
* **Description**: Helper text for form fields
### Custom Render Function
```tsx
"use client";
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
Plan selection
Choose the plan that suits you best
Basic Plan
Includes 100 messages per month
Premium Plan
Includes 200 messages per month
Business Plan
Unlimited messages
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import { RadioGroup, Radio } from '@heroui/react';
export default () => (
Basic Plan
Premium Plan
Business Plan
);
```
### Customizing the component classes
To customize the RadioGroup component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The RadioGroup component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/radio-group.css)):
#### Base Classes
* `.radio-group` - Base radio group container
* `.radio` - Individual radio item
* `.radio__content` - Radio button (RAC label wrapper for control + label text)
* `.radio__control` - Radio control (circular button)
* `.radio__indicator` - Radio indicator (inner dot)
* `.radio__content` - Radio content wrapper
#### Modifier Classes
* `.radio--disabled` - Disabled radio state
### Interactive States
The radio supports both CSS pseudo-classes and data attributes for flexibility:
* **Selected**: `[aria-checked="true"]` or `[data-selected="true"]` (indicator appears)
* **Hover**: `:hover` or `[data-hovered="true"]` (border color changes)
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]` (shows focus ring)
* **Pressed**: `:active` or `[data-pressed="true"]` (scale transform)
* **Disabled**: `:disabled` or `[aria-disabled="true"]` (reduced opacity, no pointer events)
* **Invalid**: `[data-invalid="true"]` or `[aria-invalid="true"]` (error border color)
## API Reference
### RadioGroup Props
| Prop | Type | Default | Description |
| -------------- | ----------------------------------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `value` | `string` | - | The current value (controlled) |
| `defaultValue` | `string` | - | The default value (uncontrolled) |
| `onChange` | `(value: string) => void` | - | Handler called when the value changes |
| `isDisabled` | `boolean` | `false` | Whether the radio group is disabled |
| `isRequired` | `boolean` | `false` | Whether the radio group is required |
| `isReadOnly` | `boolean` | `false` | Whether the radio group is read only |
| `isInvalid` | `boolean` | `false` | Whether the radio group is in an invalid state |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
| `name` | `string` | - | The name of the radio group, used when submitting an HTML form |
| `orientation` | `'horizontal' \| 'vertical'` | `'vertical'` | The orientation of the radio group |
| `children` | `React.ReactNode \| (values: RadioGroupRenderProps) => React.ReactNode` | - | Radio group content or render prop |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Radio Props
| Prop | Type | Default | Description |
| ------------ | ----------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- |
| `value` | `string` | - | The value of the radio button |
| `isDisabled` | `boolean` | `false` | Whether the radio button is disabled |
| `name` | `string` | - | The name of the radio button, used when submitting an HTML form |
| `children` | `React.ReactNode \| (values: RadioFieldRenderProps) => React.ReactNode` | - | Radio content or field render prop |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Radio.Control Props
Extends `React.HTMLAttributes`.
| Prop | Type | Default | Description |
| ---------- | ----------------- | ------- | ---------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | The content to render inside the control wrapper (typically Radio.Indicator) |
### Radio.Indicator Props
Extends `React.HTMLAttributes`.
| Prop | Type | Default | Description |
| ---------- | ------------------------------------------------------------------------ | ------- | ----------------------------------------------------------------------------- |
| `children` | `React.ReactNode \| (values: RadioButtonRenderProps) => React.ReactNode` | - | Optional content or render prop that receives the current radio button state. |
### Radio.Content Props
The clickable area of the radio (the `` wrapping the hidden input). Place `Radio.Control` and the `Label` inside it. `className` accepts a render function that receives `RadioButtonRenderProps`.
| Prop | Type | Default | Description |
| ---------- | ------------------------------------------------------------------------ | ------- | --------------------------------------------------------- |
| `children` | `React.ReactNode \| (values: RadioButtonRenderProps) => React.ReactNode` | - | The clickable content (typically Radio.Control and Label) |
### RadioFieldRenderProps
When using a render prop on the root `Radio`, these field-level values are provided:
| Prop | Type | Description |
| ------------ | --------- | ---------------------------------------- |
| `isSelected` | `boolean` | Whether the radio is currently selected |
| `isDisabled` | `boolean` | Whether the radio is disabled |
| `isReadOnly` | `boolean` | Whether the radio is read only |
| `isInvalid` | `boolean` | Whether the radio is in an invalid state |
| `isRequired` | `boolean` | Whether the radio is required |
### RadioButtonRenderProps
`Radio.Control` and `Radio.Indicator` use button-level render props (`isHovered`, `isPressed`, `isFocusVisible`, etc.). Pass a function as `Radio.Control` children or to `Radio.Indicator` to access them.
# SearchField
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/search-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/search-field.mdx
> Search input field with clear button and search icon
## Import
```tsx
import { SearchField } from '@heroui/react';
```
### Usage
```tsx
import {Label, SearchField} from "@heroui/react";
export function Basic() {
return (
Search
);
}
```
### Anatomy
```tsx
import {SearchField, Label, Description, FieldError} from '@heroui/react';
export default () => (
)
```
> **SearchField** allows users to enter and clear a search query. It includes a search icon and an optional clear button for easy reset.
### With Description
```tsx
import {Description, Label, SearchField} from "@heroui/react";
export function WithDescription() {
return (
Search products
Enter keywords to search for products
Search users
Search by name, email, or username
);
}
```
### Required Field
```tsx
import {Description, Label, SearchField} from "@heroui/react";
export function Required() {
return (
Search
Search query
Minimum 3 characters required
);
}
```
### Validation
Use `isInvalid` together with `FieldError` to surface validation messages.
```tsx
import {FieldError, Label, SearchField} from "@heroui/react";
export function Validation() {
return (
Search
Search query must be at least 3 characters
Search
Invalid characters in search query
);
}
```
### Disabled State
```tsx
import {Description, Label, SearchField} from "@heroui/react";
export function Disabled() {
return (
Search
This search field is disabled
Search
This search field is disabled
);
}
```
### Controlled
Control the value to synchronize with other components or perform custom formatting.
```tsx
"use client";
import {Button, Description, Label, SearchField} from "@heroui/react";
import React from "react";
export function Controlled() {
const [value, setValue] = React.useState("");
return (
Search
Current value: {value || "(empty)"}
setValue("")}>
Clear
setValue("example query")}>
Set example
);
}
```
### With Validation
Implement custom validation logic with controlled values.
```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 (
Search
{isInvalid ? (
Search query must be at least 3 characters
) : (
Enter at least 3 characters to search
)}
);
}
```
### Custom Icons
Customize the search icon and clear button icons.
```tsx
import {Description, Label, SearchField} from "@heroui/react";
export function CustomIcons() {
return (
Search (Custom Icons)
Custom icon children
);
}
```
### Full Width
```tsx
import {Label, SearchField} from "@heroui/react";
export function FullWidth() {
return (
Search
);
}
```
### Variants
The SearchField component supports two visual variants:
* **`primary`** (default) - Standard styling with shadow, suitable for most use cases
* **`secondary`** - Lower emphasis variant without shadow, suitable for use in Surface components
```tsx
import {Label, SearchField} from "@heroui/react";
export function Variants() {
return (
Primary variant
Secondary variant
);
}
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` to apply the lower emphasis variant suitable for surface backgrounds.
```tsx
import {Description, Label, SearchField, Surface} from "@heroui/react";
export function OnSurface() {
return (
Search
Enter keywords to search
Advanced search
Use filters to refine your search
);
}
```
### Form Example
Complete form integration with validation and submission handling.
```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 (
Search products
{isInvalid ? (
Search query must be at least {MIN_LENGTH} characters
) : (
Enter at least {MIN_LENGTH} characters to search
)}
{isSubmitting ? (
<>
Searching...
>
) : (
"Search"
)}
);
}
```
### With Keyboard Shortcut
Add keyboard shortcuts to quickly focus the search field.
```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 (
Search
Use keyboard shortcut to quickly focus this field
Press
S
to focus the search field
);
}
```
## Related Components
* **Label**: Accessible label for form controls
* **Description**: Helper text for form fields
* **FieldError**: Inline validation messages for form fields
### Custom Render Function
```tsx
"use client";
import {Label, SearchField} from "@heroui/react";
export function CustomRenderFunction() {
return (
}>
Search
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import {SearchField, Label} from '@heroui/react';
function CustomSearchField() {
return (
Search
);
}
```
### Customizing the component classes
SearchField uses CSS classes that can be customized. Override the component classes to match your design system.
```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 Classes
* `.search-field` – Root container with minimal styling (`flex flex-col gap-1`)
* `.search-field__group` – Container for search icon, input, and clear button with border and background styling
* `.search-field__input` – The search input field
* `.search-field__search-icon` – The search icon displayed on the left
* `.search-field__clear-button` – Button to clear the search field
* `.search-field--primary` – Primary variant with shadow (default)
* `.search-field--secondary` – Secondary variant without shadow, suitable for use in surfaces
> **Note:** Child components ([Label](/docs/components/label), [Description](/docs/components/description), [FieldError](/docs/components/field-error)) have their own CSS classes and styling. See their respective documentation for customization options.
### Interactive States
SearchField automatically manages these data attributes based on its state:
* **Invalid**: `[data-invalid="true"]` or `[aria-invalid="true"]` - Automatically hides the description slot when invalid
* **Disabled**: `[data-disabled="true"]` - Applied when `isDisabled` is true
* **Focus Within**: `[data-focus-within="true"]` - Applied when the input is focused
* **Focus Visible**: `[data-focus-visible="true"]` - Applied when focus is visible (keyboard navigation)
* **Hovered**: `[data-hovered="true"]` - Applied when hovering over the group
* **Empty**: `[data-empty="true"]` - Applied when the field is empty (hides clear button)
Additional attributes are available through render props (see SearchFieldRenderProps below).
## API Reference
### SearchField Props
SearchField inherits all props from React Aria's [SearchField](https://react-spectrum.adobe.com/react-aria/SearchField.html) component.
#### Base Props
| Prop | Type | Default | Description |
| ----------- | -------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode \| (values: SearchFieldRenderProps) => React.ReactNode` | - | Child components (Label, Group, Input, etc.) or render function. |
| `className` | `string \| (values: SearchFieldRenderProps) => string` | - | CSS classes for styling, supports render props. |
| `style` | `React.CSSProperties \| (values: SearchFieldRenderProps) => React.CSSProperties` | - | Inline styles, supports render props. |
| `fullWidth` | `boolean` | `false` | Whether the search field should take full width of its container |
| `id` | `string` | - | The element's unique identifier. |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
#### Value Props
| Prop | Type | Default | Description |
| -------------- | ------------------------- | ------- | -------------------------------------- |
| `value` | `string` | - | Current value (controlled). |
| `defaultValue` | `string` | - | Default value (uncontrolled). |
| `onChange` | `(value: string) => void` | - | Handler called when the value changes. |
#### Validation Props
| Prop | Type | Default | Description |
| -------------------- | ----------------------------------------------------------------- | ---------- | -------------------------------------------------------------- |
| `isRequired` | `boolean` | `false` | Whether user input is required before form submission. |
| `isInvalid` | `boolean` | - | Whether the value is invalid. |
| `validate` | `(value: string) => ValidationError \| true \| null \| undefined` | - | Custom validation function. |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | Whether to use native HTML form validation or ARIA attributes. |
| `validationErrors` | `string[]` | - | Server-side validation errors. |
#### State Props
| Prop | Type | Default | Description |
| ------------ | --------- | ------- | -------------------------------------------------- |
| `isDisabled` | `boolean` | - | Whether the input is disabled. |
| `isReadOnly` | `boolean` | - | Whether the input can be selected but not changed. |
#### Form Props
| Prop | Type | Default | Description |
| ----------- | --------- | ------- | ---------------------------------------------------- |
| `name` | `string` | - | Name of the input element, for HTML form submission. |
| `autoFocus` | `boolean` | - | Whether the element should receive focus on render. |
#### Event Props
| Prop | Type | Default | Description |
| ---------- | ------------------------- | ------- | ------------------------------------------------------------ |
| `onSubmit` | `(value: string) => void` | - | Handler called when the user submits the search (Enter key). |
| `onClear` | `() => void` | - | Handler called when the clear button is pressed. |
#### Accessibility Props
| Prop | Type | Default | Description |
| ------------------ | -------- | ------- | ----------------------------------------------------- |
| `aria-label` | `string` | - | Accessibility label when no visible label is present. |
| `aria-labelledby` | `string` | - | ID of elements that label this field. |
| `aria-describedby` | `string` | - | ID of elements that describe this field. |
| `aria-details` | `string` | - | ID of elements with additional details. |
### Composition Components
SearchField works with these separate components that should be imported and used directly:
* **SearchField.Group** - Container for search icon, input, and clear button
* **SearchField.Input** - The search input field
* **SearchField.SearchIcon** - The search icon displayed on the left
* **SearchField.ClearButton** - Button to clear the search field
* **Label** - Field label component from `@heroui/react`
* **Description** - Helper text component from `@heroui/react`
* **FieldError** - Validation error message from `@heroui/react`
Each of these components has its own props API. Use them directly within SearchField for composition:
```tsx
Search
Enter keywords to search
Search query is required
```
#### SearchField.Group Props
SearchField.Group inherits props from React Aria's [Group](https://react-spectrum.adobe.com/react-aria/Group.html) component.
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------------ | ------- | --------------------------------------------------------------------- |
| `children` | `React.ReactNode \| (values: GroupRenderProps) => React.ReactNode` | - | Child components (SearchIcon, Input, ClearButton) or render function. |
| `className` | `string \| (values: GroupRenderProps) => string` | - | CSS classes for styling. |
#### SearchField.Input Props
SearchField.Input inherits props from React Aria's [Input](https://react-spectrum.adobe.com/react-aria/Input.html) component.
| Prop | Type | Default | Description |
| ------------- | -------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `className` | `string` | - | CSS classes for styling. |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the input. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
| `placeholder` | `string` | - | Placeholder text displayed when the input is empty. |
| `type` | `string` | `"search"` | Input type (automatically set to "search"). |
#### SearchField.SearchIcon Props
SearchField.SearchIcon is a custom component that renders the search icon.
| Prop | Type | Default | Description |
| ----------- | ----------------- | ---------------- | --------------------------------------------- |
| `children` | `React.ReactNode` | ` ` | Custom icon element. Defaults to search icon. |
| `className` | `string` | - | CSS classes for styling. |
#### SearchField.ClearButton Props
SearchField.ClearButton inherits props from React Aria's [Button](https://react-spectrum.adobe.com/react-aria/Button.html) component.
| Prop | Type | Default | Description |
| ----------- | ----------------- | ---------------------- | ------------------------------------------------------- |
| `children` | `React.ReactNode` | ` ` | Icon or content for the button. Defaults to close icon. |
| `className` | `string` | - | CSS classes for styling. |
| `slot` | `"clear"` | `"clear"` | Must be set to "clear" (automatically set). |
### SearchFieldRenderProps
When using render props with `className`, `style`, or `children`, these values are available:
| Prop | Type | Description |
| ---------------- | --------- | -------------------------------------------------------------------------- |
| `isDisabled` | `boolean` | Whether the field is disabled. |
| `isInvalid` | `boolean` | Whether the field is currently invalid. |
| `isReadOnly` | `boolean` | Whether the field is read-only. |
| `isRequired` | `boolean` | Whether the field is required. |
| `isFocused` | `boolean` | Whether the field is currently focused (DEPRECATED - use `isFocusWithin`). |
| `isFocusWithin` | `boolean` | Whether any child element is focused. |
| `isFocusVisible` | `boolean` | Whether focus is visible (keyboard navigation). |
| `value` | `string` | Current value. |
| `isEmpty` | `boolean` | Whether the field is empty. |
# TextArea
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/text-area
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/text-area.mdx
> Primitive multiline text input component that accepts standard HTML attributes
## Import
```tsx
import { TextArea } from '@heroui/react';
```
For validation, labels, and error messages, see **[TextField](/docs/components/text-field)**.
### Usage
```tsx
import {TextArea} from "@heroui/react";
export function Basic() {
return (
);
}
```
### Controlled
```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)}
/>
Characters: {value.length} / 280
);
}
```
### Rows and Resizing
```tsx
import {Label, TextArea} from "@heroui/react";
export function Rows() {
return (
Short feedback
Detailed notes
);
}
```
### Full Width
```tsx
import {TextArea} from "@heroui/react";
export function FullWidth() {
return (
);
}
```
### Variants
The TextArea component supports two visual variants:
* **`primary`** (default) - Standard styling with shadow, suitable for most use cases
* **`secondary`** - Lower emphasis variant without shadow, suitable for use in Surface components
```tsx
import {TextArea} from "@heroui/react";
export function Variants() {
return (
);
}
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` to apply the lower emphasis variant suitable for surface backgrounds.
```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
## Styling
### Passing Tailwind CSS classes
```tsx
import {Label, TextArea} from '@heroui/react';
function CustomTextArea() {
return (
Message
);
}
```
### Customizing the component classes
Override the shared `.textarea` class once with Tailwind's `@layer components`.
```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 Classes
* `.textarea` – Underlying `` element styling
### Interactive States
* **Hover**: `:hover` or `[data-hovered="true"]`
* **Focus Visible**: `:focus-visible` or `[data-focus-visible="true"]`
* **Invalid**: `[data-invalid="true"]`
* **Disabled**: `:disabled` or `[aria-disabled="true"]`
## API Reference
### TextArea Props
TextArea accepts all standard HTML `` attributes plus the following:
| Prop | Type | Default | Description |
| -------------- | --------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `className` | `string` | - | Tailwind classes merged with the base styles. |
| `rows` | `number` | `3` | Number of visible text lines. |
| `cols` | `number` | - | Visible width of the text control. |
| `value` | `string` | - | Controlled value for the textarea. |
| `defaultValue` | `string` | - | Initial uncontrolled value. |
| `onChange` | `(event: React.ChangeEvent) => void` | - | Change handler. |
| `placeholder` | `string` | - | Placeholder text. |
| `disabled` | `boolean` | `false` | Disables the textarea. |
| `readOnly` | `boolean` | `false` | Makes the textarea read-only. |
| `required` | `boolean` | `false` | Marks the textarea as required. |
| `name` | `string` | - | Name for form submission. |
| `autoComplete` | `string` | - | Autocomplete hint for the browser. |
| `maxLength` | `number` | - | Maximum number of characters. |
| `minLength` | `number` | - | Minimum number of characters. |
| `wrap` | `'soft' \| 'hard'` | - | How text wraps when submitted. |
| `fullWidth` | `boolean` | `false` | Whether the textarea should take full width of its container |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
> For validation props like `isInvalid`, `isRequired`, and error handling, use **[TextField](/docs/components/text-field)** with TextArea as a child component.
# TextField
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/text-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(forms)/text-field.mdx
> Composition-friendly text fields with labels, descriptions, and inline validation
## Import
```tsx
import { TextField } from '@heroui/react';
```
### Usage
```tsx
import {Input, Label, TextField} from "@heroui/react";
export function Basic() {
return (
Email
);
}
```
### Anatomy
```tsx
import {TextField, Label, Input, Description, FieldError} from '@heroui/react';
export default () => (
)
```
> **TextField** combines label, input, description, and error into a single accessible component.
> For standalone inputs, use **[Input](/docs/components/input)** or **[TextArea](/docs/components/textarea)**.
### With Description
```tsx
import {Description, Input, Label, TextField} from "@heroui/react";
export function WithDescription() {
return (
Username
Choose a unique username for your account
);
}
```
### Required Field
```tsx
import {Description, Input, Label, TextField} from "@heroui/react";
export function Required() {
return (
Full Name
This field is required
);
}
```
### Validation
Use `isInvalid` together with `FieldError` to surface validation messages.
```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 (
Username
{isUsernameInvalid ? (
Username must be at least 3 characters.
) : (
Choose a unique username for your profile.
)}
Bio
{isBioInvalid ? (
Bio must contain at least 20 characters.
) : (
Minimum 20 characters ({bio.length}/20).
)}
);
}
```
### Controlled
Control the value to synchronize counters, previews, or formatting.
```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 (
Display name
Characters: {name.length}
Bio
Characters: {bio.length} / 200
);
}
```
### Error Message
```tsx
import {FieldError, Input, Label, TextField} from "@heroui/react";
export function WithError() {
return (
Email
Please enter a valid email address
);
}
```
### Disabled State
```tsx
import {Description, Input, Label, TextField} from "@heroui/react";
export function Disabled() {
return (
Account ID
This field cannot be edited
);
}
```
### TextArea
Use [TextArea](/docs/components/textarea) instead of [Input](/docs/components/input) for multiline content.
```tsx
import {Description, Label, TextArea, TextField} from "@heroui/react";
export function TextAreaExample() {
return (
Message
Maximum 500 characters
);
}
```
### Input Types
```tsx
import {Input, Label, TextField} from "@heroui/react";
export function InputTypes() {
return (
Password
Age
Email
Website
Phone
);
}
```
### Full Width
```tsx
import {FieldError, Input, Label, TextField} from "@heroui/react";
export function FullWidth() {
return (
Your name
Password
Password must be longer than 8 characters
);
}
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` on Input or TextArea components to apply the lower emphasis variant suitable for surface backgrounds.
```tsx
import {Description, Input, Label, Surface, TextArea, TextField} from "@heroui/react";
export function OnSurface() {
return (
Your name
We'll never share this with anyone else
Email
Bio
Minimum 4 rows
);
}
```
## 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
### Custom Render Function
```tsx
"use client";
import {Input, Label, TextField} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
type="email"
>
Email
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import {TextField, Label, Input, Description} from '@heroui/react';
function CustomTextField() {
return (
Project name
Keep it short and memorable.
);
}
```
### Customizing the component classes
TextField has minimal default styling. Override the `.textfield` class to customize the container styling.
```css
@layer components {
.textfield {
@apply flex flex-col gap-1;
}
/* When invalid, the description is hidden automatically */
.textfield[data-invalid="true"] [data-slot="description"],
.textfield[aria-invalid="true"] [data-slot="description"] {
@apply hidden;
}
/* Description has default padding */
.textfield [data-slot="description"] {
@apply px-1;
}
}
```
### CSS Classes
* `.textfield` – Root container with minimal styling (`flex flex-col gap-1`)
> **Note:** Child components ([Label](/docs/components/label), [Input](/docs/components/input), [TextArea](/docs/components/textarea), [Description](/docs/components/description), [FieldError](/docs/components/field-error)) have their own CSS classes and styling. See their respective documentation for customization options.
### Interactive States
TextField automatically manages these data attributes based on its state:
* **Invalid**: `[data-invalid="true"]` or `[aria-invalid="true"]` - Automatically hides the description slot when invalid
* **Disabled**: `[data-disabled="true"]` - Applied when `isDisabled` is true
* **Focus Within**: `[data-focus-within="true"]` - Applied when any child input is focused
* **Focus Visible**: `[data-focus-visible="true"]` - Applied when focus is visible (keyboard navigation)
Additional attributes are available through render props (see TextFieldRenderProps below).
## API Reference
### TextField Props
TextField inherits all props from React Aria's [TextField](https://react-spectrum.adobe.com/react-aria/TextField.html) component.
#### Base Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------------------------ | ------- | ---------------------------------------------------------------- |
| `children` | `React.ReactNode \| (values: TextFieldRenderProps) => React.ReactNode` | - | Child components (Label, Input, etc.) or render function. |
| `className` | `string \| (values: TextFieldRenderProps) => string` | - | CSS classes for styling, supports render props. |
| `style` | `React.CSSProperties \| (values: TextFieldRenderProps) => React.CSSProperties` | - | Inline styles, supports render props. |
| `fullWidth` | `boolean` | `false` | Whether the text field should take full width of its container |
| `id` | `string` | - | The element's unique identifier. |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
#### Validation Props
| Prop | Type | Default | Description |
| -------------------- | ----------------------------------------------------------------- | ---------- | -------------------------------------------------------------- |
| `isRequired` | `boolean` | `false` | Whether user input is required before form submission. |
| `isInvalid` | `boolean` | - | Whether the value is invalid. |
| `validate` | `(value: string) => ValidationError \| true \| null \| undefined` | - | Custom validation function. |
| `validationBehavior` | `'native' \| 'aria'` | `'native'` | Whether to use native HTML form validation or ARIA attributes. |
| `validationErrors` | `string[]` | - | Server-side validation errors. |
#### Value Props
| Prop | Type | Default | Description |
| -------------- | ------------------------- | ------- | -------------------------------------- |
| `value` | `string` | - | Current value (controlled). |
| `defaultValue` | `string` | - | Default value (uncontrolled). |
| `onChange` | `(value: string) => void` | - | Handler called when the value changes. |
#### State Props
| Prop | Type | Default | Description |
| ------------ | --------- | ------- | -------------------------------------------------- |
| `isDisabled` | `boolean` | - | Whether the input is disabled. |
| `isReadOnly` | `boolean` | - | Whether the input can be selected but not changed. |
#### Form Props
| Prop | Type | Default | Description |
| ----------- | --------- | ------- | ---------------------------------------------------- |
| `name` | `string` | - | Name of the input element, for HTML form submission. |
| `autoFocus` | `boolean` | - | Whether the element should receive focus on render. |
#### Accessibility Props
| Prop | Type | Default | Description |
| ------------------ | -------- | ------- | ----------------------------------------------------- |
| `aria-label` | `string` | - | Accessibility label when no visible label is present. |
| `aria-labelledby` | `string` | - | ID of elements that label this field. |
| `aria-describedby` | `string` | - | ID of elements that describe this field. |
| `aria-details` | `string` | - | ID of elements with additional details. |
### Composition Components
TextField works with these separate components that should be imported and used directly:
* **Label** - Field label component from `@heroui/react`
* **Input** - Single-line text input from `@heroui/react`
* **TextArea** - Multi-line text input from `@heroui/react`
* **Description** - Helper text component from `@heroui/react`
* **FieldError** - Validation error message from `@heroui/react`
Each of these components has its own props API. Use them directly within TextField for composition:
```tsx
Email Address
setEmail(e.target.value)} />
We'll never share your email.
Please enter a valid email address.
```
### TextFieldRenderProps
When using render props with `className`, `style`, or `children`, these values are available:
| Prop | Type | Description |
| ---------------- | --------- | -------------------------------------------------------------------------- |
| `isDisabled` | `boolean` | Whether the field is disabled. |
| `isInvalid` | `boolean` | Whether the field is currently invalid. |
| `isReadOnly` | `boolean` | Whether the field is read-only. |
| `isRequired` | `boolean` | Whether the field is required. |
| `isFocused` | `boolean` | Whether the field is currently focused (DEPRECATED - use `isFocusWithin`). |
| `isFocusWithin` | `boolean` | Whether any child element is focused. |
| `isFocusVisible` | `boolean` | Whether focus is visible (keyboard navigation). |
# Card
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/card
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(layout)/card.mdx
> Flexible container component for grouping related content and actions
## Import
```tsx
import { Card } from "@heroui/react";
```
### Usage
```tsx
import {CircleDollar} from "@gravity-ui/icons";
import {Card, Link} from "@heroui/react";
export function Default() {
return (
Become an Acme Creator!
Visit the Acme Creator Hub to sign up today and start earning credits from your fans and
followers.
Creator Hub
);
}
```
### Anatomy
Import the Card component and access all parts using dot notation.
```tsx
import { Card } from "@heroui/react";
export default () => (
);
```
### Variants
Cards come in semantic variants that describe their prominence level rather than specific visual styles. This allows themes to interpret them differently:
```tsx
import {Card} from "@heroui/react";
export function Variants() {
return (
Transparent
Minimal prominence with transparent background
Use for less important content or nested cards
Default
Standard card appearance (bg-surface)
The default card variant for most use cases
Secondary
Medium prominence (bg-surface-secondary)
Use to draw moderate attention
Tertiary
Higher prominence (bg-surface-tertiary)
Use for primary or featured content
);
}
```
* **`transparent`** - Minimal prominence, transparent background (great for nested cards)
* **`default`** - Standard card for most use cases (surface-secondary)
* **`secondary`** - Medium prominence to draw moderate attention (surface-tertiary)
* **`tertiary`** - Higher prominence for important content (surface-tertiary)
### Horizontal Layout
```tsx
import {Button, Card, CloseButton} from "@heroui/react";
export function Horizontal() {
return (
Become an ACME Creator!
Lorem ipsum dolor sit amet consectetur. Sed arcu donec id aliquam dolor sed amet
faucibus etiam.
Only 10 spots
Submission ends Oct 10.
Apply Now
);
}
```
### With Avatar
```tsx
import {Avatar, Card} from "@heroui/react";
export function WithAvatar() {
return (
Indie Hackers
148 members
IH
By Martha
AI Builders
362 members
B
By John
);
}
```
### With Images
```tsx
import {CircleDollar} from "@gravity-ui/icons";
import {Avatar, Button, Card, CloseButton, Link} from "@heroui/react";
export function WithImages() {
return (
{/* Row 1: Large Product Card - Available Soon */}
Become an ACME Creator!
Lorem ipsum dolor sit amet consectetur. Sed arcu donec id aliquam dolor sed amet
faucibus etiam.
Only 10 spots
Submission ends Oct 10.
Apply Now
{/* Row 2 */}
{/* Left Column */}
{/* Top Card */}
PAYMENT
You can now withdraw on crypto
Add your wallet in settings to withdraw
Go to settings
{/* Bottom cards */}
{/* Left Card */}
JK
Indie Hackers
148 members
JK
By John
{/* Right Card */}
AB
AI Builders
362 members
M
By Martha
{/* Right Column */}
{/* Background image */}
{/* Header */}
NEO
Home Robot
{/* Footer */}
Available soon
Get notified
Notify me
{/* Row 3 */}
{/* Left Column: Card */}
Get now
{/* Right Column: Cards Stack */}
{/* 1 */}
Bridging the Future
Today, 6:30 PM
{/* 2 */}
Avocado Hackathon
Wed, 4:30 PM
{/* 3 */}
Sound Electro | Beyond art
Fri, 8:00 PM
);
}
```
### With Form
```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("Form submitted successfully!");
};
return (
Login
Enter your credentials to access your account
Email
Password
Sign In
Forgot password?
);
}
```
## Accessibility
```tsx
import { Card } from '@heroui/react';
import { cardVariants } from '@heroui/styles';
// Semantic markup
Article Title
// Interactive cards
Product Name
```
## Related Components
* **Surface**: Base container surface
* **Avatar**: Display user profile images
* **Form**: Form validation and submission handling
## Styling
### Component Customization
```tsx
Custom Styled Card
Custom colors applied
Content with custom styling
```
### CSS Variable Overrides
```css
/* Override specific variants */
.card--secondary {
@apply bg-gradient-to-br from-blue-50 to-purple-50;
}
/* Custom element styles */
.card__title {
@apply text-xl font-bold;
}
```
## CSS Classes
Card uses [BEM](https://getbem.com/) naming for predictable styling, ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/card.css)):
#### Base Classes
* `.card` - Base container with padding and border
* `.card__header` - Header section container
* `.card__title` - Title with base font size and weight
* `.card__description` - Muted description text
* `.card__content` - Flexible content container
* `.card__footer` - Footer with row layout
#### Variant Classes
* `.card--transparent` - Minimal prominence, transparent background (maps to `transparent` variant)
* `.card--default` - Standard appearance with surface-secondary (default)
* `.card--secondary` - Medium prominence with surface-tertiary (maps to `secondary` variant)
* `.card--tertiary` - Higher prominence with surface-tertiary (maps to `tertiary` variant)
## API Reference
### Card
| Prop | Type | Default | Description |
| ----------- | --------------------------------------------------------- | ----------- | -------------------------------------------- |
| `variant` | `"transparent" \| "default" \| "secondary" \| "tertiary"` | `"default"` | Semantic variant indicating prominence level |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Card content |
### Card.Header
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Header content |
### Card.Title
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Title content (renders as `h3`) |
### Card.Description
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ------------------------------------ |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Description content (renders as `p`) |
### Card.Content
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Main content |
### Card.Footer
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `React.ReactNode` | - | Footer content |
# Separator
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/separator
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(layout)/separator.mdx
> Visually divide content sections
## Import
```tsx
import { Separator } from '@heroui/react';
```
### Usage
```tsx
import {Separator} from "@heroui/react";
export function Basic() {
return (
HeroUI v3 Components
Beautiful, fast and modern React UI library.
);
}
```
### Vertical
```tsx
import {Separator} from "@heroui/react";
export function Vertical() {
return (
);
}
```
### With Content
```tsx
import {Separator} from "@heroui/react";
const items = [
{
iconUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/bell-small.png",
subtitle: "Receive account activity updates",
title: "Set Up Notifications",
},
{
iconUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/compass-small.png",
subtitle: "Connect your browser to your account",
title: "Set up Browser Extension",
},
{
iconUrl:
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/mint-collective-small.png",
subtitle: "Create your first collectible",
title: "Mint Collectible",
},
];
export function WithContent() {
return (
{items.map((item, index) => (
{item.title}
{item.subtitle}
{index < items.length - 1 &&
}
))}
);
}
```
### Variants
```tsx
import {Separator} from "@heroui/react";
export function Variants() {
return (
Default Variant
Secondary Variant
Tertiary Variant
);
}
```
### With Surface
The Separator component adapts to different surface backgrounds for better visibility.
```tsx
import {Separator, Surface} from "@heroui/react";
export function WithSurface() {
return (
Default Surface
Surface Content
Secondary Surface
Surface Content
Tertiary Surface
Surface Content
Transparent Surface
Surface Content
);
}
```
## Related Components
* **Card**: Content container with header, body, and footer
* **Chip**: Compact elements for tags and filters
* **Avatar**: Display user profile images
### Custom Render Function
```tsx
"use client";
import {Separator} from "@heroui/react";
export function CustomRenderFunction() {
return (
HeroUI v3 Components
Beautiful, fast and modern React UI library.
} />
Blog
}
/>
Docs
}
/>
Source
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import {Separator} from '@heroui/react';
function CustomSeparator() {
return (
);
}
```
### Customizing the component classes
To customize the Separator component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Separator component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/separator.css)):
#### Base & Orientation Classes
* `.separator` - Base separator styles with default horizontal orientation
* `.separator--horizontal` - Horizontal orientation (full width, 1px height)
* `.separator--vertical` - Vertical orientation (full height, 1px width)
#### Variant Classes
* `.separator--default` - Default variant with standard contrast
* `.separator--secondary` - Secondary variant with medium contrast
* `.separator--tertiary` - Tertiary variant with subtle contrast
## API Reference
### Separator Props
| Prop | Type | Default | Description |
| ------------- | ----------------------------------------------------------------- | -------------- | ---------------------------------------------------------------- |
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | The orientation of the separator |
| `variant` | `'default' \| 'secondary' \| 'tertiary'` | `'default'` | The visual variant of the separator |
| `className` | `string` | - | Additional CSS classes |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
# Surface
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/surface
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(layout)/surface.mdx
> Container component that provides surface-level styling and context for child components
## Import
```tsx
import { Surface } from '@heroui/react';
```
### Usage
```tsx
import {Surface} from "@heroui/react";
export function Variants() {
return (
Default
Surface Content
This is a default surface variant. It uses bg-surface styling.
Secondary
Surface Content
This is a secondary surface variant. It uses bg-surface-secondary styling.
Tertiary
Surface Content
This is a tertiary surface variant. It uses bg-surface-tertiary styling.
Transparent
Surface Content
This is a transparent surface variant. It has no background, suitable for overlays and
cards with custom backgrounds.
);
}
```
## Overview
The Surface component is a semantic container that provides different levels of visual prominence through variants.
### Variants
Surface comes in semantic variants that describe their prominence level:
* **`default`** - Standard surface appearance (bg-surface)
* **`secondary`** - Medium prominence (bg-surface-secondary)
* **`tertiary`** - Higher prominence (bg-surface-tertiary)
## Usage with Form Components
When using form components inside a Surface, use the `variant="secondary"` prop to apply the lower emphasis variant suitable for surface backgrounds.
```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
## Styling
### Passing Tailwind CSS classes
```tsx
import { Surface } from '@heroui/react';
function CustomSurface() {
return (
Custom Styled Surface
Content goes here
);
}
```
### Customizing the component classes
To customize the Surface component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Surface component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/surface.css)):
#### Base Classes
* `.surface` - Base surface container
#### Variant Classes
* `.surface--default` - Default surface variant (bg-surface)
* `.surface--secondary` - Secondary surface variant (bg-surface-secondary)
* `.surface--tertiary` - Tertiary surface variant (bg-surface-tertiary)
## API Reference
### Surface Props
| Prop | Type | Default | Description |
| ----------- | ---------------------------------------------------------- | ----------- | --------------------------------- |
| `variant` | ` "transparent" \| "default" \| "secondary" \| "tertiary"` | `"default"` | The visual variant of the surface |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | The surface content |
## Context API
### SurfaceContext
Child components can access the Surface context to get the current variant:
```tsx
import { useContext } from 'react';
import { SurfaceContext } from '@heroui/react';
function MyComponent() {
const { variant } = useContext(SurfaceContext);
// variant will be "transparent" | "default" | "secondary" | "tertiary" | undefined
}
```
# Toolbar
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/toolbar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(layout)/toolbar.mdx
> A container for interactive controls with arrow key navigation.
## Import
```tsx
import { Toolbar } from '@heroui/react';
```
### Usage
```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 (
);
}
```
### Vertical
```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 (
);
}
```
### With 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 (
Undo
Redo
);
}
```
### 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
## Styling
### Passing Tailwind CSS classes
```tsx
import { Toolbar } from '@heroui/react';
function CustomToolbar() {
return (
{/* toolbar content */}
);
}
```
### Customizing the component classes
To customize the Toolbar component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.toolbar {
@apply gap-4 rounded-lg bg-surface p-3;
}
}
```
HeroUI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Toolbar component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/toolbar.css)):
* `.toolbar` - Base container
* `.toolbar--horizontal` - Horizontal orientation (default)
* `.toolbar--vertical` - Vertical orientation
* `.toolbar--attached` - Attached variant with surface background and full rounding
## API Reference
### Toolbar Props
Inherits from [React Aria Toolbar](https://react-spectrum.adobe.com/react-aria/Toolbar.html).
| Prop | Type | Default | Description |
| ----------------- | -------------------------------------------------------------------- | -------------- | --------------------------------------------------------------- |
| `isAttached` | `boolean` | `false` | Whether the toolbar has a surface background with full rounding |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | The orientation of the toolbar |
| `aria-label` | `string` | - | An accessible label for the toolbar |
| `aria-labelledby` | `string` | - | The id of an element that labels the toolbar |
| `children` | `React.ReactNode \| (values: ToolbarRenderProps) => React.ReactNode` | - | Content or render prop |
| `className` | `string \| (values: ToolbarRenderProps) => string` | - | Additional CSS classes |
### ToolbarRenderProps
When using the render prop pattern, these values are provided:
| Prop | Type | Description |
| ------------- | ---------------------------- | -------------------------------------- |
| `orientation` | `"horizontal" \| "vertical"` | The current orientation of the toolbar |
# Avatar
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/avatar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(media)/avatar.mdx
> Display user profile images with customizable fallback content
## Import
```tsx
import { Avatar } from '@heroui/react';
```
### Usage
```tsx
import {Avatar} from "@heroui/react";
export function Basic() {
return (
);
}
```
### Anatomy
Import the Avatar component and access all parts using dot notation.
```tsx
import { Avatar } from '@heroui/react';
export default () => (
)
```
### Sizes
```tsx
import {Avatar} from "@heroui/react";
export function Sizes() {
return (
);
}
```
### Colors
```tsx
import {Avatar} from "@heroui/react";
export function Colors() {
return (
);
}
```
### Variants
```tsx
import {Person} from "@gravity-ui/icons";
import {Avatar, Separator} from "@heroui/react";
export function Variants() {
const colors = ["accent", "default", "success", "warning", "danger"] as const;
const variants = [
{content: "AG", label: "letter", type: "letter"},
{content: "AG", label: "letter soft", type: "letter-soft"},
{content: , label: "icon", type: "icon"},
{content: , label: "icon soft", 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: "img",
type: "img",
},
] as const;
return (
{/* Color labels header */}
{colors.map((color) => (
{color}
))}
{/* Variant rows */}
{variants.map((variant) => (
{variant.label}
{colors.map((color, colorIndex) => (
{variant.type === "img" ? (
<>
{color.charAt(0).toUpperCase()}
>
) : (
{variant.content}
)}
))}
))}
);
}
```
### Fallback Content
```tsx
import {Person} from "@gravity-ui/icons";
import {Avatar} from "@heroui/react";
export function Fallback() {
return (
{/* Text fallback */}
JD
{/* Icon fallback */}
{/* Fallback with delay */}
NA
{/* Custom styled fallback */}
GB
);
}
```
### Avatar Group
```tsx
import {Avatar} from "@heroui/react";
const users = [
{
id: 1,
image: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg",
name: "John Doe",
},
{
id: 2,
image: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg",
name: "Kate Wilson",
},
{
id: 3,
image: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg",
name: "Emily Chen",
},
{
id: 4,
image: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg",
name: "Michael Brown",
},
{
id: 5,
image: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg",
name: "Olivia Davis",
},
];
export function Group() {
return (
{/* Basic avatar group */}
{users.slice(0, 4).map((user) => (
{user.name
.split(" ")
.map((n) => n[0])
.join("")}
))}
{/* Avatar group with counter */}
{users.slice(0, 3).map((user) => (
{user.name
.split(" ")
.map((n) => n[0])
.join("")}
))}
+{users.length - 3}
);
}
```
### Custom Styles
```tsx
import {Avatar} from "@heroui/react";
export function CustomStyles() {
return (
{/* Custom size with Tailwind classes */}
XL
{/* Square avatar */}
SQ
{/* Gradient border */}
{/* Status indicator */}
);
}
```
## Related Components
* **Separator**: Visual divider between content
* **Badge**: Small indicator positioned relative to another element
## Styling
### Passing Tailwind CSS classes
```tsx
import { Avatar } from '@heroui/react';
function CustomAvatar() {
return (
XL
);
}
```
### Customizing the component classes
To customize the Avatar component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Avatar component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/avatar.css)):
#### Base Classes
* `.avatar` - Base container with default size (size-10)
* `.avatar__image` - Image element with aspect-square sizing
* `.avatar__fallback` - Fallback container with centered content
#### Size Modifiers
* `.avatar--sm` - Small avatar (size-8)
* `.avatar--md` - Medium avatar (default, no additional styles)
* `.avatar--lg` - Large avatar (size-12)
#### Variant Modifiers
* `.avatar--soft` - Soft variant with lighter background
#### Color Modifiers
* `.avatar__fallback--default` - Default text color
* `.avatar__fallback--accent` - Accent text color
* `.avatar__fallback--success` - Success text color
* `.avatar__fallback--warning` - Warning text color
* `.avatar__fallback--danger` - Danger text color
## API Reference
### Avatar Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------- | ----------- | ---------------------- |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Avatar size |
| `color` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | `'default'` | Fallback color theme |
| `variant` | `'default' \| 'soft'` | `'default'` | Visual style variant |
| `className` | `string` | - | Additional CSS classes |
### Avatar.Image Props
| Prop | Type | Default | Description |
| ------------- | --------------------------------------------------- | ------- | -------------------------------------------------- |
| `src` | `string` | - | Image source URL |
| `srcSet` | `string` | - | The image `srcset` attribute for responsive images |
| `sizes` | `string` | - | The image `sizes` attribute for responsive images |
| `alt` | `string` | - | Alternative text for the image |
| `onLoad` | `(event: SyntheticEvent) => void` | - | Callback when the image loads successfully |
| `onError` | `(event: SyntheticEvent) => void` | - | Callback when there's an error loading the image |
| `crossOrigin` | `'anonymous' \| 'use-credentials'` | - | CORS setting for the image request |
| `loading` | `'eager' \| 'lazy'` | - | Native lazy loading attribute |
| `className` | `string` | - | Additional CSS classes |
### Avatar.Fallback Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------- | ------- | ---------------------------------------------- |
| `delayMs` | `number` | - | Delay before showing fallback (prevents flash) |
| `color` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | - | Override color from parent |
| `className` | `string` | - | Additional CSS classes |
# AlertDialog
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/alert-dialog
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(overlays)/alert-dialog.mdx
> Modal dialog for critical confirmations requiring user attention and explicit action
## Import
```tsx
import { AlertDialog } from "@heroui/react";
```
### Usage
```tsx
"use client";
import {AlertDialog, Button} from "@heroui/react";
export function Default() {
return (
Delete Project
Delete project permanently?
This will permanently delete My Awesome Project and all of its
data. This action cannot be undone.
Cancel
Delete Project
);
}
```
### Anatomy
Import the AlertDialog component and access all parts using dot notation.
```tsx
import {AlertDialog, Button} from "@heroui/react";
export default () => (
Open Alert Dialog
{/* Optional: Close button */}
{/* Optional: Status icon */}
);
```
### Statuses
```tsx
"use client";
import {AlertDialog, Button} from "@heroui/react";
export function Statuses() {
const examples = [
{
actions: {
cancel: "Stay Signed In",
confirm: "Sign Out",
},
body: "You'll need to sign in again to access your account. Any unsaved changes will be lost.",
classNames: "bg-accent-soft text-accent-soft-foreground",
header: "Sign out of your account?",
status: "accent",
trigger: "Sign Out",
},
{
actions: {
cancel: "Not Yet",
confirm: "Mark Complete",
},
body: "This will mark the task as complete and notify all team members. The task will be moved to your completed list.",
classNames: "bg-success-soft text-success-soft-foreground",
header: "Complete this task?",
status: "success",
trigger: "Complete Task",
},
{
actions: {
cancel: "Keep Editing",
confirm: "Discard",
},
body: "You have unsaved changes that will be permanently lost. Are you sure you want to discard them?",
classNames: "bg-warning-soft text-warning-soft-foreground",
header: "Discard unsaved changes?",
status: "warning",
trigger: "Discard Changes",
},
{
actions: {
cancel: "Cancel",
confirm: "Delete Account",
},
body: "This will permanently delete your account and remove all your data from our servers. This action is irreversible.",
classNames: "bg-danger-soft text-danger-soft-foreground",
header: "Delete your account?",
status: "danger",
trigger: "Delete Account",
},
] as const;
return (
{examples.map(({actions, body, classNames, header, status, trigger}) => (
{trigger}
{header}
{body}
{actions.cancel}
{actions.confirm}
))}
);
}
```
### Placements
```tsx
"use client";
import {AlertDialog, Button} from "@heroui/react";
export function Placements() {
const placements = ["auto", "top", "center", "bottom"] as const;
return (
{placements.map((placement) => (
{placement.charAt(0).toUpperCase() + placement.slice(1)}
{placement === "auto"
? "Auto Placement"
: `${placement.charAt(0).toUpperCase() + placement.slice(1)} Position`}
{placement === "auto"
? "Automatically positions at the bottom on mobile and center on desktop for optimal user experience."
: `This dialog is positioned at the ${placement} of the viewport. Critical confirmations are typically centered for maximum attention.`}
Cancel
Confirm
))}
);
}
```
### Backdrop Variants
```tsx
"use client";
import {AlertDialog, Button} from "@heroui/react";
export function BackdropVariants() {
const variants = ["opaque", "blur", "transparent"] as const;
return (
{variants.map((variant) => (
{variant.charAt(0).toUpperCase() + variant.slice(1)}
Backdrop: {variant.charAt(0).toUpperCase() + variant.slice(1)}
{variant === "opaque"
? "An opaque dark backdrop that completely obscures the background, providing maximum focus on the dialog."
: variant === "blur"
? "A blurred backdrop that softly obscures the background while maintaining visual context."
: "A transparent backdrop that keeps the background fully visible, useful for less critical confirmations."}
Cancel
Confirm
))}
);
}
```
### Sizes
```tsx
"use client";
import {Rocket} from "@gravity-ui/icons";
import {AlertDialog, Button} from "@heroui/react";
export function Sizes() {
const sizes = ["xs", "sm", "md", "lg", "cover"] as const;
return (
{sizes.map((size) => (
{size.charAt(0).toUpperCase() + size.slice(1)}
Size: {size.charAt(0).toUpperCase() + size.slice(1)}
{size === "cover" ? (
<>
This alert dialog uses the cover size variant. It spans the
full screen with margins: 16px on mobile and 40px on desktop. Maintains
rounded corners and standard padding. Perfect for critical confirmations
that need maximum width while preserving alert dialog aesthetics.
>
) : (
<>
This alert dialog uses the {size} size variant. On mobile
devices, all sizes adapt to near full-width for optimal viewing. On desktop,
each size provides a different maximum width to suit various content needs.
>
)}
Cancel
Confirm
))}
);
}
```
### Custom Icon
```tsx
"use client";
import {LockOpen} from "@gravity-ui/icons";
import {AlertDialog, Button} from "@heroui/react";
export function CustomIcon() {
return (
Reset Password
Reset your password?
We'll send a password reset link to your email address. You'll need to create a new
password to regain access to your account.
Cancel
Send Reset Link
);
}
```
### Custom Backdrop
```tsx
"use client";
import {TriangleExclamation} from "@gravity-ui/icons";
import {AlertDialog, Button} from "@heroui/react";
export function CustomBackdrop() {
return (
Delete Account
Permanently delete your account?
This action cannot be undone. All your data, settings, and content will be
permanently removed from our servers. The dramatic red backdrop emphasizes the
severity and irreversibility of this decision.
Keep Account
Delete Forever
);
}
```
### Dismiss Behavior
```tsx
"use client";
import {CircleInfo} from "@gravity-ui/icons";
import {AlertDialog, Button} from "@heroui/react";
export function DismissBehavior() {
return (
isDismissable
Controls whether the alert dialog can be dismissed by clicking the overlay backdrop. Alert
dialogs typically require explicit action, so this defaults to false. Set to{" "}
true for less critical confirmations.
Open Alert Dialog
isDismissable = false
Clicking the backdrop won't close this alert dialog
Try clicking outside this alert dialog on the overlay - it won't close. You must
use the action buttons to dismiss it.
Cancel
Confirm
isKeyboardDismissDisabled
Controls whether the ESC key can dismiss the alert dialog. Alert dialogs typically require
explicit action, so this defaults to true. When set to false,
the ESC key will be enabled.
Open Alert Dialog
isKeyboardDismissDisabled = true
ESC key is disabled
Press ESC - nothing happens. You must use the action buttons to dismiss this
alert dialog.
Cancel
Confirm
);
}
```
### Close Methods
```tsx
"use client";
import {AlertDialog, Button} from "@heroui/react";
export function CloseMethods() {
return (
Using slot="close"
The simplest way to close a dialog. Add slot="close" to any Button component
within the dialog. When clicked, it will automatically close the dialog.
Open Dialog
Using slot="close"
Click either button below - both have slot="close" and will close
the dialog automatically.
Cancel
Confirm
Using Dialog render props
Access the close method from the Dialog's render props. This gives you full
control over when and how to close the dialog, allowing you to add custom logic before
closing.
Open Dialog
{(renderProps) => (
<>
Using Dialog render props
The buttons below use the close method from render props. You
can add validation or other logic before calling{" "}
renderProps.close().
renderProps.close()}>
Cancel
renderProps.close()}>Confirm
>
)}
);
}
```
### Controlled State
```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 (
With React.useState()
Control the alert dialog using React's useState{" "}
hook for simple state management. Perfect for basic use cases.
Status:{" "}
{isOpen ? "open" : "closed"}
setIsOpen(true)}>
Open Dialog
setIsOpen(!isOpen)}>
Toggle
Controlled with useState()
This alert dialog is controlled by React's useState hook. Pass{" "}
isOpen and onOpenChange props to manage the dialog state
externally.
Cancel
Confirm
With useOverlayState()
Use the useOverlayState hook for a cleaner API
with convenient methods like open(), close(), and{" "}
toggle().
Status:{" "}
{state.isOpen ? "open" : "closed"}
Open Dialog
Toggle
Controlled with useOverlayState()
The useOverlayState hook provides dedicated methods for common
operations. No need to manually create callbacks—just use{" "}
state.open(), state.close(), or{" "}
state.toggle().
Cancel
Confirm
);
}
```
### Custom Trigger
```tsx
"use client";
import {TrashBin} from "@gravity-ui/icons";
import {AlertDialog, Button} from "@heroui/react";
export function CustomTrigger() {
return (
Delete Item
Permanently remove this item
Delete this item?
Use AlertDialog.Trigger to create custom trigger elements beyond
standard buttons. This example shows a card-style trigger with icons and descriptive
text.
Cancel
Delete Item
);
}
```
### Custom Animations
```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:
"Physics-based elastic scaling. Simulates a high-damping spring system with fast transient response and prolonged settling time. Ideal for Alert Dialogs and Modals.",
icon: "gravity-ui:sparkles",
name: "Kinematic Scale",
},
{
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:
"Simulates movement through a medium with fluid resistance. Eliminates mechanical linearity for a natural, grounded feel. Perfect for Bottom Sheets or Toasts.",
icon: "gravity-ui:arrow-up-from-line",
name: "Fluid Slide",
},
];
return (
{animations.map(({classNames, description, icon, name}) => {
const IconComponent = iconMap[icon];
return (
{name}
{!!IconComponent && }
{name} Animation
{description}
Close
Try Again
);
})}
);
}
```
### Custom 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 (
Render alert dialogs inside a custom container instead of document.body
Apply transform: translateZ(0) to the
container to create a new stacking context.
{!!portalContainer && (
Open Alert Dialog
Custom 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.
Cancel
Confirm
)}
);
}
```
## Related Components
* **Button**: Allows a user to perform an action
* **CloseButton**: Button for dismissing overlays
## Styling
### Passing Tailwind CSS classes
```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
);
}
```
### Customizing the component classes
To customize the AlertDialog component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The AlertDialog component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/alert-dialog.css)):
#### Base Classes
* `.alert-dialog__trigger` - Trigger element that opens the alert dialog
* `.alert-dialog__backdrop` - Overlay backdrop behind the dialog
* `.alert-dialog__container` - Positioning wrapper with placement support
* `.alert-dialog__dialog` - Dialog content container
* `.alert-dialog__header` - Header section for icon and title
* `.alert-dialog__heading` - Heading text styles
* `.alert-dialog__body` - Main content area
* `.alert-dialog__footer` - Footer section for actions
* `.alert-dialog__icon` - Icon container with status colors
* `.alert-dialog__close-trigger` - Close button element
#### Backdrop Variants
* `.alert-dialog__backdrop--opaque` - Opaque colored backdrop (default)
* `.alert-dialog__backdrop--blur` - Blurred backdrop with glass effect
* `.alert-dialog__backdrop--transparent` - Transparent backdrop (no overlay)
#### Status Variants (Icon)
* `.alert-dialog__icon--default` - Default gray status
* `.alert-dialog__icon--accent` - Accent blue status
* `.alert-dialog__icon--success` - Success green status
* `.alert-dialog__icon--warning` - Warning orange status
* `.alert-dialog__icon--danger` - Danger red status
### Interactive States
The component supports these interactive states:
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]` - Applied to trigger, dialog, and close button
* **Hover**: `:hover` or `[data-hovered="true"]` - Applied to close button on hover
* **Active**: `:active` or `[data-pressed="true"]` - Applied to close button when pressed
* **Entering**: `[data-entering]` - Applied during dialog opening animation
* **Exiting**: `[data-exiting]` - Applied during dialog closing animation
* **Placement**: `[data-placement="*"]` - Applied based on dialog position (auto, top, center, bottom)
## API Reference
### AlertDialog
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ------------------------------ |
| `children` | `ReactNode` | - | Trigger and container elements |
### AlertDialog.Trigger
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ---------------------- |
| `children` | `ReactNode` | - | Custom trigger content |
| `className` | `string` | - | CSS classes |
### AlertDialog.Backdrop
| Prop | Type | Default | Description |
| --------------------------- | ------------------------------------- | ---------- | ------------------------- |
| `variant` | `"opaque" \| "blur" \| "transparent"` | `"opaque"` | Backdrop overlay style |
| `isDismissable` | `boolean` | `false` | Close on backdrop click |
| `isKeyboardDismissDisabled` | `boolean` | `true` | Disable ESC key to close |
| `isOpen` | `boolean` | - | Controlled open state |
| `onOpenChange` | `(isOpen: boolean) => void` | - | Open state change handler |
| `className` | `string \| (values) => string` | - | Backdrop CSS classes |
| `UNSTABLE_portalContainer` | `HTMLElement` | - | Custom portal container |
### AlertDialog.Container
| Prop | Type | Default | Description |
| ----------- | ----------------------------------------- | -------- | ------------------------- |
| `placement` | `"auto" \| "center" \| "top" \| "bottom"` | `"auto"` | Dialog position on screen |
| `size` | `"xs" \| "sm" \| "md" \| "lg" \| "cover"` | `"md"` | Alert Dialog size variant |
| `className` | `string \| (values) => string` | - | Container CSS classes |
### AlertDialog.Dialog
| Prop | Type | Default | Description |
| ------------------ | ------------------------------------- | --------------- | -------------------------- |
| `children` | `ReactNode \| ({close}) => ReactNode` | - | Content or render function |
| `className` | `string` | - | CSS classes |
| `role` | `string` | `"alertdialog"` | ARIA role |
| `aria-label` | `string` | - | Accessibility label |
| `aria-labelledby` | `string` | - | ID of label element |
| `aria-describedby` | `string` | - | ID of description element |
### AlertDialog.Header
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ------------------------------------------- |
| `children` | `ReactNode` | - | Header content (typically Icon and Heading) |
| `className` | `string` | - | CSS classes |
### AlertDialog.Heading
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ------------ |
| `children` | `ReactNode` | - | Heading text |
| `className` | `string` | - | CSS classes |
### AlertDialog.Body
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ------------ |
| `children` | `ReactNode` | - | Body content |
| `className` | `string` | - | CSS classes |
### AlertDialog.Footer
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ----------------------------------------- |
| `children` | `ReactNode` | - | Footer content (typically action buttons) |
| `className` | `string` | - | CSS classes |
### AlertDialog.Icon
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------- | ---------- | -------------------- |
| `children` | `ReactNode` | - | Custom icon element |
| `status` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"danger"` | Status color variant |
| `className` | `string` | - | CSS classes |
### AlertDialog.CloseTrigger
| Prop | Type | Default | Description |
| ----------- | ------------------------------ | ------- | ------------------- |
| `children` | `ReactNode` | - | Custom close button |
| `className` | `string \| (values) => string` | - | CSS classes |
### 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
```
## Accessibility
Implements [WAI-ARIA AlertDialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/):
* **Focus trap**: Focus locked within alert dialog
* **Keyboard**: `ESC` closes (when enabled), `Tab` cycles elements
* **Screen readers**: Proper ARIA attributes with `role="alertdialog"`
* **Scroll lock**: Body scroll disabled when open
* **Required action**: Defaults to requiring explicit user action (no backdrop/ESC dismiss)
# Drawer
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/drawer
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(overlays)/drawer.mdx
> Slide-out panel for supplementary content and actions
## Import
```tsx
import { Drawer, Button } from "@heroui/react";
```
### Usage
```tsx
import {Button, Drawer} from "@heroui/react";
export function Basic() {
return (
Open Drawer
Drawer Title
This is a bottom drawer built with React Aria's Modal component. It slides up from
the bottom of the screen with a smooth CSS transition.
Cancel
Confirm
);
}
```
### Anatomy
```tsx
import { Drawer, Button } from "@heroui/react";
export default () => (
Open Drawer
{/* Optional: Drag handle */}
{/* Optional: Close button */}
);
```
### Placement
```tsx
import {Button, Drawer} from "@heroui/react";
export function Placements() {
const placements = ["bottom", "top", "left", "right"] as const;
return (
{placements.map((placement) => (
{placement.charAt(0).toUpperCase() + placement.slice(1)}
{placement === "bottom" && }
{placement.charAt(0).toUpperCase() + placement.slice(1)} Drawer
This drawer slides in from the {placement} edge of the screen.
Cancel
Done
{placement === "top" && }
))}
);
}
```
### Backdrop Variants
```tsx
import {Button, Drawer} from "@heroui/react";
export function BackdropVariants() {
const variants = ["opaque", "blur", "transparent"] as const;
return (
{variants.map((variant) => (
{variant.charAt(0).toUpperCase() + variant.slice(1)}
Backdrop: {variant.charAt(0).toUpperCase() + variant.slice(1)}
This drawer uses the {variant} backdrop variant.
Close
))}
);
}
```
### Non-Dismissable
Set `isDismissable={false}` on `Drawer.Backdrop` to prevent closing by clicking outside or dragging. The user must interact with the drawer's action buttons.
```tsx
import {Button, Drawer} from "@heroui/react";
export function NonDismissable() {
return (
Important Action
Confirm Action
This drawer cannot be dismissed by clicking outside or dragging. You must use one of
the buttons below.
Cancel
Confirm
);
}
```
### Scrollable Content
The `Drawer.Body` automatically handles overflow with native scrolling. Drag-to-dismiss is excluded from the body area to avoid scroll conflicts.
```tsx
import {Button, Drawer} from "@heroui/react";
export function ScrollableContent() {
return (
Terms & Conditions
Terms & Conditions
{Array.from({length: 20}).map((_, i) => (
Paragraph {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.
))}
Decline
Accept
);
}
```
### Controlled State
```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 (
With React.useState()
Control the drawer using React's useState hook
for simple state management.
Status:{" "}
{isOpen ? "open" : "closed"}
setIsOpen(true)}>
Open Drawer
setIsOpen(!isOpen)}>
Toggle
Controlled with useState()
This drawer is controlled by React's useState hook. Pass{" "}
isOpen and onOpenChange props to manage the drawer state
externally.
Close
With useOverlayState()
Use the useOverlayState hook for a cleaner API
with convenient methods like open(), close(), and{" "}
toggle().
Status:{" "}
{state.isOpen ? "open" : "closed"}
Open Drawer
Toggle
Controlled with useOverlayState()
The useOverlayState hook provides dedicated methods for common
operations. No need to manually create callbacks—just use{" "}
state.open(), state.close(), or{" "}
state.toggle().
Close
);
}
```
### With Form
```tsx
import {Button, Drawer, Input, Label, TextField} from "@heroui/react";
export function WithForm() {
return (
Edit Profile
Edit Profile
Name
Email
Bio
Cancel
Save Changes
);
}
```
### Navigation Drawer
```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: "Home"},
{icon: Magnifier, label: "Search"},
{icon: Bell, label: "Notifications"},
{icon: Envelope, label: "Messages"},
{icon: Person, label: "Profile"},
{icon: Gear, label: "Settings"},
];
return (
Menu
Navigation
{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
## Styling
### Passing Tailwind CSS classes
```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
);
}
```
### Customizing the component classes
To customize the Drawer component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Drawer component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/drawer.css)):
#### Base Classes
* `.drawer__trigger` - Trigger element that opens the drawer
* `.drawer__backdrop` - Overlay backdrop behind the drawer
* `.drawer__content` - Positioning wrapper for the drawer panel
* `.drawer__dialog` - The drawer panel itself
* `.drawer__header` - Header section for titles
* `.drawer__heading` - Main title text
* `.drawer__body` - Main scrollable content area
* `.drawer__footer` - Footer section for actions
* `.drawer__handle` - Visual drag handle indicator
* `.drawer__close-trigger` - Close button element
#### Backdrop Variants
* `.drawer__backdrop--opaque` - Opaque colored backdrop (default)
* `.drawer__backdrop--blur` - Blurred backdrop with glass effect
* `.drawer__backdrop--transparent` - Transparent backdrop (no overlay)
#### Placement Variants
* `.drawer__content--bottom` - Slides up from the bottom edge (default)
* `.drawer__content--top` - Slides down from the top edge
* `.drawer__content--left` - Slides in from the left edge
* `.drawer__content--right` - Slides in from the right edge
#### Dialog Variants
* `.drawer__dialog--top` - Slides down from the top edge
* `.drawer__dialog--bottom` - Slides up from the bottom edge
* `.drawer__dialog--left` - Slides in from the left edge
* `.drawer__dialog--right` - Slides in from the right edge
### Interactive States
The component supports these interactive states:
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]` - Applied to trigger and close button
* **Hover**: `:hover` or `[data-hovered="true"]` - Applied to close button on hover
* **Active**: `:active` or `[data-pressed="true"]` - Applied to trigger and close button when pressed
* **Entering**: `[data-entering]` - Applied during drawer opening animation
* **Exiting**: `[data-exiting]` - Applied during drawer closing animation
* **Placement**: `[data-placement="*"]` - Applied based on drawer position (top, bottom, left, right)
## API Reference
### Drawer
| Prop | Type | Default | Description |
| ---------- | ----------------------- | ------- | ----------------------------- |
| `children` | `ReactNode` | - | Trigger and backdrop elements |
| `state` | `UseOverlayStateReturn` | - | Controlled overlay state |
### Drawer.Trigger
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ---------------------- |
| `children` | `ReactNode` | - | Custom trigger content |
| `className` | `string` | - | CSS classes |
### Drawer.Backdrop
| Prop | Type | Default | Description |
| --------------------------- | ------------------------------------- | ---------- | ------------------------- |
| `variant` | `"opaque" \| "blur" \| "transparent"` | `"opaque"` | Backdrop overlay style |
| `isDismissable` | `boolean` | `true` | Close on backdrop click |
| `isKeyboardDismissDisabled` | `boolean` | `false` | Disable ESC key to close |
| `isOpen` | `boolean` | - | Controlled open state |
| `onOpenChange` | `(isOpen: boolean) => void` | - | Open state change handler |
| `className` | `string \| (values) => string` | - | Backdrop CSS classes |
### Drawer.Content
| Prop | Type | Default | Description |
| ----------- | ---------------------------------------- | ---------- | --------------------------- |
| `placement` | `"top" \| "bottom" \| "left" \| "right"` | `"bottom"` | Edge the drawer slides from |
| `className` | `string \| (values) => string` | - | Content CSS classes |
### Drawer.Dialog
| Prop | Type | Default | Description |
| ----------------- | ----------- | ---------- | ------------------- |
| `children` | `ReactNode` | - | Dialog content |
| `className` | `string` | - | CSS classes |
| `role` | `string` | `"dialog"` | ARIA role |
| `aria-label` | `string` | - | Accessibility label |
| `aria-labelledby` | `string` | - | ID of label element |
### Drawer.Header
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | -------------- |
| `children` | `ReactNode` | - | Header content |
| `className` | `string` | - | CSS classes |
### Drawer.Heading
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ----------- |
| `children` | `ReactNode` | - | Title text |
| `className` | `string` | - | CSS classes |
### Drawer.Body
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ------------ |
| `children` | `ReactNode` | - | Body content |
| `className` | `string` | - | CSS classes |
### Drawer.Footer
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | -------------- |
| `children` | `ReactNode` | - | Footer content |
| `className` | `string` | - | CSS classes |
### Drawer.Handle
| Prop | Type | Default | Description |
| ----------- | -------- | ------- | ----------- |
| `className` | `string` | - | CSS classes |
### Drawer.CloseTrigger
| Prop | Type | Default | Description |
| ----------- | ------------------------------ | ------- | ------------------- |
| `children` | `ReactNode` | - | Custom close button |
| `className` | `string \| (values) => string` | - | CSS classes |
### 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
```
## Accessibility
Implements [WAI-ARIA Dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/):
* **Focus trap**: Focus locked within drawer when open
* **Keyboard**: `ESC` closes (when dismissable), `Tab` cycles elements
* **Screen readers**: Proper ARIA attributes via React Aria
* **Scroll lock**: Body scroll disabled when open
* **Drag to dismiss**: Supports pointer-based drag gestures on handle, header, and footer areas
# Modal
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/modal
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(overlays)/modal.mdx
> Dialog overlay for focused user interactions and important content
## Import
```tsx
import { Modal } from "@heroui/react";
```
### Usage
```tsx
"use client";
import {Rocket} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
export function Default() {
return (
Open Modal
Welcome to HeroUI
A beautiful, fast, and modern React UI library for building accessible and
customizable web applications with ease.
Continue
);
}
```
### Anatomy
Import the Modal component and access all parts using dot notation.
```tsx
import {Modal, Button} from "@heroui/react";
export default () => (
Open Modal
{/* Optional: Close button */}
{/* Optional: Icon */}
);
```
### Placement
```tsx
"use client";
import {Rocket} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
export function Placements() {
const placements = ["auto", "top", "center", "bottom"] as const;
return (
{placements.map((placement) => (
{placement.charAt(0).toUpperCase() + placement.slice(1)}
Placement: {placement.charAt(0).toUpperCase() + placement.slice(1)}
This modal uses the {placement} placement option. Try different
placements to see how the modal positions itself on the screen.
Continue
))}
);
}
```
### Backdrop Variants
```tsx
"use client";
import {Rocket} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
export function BackdropVariants() {
const variants = ["opaque", "blur", "transparent"] as const;
return (
{variants.map((variant) => (
{variant.charAt(0).toUpperCase() + variant.slice(1)}
Backdrop: {variant.charAt(0).toUpperCase() + variant.slice(1)}
This modal uses the {variant} backdrop variant. Compare the
different visual effects: opaque provides full opacity, blur adds a backdrop
filter, and transparent removes the background.
Continue
))}
);
}
```
### Sizes
```tsx
"use client";
import {Rocket} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
export function Sizes() {
const sizes = ["xs", "sm", "md", "lg", "cover", "full"] as const;
return (
{sizes.map((size) => (
{size.charAt(0).toUpperCase() + size.slice(1)}
Size: {size.charAt(0).toUpperCase() + size.slice(1)}
{size === "cover" ? (
<>
This modal uses the cover size variant. It spans the full
screen with margins: 16px on mobile and 40px on desktop. Maintains rounded
corners and standard padding. Perfect for cover-style content that needs
maximum width while preserving modal aesthetics.
>
) : size === "full" ? (
<>
This modal uses the full size variant. It occupies the entire
viewport without any margins, rounded corners, or shadows, creating a true
fullscreen experience. Ideal for immersive content or full-page
interactions.
>
) : (
<>
This modal uses the {size} size variant. On mobile devices, all
sizes adapt to near full-width for optimal viewing. On desktop, each size
provides a different maximum width to suit various content needs.
>
)}
Cancel
Confirm
))}
);
}
```
### Custom Backdrop
```tsx
"use client";
import {Sparkles} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
export function CustomBackdrop() {
return (
Custom Backdrop
Premium Backdrop
This backdrop features a sophisticated gradient that transitions from a dark color
at the bottom to complete transparency at the top, combined with a smooth blur
effect. The gradient automatically adapts its intensity for optimal contrast in both
light and dark modes.
Amazing!
Close
);
}
```
### Dismiss Behavior
```tsx
"use client";
import {CircleInfo} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
export function DismissBehavior() {
return (
isDismissable
Controls whether the modal can be dismissed by clicking the overlay backdrop. Defaults to{" "}
true. Set to false to require explicit close action.
Open Modal
isDismissable = false
Clicking the backdrop won't close this modal
Try clicking outside this modal on the overlay - it won't close. You must use
the close button or press ESC to dismiss it.
Close
isKeyboardDismissDisabled
Controls whether the ESC key can dismiss the modal. When set to true, the ESC
key will be disabled and users must use explicit close actions.
Open Modal
isKeyboardDismissDisabled = true
ESC key is disabled
Press ESC - nothing happens. You must use the close button or click the overlay
backdrop to dismiss this modal.
Close
);
}
```
### Close Methods
```tsx
"use client";
import {CircleCheck, CircleInfo} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
export function CloseMethods() {
return (
Using slot="close"
The simplest way to close a modal. Add slot="close" to any Button component
within the modal. When clicked, it will automatically close the modal.
Open Modal
Using slot="close"
Click either button below - both have slot="close" and will close
the modal automatically.
Cancel
Confirm
Using Dialog render props
Access the close method from the Dialog's render props. This gives you full
control over when and how to close the modal, allowing you to add custom logic before
closing.
Open Modal
{(renderProps) => (
<>
Using Dialog render props
The buttons below use the close method from render props. You
can add validation or other logic before calling{" "}
renderProps.close().
renderProps.close()}>
Cancel
renderProps.close()}>Confirm
>
)}
);
}
```
### Scroll Behavior
```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")}
>
Inside
Outside
Open Modal ({scroll.charAt(0).toUpperCase() + scroll.slice(1)})
Scroll: {scroll.charAt(0).toUpperCase() + scroll.slice(1)}
Compare scroll behaviors - inside keeps content scrollable within the modal,
outside allows page scrolling
{Array.from({length: 30}).map((_, i) => (
Paragraph {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.
))}
Cancel
Confirm
);
}
```
### Controlled State
```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 (
With React.useState()
Control the modal using React's useState hook for
simple state management. Perfect for basic use cases.
Status:{" "}
{isOpen ? "open" : "closed"}
setIsOpen(true)}>
Open Modal
setIsOpen(!isOpen)}>
Toggle
Controlled with useState()
This modal is controlled by React's useState hook. Pass{" "}
isOpen and onOpenChange props to manage the modal state
externally.
Cancel
Confirm
With useOverlayState()
Use the useOverlayState hook for a cleaner API
with convenient methods like open(), close(), and{" "}
toggle().
Status:{" "}
{state.isOpen ? "open" : "closed"}
Open Modal
Toggle
Controlled with useOverlayState()
The useOverlayState hook provides dedicated methods for common
operations. No need to manually create callbacks—just use{" "}
state.open(), state.close(), or{" "}
state.toggle().
Cancel
Confirm
);
}
```
### With Form
```tsx
"use client";
import {Envelope} from "@gravity-ui/icons";
import {Button, Input, Label, Modal, Surface, TextField} from "@heroui/react";
export function WithForm() {
return (
Open Contact Form
Contact Us
Fill out the form below and we'll get back to you. The modal adapts automatically
when the keyboard appears on mobile.
Name
Email
Phone
Company
Message
Cancel
Send Message
);
}
```
### Custom Trigger
```tsx
"use client";
import {Gear} from "@gravity-ui/icons";
import {Button, Modal} from "@heroui/react";
export function CustomTrigger() {
return (
Settings
Manage your preferences
Settings
Use Modal.Trigger to create custom trigger elements beyond standard
buttons. This example shows a card-style trigger with icons and descriptive text.
Cancel
Save
);
}
```
### Custom Animations
```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:
"Physics-based elastic scaling. Simulates a high-damping spring system with fast transient response and prolonged settling time. Ideal for Modals and Popovers.",
icon: "gravity-ui:sparkles",
name: "Kinematic Scale",
},
{
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:
"Simulates movement through a medium with fluid resistance. Eliminates mechanical linearity for a natural, grounded feel. Perfect for Bottom Sheets or Toasts.",
icon: "gravity-ui:arrow-up-from-line",
name: "Fluid Slide",
},
];
return (
{animations.map(({classNames, description, icon, name}) => {
const IconComponent = iconMap[icon];
return (
{name}
{!!IconComponent && }
{name} Animation
{description}
Close
Try Again
);
})}
);
}
```
### Custom 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 (
Render modals inside a custom container instead of document.body
Apply transform: translateZ(0) to the
container to create a new stacking context.
{!!portalContainer && (
Open Modal
Custom 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.
Close
)}
);
}
```
## Styling
### Passing Tailwind CSS classes
```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
);
}
```
### Customizing the component classes
To customize the Modal component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Modal component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/modal.css)):
#### Base Classes
* `.modal__trigger` - Trigger element that opens the modal
* `.modal__backdrop` - Overlay backdrop behind the modal
* `.modal__container` - Positioning wrapper with placement support
* `.modal__dialog` - Modal content container
* `.modal__header` - Header section for titles and icons
* `.modal__body` - Main content area
* `.modal__footer` - Footer section for actions
* `.modal__close-trigger` - Close button element
#### Backdrop Variants
* `.modal__backdrop--opaque` - Opaque colored backdrop (default)
* `.modal__backdrop--blur` - Blurred backdrop with glass effect
* `.modal__backdrop--transparent` - Transparent backdrop (no overlay)
#### Scroll Variants
* `.modal__container--scroll-outside` - Enables scrolling the entire modal
* `.modal__dialog--scroll-inside` - Constrains modal height for body scrolling
* `.modal__body--scroll-inside` - Makes only the body scrollable
* `.modal__body--scroll-outside` - Allows full-page scrolling
### Interactive States
The component supports these interactive states:
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]` - Applied to trigger, dialog, and close button
* **Hover**: `:hover` or `[data-hovered="true"]` - Applied to close button on hover
* **Active**: `:active` or `[data-pressed="true"]` - Applied to close button when pressed
* **Entering**: `[data-entering]` - Applied during modal opening animation
* **Exiting**: `[data-exiting]` - Applied during modal closing animation
* **Placement**: `[data-placement="*"]` - Applied based on modal position (auto, top, center, bottom)
## API Reference
### Modal
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ------------------------------ |
| `children` | `ReactNode` | - | Trigger and container elements |
### Modal.Trigger
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ---------------------- |
| `children` | `ReactNode` | - | Custom trigger content |
| `className` | `string` | - | CSS classes |
### Modal.Backdrop
| Prop | Type | Default | Description |
| --------------------------- | ------------------------------------- | ---------- | ------------------------- |
| `variant` | `"opaque" \| "blur" \| "transparent"` | `"opaque"` | Backdrop overlay style |
| `isDismissable` | `boolean` | `true` | Close on backdrop click |
| `isKeyboardDismissDisabled` | `boolean` | `false` | Disable ESC key to close |
| `isOpen` | `boolean` | - | Controlled open state |
| `onOpenChange` | `(isOpen: boolean) => void` | - | Open state change handler |
| `className` | `string \| (values) => string` | - | Backdrop CSS classes |
| `UNSTABLE_portalContainer` | `HTMLElement` | - | Custom portal container |
### Modal.Container
| Prop | Type | Default | Description |
| ----------- | --------------------------------------------------- | ---------- | ------------------------ |
| `placement` | `"auto" \| "center" \| "top" \| "bottom"` | `"auto"` | Modal position on screen |
| `scroll` | `"inside" \| "outside"` | `"inside"` | Scroll behavior |
| `size` | `"xs" \| "sm" \| "md" \| "lg" \| "cover" \| "full"` | `"md"` | Modal size variant |
| `className` | `string \| (values) => string` | - | Container CSS classes |
### Modal.Dialog
| Prop | Type | Default | Description |
| ------------------ | ------------------------------------- | ---------- | -------------------------- |
| `children` | `ReactNode \| ({close}) => ReactNode` | - | Content or render function |
| `className` | `string \| (values) => string` | - | CSS classes |
| `role` | `string` | `"dialog"` | ARIA role |
| `aria-label` | `string` | - | Accessibility label |
| `aria-labelledby` | `string` | - | ID of label element |
| `aria-describedby` | `string` | - | ID of description element |
### Modal.Header
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | -------------- |
| `children` | `ReactNode` | - | Header content |
| `className` | `string` | - | CSS classes |
### Modal.Body
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ------------ |
| `children` | `ReactNode` | - | Body content |
| `className` | `string` | - | CSS classes |
### Modal.Footer
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | -------------- |
| `children` | `ReactNode` | - | Footer content |
| `className` | `string` | - | CSS classes |
### Modal.CloseTrigger
| Prop | Type | Default | Description |
| ----------- | ------------------------------ | ------- | ------------------- |
| `children` | `ReactNode` | - | Custom close button |
| `className` | `string \| (values) => string` | - | CSS classes |
### 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 modal
state.close(); // Close modal
state.toggle(); // Toggle state
state.setOpen(); // Set state directly
```
## Accessibility
Implements [WAI-ARIA Dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/):
* **Focus trap**: Focus locked within modal
* **Keyboard**: `ESC` closes (when enabled), `Tab` cycles elements
* **Screen readers**: Proper ARIA attributes
* **Scroll lock**: Body scroll disabled when open
# Popover
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/popover
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(overlays)/popover.mdx
> Displays rich content in a portal triggered by a button or any custom element
## Import
```tsx
import { Popover } from '@heroui/react';
```
### Usage
```tsx
import {Button, Popover} from "@heroui/react";
export function PopoverBasic() {
return (
Click me
Popover Title
This is the popover content. You can put any content here.
);
}
```
### Anatomy
Import the Popover component and access all parts using dot notation.
```tsx
import { Popover } from '@heroui/react';
export default () => (
{/* content goes here */}
)
```
### With Arrow
```tsx
import {Ellipsis} from "@gravity-ui/icons";
import {Button, Popover} from "@heroui/react";
export function PopoverWithArrow() {
return (
With Arrow
Popover with Arrow
The arrow shows which element triggered the popover.
Popover with Arrow
The arrow shows which element triggered the popover.
);
}
```
### Placement
```tsx
import {Button, Popover} from "@heroui/react";
export function PopoverPlacement() {
return (
Top
Top placement
Left
Left placement
Click buttons
Right
Right placement
Bottom
Bottom placement
);
}
```
### Interactive Content
```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 ? "Following" : "Follow"}
Product designer and creative director. Building beautiful experiences that matter.
892
Following
12.5K
Followers
);
}
```
## Related Components
* **Button**: Allows a user to perform an action
* **Tooltip**: Contextual information on hover or focus
* **Select**: Dropdown select control
### Custom Render Function
```tsx
"use client";
import {Button, Popover} from "@heroui/react";
export function CustomRenderFunction() {
return (
Click me
}
>
Popover Title
This is the popover content. You can put any content here.
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import { Popover, Button } from '@heroui/react';
function CustomPopover() {
return (
Open
Custom Styled
This popover has custom styling
);
}
```
### Customizing the component classes
To customize the Popover component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Popover component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/popover.css)):
#### Base Classes
* `.popover` - Base popover container styles
* `.popover__dialog` - Dialog content wrapper
* `.popover__heading` - Heading text styles
* `.popover__trigger` - Trigger element styles
### Interactive States
The component supports animation states:
* **Entering**: `[data-entering]` - Applied during popover appearance
* **Exiting**: `[data-exiting]` - Applied during popover disappearance
* **Placement**: `[data-placement="*"]` - Applied based on popover position
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]`
## API Reference
### Popover Props
| Prop | Type | Default | Description |
| -------------- | --------------------------- | ------- | ---------------------------------------- |
| `children` | `React.ReactNode` | - | Trigger and content elements |
| `isOpen` | `boolean` | - | Controls popover visibility (controlled) |
| `defaultOpen` | `boolean` | `false` | Initial open state (uncontrolled) |
| `onOpenChange` | `(isOpen: boolean) => void` | - | Called when open state changes |
### Popover.Content Props
| Prop | Type | Default | Description |
| ------------ | -------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to display in the popover |
| `placement` | `"top" \| "bottom" \| "left" \| "right"` (and variants) | `"bottom"` | Placement of the popover |
| `offset` | `number` | `8` | Distance from the trigger element |
| `shouldFlip` | `boolean` | `true` | Whether popover can change orientation to fit |
| `className` | `string` | - | Additional CSS classes |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Popover.Dialog Props
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ---------------------- |
| `children` | `React.ReactNode` | - | Dialog content |
| `className` | `string` | - | Additional CSS classes |
### Popover.Trigger Props
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | --------------------------------- |
| `children` | `React.ReactNode` | - | Element that triggers the popover |
| `className` | `string` | - | Additional CSS classes |
### Popover.Arrow Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom arrow element |
| `className` | `string` | - | Additional CSS classes |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
# Toast
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/toast
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(overlays)/toast.mdx
> Display temporary notifications and messages to users with automatic dismissal and customizable placement
## Import
```tsx
import { Toast, toast } from '@heroui/react';
```
## Setup
Render the provider in the root of your app.
```tsx
import { Toast, Button, toast } from '@heroui/react';
function App() {
return (
toast("Simple message")}>
Show toast
);
}
```
### Usage
```tsx
"use client";
import {Persons} from "@gravity-ui/icons";
import {Button, toast} from "@heroui/react";
export function Default() {
return (
{
toast("You have been invited to join a team", {
actionProps: {
children: "Dismiss",
onPress: () => toast.clear(),
variant: "tertiary",
},
description: "Bob sent you an invitation to join HeroUI team",
indicator: ,
variant: "default",
});
}}
>
Show toast
);
}
```
### Simple Toasts
```tsx
"use client";
import {Button, toast} from "@heroui/react";
export function Simple() {
return (
toast("Simple message")}>
Default
toast.success("Operation completed")}>
Success
toast.info("New update available")}>
Info
toast.warning("Please check your settings")}
>
Warning
toast.danger("Something went wrong")}>
Error
);
}
```
### Variants
```tsx
"use client";
import {HardDrive, Persons} from "@gravity-ui/icons";
import {Button, toast} from "@heroui/react";
const noop = () => {};
export function Variants() {
return (
{
toast("You have been invited to join a team", {
actionProps: {
children: "Dismiss",
onPress: () => toast.clear(),
variant: "tertiary",
},
description: "Bob sent you an invitation to join HeroUI team",
indicator: ,
variant: "default",
});
}}
>
Default toast
toast.info("You have 2 credits left", {
actionProps: {children: "Upgrade", onPress: noop},
description: "Get a paid plan for more credits",
})
}
>
Accent toast
toast.success("You have upgraded your plan", {
actionProps: {
children: "Billing",
className: "bg-success text-success-foreground",
onPress: noop,
},
description: "You can continue using HeroUI Chat",
})
}
>
Success toast
toast.warning("You have no credits left", {
actionProps: {
children: "Upgrade",
className: "bg-warning text-warning-foreground",
onPress: noop,
},
description: "Upgrade to a paid plan to continue",
})
}
>
Warning toast
toast.danger("Storage is full", {
actionProps: {children: "Remove", onPress: noop, variant: "danger"},
description:
"Remove files to release space. Adding more text to demonstrate longer content display",
indicator: ,
})
}
>
Danger toast
);
}
```
### Custom Indicators
```tsx
"use client";
import {Star} from "@gravity-ui/icons";
import {Button, toast} from "@heroui/react";
export function CustomIndicator() {
return (
toast("Custom icon indicator", {
indicator: ,
})
}
>
Custom indicator
);
}
```
### Promise & Loading
```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("Network error. Please try again.")), 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("Failed to save data"));
}
}, 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 */}
Using toast.promise()
Automatically handles loading, success, and error states
{
toast.promise(uploadFile(), {
error: "Failed to upload file",
loading: "Uploading file...",
success: (data) => `File ${data.filename} uploaded (${data.size}KB)`,
});
}}
>
Upload file
{
toast.promise(createEvent(), {
error: (err) => err.message,
loading: "Creating event...",
success: "Event created",
});
}}
>
Create event (error)
{
toast.promise(saveData(), {
error: (err) => err.message,
loading: "Saving changes...",
success: (data) => `Saved ${data.count} items`,
});
}}
>
Save data (random)
{
toast.promise(fetchUser(), {
error: "Failed to fetch user",
loading: "Loading user...",
success: (data) => `Welcome back, ${data.name}!`,
});
}}
>
Fetch user
{/* Manual Loading Section */}
Manual Loading State
Manually control loading state with isLoading prop
{
const loadingId = toast("Uploading file...", {
description: "Please wait while we upload your file",
isLoading: true,
timeout: 0,
});
setTimeout(() => {
toast.close(loadingId);
toast.success("File uploaded", {
description: "Your file has been uploaded successfully",
});
}, 3000);
}}
>
Upload with loading
{
const loadingId = toast("Processing payment...", {
isLoading: true,
timeout: 0,
});
setTimeout(() => {
toast.close(loadingId);
toast.success("Payment processed", {
description: "Your payment has been processed successfully",
});
}, 2500);
}}
>
Payment processing
{
const loadingId = toast("Saving changes...", {
isLoading: true,
timeout: 0,
});
setTimeout(() => {
toast.close(loadingId);
toast.danger("Failed to save", {
description: "Please try again",
});
}, 2000);
}}
>
Loading to error
);
}
```
### Callbacks
```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("File saved", {
onClose: () => {
addToHistory("File saved (closed after 3 seconds)");
},
timeout: 3000,
})
}
>
Custom timeout (3s)
toast("Changes saved", {
onClose: () => {
addToHistory("Changes saved (closed after 10 seconds)");
},
timeout: 10000,
})
}
>
Custom timeout (10s)
toast.success("Event created", {
onClose: () => {
addToHistory("Event created (closed after default timeout)");
},
})
}
>
With onClose callback
toast("Important notification", {
description: "This toast will stay until dismissed",
onClose: () => {
addToHistory("Important notification (manually closed)");
},
timeout: 0,
})
}
>
Persistent toast
{/* Closed History Panel */}
Closed History
{closedHistory.length > 0 && (
setClosedHistory([])}
>
Clear
)}
{closedHistory.length === 0 ? (
No toasts closed yet. Try closing one above!
) : (
closedHistory.map((item, index) => (
{item.message}
({item.time})
))
)}
);
}
```
### Placements
```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: "Event has been created",
title: "Event created",
variant: "default",
});
};
return (
{/* Render a ToastProvider for each placement */}
{placements.map((p) => (
))}
{placements.map((p) => (
showToast(p)}>
{p}
))}
);
}
```
### Custom Toast Rendering
```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: "This uses a custom render function",
title: "Custom layout toast",
variant: "default",
});
}}
>
Custom toast
);
}
```
### Custom Queues
```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: "You have a new message",
title: "New notification",
variant: "default",
});
}}
>
Add notification (max 2)
{/* Error Queue */}
{
errorQueue.add({
description: "Failed to save changes",
title: "Error occurred",
variant: "danger",
});
}}
>
Add error (max 3)
{/* Success Queue */}
{
successQueue.add({
description: `Operation ${Date.now()}`,
title: "Success!",
variant: "success",
});
}}
>
Add success (max 1)
);
}
```
### Anatomy
```tsx
```
## Related Components
* **Button**: Allows a user to perform an action
* **Alert**: Display important messages and notifications
* **CloseButton**: Button for dismissing overlays
## Styling
### Passing Tailwind CSS classes
```tsx
```
### Customizing the component classes
To customize the Toast component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Toast component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/toast.css)):
#### Base Classes
* `.toast` - Base toast container
* `.toast__region` - Toast region container
* `.toast__content` - Content wrapper for title and description
* `.toast__indicator` - Icon/indicator container
* `.toast__title` - Toast title text
* `.toast__description` - Toast description text
* `.toast__action` - Action button container
* `.toast__close` - Close button container
#### Variant Classes
* `.toast--default` - Default gray variant
* `.toast--accent` - Accent blue variant
* `.toast--success` - Success green variant
* `.toast--warning` - Warning yellow/orange variant
* `.toast--danger` - Danger red variant
### Interactive States
The component supports various states:
* **Frontmost**: `[data-frontmost]` - Applied to the topmost visible toast
* **Index**: `[data-index]` - Applied based on toast position in stack
* **Placement**: `[data-placement="*"]` - Applied based on toast region placement
## API Reference
### Toast.Provider Props
| Prop | Type | Default | Description |
| ------------------ | --------------------------------------------------------------------------------- | ---------- | ------------------------------------------- |
| `placement` | `"top start" \| "top" \| "top end" \| "bottom start" \| "bottom" \| "bottom end"` | `"bottom"` | Placement of the toast region |
| `gap` | `number` | `12` | The gap between toasts in pixels |
| `maxVisibleToasts` | `number` | `3` | Maximum number of toasts to display at once |
| `scaleFactor` | `number` | `0.05` | Scale factor for stacked toasts (0-1) |
| `width` | `number \| string` | `460` | Width of the toast in pixels or CSS value |
| `queue` | `ToastQueue` | - | Custom toast queue instance |
| `children` | `ReactNode \| ((props: {toast: QueuedToast}) => ReactNode)` | - | Custom render function or children |
| `className` | `string` | - | Additional CSS classes |
### Toast Props
| Prop | Type | Default | Description |
| ------------- | ------------------------------------------------------------- | ----------- | -------------------------------------------------- |
| `toast` | `QueuedToast` | - | Toast data from queue (required) |
| `variant` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"default"` | Visual variant of the toast |
| `placement` | `ToastVariants["placement"]` | - | Placement (inherited from Provider) |
| `scaleFactor` | `number` | - | Scale factor (inherited from Provider) |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Toast content (ToastContent, ToastIndicator, etc.) |
### Toast.Content Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | --------------------------------------------------- |
| `children` | `ReactNode` | - | Content (typically ToastTitle and ToastDescription) |
| `className` | `string` | - | Additional CSS classes |
### Toast.Indicator Props
| Prop | Type | Default | Description |
| ----------- | -------------------------- | ------- | ------------------------------------------------ |
| `variant` | `ToastVariants["variant"]` | - | Variant for default icon |
| `children` | `ReactNode` | - | Custom indicator icon (defaults to variant icon) |
| `className` | `string` | - | Additional CSS classes |
### Toast.Title Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ---------------------- |
| `children` | `ReactNode` | - | Title text |
| `className` | `string` | - | Additional CSS classes |
### Toast.Description Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ---------------------- |
| `children` | `ReactNode` | - | Description text |
| `className` | `string` | - | Additional CSS classes |
### Toast.ActionButton Props
| Prop | Type | Default | Description |
| ------------------ | ----------- | ------- | ---------------------------------- |
| `children` | `ReactNode` | - | Action button content |
| `className` | `string` | - | Additional CSS classes |
| All `Button` props | - | - | Accepts all Button component props |
### Toast.CloseButton Props
| Prop | Type | Default | Description |
| ----------------------- | -------- | ------- | --------------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| All `CloseButton` props | - | - | Accepts all CloseButton component props |
### ToastQueue
A `ToastQueue` manages the state for a ``. The state is stored outside React so you can trigger toasts from anywhere in your application.
#### Constructor Options
| Option | Type | Default | Description |
| ------------------ | -------------------------- | ------- | ----------------------------------------------------------- |
| `maxVisibleToasts` | `number` | `3` | Maximum number of toasts to display at once (visual only) |
| `wrapUpdate` | `(fn: () => void) => void` | - | Function to wrap state updates (e.g., for view transitions) |
#### Methods
| Method | Parameters | Returns | Description |
| ----------- | -------------------------------------- | ------------ | -------------------------------------------------------- |
| `add` | `(content: T, options?: ToastOptions)` | `string` | Add a toast to the queue, returns toast key |
| `close` | `(key: string)` | `void` | Close a toast by its key |
| `pauseAll` | `()` | `void` | Pause all toast timers |
| `resumeAll` | `()` | `void` | Resume all toast timers |
| `clear` | `()` | `void` | Close all toasts |
| `subscribe` | `(fn: () => void)` | `() => void` | Subscribe to queue changes, returns unsubscribe function |
### toast Function
The default `toast` function provides convenient methods for showing toasts:
```tsx
import { toast } from '@heroui/react';
// Basic toast (auto-dismisses after 4 seconds by default)
toast("Event has been created");
// Variant methods (also auto-dismiss after 4 seconds by default)
toast.success("File saved");
toast.info("New update available");
toast.warning("Please check your settings");
toast.danger("Something went wrong");
// With options
toast("Event has been created", {
description: "Your event has been scheduled for tomorrow",
variant: "default",
timeout: 5000, // Custom timeout: 5 seconds
onClose: () => console.log("Closed"),
actionProps: {
children: "View",
onPress: () => {},
},
indicator: ,
});
// Promise support (automatically shows loading spinner)
toast.promise(
uploadFile(),
{
loading: "Uploading file...",
success: (data) => `File ${data.filename} uploaded`,
error: "Failed to upload file",
}
);
// Manual loading state (persistent toast - no auto-dismiss)
const loadingId = toast("Creating event...", {
isLoading: true,
timeout: 0, // Persistent toast that doesn't auto-dismiss
});
// Later, close and show result
toast.close(loadingId);
toast.success("Event created");
// Queue methods
toast.close(key);
toast.clear();
toast.pauseAll();
toast.resumeAll();
```
#### toast Options
| Option | Type | Default | Description |
| ------------- | ------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `title` | `ReactNode` | - | Toast title (first parameter for variant methods) |
| `description` | `ReactNode` | - | Optional description text |
| `variant` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"default"` | Visual variant |
| `indicator` | `ReactNode` | - | Custom indicator icon (null to hide) |
| `actionProps` | `ButtonProps` | - | Props for action button |
| `isLoading` | `boolean` | `false` | Show loading spinner instead of indicator |
| `timeout` | `number` | `4000` | Auto-dismiss timeout in milliseconds. Defaults to 4000ms (4 seconds). Set to `0` for persistent toasts that don't auto-dismiss |
| `onClose` | `() => void` | - | Callback when toast is closed |
#### toast.promise Options
| Option | Type | Default | Description |
| --------- | -------------------------------------------- | ------- | ------------------------------------------ |
| `loading` | `ReactNode` | - | Message shown while promise is pending |
| `success` | `ReactNode \| ((data: T) => ReactNode)` | - | Message shown on success (can be function) |
| `error` | `ReactNode \| ((error: Error) => ReactNode)` | - | Message shown on error (can be function) |
# Tooltip
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/tooltip
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(overlays)/tooltip.mdx
> Displays informative text when users hover over or focus on an element
## Import
```tsx
import { Tooltip } from '@heroui/react';
```
### Usage
```tsx
import {CircleInfo} from "@gravity-ui/icons";
import {Button, Tooltip} from "@heroui/react";
export function TooltipBasic() {
return (
Hover me
This is a tooltip
More information
);
}
```
### Anatomy
Import the Tooltip component and access all parts using dot notation.
```tsx
import { Tooltip, Button } from '@heroui/react';
export default () => (
Hover for tooltip
Helpful information about this element
)
```
### With Arrow
```tsx
import {Button, Tooltip} from "@heroui/react";
export function TooltipWithArrow() {
return (
With Arrow
Tooltip with arrow indicator
Custom Offset
Custom offset from trigger
);
}
```
### Placement
```tsx
import {Button, Tooltip} from "@heroui/react";
export function TooltipPlacement() {
return (
Top
Top placement
Left
Left placement
Hover buttons
Right
Right placement
Bottom
Bottom placement
);
}
```
### Custom Triggers
```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
Active
Jane is currently online
Help Information
This is a helpful tooltip with more detailed information about this feature.
);
}
```
## Related Components
* **Button**: Allows a user to perform an action
* **Popover**: Displays content in context with a trigger
### Custom Render Function
```tsx
"use client";
import {CircleInfo} from "@gravity-ui/icons";
import {Button, Tooltip} from "@heroui/react";
export function CustomRenderFunction() {
return (
Hover me
}>
This is a tooltip
}>
More information
);
}
```
## Styling
### Global Delay Configuration
You can set default show and hide delays for all Tooltip components in your application by defining CSS variables:
```css
/* In your global CSS file */
:root {
--tooltip-delay: 1500ms;
--tooltip-close-delay: 500ms;
}
/* You can also set different values for light/dark themes */
.light, [data-theme="light"] {
--tooltip-delay: 1200ms;
}
.dark, [data-theme="dark"] {
--tooltip-close-delay: 300ms;
}
```
Values accept CSS time units such as `ms` and `s`. These global settings are overridden by the `delay` and `closeDelay` props when specified on individual tooltips.
### Passing Tailwind CSS classes
```tsx
import { Tooltip, Button } from '@heroui/react';
function CustomTooltip() {
return (
Hover me
Custom styled tooltip
);
}
```
### Customizing the component classes
To customize the Tooltip component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Tooltip component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/tooltip.css)):
#### Base Classes
* `.tooltip` - Base tooltip styles with animations
* `.tooltip__trigger` - Trigger element styles
### Interactive States
The component supports animation states:
* **Entering**: `[data-entering]` - Applied during tooltip appearance
* **Exiting**: `[data-exiting]` - Applied during tooltip disappearance
* **Placement**: `[data-placement="*"]` - Applied based on tooltip position
## API Reference
### Tooltip Props
| Prop | Type | Default | Description |
| ------------ | -------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Trigger element and content |
| `delay` | `number` | `1500` or CSS variable | Delay in milliseconds before showing the tooltip. Can be globally configured via `--tooltip-delay` |
| `closeDelay` | `number` | `500` or CSS variable | Delay in milliseconds before hiding the tooltip. Can be globally configured via `--tooltip-close-delay` |
| `trigger` | `"hover" \| "focus"` | `"hover"` | How the tooltip is triggered |
| `isDisabled` | `boolean` | `false` | Whether the tooltip is disabled |
### Tooltip.Content Props
| Prop | Type | Default | Description |
| ----------- | -------------------------------------------------------------------------- | ------------------ | ---------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to display in the tooltip |
| `showArrow` | `boolean` | `false` | Whether to show the arrow indicator |
| `offset` | `number` | `3` (7 with arrow) | Distance from the trigger element |
| `placement` | `"top" \| "bottom" \| "left" \| "right"` (and variants) | `"top"` | Placement of the tooltip |
| `className` | `string` | - | Additional CSS classes |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Tooltip.Trigger Props
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | --------------------------------- |
| `children` | `React.ReactNode` | - | Element that triggers the tooltip |
| `className` | `string` | - | Additional CSS classes |
### Tooltip.Arrow Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom arrow element |
| `className` | `string` | - | Additional CSS classes |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
# Autocomplete
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/autocomplete
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(pickers)/autocomplete.mdx
> An autocomplete combines a select with filtering, allowing users to search and select from a list of options
## Import
```tsx
import { Autocomplete, useFilter } from "@heroui/react";
```
### Usage
```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[])}
>
States to Visit
{({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}
);
})}
);
}}
No results found }>
{items.map((item) => (
{item.name}
))}
);
}
```
### Anatomy
Import the Autocomplete component and access all parts using dot notation.
```tsx
import {Autocomplete, Label, Description, SearchField, ListBox} from "@heroui/react";
export default () => (
);
```
### With Description
```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 (
State
No results found }>
{items.map((item) => (
{item.name}
))}
Select your state of residence
);
}
```
### Multiple Select
```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[])}
>
States
{({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}
);
})}
);
}}
No results found }>
{items.map((item) => (
{item.name}
))}
);
}
```
### With Sections
```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 (
Country
No results found }>
United States
Canada
Mexico
United Kingdom
France
Germany
Spain
Italy
Japan
China
India
South Korea
);
}
```
### With Disabled Options
```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 (
Animal
No results found }>
Dog
Cat
Bird
Kangaroo
Elephant
Tiger
);
}
```
### Allows Empty Collection
The `allowsEmptyCollection` prop enables the autocomplete to function even when there are no items in the collection. This is useful for scenarios where the list might be empty initially or when all items are filtered out.
```tsx
"use client";
import {Autocomplete, EmptyState, Label, ListBox, SearchField, useFilter} from "@heroui/react";
export function AllowsEmptyCollection() {
const {contains} = useFilter({sensitivity: "base"});
return (
State
No results found } />
);
}
```
### Custom Indicator
```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 (
State
No results found }>
{items.map((item) => (
{item.name}
))}
);
}
```
### Required
```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("Form submitted successfully!");
};
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 (
State
No results found }>
{states.map((state) => (
{state.name}
))}
Country
No results found }>
{countries.map((country) => (
{country.name}
))}
Submit
);
}
```
### Full Width
```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 (
State
No results found }>
{items.map((item) => (
{item.name}
))}
);
}
```
### Variants
The Autocomplete component supports two visual variants:
* **`primary`** (default) - Standard styling with shadow, suitable for most use cases
* **`secondary`** - Lower emphasis variant without shadow, suitable for use in Surface components
```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: "Option 1"},
{id: "option2", name: "Option 2"},
{id: "option3", name: "Option 3"},
{id: "option4", name: "Option 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 (
Single Select Variants
Primary variant
No results found }>
{items.map((item) => (
{item.name}
))}
Secondary variant
No results found }>
{items.map((item) => (
{item.name}
))}
Multiple Select Variants
setSelectedKeys1(keys as Key[])}
>
Primary variant
{({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}
);
})}
);
}}
No results found }>
{items.map((item) => (
{item.name}
))}
setSelectedKeys2(keys as Key[])}
>
Secondary variant
{({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}
);
})}
);
}}
No results found }>
{items.map((item) => (
{item.name}
))}
);
}
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` to apply the lower emphasis variant suitable for surface backgrounds.
```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 (
State
No results found }>
{items.map((item) => (
{item.name}
))}
);
}
```
### Custom Value
You can customize the displayed value using render props:
```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 (
User
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItems = state.selectedItems;
if (selectedItems.length > 1) {
return `${selectedItems.length} users selected`;
}
const selectedItem = users.find((user) => user.id === selectedItems[0]?.key);
if (!selectedItem) {
return defaultChildren;
}
return (
{selectedItem.fallback}
{selectedItem.name}
);
}}
No results found }>
{users.map((user) => (
{user.fallback}
{user.name}
{user.email}
))}
);
}
```
### Controlled
```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 (
State (controlled)
No results found }>
{states.map((state) => (
{state.name}
))}
Selected: {selectedState?.name || "None"}
);
}
```
### Controlled Multiple
```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[])}
>
States
{({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}
);
})}
);
}}
No results found }>
{items.map((item) => (
{item.name}
))}
);
}
```
### Controlled Open State
```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 (
State
No results found }>
{items.map((item) => (
{item.name}
))}
setIsOpen(!isOpen)}>{isOpen ? "Close" : "Open"} Autocomplete
Autocomplete is {isOpen ? "open" : "closed"}
);
}
```
### Asynchronous Filtering
```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 (
Search a Star Wars characters
No results found }
>
{(item: Character) => (
{item.name}
)}
);
}
```
### Virtualization
Autocomplete supports virtualization through [Virtualizer](https://react-aria.adobe.com/Virtualizer), enabling efficient rendering of large datasets by displaying only the rows visible within the viewport.
```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
No results found }
>
{(user) => (
{user.name}
{user.email}
)}
);
}
```
### Disabled
```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 (
State
No results found }>
{items.map((item) => (
{item.name}
))}
Countries to Visit
No results found }>
{countries.map((country) => (
{country.name}
))}
);
}
```
### Advanced Examples
#### User Selection
```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 (
User
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItems = state.selectedItems;
if (selectedItems.length > 1) {
return `${selectedItems.length} users selected`;
}
const selectedItem = users.find((user) => user.id === selectedItems[0]?.key);
if (!selectedItem) {
return defaultChildren;
}
return (
{selectedItem.fallback}
{selectedItem.name}
);
}}
No results found }>
{users.map((user) => (
{user.fallback}
{user.name}
{user.email}
))}
);
}
```
#### User Selection Multiple
```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[])}
>
Users
{({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}
);
})}
);
}}
No results found }>
{users.map((user) => (
{user.fallback}
{user.name}
{user.email}
))}
);
}
```
#### Location Search
```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: "USA", name: "New York"},
{country: "USA", name: "Los Angeles"},
{country: "USA", name: "Chicago"},
{country: "UK", name: "London"},
{country: "France", name: "Paris"},
{country: "Japan", name: "Tokyo"},
{country: "Australia", name: "Sydney"},
{country: "Canada", name: "Toronto"},
{country: "Germany", name: "Berlin"},
{country: "Spain", name: "Madrid"},
];
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 (
City
(
{isLoading ? "Searching..." : "No cities found"}
)}
>
{allCities.map((city) => (
{city.name}
{city.country}
))}
);
}
```
#### Tag Group Selection
```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[])}
>
Tags
{({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}
);
})}
);
}}
No tags found }>
{tags.map((tag) => (
{tag.name}
))}
);
}
```
#### Email Recipients
```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[])}
>
To
{({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}
);
})}
);
}}
No recipients found }>
{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
## Styling
### Passing Tailwind CSS classes
```tsx
import {Autocomplete, SearchField, ListBox} from "@heroui/react";
function CustomAutocomplete() {
return (
State
Item 1
);
}
```
### Customizing the component classes
To customize the Autocomplete component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Autocomplete component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/autocomplete.css)):
#### Base Classes
* `.autocomplete` - Base autocomplete container
* `.autocomplete__trigger` - The button that triggers the autocomplete
* `.autocomplete__value` - The displayed value or placeholder
* `.autocomplete__clear-button` - The clear button that removes the selected value
* `.autocomplete__indicator` - The dropdown indicator icon
* `.autocomplete__popover` - The popover container
* `.autocomplete__filter` - The filter wrapper
#### Variant Classes
* `.autocomplete--primary` - Primary variant with shadow (default)
* `.autocomplete--secondary` - Secondary variant without shadow, suitable for use in surfaces
#### State Classes
* `.autocomplete[data-invalid="true"]` - Invalid state
* `.autocomplete__trigger[data-focus-visible="true"]` - Focused trigger state
* `.autocomplete__trigger[data-disabled="true"]` - Disabled trigger state
* `.autocomplete__value[data-placeholder="true"]` - Placeholder state
* `.autocomplete__clear-button[data-empty="true"]` - Clear button hidden when no selection
* `.autocomplete__indicator[data-open="true"]` - Open indicator state
### Interactive States
The component supports both CSS pseudo-classes and data attributes for flexibility:
* **Hover**: `:hover` or `[data-hovered="true"]` on trigger
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]` on trigger
* **Disabled**: `:disabled` or `[data-disabled="true"]` on autocomplete
* **Open**: `[data-open="true"]` on indicator
## API Reference
### Autocomplete Props
| Prop | Type | Default | Description |
| ----------------------- | --------------------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `placeholder` | `string` | `'Select an item'` | Temporary text that occupies the autocomplete when it is empty |
| `selectionMode` | `"single" \| "multiple"` | `"single"` | Whether single or multiple selection is enabled |
| `allowsEmptyCollection` | `boolean` | `false` | Whether the autocomplete allows an empty collection. When true, the autocomplete can function even with no items. |
| `isOpen` | `boolean` | - | Sets the open state of the popover (controlled) |
| `defaultOpen` | `boolean` | - | Sets the default open state of the popover (uncontrolled) |
| `onOpenChange` | `(isOpen: boolean) => void` | - | Handler called when the open state changes |
| `disabledKeys` | `Iterable` | - | Keys of disabled items |
| `isDisabled` | `boolean` | - | Whether the autocomplete is disabled |
| `value` | `Key \| Key[] \| null` | - | Current value (controlled) |
| `defaultValue` | `Key \| Key[] \| null` | - | Default value (uncontrolled) |
| `onChange` | `(value: Key \| Key[] \| null) => void` | - | Handler called when the value changes |
| `isRequired` | `boolean` | - | Whether user input is required |
| `isInvalid` | `boolean` | - | Whether the autocomplete value is invalid |
| `name` | `string` | - | The name of the input, used when submitting an HTML form |
| `fullWidth` | `boolean` | `false` | Whether the autocomplete should take full width of its container |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode \| RenderFunction` | - | Autocomplete content or render function |
### Autocomplete.Trigger Props
| Prop | Type | Default | Description |
| ----------- | ----------------------------- | ------- | ---------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode \| RenderFunction` | - | Trigger content or render function |
### Autocomplete.Value Props
| Prop | Type | Default | Description |
| ----------- | ----------------------------- | ------- | -------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode \| RenderFunction` | - | Value content or render function |
### Autocomplete.Indicator Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ------------------------ |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Custom indicator content |
### Autocomplete.ClearButton Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------ | ------- | ------------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `onClick` | `(e: MouseEvent) => void` | - | Handler called when button is clicked |
| `ref` | `RefObject` | - | Ref to the clear button element |
### Autocomplete.Popover Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ------------------------------------------------ |
| `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"` | Placement of the popover relative to the trigger |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Content children |
### Autocomplete.Filter Props
| Prop | Type | Default | Description |
| --------------- | ------------------------------------------ | ------- | ---------------------------------------- |
| `filter` | `(text: string, input: string) => boolean` | - | Custom filter function |
| `inputValue` | `string` | - | Controlled input value |
| `onInputChange` | `(value: string) => void` | - | Handler called when input value changes |
| `children` | `ReactNode` | - | Filter content (SearchField and ListBox) |
### useFilter Hook
The `useFilter` hook from React Aria provides filtering functions for autocomplete functionality.
```tsx
import {useFilter} from "@heroui/react";
const {contains} = useFilter({sensitivity: "base"});
...
...
```
**Options:**
| Option | Type | Default | Description |
| ------------- | ------------------------------------------- | -------- | ------------------------------- |
| `sensitivity` | `"base" \| "accent" \| "case" \| "variant"` | `"base"` | Locale sensitivity for matching |
**Returns:**
| Function | Type | Description |
| ------------ | ------------------------------------------------ | ------------------------------------------------------ |
| `contains` | `(string: string, substring: string) => boolean` | Returns whether a string contains a given substring |
| `startsWith` | `(string: string, substring: string) => boolean` | Returns whether a string starts with a given substring |
| `endsWith` | `(string: string, substring: string) => boolean` | Returns whether a string ends with a given substring |
### RenderProps
When using render functions with Autocomplete.Value, these values are provided:
| Prop | Type | Description |
| ----------------- | ------------- | ---------------------------------- |
| `defaultChildren` | `ReactNode` | The default rendered value |
| `isPlaceholder` | `boolean` | Whether the value is a placeholder |
| `state` | `SelectState` | The state of the autocomplete |
| `selectedItems` | `Node[]` | The currently selected items |
## Accessibility
The Autocomplete component implements the ARIA select pattern with filtering and provides:
* Full keyboard navigation support
* Screen reader announcements for selection changes
* Proper focus management
* Support for disabled states
* Search functionality with filtering
* HTML form integration
For more information, see the [React Aria Select documentation](https://react-spectrum.adobe.com/react-aria/Select.html).
# ComboBox
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/combo-box
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(pickers)/combo-box.mdx
> A combo box combines a text input with a listbox, allowing users to filter a list of options to items matching a query
## Import
```tsx
import { ComboBox } from '@heroui/react';
```
### Usage
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function Default() {
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
);
}
```
### Anatomy
Import the ComboBox component and access all parts using dot notation.
```tsx
import { ComboBox, Input, Label, Description, Header, ListBox, Separator } from '@heroui/react';
export default () => (
)
```
### With Description
```tsx
"use client";
import {ComboBox, Description, Input, Label, ListBox} from "@heroui/react";
export function WithDescription() {
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
Search and select your favorite animal
);
}
```
### With Sections
```tsx
"use client";
import {ComboBox, Header, Input, Label, ListBox, Separator} from "@heroui/react";
export function WithSections() {
return (
Country
United States
Canada
Mexico
United Kingdom
France
Germany
Spain
Italy
Japan
China
India
South Korea
);
}
```
### With Disabled Options
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function WithDisabledOptions() {
return (
Animal
Dog
Cat
Bird
Kangaroo
Elephant
Tiger
);
}
```
### Custom Indicator
```tsx
"use client";
import {ChevronsExpandVertical} from "@gravity-ui/icons";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function CustomIndicator() {
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
);
}
```
### Required
```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("Form submitted successfully!");
};
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
Submit
);
}
```
### Custom Value
```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 (
User
{users.map((user) => (
{user.fallback}
{user.name}
{user.email}
))}
);
}
```
### Controlled
```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: "Cat",
},
{
id: "dog",
name: "Dog",
},
{
id: "bird",
name: "Bird",
},
{
id: "fish",
name: "Fish",
},
{
id: "hamster",
name: "Hamster",
},
];
const [selectedKey, setSelectedKey] = useState("cat");
const selectedAnimal = animals.find((a) => a.id === selectedKey);
return (
);
}
```
### Controlled Input Value
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
import {useState} from "react";
export function ControlledInputValue() {
const [inputValue, setInputValue] = useState("");
return (
);
}
```
### Asynchronous Loading
```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 (
Pick a Character
}>
{(item) => (
{item.name}
)}
Loading more...
);
}
```
### Custom Filtering
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function CustomFiltering() {
const animals = [
{id: "cat", name: "Cat"},
{id: "dog", name: "Dog"},
{id: "bird", name: "Bird"},
{id: "fish", name: "Fish"},
{id: "hamster", name: "Hamster"},
];
return (
{
if (!inputValue) return true;
return text.toLowerCase().includes(inputValue.toLowerCase());
}}
>
Animal (custom filter)
{animals.map((animal) => (
{animal.name}
))}
);
}
```
### Allows Custom Value
```tsx
"use client";
import {ComboBox, Description, Input, Label, ListBox} from "@heroui/react";
export function AllowsCustomValue() {
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
You can type any animal name, even if it's not in the list
);
}
```
### Disabled
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function Disabled() {
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
);
}
```
### Default Selected Key
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function DefaultSelectedKey() {
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
);
}
```
### Full Width
```tsx
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function FullWidth() {
return (
Favorite Animal
Aardvark
Cat
Dog
);
}
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` to apply the lower emphasis variant suitable for surface backgrounds.
```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("Form submitted successfully!");
};
return (
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
Submit
);
}
```
### Menu Trigger
Use the `menuTrigger` prop to control when the popover opens:
* `focus` (default): popover opens when the user focuses the input
* `input`: popover opens when the user edits the input text
* `manual`: popover only opens when the user presses the trigger button or uses the arrow keys
```tsx
"use client";
import {ComboBox, Description, Input, Label, ListBox} from "@heroui/react";
export function MenuTrigger() {
return (
);
}
```
### Custom Render Function
```tsx
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function CustomRenderFunction() {
return (
}>
Favorite Animal
Aardvark
Cat
Dog
Kangaroo
Panda
Snake
);
}
```
## 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
## Styling
### Passing Tailwind CSS classes
```tsx
import { ComboBox, Input } from '@heroui/react';
function CustomComboBox() {
return (
Favorite Animal
Item 1
);
}
```
### Customizing the component classes
To customize the ComboBox component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The ComboBox component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/combo-box.css)):
#### Base Classes
* `.combo-box` - Base ComboBox container
* `.combo-box__input-group` - Container for the input and trigger button
* `.combo-box__trigger` - The button that triggers the popover
* `.combo-box__popover` - The popover container
#### State Classes
* `.combo-box[data-invalid="true"]` - Invalid state
* `.combo-box[data-disabled="true"]` - Disabled ComboBox state
* `.combo-box__trigger[data-focus-visible="true"]` - Focused trigger state
* `.combo-box__trigger[data-disabled="true"]` - Disabled trigger state
* `.combo-box__trigger[data-open="true"]` - Open trigger state
### Interactive States
The component supports both CSS pseudo-classes and data attributes for flexibility:
* **Hover**: `:hover` or `[data-hovered="true"]` on trigger
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]` on trigger
* **Disabled**: `:disabled` or `[data-disabled="true"]` on ComboBox
* **Open**: `[data-open="true"]` on trigger
## API Reference
### ComboBox Props
| Prop | Type | Default | Description |
| ----------------------- | ---------------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `inputValue` | `string` | - | Current input value (controlled) |
| `defaultInputValue` | `string` | - | Default input value (uncontrolled) |
| `onInputChange` | `(value: string) => void` | - | Handler called when the input value changes |
| `selectedKey` | `Key \| null` | - | Current selected key (controlled) |
| `defaultSelectedKey` | `Key \| null` | - | Default selected key (uncontrolled) |
| `onSelectionChange` | `(key: Key \| null) => void` | - | Handler called when the selection changes |
| `isOpen` | `boolean` | - | Sets the open state of the popover (controlled) |
| `defaultOpen` | `boolean` | - | Sets the default open state of the popover (uncontrolled) |
| `onOpenChange` | `(isOpen: boolean) => void` | - | Handler called when the open state changes |
| `items` | `Iterable` | - | The items to display in the listbox |
| `disabledKeys` | `Iterable` | - | Keys of disabled items |
| `defaultFilter` | `(text: string, inputValue: string) => boolean` | - | Custom filter function for filtering items |
| `isDisabled` | `boolean` | - | Whether the ComboBox is disabled |
| `isReadOnly` | `boolean` | - | Whether the input can be selected but not changed by the user |
| `isRequired` | `boolean` | - | Whether user input is required |
| `isInvalid` | `boolean` | - | Whether the ComboBox value is invalid |
| `validate` | `(value: ComboBoxValidationValue) => ValidationError \| true \| null \| undefined` | - | A function that returns an error message if a given value is invalid. Validation errors are displayed to the user when the form is submitted if `validationBehavior="native"`. For realtime validation, use the `isInvalid` prop instead |
| `validationBehavior` | `"native" \| "aria"` | `"native"` | Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA |
| `name` | `string` | - | The name of the input, used when submitting an HTML form |
| `form` | `string` | - | The id of a `` element to associate the input with |
| `formValue` | `"text" \| "key"` | `"key"` | Whether the text or key of the selected item is submitted as part of an HTML form. When `allowsCustomValue` is `true`, this option does not apply and the text is always submitted |
| `autoComplete` | `string` | - | Describes the type of autocomplete functionality |
| `autoFocus` | `boolean` | - | Whether the element should receive focus on render |
| `allowsCustomValue` | `boolean` | - | Whether the ComboBox allows custom values not in the list |
| `allowsEmptyCollection` | `boolean` | - | Whether the ComboBox allows an empty collection |
| `menuTrigger` | `"focus" \| "input" \| "manual"` | `"focus"` | The interaction required to display the ComboBox menu |
| `shouldFocusWrap` | `boolean` | - | Whether keyboard navigation is circular |
| `fullWidth` | `boolean` | `false` | Whether the ComboBox should take full width of its container |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode \| RenderFunction` | - | ComboBox content or render function |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### ComboBox.InputGroup Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | InputGroup content |
### ComboBox.Trigger Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Custom trigger content |
### ComboBox.Popover Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ------------------------------------------------ |
| `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"` | Placement of the popover relative to the trigger |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Content children |
### RenderProps
When using render functions with ComboBox, these values are provided:
| Prop | Type | Description |
| -------------- | --------------- | --------------------------- |
| `state` | `ComboBoxState` | The state of the ComboBox |
| `inputValue` | `string` | The current input value |
| `selectedKey` | `Key \| null` | The currently selected key |
| `selectedItem` | `Node \| null` | The currently selected item |
## Examples
### Basic Usage
```tsx
import { ComboBox, Input, Label, ListBox } from '@heroui/react';
Favorite Animal
Cat
Dog
```
### With Sections
```tsx
import { ComboBox, Input, Label, ListBox, Header, Separator } from '@heroui/react';
Country
United States
United Kingdom
```
### Controlled Selection
```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
);
}
```
### Controlled Input Value
```tsx
import { ComboBox, Input, Label, ListBox } from '@heroui/react';
import { useState } from 'react';
function ControlledInputComboBox() {
const [inputValue, setInputValue] = useState('');
return (
Search
Cat
Dog
);
}
```
### Asynchronous Loading
```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...
);
}
```
### Custom Filtering
```tsx
import { ComboBox, Input, Label, ListBox } from '@heroui/react';
{
if (!inputValue) return true;
return text.toLowerCase().includes(inputValue.toLowerCase());
}}
>
Animal
Cat
Dog
```
### Menu Trigger
Control when the popover opens using the `menuTrigger` prop:
```tsx
import { ComboBox, Description, Input, Label, ListBox } from '@heroui/react';
// Opens on focus (default)
Favorite Animal
Cat
Popover opens when the input is focused
// Opens when typing
Favorite Animal
Cat
Popover opens when the user edits the input text
// Opens only manually
Favorite Animal
Cat
Popover only opens when the trigger button is pressed or arrow keys are used
```
### Form Value
Use the `formValue` prop to control whether the selected item's key or text is submitted in forms:
```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
);
}
```
### Validation Behavior
Control how validation is displayed using the `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
);
}
```
### Custom Validation
Use the `validate` prop to add custom validation logic:
```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
);
}
```
### Read Only
Use the `isReadOnly` prop to make the comboBox read-only:
```tsx
import { ComboBox, Input, Label, ListBox } from '@heroui/react';
Favorite Animal
Cat
Dog
```
## Accessibility
The ComboBox component implements the ARIA comboBox pattern and provides:
* Full keyboard navigation support
* Screen reader announcements for selection changes and input changes
* Proper focus management
* Support for disabled states
* Typeahead search functionality
* HTML form integration
* Support for custom values
For more information, see the [React Aria ComboBox documentation](https://react-spectrum.adobe.com/react-aria/ComboBox.html).
# Select
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/select
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(pickers)/select.mdx
> A select displays a collapsible list of options and allows a user to select one of them
## Import
```tsx
import { Select } from "@heroui/react";
```
### Usage
```tsx
import {Label, ListBox, Select} from "@heroui/react";
export function Default() {
return (
State
Florida
Delaware
California
Texas
New York
Washington
);
}
```
### Anatomy
Import the Select component and access all parts using dot notation.
```tsx
import {Select, Label, Description, Header, ListBox, Separator} from "@heroui/react";
export default () => (
);
```
### With Description
```tsx
import {Description, Label, ListBox, Select} from "@heroui/react";
export function WithDescription() {
return (
State
Florida
Delaware
California
Texas
New York
Washington
Select your state of residence
);
}
```
### Multiple Select
```tsx
import {Label, ListBox, Select} from "@heroui/react";
export function MultipleSelect() {
return (
Countries to Visit
Argentina
Venezuela
Japan
France
Italy
Spain
Thailand
New Zealand
Iceland
);
}
```
### With Sections
```tsx
import {Header, Label, ListBox, Select, Separator} from "@heroui/react";
export function WithSections() {
return (
Country
United States
Canada
Mexico
United Kingdom
France
Germany
Spain
Italy
Japan
China
India
South Korea
);
}
```
### With Disabled Options
```tsx
import {Label, ListBox, Select} from "@heroui/react";
export function WithDisabledOptions() {
return (
Animal
Dog
Cat
Bird
Kangaroo
Elephant
Tiger
);
}
```
### Custom Indicator
```tsx
import {ChevronsExpandVertical} from "@gravity-ui/icons";
import {Label, ListBox, Select} from "@heroui/react";
export function CustomIndicator() {
return (
State
Florida
Delaware
California
Texas
New York
Washington
);
}
```
### Required
```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("Form submitted successfully!");
};
return (
State
Florida
Delaware
California
Texas
New York
Washington
Country
United States
Canada
Mexico
United Kingdom
France
Germany
Submit
);
}
```
### Full Width
```tsx
import {Label, ListBox, Select} from "@heroui/react";
export function FullWidth() {
return (
Favorite Animal
Cat
Dog
Bird
);
}
```
### Variants
The Select component supports two visual variants:
* **`primary`** (default) - Standard styling with shadow, suitable for most use cases
* **`secondary`** - Lower emphasis variant without shadow, suitable for use in Surface components
```tsx
import {Label, ListBox, Select} from "@heroui/react";
export function Variants() {
return (
Primary variant
Option 1
Option 2
Secondary variant
Option 1
Option 2
);
}
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` to apply the lower emphasis variant suitable for surface backgrounds.
```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("Form submitted successfully!");
};
return (
State
Florida
Delaware
California
Texas
New York
Washington
Country
United States
Canada
Mexico
United Kingdom
France
Germany
Submit
);
}
```
### Custom Value
```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 (
User
{({defaultChildren, isPlaceholder, state}) => {
if (isPlaceholder || state.selectedItems.length === 0) {
return defaultChildren;
}
const selectedItems = state.selectedItems;
if (selectedItems.length > 1) {
return `${selectedItems.length} users selected`;
}
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}
))}
);
}
```
### Controlled
```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: "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 selectedState = states.find((s) => s.id === state);
return (
setState(value)}
>
State (controlled)
{states.map((state) => (
{state.name}
))}
Selected: {selectedState?.name || "None"}
);
}
```
### Controlled Multiple
```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[])}
>
States (controlled multiple)
California
Texas
Florida
New York
Illinois
Pennsylvania
Selected: {selected.length > 0 ? selected.join(", ") : "None"}
);
}
```
### Controlled Open State
```tsx
"use client";
import {Button, Label, ListBox, Select} from "@heroui/react";
import {useState} from "react";
export function ControlledOpenState() {
const [isOpen, setIsOpen] = useState(false);
return (
State
Florida
Delaware
California
Texas
New York
Washington
setIsOpen(!isOpen)}>{isOpen ? "Close" : "Open"} Select
Select is {isOpen ? "open" : "closed"}
);
}
```
### Asynchronous Loading
```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 (
Pick a Pokemon
{(item: Pokemon) => (
{item.name}
)}
Loading more...
);
}
```
### Disabled
```tsx
import {Label, ListBox, Select} from "@heroui/react";
export function Disabled() {
return (
State
Florida
Delaware
California
Texas
New York
Washington
Countries to Visit
Argentina
Venezuela
Japan
France
Italy
Spain
);
}
```
## Related Components
* **Listbox**: Scrollable list of selectable items
* **Popover**: Displays content in context with a trigger
* **Label**: Accessible label for form controls
### Custom Render Function
```tsx
"use client";
import {Label, ListBox, Select} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
State
Florida
Delaware
California
Texas
New York
Washington
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import {Select} from "@heroui/react";
function CustomSelect() {
return (
State
Item 1
);
}
```
### Customizing the component classes
To customize the Select component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Select component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/select.css)):
#### Base Classes
* `.select` - Base select container
* `.select__trigger` - The button that triggers the select
* `.select__value` - The displayed value or placeholder
* `.select__indicator` - The dropdown indicator icon
* `.select__popover` - The popover container
#### Variant Classes
* `.select--primary` - Primary variant with shadow (default)
* `.select--secondary` - Secondary variant without shadow, suitable for use in surfaces
#### State Classes
* `.select[data-invalid="true"]` - Invalid state
* `.select__trigger[data-focus-visible="true"]` - Focused trigger state
* `.select__trigger[data-disabled="true"]` - Disabled trigger state
* `.select__value[data-placeholder="true"]` - Placeholder state
* `.select__indicator[data-open="true"]` - Open indicator state
### Interactive States
The component supports both CSS pseudo-classes and data attributes for flexibility:
* **Hover**: `:hover` or `[data-hovered="true"]` on trigger
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]` on trigger
* **Disabled**: `:disabled` or `[data-disabled="true"]` on select
* **Open**: `[data-open="true"]` on indicator
## API Reference
### Select Props
| Prop | Type | Default | Description |
| --------------- | ------------------------------------------------------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `placeholder` | `string` | `'Select an item'` | Temporary text that occupies the select when it is empty |
| `selectionMode` | `"single" \| "multiple"` | `"single"` | Whether single or multiple selection is enabled |
| `isOpen` | `boolean` | - | Sets the open state of the menu (controlled) |
| `defaultOpen` | `boolean` | - | Sets the default open state of the menu (uncontrolled) |
| `onOpenChange` | `(isOpen: boolean) => void` | - | Handler called when the open state changes |
| `disabledKeys` | `Iterable` | - | Keys of disabled items |
| `isDisabled` | `boolean` | - | Whether the select is disabled |
| `value` | `Key \| Key[] \| null` | - | Current value (controlled) |
| `defaultValue` | `Key \| Key[] \| null` | - | Default value (uncontrolled) |
| `onChange` | `(value: Key \| Key[] \| null) => void` | - | Handler called when the value changes |
| `isRequired` | `boolean` | - | Whether user input is required |
| `isInvalid` | `boolean` | - | Whether the select value is invalid |
| `name` | `string` | - | The name of the input, used when submitting an HTML form |
| `autoComplete` | `string` | - | Describes the type of autocomplete functionality |
| `fullWidth` | `boolean` | `false` | Whether the select should take full width of its container |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode \| RenderFunction` | - | Select content or render function |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Select.Trigger Props
| Prop | Type | Default | Description |
| ----------- | ----------------------------- | ------- | ---------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode \| RenderFunction` | - | Trigger content or render function |
### Select.Value Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------------------------ | ------- | ---------------------------------------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode \| RenderFunction` | - | Value content or render function |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Select.Indicator Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ------------------------ |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Custom indicator content |
### Select.Popover Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ------------------------------------------------ |
| `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"` | Placement of the popover relative to the trigger |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Content children |
### RenderProps
When using render functions with Select.Value, these values are provided:
| Prop | Type | Description |
| ----------------- | ------------- | ---------------------------------- |
| `defaultChildren` | `ReactNode` | The default rendered value |
| `isPlaceholder` | `boolean` | Whether the value is a placeholder |
| `state` | `SelectState` | The state of the select |
| `selectedItems` | `Node[]` | The currently selected items |
## Accessibility
The Select component implements the ARIA listbox pattern and provides:
* Full keyboard navigation support
* Screen reader announcements for selection changes
* Proper focus management
* Support for disabled states
* Typeahead search functionality
* HTML form integration
For more information, see the [React Aria Select documentation](https://react-spectrum.adobe.com/react-aria/Select.html).
# Accordion
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/accordion
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(navigation)/accordion.mdx
> A collapsible content panel for organizing information in a compact space
## Import
```tsx
import { Accordion } from '@heroui/react';
```
### Usage
```tsx
import {
ArrowsRotateLeft,
Box,
ChevronDown,
CreditCard,
PlanetEarth,
Receipt,
ShoppingBag,
} from "@gravity-ui/icons";
import {Accordion} from "@heroui/react";
const items = [
{
content:
"Browse our products, add items to your cart, and proceed to checkout. You'll need to provide shipping and payment information to complete your purchase.",
icon: ,
title: "How do I place an order?",
},
{
content:
"Yes, you can modify or cancel your order before it's shipped. Once your order is processed, you can't make changes.",
icon: ,
title: "Can I modify or cancel my order?",
},
{
content: "We accept all major credit cards, including Visa, Mastercard, and American Express.",
icon: ,
title: "What payment methods do you accept?",
},
{
content:
"Shipping costs vary based on your location and the size of your order. We offer free shipping for orders over $50.",
icon: ,
title: "How much does shipping cost?",
},
{
content:
"Yes, we ship to most countries. Please check our shipping rates and policies for more information.",
icon: ,
title: "Do you ship internationally?",
},
{
content:
"If you're not satisfied with your purchase, you can request a refund within 30 days of purchase. Please contact our customer support team for assistance.",
icon: ,
title: "How do I request a refund?",
},
];
export function Basic() {
return (
{items.map((item, index) => (
{item.icon ? (
{item.icon}
) : null}
{item.title}
{item.content}
))}
);
}
```
### Anatomy
Import the Accordion component and access all parts using dot notation.
```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:
"Browse our products, add items to your cart, and proceed to checkout. You'll need to provide shipping and payment information to complete your purchase.",
icon: ,
title: "How do I place an order?",
},
{
content:
"Yes, you can modify or cancel your order before it's shipped. Once your order is processed, you can't make changes.",
icon: ,
title: "Can I modify or cancel my order?",
},
{
content: "We accept all major credit cards, including Visa, Mastercard, and American Express.",
icon: ,
title: "What payment methods do you accept?",
},
{
content:
"Shipping costs vary based on your location and the size of your order. We offer free shipping for orders over $50.",
icon: ,
title: "How much does shipping cost?",
},
{
content:
"Yes, we ship to most countries. Please check our shipping rates and policies for more information.",
icon: ,
title: "Do you ship internationally?",
},
{
content:
"If you're not satisfied with your purchase, you can request a refund within 30 days of purchase. Please contact our customer support team for assistance.",
icon: ,
title: "How do I request a refund?",
},
];
export function Surface() {
return (
{items.map((item, index) => (
{item.icon ? (
{item.icon}
) : null}
{item.title}
{item.content}
))}
);
}
```
### Multiple Expanded
```tsx
import {Accordion} from "@heroui/react";
export function Multiple() {
return (
Getting Started
Learn the basics of HeroUI and how to integrate it into your React project. This section
covers installation, setup, and your first component.
Core Concepts
Understand the fundamental concepts behind HeroUI, including the compound component
pattern, styling with Tailwind CSS, and accessibility features.
Advanced Usage
Explore advanced features like custom variants, theme customization, and integration
with other libraries in your React ecosystem.
Best Practices
Follow our recommended best practices for building performant, accessible, and
maintainable applications with HeroUI components.
);
}
```
### Controlled
```tsx
"use client";
import {ChevronDown, ChevronUp} from "@gravity-ui/icons";
import {Accordion, Button, useDisclosureGroupNavigation} from "@heroui/react";
import React from "react";
const items = [
{
content:
"Learn the basics of HeroUI and how to integrate it into your React project. This section covers installation, setup, and your first component.",
id: "getting-started",
title: "Getting Started",
},
{
content:
"Understand the fundamental concepts behind HeroUI, including the compound component pattern, styling with Tailwind CSS, and accessibility features.",
id: "core-concepts",
title: "Core Concepts",
},
{
content:
"Explore advanced features like custom variants, theme customization, and integration with other libraries in your React ecosystem.",
id: "advanced-usage",
title: "Advanced Usage",
},
];
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 (
Expanded: {[...expandedKeys].join(", ") || "none"}
{items.map((item) => (
{item.title}
{item.content}
))}
);
}
```
### Custom Indicator
```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 (
Using Plus/Minus Icon
{expandedKeys.has("1") ? : }
This accordion uses a plus icon that transforms when expanded. The icon automatically
rotates 45 degrees to form an X.
Using Caret Icon
This item uses a caret icon for the indicator. The rotation animation is applied
automatically.
Using Arrow Icon
This item uses an arrow icon. Any icon you pass will receive the rotation animation when
the item expands.
);
}
```
### Disabled State
```tsx
import {Accordion} from "@heroui/react";
export function Disabled() {
return (
Entire accordion disabled
Disabled Item 1
This content cannot be accessed when the accordion is disabled.
Disabled Item 2
This content cannot be accessed when the accordion is disabled.
Individual items disabled
Active Item
This item is active and can be toggled normally.
Disabled Item
This content cannot be accessed when the item is disabled.
Another Active Item
This item is also active and can be toggled.
);
}
```
### FAQ Layout
```tsx
import {ChevronDown} from "@gravity-ui/icons";
import {Accordion} from "@heroui/react";
export function FAQ() {
const categories = [
{
items: [
{
content:
"Browse our products, add items to your cart, and proceed to checkout. You'll need to provide shipping and payment information to complete your purchase.",
title: "How do I place an order?",
},
{
content:
"Yes, you can modify or cancel your order before it's shipped. Once your order is processed, you can't make changes.",
title: "Can I modify or cancel my order?",
},
],
title: "General",
},
{
items: [
{
content:
"You can purchase a license directly from our website. Select the license type that fits your needs and proceed to checkout.",
title: "How do I purchase a license?",
},
{
content:
"A standard license is for personal use or small projects, while a pro license includes commercial use rights and priority support.",
title: "What is the difference between a standard and a pro license?",
},
],
title: "Licensing",
},
{
items: [
{
content:
"You can reach our support team through the contact form on our website, or email us directly at support@example.com.",
title: "How do I get support?",
},
],
title: "Support",
},
];
return (
Frequently Asked Questions
Everything you need to know about licensing and usage.
{categories.map((category) => (
{category.title}
{category.items.map((item, index) => (
{item.title}
{item.content}
))}
))}
);
}
```
### Custom Styles
```tsx
import {ChevronDown} from "@gravity-ui/icons";
import {Accordion, cn} from "@heroui/react";
const items = [
{
content: "Stay informed about your account activity with real-time notifications. ",
iconUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/bell-small.png",
subtitle: "Receive account activity updates",
title: "Set Up Notifications",
},
{
content: "Enhance your browsing experience by installing our official browser extension",
iconUrl: "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/compass-small.png",
subtitle: "Connect you browser to your account",
title: "Set up Browser Extension",
},
{
content:
"Begin your journey into the world of digital collectibles by creating your first NFT. ",
iconUrl:
"https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/3dicons/mint-collective-small.png",
subtitle: "Create your first collectible",
title: "Mint Collectible",
},
];
export function CustomStyles() {
return (
{items.map((item, index) => (
{item.iconUrl ? (
) : null}
{item.title}
{item.subtitle}
{item.content}
))}
);
}
```
### Without Separator
```tsx
import {ChevronDown, CreditCard, Receipt, ShoppingBag} from "@gravity-ui/icons";
import {Accordion} from "@heroui/react";
const items = [
{
content:
"Browse our products, add items to your cart, and proceed to checkout. You'll need to provide shipping and payment information to complete your purchase.",
icon: ,
title: "How do I place an order?",
},
{
content:
"Yes, you can modify or cancel your order before it's shipped. Once your order is processed, you can't make changes.",
icon: ,
title: "Can I modify or cancel my order?",
},
{
content: "We accept all major credit cards, including Visa, Mastercard, and American Express.",
icon: ,
title: "What payment methods do you accept?",
},
];
export function WithoutSeparator() {
return (
{items.map((item, index) => (
{item.icon ? (
{item.icon}
) : null}
{item.title}
{item.content}
))}
);
}
```
### Custom Render Function
```tsx
"use client";
import {
ArrowsRotateLeft,
Box,
ChevronDown,
CreditCard,
PlanetEarth,
Receipt,
ShoppingBag,
} from "@gravity-ui/icons";
import {Accordion} from "@heroui/react";
const items = [
{
content:
"Browse our products, add items to your cart, and proceed to checkout. You'll need to provide shipping and payment information to complete your purchase.",
icon: ,
title: "How do I place an order?",
},
{
content:
"Yes, you can modify or cancel your order before it's shipped. Once your order is processed, you can't make changes.",
icon: ,
title: "Can I modify or cancel my order?",
},
{
content: "We accept all major credit cards, including Visa, Mastercard, and American Express.",
icon: ,
title: "What payment methods do you accept?",
},
{
content:
"Shipping costs vary based on your location and the size of your order. We offer free shipping for orders over $50.",
icon: ,
title: "How much does shipping cost?",
},
{
content:
"Yes, we ship to most countries. Please check our shipping rates and policies for more information.",
icon: ,
title: "Do you ship internationally?",
},
{
content:
"If you're not satisfied with your purchase, you can request a refund within 30 days of purchase. Please contact our customer support team for assistance.",
icon: ,
title: "How do I request a refund?",
},
];
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
## Styling
### Passing Tailwind CSS classes
```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}
))}
);
}
```
### Customizing the component classes
To customize the Accordion component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Accordion component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/accordion.css)):
#### Base Classes
* `.accordion` - Base accordion container
* `.accordion__body` - Content body container
* `.accordion__heading` - Heading wrapper
* `.accordion__indicator` - Expand/collapse indicator icon
* `.accordion__item` - Individual accordion item
* `.accordion__panel` - Collapsible panel container
* `.accordion__trigger` - Clickable trigger button
#### Variant Classes
* `.accordion--outline` - Outline variant with border and background
#### State Classes
* `.accordion__trigger[aria-expanded="true"]` - Expanded state
* `.accordion__panel[aria-hidden="false"]` - Panel visible state
### Interactive States
The component supports both CSS pseudo-classes and data attributes for flexibility:
* **Hover**: `:hover` or `[data-hovered="true"]` on trigger
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]` on trigger
* **Disabled**: `:disabled` or `[aria-disabled="true"]` on trigger
* **Expanded**: `[aria-expanded="true"]` on trigger
## API Reference
### Accordion Props
| Prop | Type | Default | Description |
| ------------------------ | ---------------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------- |
| `allowsMultipleExpanded` | `boolean` | `false` | Whether multiple items can be expanded at once |
| `defaultExpandedKeys` | `Iterable` | - | The initial expanded keys |
| `expandedKeys` | `Iterable` | - | The controlled expanded keys |
| `onExpandedChange` | `(keys: Set) => void` | - | Handler called when expanded keys change |
| `isDisabled` | `boolean` | `false` | Whether the entire accordion is disabled |
| `variant` | `"default" \| "surface"` | `"default"` | The visual variant of the accordion |
| `hideSeparator` | `boolean` | `false` | Hide separator lines between accordion items |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | The accordion items |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Accordion.Item Props
| Prop | Type | Default | Description |
| ------------------ | -------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- |
| `id` | `Key` | - | Unique identifier for the item |
| `isDisabled` | `boolean` | `false` | Whether this item is disabled |
| `defaultExpanded` | `boolean` | `false` | Whether item is initially expanded |
| `isExpanded` | `boolean` | - | Controlled expanded state |
| `onExpandedChange` | `(isExpanded: boolean) => void` | - | Handler for expanded state changes |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | The item content |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Accordion.Trigger Props
| Prop | Type | Default | Description |
| ------------ | -------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode \| RenderFunction` | - | Trigger content or render function |
| `onPress` | `() => void` | - | Additional press handler |
| `isDisabled` | `boolean` | - | Whether trigger is disabled |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Accordion.Panel Props
| Prop | Type | Default | Description |
| ----------- | --------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Panel content |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Accordion.Indicator Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Custom indicator icon |
### Accordion.Body Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Body content |
# Breadcrumbs
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/breadcrumbs
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(navigation)/breadcrumbs.mdx
> Navigation breadcrumbs showing the current page's location within a hierarchy
## Import
```tsx
import { Breadcrumbs } from '@heroui/react';
```
### Usage
```tsx
"use client";
import {Breadcrumbs} from "@heroui/react";
export default function BreadcrumbsBasic() {
return (
Home
Products
Electronics
Laptop
);
}
```
### Anatomy
Import the Breadcrumbs component and access all parts using dot notation.
```tsx
import { Breadcrumbs } from '@heroui/react';
export default () => (
Home
Category
Current Page
)
```
### Navigation Levels
```tsx
"use client";
import {Breadcrumbs} from "@heroui/react";
export default function BreadcrumbsLevel2() {
return (
Home
Current Page
);
}
```
```tsx
"use client";
import {Breadcrumbs} from "@heroui/react";
export default function BreadcrumbsLevel3() {
return (
Home
Category
Current Page
);
}
```
### Custom Separator
```tsx
"use client";
import {Breadcrumbs} from "@heroui/react";
export default function BreadcrumbsCustomSeparator() {
return (
}
>
Home
Products
Electronics
Laptop
);
}
```
### Disabled State
```tsx
"use client";
import {Breadcrumbs} from "@heroui/react";
export default function BreadcrumbsDisabled() {
return (
Home
Products
Electronics
Laptop
);
}
```
### Custom Render Function
```tsx
"use client";
import {Breadcrumbs} from "@heroui/react";
export function CustomRenderFunction() {
return (
}>
}>
Home
}>
Products
}>
Electronics
}>
Laptop
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import { Breadcrumbs } from '@heroui/react';
function CustomBreadcrumbs() {
return (
Home
Current
);
}
```
### Customizing the component classes
To customize the Breadcrumbs component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Breadcrumbs component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/breadcrumbs.css)):
#### Base Classes
* `.breadcrumbs` - Base breadcrumbs container
* `.breadcrumbs__item` - Individual breadcrumb item wrapper
* `.breadcrumbs__link` - Breadcrumb link element
* `.breadcrumbs__separator` - Separator icon between items
#### State Classes
* `.breadcrumbs__link[data-current="true"]` - Current page indicator (not a link)
### Interactive States
The component supports both CSS pseudo-classes and data attributes for flexibility:
* **Current**: `[data-current="true"]` on link (indicates current page)
* **Hover**: Link elements support standard hover states
* **Disabled**: `isDisabled` prop disables all links
## API Reference
### Breadcrumbs Props
| Prop | Type | Default | Description |
| ------------ | ----------------------------------------------------------------- | ------------------ | --------------------------------------------------------------- |
| `separator` | `ReactNode` | chevron-right icon | Custom separator between breadcrumb items |
| `isDisabled` | `boolean` | `false` | Whether all breadcrumb links are disabled |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | The breadcrumb items |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function |
### Breadcrumbs.Item Props
| Prop | Type | Default | Description |
| ----------- | ----------------------------------------------------------------------------- | ------- | --------------------------------------------------------------- |
| `href` | `string` | - | The URL to link to (omit for current page) |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode \| RenderFunction` | - | Item content or render function |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function |
## Accessibility
Breadcrumbs uses React Aria Components' Breadcrumbs primitive, which provides:
* Proper ARIA attributes for navigation landmarks
* Current page indication via `aria-current="page"`
* Keyboard navigation support
* Screen reader announcements for navigation context
The last breadcrumb item (without `href`) automatically becomes the current page indicator.
# DisclosureGroup
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/disclosure-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(navigation)/disclosure-group.mdx
> Container that manages multiple Disclosure items with coordinated expanded states
## Import
```tsx
import { DisclosureGroup } from '@heroui/react';
```
### Usage
```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 (
Preview HeroUI Native
Scan this QR code with your camera app to preview the HeroUI native components.
Expo must be installed on your device.
Preview on Expo Go
Download App
Download the HeroUI native app to explore our mobile components directly on your
device.
Available on iOS and Android devices.
Download on App Store
);
}
```
### Anatomy
Import all parts and piece them together.
```tsx
import {DisclosureGroup, Disclosure} from '@heroui/react';
export default () => (
)
```
### Controlled
You can control which disclosures are expanded with external navigation controls using the `expandedKeys` and `onExpandedChange` props.
```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 (
Preview HeroUI Native
Scan this QR code with your camera app to preview the HeroUI native components.
Expo must be installed on your device.
Preview on Expo Go
Download HeroUI Native
Scan this QR code with your camera app to preview the HeroUI native components.
Expo must be installed on your device.
Download on App Store
);
}
```
## Related Components
* **Accordion**: Collapsible content sections
* **Disclosure**: Single collapsible content section
* **Button**: Allows a user to perform an action
## Styling
### Passing Tailwind CSS classes
```tsx
import {
DisclosureGroup,
Disclosure,
DisclosureTrigger,
DisclosurePanel
} from '@heroui/react';
function CustomDisclosureGroup() {
return (
Item 1
Content 1
Item 2
Content 2
);
}
```
### Customizing the component classes
To customize the DisclosureGroup component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.disclosure-group {
@apply w-full;
/* Performance optimization */
contain: layout style;
}
}
```
HeroUI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The DisclosureGroup component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/disclosure-group.css)):
#### Base Classes
* `.disclosure-group` - Base container styles with layout containment
### Interactive States
The component supports both CSS pseudo-classes and data attributes for flexibility:
* **Disabled**: `:disabled` or `[aria-disabled="true"]` on entire group
* **Expanded Management**: Automatically manages `[data-expanded]` states on child Disclosure items
## API Reference
### DisclosureGroup Props
| Prop | Type | Default | Description |
| ------------------------ | ----------------------------- | ------- | ----------------------------------------------------- |
| `expandedKeys` | `Set` | - | The currently expanded items (controlled) |
| `defaultExpandedKeys` | `Iterable` | - | The initially expanded items (uncontrolled) |
| `onExpandedChange` | `(keys: Set) => void` | - | Handler called when expanded items change |
| `allowsMultipleExpanded` | `boolean` | `false` | Whether multiple items can be expanded simultaneously |
| `isDisabled` | `boolean` | `false` | Whether all disclosures in the group are disabled |
| `children` | `ReactNode \| RenderFunction` | - | Disclosure items to render |
| `className` | `string` | - | Additional CSS classes |
### RenderProps
When using the render prop pattern, these values are provided:
| Prop | Type | Description |
| -------------- | ---------- | ----------------------------- |
| `expandedKeys` | `Set` | Currently expanded item keys |
| `isDisabled` | `boolean` | Whether the group is disabled |
# Disclosure
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/disclosure
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(navigation)/disclosure.mdx
> A disclosure is a collapsible section with a header containing a heading and a trigger button, and a panel that wraps the content.
## Import
```tsx
import { Disclosure } from '@heroui/react';
```
### Usage
```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 (
Preview HeroUI Native
Scan this QR code with your camera app to preview the HeroUI native components.
Expo must be installed on your device.
Download on App Store
);
}
```
### Anatomy
Import the Disclosure component and access all parts using dot notation.
```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
### Custom Render Function
```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}
>
Preview HeroUI Native
}>
Scan this QR code with your camera app to preview the HeroUI native components.
Expo must be installed on your device.
Download on App Store
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import { Disclosure } from '@heroui/react';
function CustomDisclosure() {
return (
Click to expand
Hidden content
);
}
```
### Customizing the component classes
To customize the Disclosure component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Disclosure component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/disclosure.css)):
#### Base Classes
* `.disclosure` - Base container styles
* `.disclosure__heading` - Heading wrapper
* `.disclosure__trigger` - Trigger button styles
* `.disclosure__indicator` - Chevron indicator styles
* `.disclosure__content` - Content container with animations
### Interactive States
The component supports both CSS pseudo-classes and data attributes for flexibility:
* **Expanded**: `[data-expanded="true"]` on indicator for rotation
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]` on trigger
* **Disabled**: `:disabled` or `[aria-disabled="true"]` on trigger
* **Hidden**: `[aria-hidden="false"]` on content for visibility
## API Reference
### Disclosure Props
| Prop | Type | Default | Description |
| ------------------ | ----------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- |
| `isExpanded` | `boolean` | `false` | Controls the expanded state |
| `onExpandedChange` | `(isExpanded: boolean) => void` | - | Callback when expanded state changes |
| `isDisabled` | `boolean` | `false` | Whether the disclosure is disabled |
| `children` | `ReactNode \| RenderFunction` | - | Content to render |
| `className` | `string` | - | Additional CSS classes |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### DisclosureTrigger Props
| Prop | Type | Default | Description |
| ----------- | ----------------------------- | ------- | ---------------------- |
| `children` | `ReactNode \| RenderFunction` | - | Trigger content |
| `className` | `string` | - | Additional CSS classes |
### DisclosureContent Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------------------------------ | ------- | ---------------------------------------------------------------- |
| `children` | `ReactNode` | - | Content to show/hide |
| `className` | `string` | - | Additional CSS classes |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### RenderProps
When using the render prop pattern, these values are provided:
| Prop | Type | Description |
| ------------ | --------- | ------------------------------ |
| `isExpanded` | `boolean` | Current expanded state |
| `isDisabled` | `boolean` | Whether disclosure is disabled |
# Link
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/link
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(navigation)/link.mdx
> A styled anchor component for navigation with built-in icon support
## Import
```tsx
import { Link } from '@heroui/react';
```
### Usage
```tsx
import {Link} from "@heroui/react";
export function LinkBasic() {
return (
Call to action
);
}
```
### Anatomy
Import the Link component and access all parts using dot notation.
```tsx
import { Link } from '@heroui/react';
export default () => (
Call to action
);
```
### Custom Icon
```tsx
import {ArrowUpRightFromSquare, Link as LinkIcon} from "@gravity-ui/icons";
import {Link} from "@heroui/react";
export function LinkCustomIcon() {
return (
);
}
```
### Icon Placement
```tsx
import {Link} from "@heroui/react";
export function LinkIconPlacement() {
return (
Icon at end (default)
Icon at start
);
}
```
### Text Decoration with Tailwind CSS
Link is underlined on hover by default. Use Tailwind CSS text-decoration utilities to make the underline always visible, remove it entirely, or customize its color, style, thickness, and offset.
```tsx
import {Link} from "@heroui/react";
export function LinkUnderlineAndOffset() {
return (
Default hover underline
Hover to see the underline
Always visible underline
Underline always visible
No underline
Link without any underline
Changing the underline offset
Offset 1 (1px space)
Offset 2 (2px space)
Offset 3 (3px space)
Offset 4 (4px space)
);
}
```
**Text Decoration Line:**
* `underline` - Always visible underline
* `no-underline` - Remove underline
* default `Link` styles - Underline appears on hover
**Text Decoration Color:**
* `decoration-primary`, `decoration-secondary`, etc. - Set underline color using theme colors
* `decoration-muted/50` - Use opacity modifiers for semi-transparent underlines
**Text Decoration Style:**
* `decoration-solid` - Solid line (default)
* `decoration-double` - Double line
* `decoration-dotted` - Dotted line
* `decoration-dashed` - Dashed line
* `decoration-wavy` - Wavy line
**Text Decoration Thickness:**
* `decoration-1`, `decoration-2`, `decoration-4`, etc. - Control underline thickness
**Underline Offset:**
* `underline-offset-1`, `underline-offset-2`, `underline-offset-4`, etc. - Adjust spacing between text and underline
For more details, see the Tailwind CSS documentation:
* [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)
Available BEM classes:
* Base: `link`
* Icon: `link__icon`
## Related Components
* **Breadcrumbs**: Display the user's current location within a hierarchy
### Custom Render Function
```tsx
"use client";
import {Link} from "@heroui/react";
export function CustomRenderFunction() {
return (
}>
Call to action
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import { Link } from '@heroui/react';
function CustomLink() {
return (
Custom styled link
);
}
```
### Customizing the component classes
To customize the Link component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).
```css
@layer components {
.link {
@apply font-semibold;
}
}
```
HeroUI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Link component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/link.css)):
#### Base Classes
* `.link` - Base link styles
* `.link__icon` - Link icon styles
### Interactive States
The component supports both CSS pseudo-classes and data attributes for flexibility:
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]`
* **Hover**: `:hover` or `[data-hovered="true"]`
* **Pressed**: `:active` or `[data-pressed="true"]`
* **Disabled**: `:disabled` or `[aria-disabled="true"]`
## API Reference
### Link Props
| Prop | Type | Default | Description |
| ------------ | ----------------------------------------------------------------------- | --------- | ---------------------------------------------------------------- |
| `href` | `string` | - | Destination URL for the anchor |
| `target` | `string` | `"_self"` | Controls where to open the linked document |
| `rel` | `string` | - | Relationship between the current and linked documents |
| `download` | `boolean \| string` | - | Prompts file download instead of navigation |
| `isDisabled` | `boolean` | `false` | Disables pointer and keyboard interaction |
| `className` | `string` | - | Custom classes merged with the default styles |
| `children` | `React.ReactNode` | - | Content rendered inside the link |
| `onPress` | `(e: PressEvent) => void` | - | Fired when the link is activated |
| `autoFocus` | `boolean` | - | Whether the element should receive focus on render |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Link.Icon Props
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | --------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom icon element; defaults to the built-in arrow icon when omitted |
| `className` | `string` | - | Additional CSS classes |
### Using with Routing Libraries
Use variant functions to style framework-specific links like 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
);
}
```
### Direct Class Application
Since HeroUI uses [BEM](https://getbem.com/) classes, you can apply Link styles directly to any link element:
```tsx
import NextLink from 'next/link';
// Apply classes directly with Tailwind utilities
export default function Demo() {
return (
About Page
);
}
// Or with a native anchor
export default function NativeLink() {
return (
About Page
);
}
```
# Pagination
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/pagination
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(navigation)/pagination.mdx
> Page navigation with composable page links, previous/next buttons, and ellipsis indicators
## Import
```tsx
import { Pagination } from '@heroui/react';
```
### Usage
```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)}>
Previous
{Array.from({length: totalPages}, (_, i) => i + 1).map((p) => (
setPage(p)}>
{p}
))}
setPage((p) => p + 1)}>
Next
);
}
```
### Anatomy
Import the Pagination component and access all parts using dot notation.
```tsx
import { Pagination } from '@heroui/react';
export default () => (
Showing 1-10 of 100 results
Previous
1
10
Next
);
```
### Sizes
```tsx
"use client";
import {Pagination} from "@heroui/react";
import {useState} from "react";
function SizePagination({size}: {size: "sm" | "md" | "lg"}) {
const [page, setPage] = useState(1);
const totalPages = 3;
return (
{size}
setPage((p) => p - 1)}>
Previous
{Array.from({length: totalPages}, (_, i) => i + 1).map((p) => (
setPage(p)}>
{p}
))}
setPage((p) => p + 1)}>
Next
);
}
export function PaginationSizes() {
return (
{(["sm", "md", "lg"] as const).map((size) => (
))}
);
}
```
### With 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)}>
Previous
{getPageNumbers().map((p, i) =>
p === "ellipsis" ? (
) : (
setPage(p)}>
{p}
),
)}
setPage((p) => p + 1)}>
Next
);
}
```
### Simple (Previous / Next)
```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} to {endItem} of {totalItems} invoices
setPage((p) => p - 1)}>
Prev
setPage((p) => p + 1)}>
Next
);
}
```
### With Summary
```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 (
Showing {startItem}-{endItem} of {totalItems} results
setPage((p) => p - 1)}>
Previous
{getPageNumbers().map((p, i) =>
p === "ellipsis" ? (
) : (
setPage(p)}>
{p}
),
)}
setPage((p) => p + 1)}>
Next
);
}
```
### Custom Icons
You can replace the default chevron icons by passing custom children to `PreviousIcon` and `NextIcon`.
```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)}>
Back
{Array.from({length: totalPages}, (_, i) => i + 1).map((p) => (
setPage(p)}>
{p}
))}
setPage((p) => p + 1)}>
Forward
);
}
```
### Controlled
```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 (
Showing {startItem}-{endItem} of {totalItems} results
setPage((p) => p - 1)}>
Previous
{getPageNumbers().map((p, i) =>
p === "ellipsis" ? (
) : (
setPage(p)}>
{p}
),
)}
setPage((p) => p + 1)}>
Next
);
}
```
### Disabled
```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)}>
Previous
{Array.from({length: totalPages}, (_, i) => i + 1).map((p) => (
setPage(p)}>
{p}
))}
setPage((p) => p + 1)}>
Next
);
}
```
## Related Components
* **Button**: Allows a user to perform an action
* **Link**: Styled anchor links
## Styling
### Passing Tailwind CSS classes
You can customize individual Pagination parts:
```tsx
import { Pagination } from '@heroui/react';
function CustomPagination() {
return (
1
);
}
```
### Customizing the component classes
To customize the Pagination component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Pagination component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/pagination.css)):
#### Base & Layout Classes
* `.pagination` - Root navigation container with flex layout
* `.pagination__summary` - Left-side info text container
* `.pagination__content` - Container for pagination items
* `.pagination__item` - Individual item wrapper
* `.pagination__link` - Page number button (ghost button style)
* `.pagination__link--nav` - Navigation button modifier (Previous/Next)
* `.pagination__ellipsis` - Ellipsis indicator
#### Size Classes
* `.pagination--sm` - Small size variant
* `.pagination--md` - Medium size variant (default)
* `.pagination--lg` - Large size variant
### Interactive States
The component supports both CSS pseudo-classes and data attributes for flexibility:
* **Active page**: `[data-active="true"]` or `[aria-current="page"]`
* **Hover**: `:hover` or `[data-hovered="true"]`
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]`
* **Disabled**: `:disabled` or `[aria-disabled="true"]`
* **Pressed**: `:active` or `[data-pressed="true"]`
## API Reference
### Pagination Props
| Prop | Type | Default | Description |
| ----------- | ---------------------- | ------- | ----------------------------------- |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Size of the pagination items |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Pagination parts (Summary, Content) |
### Pagination.Summary Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | --------------------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Summary content (e.g., "Showing 1-10 of 120") |
### Pagination.Content Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Pagination items |
### Pagination.Item Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ------------------------------------------------ |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Item content (Link, Previous, Next, or Ellipsis) |
### Pagination.Link Props
| Prop | Type | Default | Description |
| ------------ | ------------------------- | ------- | -------------------------------- |
| `isActive` | `boolean` | `false` | Whether this is the current page |
| `isDisabled` | `boolean` | `false` | Whether the link is disabled |
| `onPress` | `(e: PressEvent) => void` | - | Press handler (from React Aria) |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Page number content |
### Pagination.Previous / Pagination.Next Props
| Prop | Type | Default | Description |
| ------------ | ------------------------- | ------- | --------------------------------------------------- |
| `isDisabled` | `boolean` | `false` | Whether the button is disabled |
| `onPress` | `(e: PressEvent) => void` | - | Press handler (from React Aria) |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | - | Button content (compose with PreviousIcon/NextIcon) |
### Pagination.PreviousIcon / Pagination.NextIcon Props
| Prop | Type | Default | Description |
| ----------- | ----------- | ------------------- | ------------------------------------------ |
| `className` | `string` | - | Additional CSS classes |
| `children` | `ReactNode` | Default chevron SVG | Custom icon to replace the default chevron |
### Pagination.Ellipsis Props
| Prop | Type | Default | Description |
| ----------- | -------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
## Accessibility
The Pagination component is built on [React Aria's Button](https://react-spectrum.adobe.com/react-aria/Button.html) primitive for all interactive elements, providing:
* Semantic `` element with `aria-label="pagination"` and `role="navigation"`
* Active page indicated via `aria-current="page"` on the current link
* Keyboard navigation via Tab key through all interactive elements
* Press events handled across mouse, touch, and keyboard interactions via React Aria
* Focus ring on keyboard navigation via `:focus-visible`
* Ellipsis marked with `aria-hidden="true"` to avoid screen reader confusion
* Disabled states properly communicated to assistive technology via `isDisabled`
> **Note:** Pagination buttons use `onPress` instead of `onClick`. The `onPress` handler from React Aria normalizes press behavior across pointer types and provides accessibility improvements out of the box.
# Tabs
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/tabs
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(navigation)/tabs.mdx
> Tabs organize content into multiple sections and allow users to navigate between them.
## Import
```tsx
import { Tabs } from '@heroui/react';
```
### Usage
### Anatomy
Import the Tabs component and access all parts using dot notation.
```tsx
import { Tabs } from '@heroui/react';
export default () => (
{/* Optional */}
)
```
### Vertical
### Disabled Tab
### With Separator
Add ` ` inside each `` (except the first) to display separator lines between tabs.
### Custom Styles
### Secondary Variant
### Secondary Variant Vertical
## Related Components
* **Breadcrumbs**: Display the user's current location within a hierarchy
### Custom Render Function
```tsx
"use client";
import {Tabs} from "@heroui/react";
import Link from "next/link";
export function CustomRenderFunction() {
return (
}>
}
>
Getting Started
}
>
Components
}
>
Releases
View your project overview and recent activity.
Track your metrics and analyze performance data.
Generate and download detailed reports.
);
}
```
## Styling
### Passing Tailwind CSS classes
```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 Classes
The Tabs component uses these CSS classes:
#### Base Classes
* `.tabs` - Base tabs container
* `.tabs__list-container` - Tab list container wrapper
* `.tabs__list` - Tab list container
* `.tabs__tab` - Individual tab button
* `.tabs__separator` - Separator between tabs
* `.tabs__panel` - Tab panel content
* `.tabs__indicator` - Tab indicator
#### Orientation Attributes
* `.tabs[data-orientation="horizontal"]` - Horizontal tab layout (default)
* `.tabs[data-orientation="vertical"]` - Vertical tab layout
#### Variant Classes
* `.tabs--secondary` - Secondary variant with underline indicator
### Interactive States
The component supports both CSS pseudo-classes and data attributes:
* **Selected**: `[aria-selected="true"]`
* **Hover**: `:hover` or `[data-hovered="true"]`
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]`
* **Disabled**: `[aria-disabled="true"]`
## API Reference
### Tabs Props
| Prop | Type | Default | Description |
| -------------------- | ----------------------------------------------------------------------- | -------------- | -------------------------------------------------------------------------------------------- |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual style variant. Primary uses a filled indicator, secondary uses an underline indicator |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Tab layout orientation |
| `selectedKey` | `string` | - | Controlled selected tab key |
| `defaultSelectedKey` | `string` | - | Default selected tab key |
| `onSelectionChange` | `(key: Key) => void` | - | Selection change handler |
| `className` | `string` | - | Additional CSS classes |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Tabs.List Props
| Prop | Type | Default | Description |
| ------------ | -------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- |
| `aria-label` | `string` | - | Accessibility label for tab list |
| `className` | `string` | - | Additional CSS classes |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Tabs.Tab Props
| Prop | Type | Default | Description |
| ------------ | ---------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- |
| `id` | `string` | - | Unique tab identifier |
| `isDisabled` | `boolean` | `false` | Whether tab is disabled |
| `className` | `string` | - | Additional CSS classes |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### Tabs.Separator Props
| Prop | Type | Default | Description |
| ----------- | -------- | ------- | ---------------------- |
| `className` | `string` | - | Additional CSS classes |
### Tabs.Panel Props
| Prop | Type | Default | Description |
| ----------- | --------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- |
| `id` | `string` | - | Panel identifier matching tab id |
| `className` | `string` | - | Additional CSS classes |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
# ScrollShadow
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/scroll-shadow
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(utilities)/scroll-shadow.mdx
> Apply visual shadows to indicate scrollable content overflow with automatic detection of scroll position.
## Import
```tsx
import { ScrollShadow } from "@heroui/react";
```
## Usage
```tsx
import {ScrollShadow} from "@heroui/react";
export default function Default() {
return (
{Array.from({length: 10}).map((_, idx) => (
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.
))}
);
}
```
## Orientation
```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 (
Vertical
{Array.from({length: 10}).map((_, idx) => (
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.
))}
Horizontal
{Array.from({length: 10}).map((_, idx) => (
Bridging the Future
Today, 6:30 PM
))}
);
}
```
## Hide Scroll Bar
```tsx
import {ScrollShadow} from "@heroui/react";
export default function HideScrollBar() {
return (
{Array.from({length: 10}).map((_, idx) => (
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.
))}
);
}
```
## Custom Shadow Size
```tsx
import {ScrollShadow} from "@heroui/react";
export default function CustomSize() {
return (
{Array.from({length: 10}).map((_, idx) => (
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 Change
```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",
];
export default function VisibilityChange() {
const [verticalState, setVerticalState] = useState("none");
const [horizontalState, setHorizontalState] = useState("none");
const getRandomImage = (idx: number) => {
return images[idx % images.length];
};
return (
Vertical Shadow State: {verticalState}
setVerticalState(visibility)}
>
{Array.from({length: 10}).map((_, idx) => (
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.
))}
Horizontal Shadow State: {horizontalState}
setHorizontalState(visibility)}
>
{Array.from({length: 10}).map((_, idx) => (
Bridging the Future
Today, 6:30 PM
))}
);
}
```
## With Card
```tsx
import {Button, Card, ScrollShadow} from "@heroui/react";
export default function WithCard() {
return (
Terms and Conditions
Please review before proceeding
{Array.from({length: 10}).map((_, idx) => (
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
Accept
);
}
```
## Styling
### Passing Tailwind CSS classes
```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.
))}
);
}
```
### Customizing the component classes
To customize the ScrollShadow component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The ScrollShadow component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/scroll-shadow.css)):
#### Base Classes
* `.scroll-shadow` - Root container element
#### Orientation Variants
* `.scroll-shadow--vertical` - Vertical scrolling (default)
* `.scroll-shadow--horizontal` - Horizontal scrolling
#### State Modifiers
* `.scroll-shadow--hide-scrollbar` - Hides native scrollbar
### CSS Variables
The ScrollShadow component uses CSS variables to size the fade mask and reserve space for visible native scrollbars:
| Variable | Default | Description |
| -------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `--scroll-shadow-size` | `40px` | Controls the fade gradient size. This is set from the `size` prop. |
| `--scroll-shadow-scrollbar-size` | `10px` (`0px` when `hideScrollBar`) | Reserves a solid mask gutter for the native scrollbar so the fade does not cover it. Override for wider scrollbars. |
### Data Attributes
The component uses data attributes to control shadow visibility:
* **Scroll States**: `[data-top-scroll]`, `[data-bottom-scroll]`, `[data-left-scroll]`, `[data-right-scroll]` - Applied when content can be scrolled in that direction
* **Combined States**: `[data-top-bottom-scroll]`, `[data-left-right-scroll]` - Applied when content can be scrolled in both directions
* **Orientation**: `[data-orientation="vertical"]` or `[data-orientation="horizontal"]` - Indicates scroll direction
* **Size**: `[data-scroll-shadow-size]` - Contains the shadow gradient size value
## API Reference
### ScrollShadow
| Prop | Type | Default | Description |
| -------------------- | ---------------------------------------------------------------------------------- | ------------ | ---------------------------------------------------- |
| `orientation` | `"vertical"` \| `"horizontal"` | `"vertical"` | The scroll direction |
| `variant` | `"fade"` | `"fade"` | The visual shadow effect style |
| `size` | `number` | `40` | The shadow gradient size in pixels |
| `offset` | `number` | `0` | The scroll offset before showing shadows (in pixels) |
| `hideScrollBar` | `boolean` | `false` | Whether to hide the native scrollbar |
| `isEnabled` | `boolean` | `true` | Whether scroll shadow detection is enabled |
| `visibility` | `"auto"` \| `"both"` \| `"top"` \| `"bottom"` \| `"left"` \| `"right"` \| `"none"` | `"auto"` | Controlled shadow visibility state |
| `onVisibilityChange` | `(visibility: ScrollShadowVisibility) => void` | - | Callback invoked when shadow visibility changes |
| `className` | `string` | - | Additional CSS classes to apply to the root element |
| `children` | `ReactNode` | - | The scrollable content |
# Kbd
**Category**: react
**URL**: https://v3.heroui.com/en/docs/react/components/kbd
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(typography)/kbd.mdx
> Display keyboard shortcuts and key combinations
## Import
```tsx
import { Kbd } from "@heroui/react";
```
### Usage
```tsx
import {Kbd} from "@heroui/react";
export function Basic() {
return (
K
P
C
D
);
}
```
### Anatomy
Import the Kbd component and access all parts using dot notation.
```tsx
import { Kbd } from "@heroui/react";
export default () => (
⌘
K
);
```
### Navigation Keys
```tsx
import {Kbd} from "@heroui/react";
export function NavigationKeys() {
return (
);
}
```
### Inline Usage
```tsx
import {Kbd} from "@heroui/react";
export function InlineUsage() {
return (
Press{" "}
Esc
{" "}
to close the dialog.
Use{" "}
K
{" "}
to open the command palette.
Navigate with{" "}
{" "}
and{" "}
{" "}
arrow keys.
Save your work with{" "}
S
{" "}
regularly.
);
}
```
### Instructional Text
```tsx
import {Kbd} from "@heroui/react";
export function InstructionalText() {
return (
Quick Actions
• Open search:{" "}
K
• Toggle sidebar:{" "}
B
• New file:{" "}
N
• Quick save:{" "}
S
);
}
```
### Special Keys
```tsx
import {Kbd} from "@heroui/react";
export function SpecialKeys() {
return (
Press{" "}
{" "}
to confirm or{" "}
{" "}
to cancel.
Use{" "}
{" "}
to navigate between form fields and{" "}
{" "}
to go back.
Hold{" "}
{" "}
to temporarily enable panning mode.
);
}
```
### Variants
```tsx
import {Kbd} from "@heroui/react";
export function Variants() {
return (
Copy:
C
C
Paste:
V
V
Cut:
X
X
Undo:
Z
Z
Redo:
Z
Z
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import { Kbd } from "@heroui/react";
function CustomKbd() {
return (
K
);
}
```
### Customizing the component classes
To customize the Kbd component classes, you can use the `@layer components` directive.
[Learn more](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 follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.
### CSS Classes
The Kbd component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/kbd.css)):
#### Base Classes
* `.kbd` - Base keyboard key styles with background, border, and spacing
* `.kbd__abbr` - Abbreviation element for modifier keys
* `.kbd__content` - Content wrapper for key text
## API Reference
### Kbd Props
| Prop | Type | Default | Description | |
| ----------- | ----------------- | --------- | ------------------ | --------------------------- |
| `children` | `React.ReactNode` | - | Content of the key | |
| `variant` | \`"default" | "light"\` | `default` | Variant of the keyboard key |
| `className` | `string` | - | Custom CSS classes | |
### Kbd.Abbr Props
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | --------------------------------------------------------- |
| `title` | `string` | - | Title attribute for accessibility (e.g., "Command" for ⌘) |
| `children` | `React.ReactNode` | - | The symbol or text to display (e.g., ⌘, ⌥, ⇧) |
| `className` | `string` | - | Custom CSS classes |
### Kbd.Key Props
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ----------------------- |
| `children` | `React.ReactNode` | - | Text content of the key |
| `className` | `string` | - | Custom CSS classes |
### Kbd.Content Type
Available key values for the `keyValue` prop:
| 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/en/docs/react/components/typography
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/react/components/(typography)/typography.mdx
> A semantic typography primitive for headings, body copy, and inline code built on React Aria Components Text.
## Import
```tsx
import {Typography} from "@heroui/react";
```
## Usage
```tsx
import {Typography} from "@heroui/react";
const scale = [
{
label: "h1",
meta: "36px / 600 / 1.11 / tight",
sample: "Build better interfaces",
type: "h1" as const,
},
{
label: "h2",
meta: "30px / 600 / 1.17 / tight",
sample: "Built for the intelligence age",
type: "h2" as const,
},
{
label: "h3",
meta: "24px / 600 / 1.25 / tight",
sample: "Pricing on your terms",
type: "h3" as const,
},
{
label: "h4",
meta: "20px / 600 / 1.33 / tight",
sample: "Apply to the startup program",
type: "h4" as const,
},
{
label: "h5",
meta: "18px / 600 / 1.39 / tight",
sample: "Card titles",
type: "h5" as const,
},
{
label: "h6",
meta: "16px / 600 / 1.50 / tight",
sample: "Smaller feature headers",
type: "h6" as const,
},
{
label: "body",
meta: "16px / 400 / 1.75",
sample: "Primary body text used across documentation, marketing copy, and descriptions.",
type: "body" as const,
},
{
label: "body-sm",
meta: "14px / 400 / 1.50",
sample: "Secondary body, table cells, navigation, and sidebar items.",
type: "body-sm" as const,
},
{
label: "body-xs",
meta: "12px / 400 / 1.25",
sample: "Captions, badges, helper text, and fine print.",
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` maps visual `type` values to semantic elements by default.
## Primitives
```tsx
import {Typography} from "@heroui/react";
export const Primitives = () => {
return (
Dashboard
Convenience primitives are thin wrappers over Typography, so you can choose explicit
composition without learning a second styling system.
Paragraph supports base, sm, and xs sizes.
Typography.Code
);
};
```
* `Typography.Heading` maps `level={1..6}` to `type="h1"` through `type="h6"`.
* `Typography.Paragraph` maps `size="base" | "sm" | "xs"` to body text styles.
* `Typography.Code` maps to the inline code style.
* `Typography.Prose` styles rich content passed as regular HTML children.
## Prose
```tsx
import {Typography} from "@heroui/react";
export const Prose = () => {
return (
Prose title
Prose is for authored content where the markup is already semantic and HeroUI applies the
default typography rhythm.
Section title
Inline code like render receives the same code treatment as the Typography
primitive.
);
};
```
## Render Prop
```tsx
"use client";
import {Typography} from "@heroui/react";
export const RenderProps = () => {
return (
{children} } type="h1">
H1 visual style, h2 semantic element
{children} }>
The render prop can swap the underlying element while preserving HeroUI props and styles.
);
};
```
Use the React Aria Components-style `render` prop when you need to customize the rendered element.
## CSS Classes
### Base Classes
* `.typography` - Base typography primitive
* `.typography-prose` - Rich prose container
### Type Classes
* `.typography--h1` through `.typography--h6`
* `.typography--body`, `.typography--body-sm`, `.typography--body-xs`
* `.typography--code`
### Modifier Classes
* `.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 Reference
### Typography Props
| Prop | Type | Default | Description |
| ---------- | -------------------------------------------------------------------------------------------- | ----------- | --------------------------------------------- |
| `type` | `'h1' \| 'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6' \| 'body' \| 'body-sm' \| 'body-xs' \| 'code'` | `'body'` | Semantic typography style. |
| `align` | `'start' \| 'center' \| 'end' \| 'justify'` | `'start'` | Text alignment. |
| `color` | `'default' \| 'muted'` | `'default'` | Text color. |
| `weight` | `'normal' \| 'medium' \| 'semibold' \| 'bold'` | - | Font weight override. |
| `truncate` | `boolean` | - | Truncates the text to one line with ellipsis. |
| `render` | `DOMRenderFunction` | - | Custom render function from React Aria. |
| `children` | `ReactNode` | - | Text content. |
# Button
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/button
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(buttons)/button.mdx
> Interactive component that triggers an action when pressed.
## Import
```tsx
import { Button } from 'heroui-native';
```
## Anatomy
```tsx
...
```
* **Button**: Main container that handles press interactions, animations, and variants. Renders string children as label or accepts compound components for custom layouts.
* **Button.Label**: Text content of the button. Inherits size and variant styling from parent Button context.
## Usage
### Basic Usage
The Button component accepts string children that automatically render as label.
```tsx
Basic Button
```
### With Compound Parts
Use Button.Label for explicit control over the label component.
```tsx
Click me
```
### With Icons
Combine icons with labels for enhanced visual communication.
```tsx
Add Item
Download
```
### Icon Only
Create square icon-only buttons using the isIconOnly prop.
```tsx
```
### Sizes
Control button dimensions with three size options.
```tsx
Small
Medium
Large
```
### Variants
Choose from seven visual variants for different emphasis levels.
```tsx
Primary
Secondary
Tertiary
Outline
Ghost
Danger
Danger Soft
```
### Feedback Variants
The `feedbackVariant` prop controls which press feedback effects are rendered:
* `'scale-highlight'` (default): Built-in scale + highlight overlay
* `'scale-ripple'`: Built-in scale + ripple overlay
* `'scale'`: Built-in scale only (no overlay)
* `'none'`: No feedback animations at all
```tsx
{/* Scale + Highlight (default) */}
Highlight Effect
{/* Scale + Ripple */}
Ripple Effect
{/* Scale only */}
Scale Only
{/* No feedback */}
No Feedback
```
### Custom Animation
The `animation` prop controls individual sub-animations. Its shape depends on the `feedbackVariant`.
```tsx
{/* Customize scale and highlight (default feedbackVariant) */}
Custom Highlight
{/* Customize scale and ripple */}
Custom Ripple
```
### Disable Individual Animations
Disable specific sub-animations by setting them to `false`:
```tsx
{/* Disable scale, keep highlight */}
No Scale
{/* Disable highlight, keep scale */}
No Highlight
{/* Disable both */}
No Animations
```
### Disable All Animations
Use `animation={false}` to disable all feedback, or `animation="disable-all"` for cascading disable:
```tsx
Disabled
Disable All (cascading)
```
### Loading State with Spinner
Transform button to loading state with spinner animation.
```tsx
const themeColorAccentForeground = useThemeColor('accent-foreground');
{
setIsDownloading(true);
setTimeout(() => {
setIsDownloading(false);
}, 3000);
}}
isIconOnly={isDownloading}
className="self-center"
>
{isDownloading ? (
) : (
'Download now'
)}
;
```
### Custom Background with LinearGradient
Add gradient backgrounds using absolute positioned elements. Use `feedbackVariant="none"` to disable the default highlight overlay, or use `feedbackVariant="scale-ripple"` for a custom ripple effect.
```tsx
import { Button, PressableFeedback } from 'heroui-native';
import { LinearGradient } from 'expo-linear-gradient';
import { StyleSheet } from 'react-native';
{/* Gradient with no feedback overlay */}
Gradient
{/* Gradient with custom ripple effect */}
Gradient with Ripple
```
## Example
```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 (
Add Item
Learn More
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/button.tsx).
## API Reference
### Button
Button extends all props from [PressableFeedback](./pressable-feedback) (except `animation`, which is redefined) with additional button-specific props.
| prop | type | default | description |
| ----------------- | --------------------------------------------------------------------------------------------- | ------------------- | -------------------------------------------------------------- |
| `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'outline' \| 'ghost' \| 'danger' \| 'danger-soft'` | `'primary'` | Visual variant of the button |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the button |
| `isIconOnly` | `boolean` | `false` | Whether the button displays an icon only (square aspect ratio) |
| `feedbackVariant` | `'scale-highlight' \| 'scale-ripple' \| 'scale' \| 'none'` | `'scale-highlight'` | Determines which feedback effects are rendered |
| `animation` | `ButtonAnimation` | - | Animation configuration (shape depends on `feedbackVariant`) |
For inherited props including `isDisabled`, `className`, `children`, and all Pressable props, see [PressableFeedback API Reference](./pressable-feedback#api-reference).
#### ButtonAnimation
The `animation` prop is a discriminated union based on `feedbackVariant`. It follows the `AnimationRoot` control flow:
* `true` or `undefined`: Use default animations
* `false` or `"disabled"`: Disable all feedback animations
* `"disable-all"`: Cascade-disable all animations including child compound parts
* `object`: Custom configuration with sub-animation keys (see below)
**When `feedbackVariant="scale-highlight"` (default):**
| prop | type | default | description |
| ----------- | ---------------------------------------- | ------- | ------------------------------------------------------------- |
| `scale` | `PressableFeedbackScaleAnimation` | - | Scale animation config (`false` to disable) |
| `highlight` | `PressableFeedbackHighlightAnimation` | - | Highlight overlay config (`false` to disable) |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Control animation state while keeping config (runtime toggle) |
**When `feedbackVariant="scale-ripple"`:**
| prop | type | default | description |
| -------- | ---------------------------------------- | ------- | ------------------------------------------------------------- |
| `scale` | `PressableFeedbackScaleAnimation` | - | Scale animation config (`false` to disable) |
| `ripple` | `PressableFeedbackRippleAnimation` | - | Ripple overlay config (`false` to disable) |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Control animation state while keeping config (runtime toggle) |
**When `feedbackVariant="scale"`:**
| prop | type | default | description |
| ------- | ---------------------------------------- | ------- | ------------------------------------------------------------- |
| `scale` | `PressableFeedbackScaleAnimation` | - | Scale animation config (`false` to disable) |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Control animation state while keeping config (runtime toggle) |
**When `feedbackVariant="none"`:**
Only `'disable-all'` is accepted as a string value. All feedback effects are disabled.
For detailed animation sub-types (`PressableFeedbackScaleAnimation`, `PressableFeedbackHighlightAnimation`, `PressableFeedbackRippleAnimation`), see [PressableFeedback API Reference](./pressable-feedback#api-reference).
### Button.Label
| prop | type | default | description |
| -------------- | ----------------- | ------- | ------------------------------------- |
| `children` | `React.ReactNode` | - | Content to be rendered as label |
| `className` | `string` | - | Additional CSS classes |
| `...TextProps` | `TextProps` | - | All standard Text props are supported |
## Hooks
### useButton
Hook to access the Button context values. Returns the button's size, variant, and disabled state.
```tsx
import { useButton } from 'heroui-native';
const { size, variant, isDisabled } = useButton();
```
#### Return Value
| property | type | description |
| ------------ | --------------------------------------------------------------------------------------------- | ------------------------------ |
| `size` | `'sm' \| 'md' \| 'lg'` | Size of the button |
| `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'outline' \| 'ghost' \| 'danger' \| 'danger-soft'` | Visual variant of the button |
| `isDisabled` | `boolean` | Whether the button is disabled |
**Note:** This hook must be used within a `Button` component. It will throw an error if called outside of the button context.
# CloseButton
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/close-button
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(buttons)/close-button.mdx
> Button component for closing dialogs, modals, or dismissing content.
## Import
```tsx
import { CloseButton } from 'heroui-native';
```
## Usage
### Basic Usage
The CloseButton component renders a close icon button with default styling.
```tsx
```
### Custom Icon Color
Customize the icon color using the `iconProps` prop.
```tsx
```
### Custom Icon Size
Adjust the icon size using the `iconProps` prop.
```tsx
```
### Custom Children
Replace the default close icon with custom content.
```tsx
```
### Disabled State
Disable the button to prevent interactions.
```tsx
```
## Example
```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 (
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/close-button.tsx).
## API Reference
### CloseButton
CloseButton extends all props from [Button](./button) component. It defaults to `variant='tertiary'`, `size='sm'`, and `isIconOnly=true`.
| prop | type | default | description |
| ----------- | ---------------------- | ------- | ------------------------------------------------ |
| `iconProps` | `CloseButtonIconProps` | - | Props for customizing the close icon |
| `children` | `React.ReactNode` | - | Custom content to replace the default close icon |
For inherited props including `isDisabled`, `className`, `animation`, `feedbackVariant` and all Pressable props, see [Button API Reference](./button#api-reference).
#### CloseButtonIconProps
| prop | type | default | description |
| ------- | -------- | ---------------------- | ----------------- |
| `size` | `number` | `20` | Size of the icon |
| `color` | `string` | Uses theme muted color | Color of the icon |
# LinkButton
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/link-button
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(buttons)/link-button.mdx
> A ghost-variant button with no highlight feedback, designed for inline link-style interactions.
## Import
```tsx
import { LinkButton } from 'heroui-native';
```
## Anatomy
```tsx
...
```
* **LinkButton**: Root pressable container. Renders a `Button` with the `ghost` variant and disabled highlight feedback enforced internally. These cannot be overridden by consumers.
* **LinkButton.Label**: Text content of the link button. Inherits size and variant styling from the parent context.
## Usage
### Basic Usage
The LinkButton component renders inline link-style text that responds to press events.
```tsx
Learn more
```
### Sizes
Control the text size with the `size` prop.
```tsx
Small
Medium
Large
```
### Disabled State
Disable the link button to prevent interaction.
```tsx
Disabled link
```
### Custom Styling
Apply custom styles using the `className` prop on both root and label.
```tsx
Styled link
```
### Inline with Text
Place link buttons inline alongside regular text for terms, policies, or contextual navigation.
```tsx
I agree to the
Terms of Service
and
Privacy Policy
```
## Example
```tsx
import { Button, Checkbox, ControlField, LinkButton } from 'heroui-native';
import React from 'react';
import { Alert, View } from 'react-native';
export default function LinkButtonExample() {
const [isAgreed, setIsAgreed] = React.useState(false);
const handleTermsPress = () => Alert.alert('Terms', 'Navigate to Terms');
const handlePrivacyPress = () =>
Alert.alert('Privacy', 'Navigate to Privacy Policy');
return (
I agree to the
Terms of Service
and
Privacy Policy
Sign up
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/link-button.tsx).
## API Reference
### LinkButton
Extends all [Button](./button#button) props except `variant` (enforced as `ghost` internally).
**Behavioral overrides applied internally:**
| override | value | description |
| ----------- | ------------ | --------------------------------------------------- |
| `variant` | `ghost` | Always renders as a ghost button, cannot be changed |
| `highlight` | `false` | Highlight feedback is disabled, cannot be changed |
| `className` | `h-auto p-0` | Removes default button height and padding |
### LinkButton.Label
Equivalent to [Button.Label](./button#buttonlabel). Accepts the same props.
# Menu
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/menu
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(collections)/menu.mdx
> A floating context menu with positioning, selection groups, and multiple presentation modes.
## Import
```tsx
import { Menu, SubMenu } from 'heroui-native';
```
## Anatomy
```tsx
...
...
...
...
...
...
...
```
* **Menu**: Main container that manages open/close state, positioning, and provides context to child components.
* **Menu.Trigger**: Clickable element that toggles the menu visibility.
* **Menu.Portal**: Renders menu content in a portal layer above other content.
* **Menu.Overlay**: Optional background overlay to capture outside clicks and close the menu.
* **Menu.Content**: Container for menu content with two presentation modes: floating popover with positioning and collision detection, or bottom sheet modal.
* **Menu.Close**: Close button that dismisses the menu when pressed.
* **Menu.Label**: Non-interactive section heading text within the menu.
* **Menu.Group**: Groups menu items with optional selection state (none, single, multiple).
* **Menu.Item**: Pressable menu item with animated press feedback. Standalone or within a Group for selection.
* **Menu.ItemTitle**: Primary label text for a menu item.
* **Menu.ItemDescription**: Secondary description text for a menu item.
* **Menu.ItemIndicator**: Visual selection indicator (checkmark or dot) for a menu item.
* **SubMenu**: Root container that manages the expand/collapse state and provides animation context to children.
* **SubMenu.Trigger**: Pressable row that toggles the submenu open/closed. Styled like a regular menu item.
* **SubMenu.TriggerIndicator**: Animated chevron icon (default: chevron-right) that rotates when the submenu opens/closes. Place inside `SubMenu.Trigger`.
* **SubMenu.Content**: Absolutely positioned container that animates its height when the submenu opens/closes. Place `Menu.Item` elements inside.
## Usage
### Basic Usage
The Menu component uses compound parts to create a floating context menu.
```tsx
...
View Profile
Settings
```
### With Item Descriptions
Add secondary description text to menu items alongside titles.
```tsx
...
New file
Create a new file
Copy link
Copy the file link
```
### Single Selection
Use `Menu.Group` with `selectionMode="single"` to allow one selected item at a time.
```tsx
const [theme, setTheme] = useState>(() => new Set(['system']));
...
Appearance
Light
Dark
System
;
```
### Multiple Selection
Use `selectionMode="multiple"` to allow selecting multiple items simultaneously.
```tsx
const [textStyles, setTextStyles] = useState>(
() => new Set(['bold', 'italic'])
);
...
Text Style
Bold
Italic
Underline
;
```
### With SubMenu
Nest a `SubMenu` inside `Menu.Content` to reveal additional items on press.
```tsx
Editor Menu
New Space
Focus
Zen Mode
Reader Mode
Lock Tab
Heading 1
```
### Danger Variant
Use `variant="danger"` on a menu item for destructive actions.
```tsx
...
Edit
Delete
```
### Placements
Control where the menu appears relative to the trigger.
```tsx
...
Option A
Option B
```
### Bottom Sheet Presentation
Use `presentation="bottom-sheet"` to display menu content as a bottom sheet modal.
```tsx
...
Option A
Option B
```
### Dot Indicator
Use `variant="dot"` on `Menu.ItemIndicator` to show a filled circle instead of a checkmark.
```tsx
...
Left
Center
Right
```
## Example
```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
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/menu.tsx).
## API Reference
### Menu
| prop | type | default | description |
| --------------- | ----------------------------- | ----------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | The content of the menu |
| `presentation` | `'popover' \| 'bottom-sheet'` | `'popover'` | Presentation mode for the menu content |
| `isOpen` | `boolean` | - | Controlled open state of the menu |
| `isDefaultOpen` | `boolean` | - | Open state when initially rendered (uncontrolled) |
| `isDisabled` | `boolean` | - | Whether the menu is disabled |
| `animation` | `MenuRootAnimation` | - | Animation configuration for menu root |
| `onOpenChange` | `(open: boolean) => void` | - | Callback fired when the menu open state changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### MenuRootAnimation
Animation configuration for menu root component. Can be:
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
### Menu.Trigger
| prop | type | default | description |
| ------------------- | ----------------- | ------- | ------------------------------------------------------- |
| `children` | `React.ReactNode` | - | The trigger element content |
| `className` | `string` | - | Additional CSS class for the trigger |
| `isDisabled` | `boolean` | `false` | Whether the trigger is disabled |
| `asChild` | `boolean` | - | Render as child element using Slot pattern |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### Menu.Portal
| prop | type | default | description |
| -------------------------------------------- | ----------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | The portal content |
| `className` | `string` | - | Additional CSS class for the portal container |
| `disableFullWindowOverlay` | `boolean` | `false` | Use a regular View instead of FullWindowOverlay on iOS |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | Controls whether VoiceOver treats the overlay window as a modal container. When `true`, VoiceOver is restricted to elements inside the overlay. iOS only. Unstable: may change with react-native-screens updates |
| `hostName` | `string` | - | Optional name of the host element for the portal |
| `forceMount` | `boolean` | - | Force mount the portal regardless of open state |
### Menu.Overlay
| prop | type | default | description |
| ----------------------- | ---------------------- | ------- | ------------------------------------------------------------ |
| `className` | `string` | - | Additional CSS class for the overlay |
| `closeOnPress` | `boolean` | `true` | Whether to close the menu when the overlay is pressed |
| `animation` | `MenuOverlayAnimation` | - | Animation configuration for overlay |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `forceMount` | `boolean` | - | Force mount the overlay regardless of open state |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### MenuOverlayAnimation
Animation configuration for menu overlay component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------------ | ----------------------- | ----------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `opacity.entering.value` | `EntryOrExitLayoutType` | `FadeIn.duration(200)` | Custom entering animation for overlay |
| `opacity.exiting.value` | `EntryOrExitLayoutType` | `FadeOut.duration(150)` | Custom exiting animation for overlay |
### Menu.Content (Popover)
Props when `presentation="popover"`.
| prop | type | default | description |
| ----------------- | ------------------------------------------------ | --------------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | The menu content |
| `presentation` | `'popover'` | - | Presentation mode (must match Menu root) |
| `placement` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | Where the menu appears relative to the trigger |
| `align` | `'start' \| 'center' \| 'end'` | `'center'` | Alignment of the menu relative to the trigger |
| `avoidCollisions` | `boolean` | `true` | Whether to reposition to avoid screen edges |
| `offset` | `number` | `9` | Distance from the trigger element in pixels |
| `alignOffset` | `number` | `0` | Offset along the alignment axis in pixels |
| `width` | `'content-fit' \| 'trigger' \| 'full' \| number` | `'content-fit'` | Content width sizing strategy |
| `className` | `string` | - | Additional CSS class for the content container |
| `animation` | `MenuContentAnimation` | - | Animation configuration for content |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### MenuContentAnimation
Animation configuration for menu popover content component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------------- | ----------------------- | ------------------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `entering.value` | `EntryOrExitLayoutType` | Scale + fade entering animation | Custom entering animation for content |
| `exiting.value` | `EntryOrExitLayoutType` | Scale + fade exiting animation | Custom exiting animation for content |
### Menu.Content (Bottom Sheet)
Props when `presentation="bottom-sheet"`. Extends `@gorhom/bottom-sheet` BottomSheet props.
| prop | type | default | description |
| --------------------------- | ---------------------------------------- | ------- | ---------------------------------------------------- |
| `children` | `React.ReactNode` | - | The bottom sheet content |
| `presentation` | `'bottom-sheet'` | - | Presentation mode (must match Menu root) |
| `className` | `string` | - | Additional CSS class for the bottom sheet |
| `backgroundClassName` | `string` | - | Additional CSS class for the background |
| `handleIndicatorClassName` | `string` | - | Additional CSS class for the handle indicator |
| `contentContainerClassName` | `string` | - | Additional CSS class for the content container |
| `contentContainerProps` | `Omit` | - | Props for the content container |
| `animation` | `AnimationDisabled` | - | Set to `false` or `"disabled"` to disable animations |
| `...BottomSheetProps` | `Partial` | - | All `@gorhom/bottom-sheet` props are supported |
### Menu.Close
Extends `CloseButtonProps`. Automatically closes the menu when pressed.
| prop | type | default | description |
| ---------------- | ---------------------- | ------- | ------------------------------------ |
| `iconProps` | `CloseButtonIconProps` | - | Props for customizing the close icon |
| `...ButtonProps` | `ButtonRootProps` | - | All Button root props are supported |
### Menu.Group
| prop | type | default | description |
| --------------------- | ---------------------------------- | -------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | The group content (Menu.Item elements) |
| `selectionMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | The type of selection allowed in the group |
| `selectedKeys` | `Iterable` | - | Currently selected keys (controlled) |
| `defaultSelectedKeys` | `Iterable` | - | Initially selected keys (uncontrolled) |
| `isDisabled` | `boolean` | `false` | Whether the entire group is disabled |
| `disabledKeys` | `Iterable` | - | Keys of items that should be disabled |
| `shouldCloseOnSelect` | `boolean` | - | Whether selecting an item should close the menu |
| `className` | `string` | - | Additional CSS class for the group container |
| `onSelectionChange` | `(keys: Set) => void` | - | Callback fired when the selection changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Menu.Label
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | The label text content |
| `className` | `string` | - | Additional CSS class for the label |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Menu.Item
| prop | type | default | description |
| ----------------------- | ---------------------------------------------------------------- | ----------- | ------------------------------------------------------------ |
| `children` | `React.ReactNode \| ((props: MenuItemRenderProps) => ReactNode)` | - | Child elements or a render function |
| `id` | `MenuKey` | - | Unique identifier, required when inside a Menu.Group |
| `variant` | `'default' \| 'danger'` | `'default'` | Visual variant of the menu item |
| `isDisabled` | `boolean` | `false` | Whether the item is disabled |
| `isSelected` | `boolean` | - | Controlled selected state for standalone items |
| `shouldCloseOnSelect` | `boolean` | `true` | Whether pressing this item should close the menu |
| `className` | `string` | - | Additional CSS class for the item |
| `animation` | `MenuItemAnimation` | - | Animation configuration for press feedback |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `onSelectedChange` | `(selected: boolean) => void` | - | Callback when standalone item's selected state changes |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### MenuItemRenderProps
Props passed to the render function when `children` is a function.
| prop | type | description |
| ------------ | ----------------------- | --------------------------------------- |
| `isSelected` | `boolean` | Whether this item is currently selected |
| `isDisabled` | `boolean` | Whether the item is disabled |
| `isPressed` | `SharedValue` | Whether the item is currently pressed |
| `variant` | `'default' \| 'danger'` | Visual variant of the item |
#### MenuItemAnimation
Animation configuration for menu item press feedback. Can be:
* `false` or `"disabled"`: Disable all item animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------------------ | ------------------ | -------------------------- | ---------------------------------------- |
| `scale.value` | `number` | `0.98` | Scale value when pressed |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | Spring animation configuration for scale |
| `backgroundColor.value` | `string` | `useThemeColor('default')` | Background color shown while pressed |
| `backgroundColor.timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | Animation timing for background color |
### Menu.ItemTitle
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | The title text content |
| `className` | `string` | - | Additional CSS class for the item title |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Menu.ItemDescription
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | The description text content |
| `className` | `string` | - | Additional CSS class for the item description |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Menu.ItemIndicator
| prop | type | default | description |
| -------------- | ---------------------------- | ------------- | ------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Custom indicator content, defaults to checkmark or dot |
| `variant` | `'checkmark' \| 'dot'` | `'checkmark'` | Visual variant of the indicator |
| `iconProps` | `MenuItemIndicatorIconProps` | - | Icon configuration (checkmark variant) |
| `forceMount` | `boolean` | `true` | Force mount the indicator regardless of selected state |
| `className` | `string` | - | Additional CSS class for the item indicator |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### MenuItemIndicatorIconProps
| prop | type | default | description |
| ------- | -------- | ------- | ---------------------------------------------- |
| `size` | `number` | `16` | Size of the indicator icon (8 for dot variant) |
| `color` | `string` | `muted` | Color of the indicator icon |
### SubMenu
| prop | type | default | description |
| --------------- | ------------------------- | ------- | -------------------------------------------------------- |
| `children` | `React.ReactNode` | - | The sub-menu content (trigger, content, and other items) |
| `isOpen` | `boolean` | - | Controlled open state of the sub-menu |
| `isDefaultOpen` | `boolean` | - | Open state when initially rendered (uncontrolled) |
| `isDisabled` | `boolean` | `false` | Whether the sub-menu is disabled |
| `className` | `string` | - | Additional CSS class for the root container |
| `animation` | `SubMenuRootAnimation` | - | Animation configuration for the sub-menu |
| `onOpenChange` | `(open: boolean) => void` | - | Callback fired when the sub-menu open state changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
##### SubMenuRootAnimation
Animation configuration for the SubMenu root component. Can be:
* `"disable-all"`: Disable all animations including children
* `false` or `"disabled"`: Disable only root animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------------------- | ----------------------- | ------------------------------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `rootContent.marginHorizontal` | `number` | `-16` | Margin horizontal when sub-menu is open |
| `rootContent.marginVertical` | `number` | `-16` | Margin vertical when sub-menu is open |
| `rootContent.paddingHorizontal` | `number` | `6` | Padding horizontal when sub-menu is open |
| `rootContent.paddingTop` | `number` | `12` | Padding top when sub-menu is open |
| `rootContent.springConfig` | `WithSpringConfig` | `{ damping: 100, stiffness: 950, mass: 3 }` | Spring configuration for expand/collapse |
#### SubMenu.Trigger
| prop | type | default | description |
| ------------------- | ----------------- | ------- | ------------------------------------------------------- |
| `children` | `React.ReactNode` | - | The trigger content (title, icons, indicator, etc.) |
| `textValue` | `string` | - | Accessible text value announced by screen readers |
| `className` | `string` | - | Additional CSS class for the trigger |
| `isDisabled` | `boolean` | `false` | Whether the trigger is disabled |
| `asChild` | `boolean` | - | Render as child element using Slot pattern |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### SubMenu.TriggerIndicator
Animated indicator icon that rotates when the submenu opens/closes. Defaults to a chevron-right icon.
| prop | type | default | description |
| ----------------------- | ---------------------------------- | ------- | ------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Custom indicator content (replaces default chevron) |
| `className` | `string` | - | Additional CSS class for the indicator |
| `iconProps` | `SubMenuTriggerIndicatorIconProps` | - | Icon configuration for the default chevron |
| `animation` | `SubMenuTriggerIndicatorAnimation` | - | Animation configuration for indicator rotation |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
##### SubMenuTriggerIndicatorIconProps
| prop | type | default | description |
| ------- | -------- | ------- | --------------------------- |
| `size` | `number` | `14` | Size of the indicator icon |
| `color` | `string` | `muted` | Color of the indicator icon |
##### SubMenuTriggerIndicatorAnimation
Animation configuration for the trigger indicator rotation. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ----------------------- | ----------------------- | -------------------------------------------- | ------------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `rotation.value` | `[number, number]` | `[0, 90]` | Rotation values \[collapsed, expanded] in degrees |
| `rotation.springConfig` | `WithSpringConfig` | `{ damping: 140, stiffness: 1000, mass: 4 }` | Spring configuration for rotation |
#### SubMenu.Content
| prop | type | default | description |
| ------------------- | ----------------- | ------- | ------------------------------------------------------- |
| `children` | `React.ReactNode` | - | The submenu items (Menu.Item, Menu.Group, etc.) |
| `className` | `string` | - | Additional CSS class for the content container |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
## Hooks
### useMenu
Hook to access the menu root context. Must be used within a `Menu` component.
```tsx
import { useMenu } from 'heroui-native';
const { isOpen, onOpenChange, presentation, isDisabled } = useMenu();
```
#### Returns
| property | type | description |
| -------------- | ----------------------------- | --------------------------------------- |
| `isOpen` | `boolean` | Whether the menu is currently open |
| `onOpenChange` | `(open: boolean) => void` | Callback to change the open state |
| `presentation` | `'popover' \| 'bottom-sheet'` | Current presentation mode |
| `isDisabled` | `boolean \| undefined` | Whether the menu is disabled |
| `nativeID` | `string` | Unique identifier for the menu instance |
### useMenuItem
Hook to access the menu item context. Must be used within a `Menu.Item` component.
```tsx
import { useMenuItem } from 'heroui-native';
const { id, isSelected, isDisabled, variant } = useMenuItem();
```
#### Returns
| property | type | description |
| ------------ | ----------------------- | -------------------------------------- |
| `id` | `MenuKey \| undefined` | Item identifier |
| `isSelected` | `boolean` | Whether the item is currently selected |
| `isDisabled` | `boolean` | Whether the item is disabled |
| `variant` | `'default' \| 'danger'` | Visual variant of the item |
### useMenuAnimation
Hook to access the menu animation context. Must be used within a `Menu` component.
```tsx
import { useMenuAnimation } from 'heroui-native';
const { progress, isDragging } = useMenuAnimation();
```
#### Returns
| property | type | description |
| ------------ | ---------------------- | --------------------------------------------------------- |
| `progress` | `SharedValue` | Animation progress shared value (0=idle, 1=open, 2=close) |
| `isDragging` | `SharedValue` | Whether the bottom sheet is currently being dragged |
### useSubMenu
Hook to access the sub-menu context. Must be used within a `SubMenu` component.
```tsx
import { useSubMenu } from 'heroui-native';
const { isOpen, onOpenChange, isDisabled } = useSubMenu();
```
#### Returns
| property | type | description |
| -------------- | ------------------------- | ------------------------------------------- |
| `isOpen` | `boolean` | Whether the sub-menu is currently open |
| `onOpenChange` | `(open: boolean) => void` | Callback to change the open state |
| `isDisabled` | `boolean` | Whether the sub-menu is disabled |
| `nativeID` | `string` | Unique identifier for the sub-menu instance |
## Special Notes
### Element Inspector (iOS)
Menu uses FullWindowOverlay on iOS. To enable the React Native element inspector during development, set `disableFullWindowOverlay={true}` on `Menu.Portal`. Tradeoff: the menu will not appear above native modals when disabled.
### Native Modal (iOS)
When a `Menu` is opened inside a screen presented as a native modal (`presentation: 'modal' | 'formSheet' | 'pageSheet'`), the menu content may render shifted upward. In the new architecture (Fabric), `react-native-screens` marks `RNSModalScreen` as a Fabric root, so the trigger's position is reported relative to the modal's origin while `FullWindowOverlay` (where the menu is mounted) is anchored to the iOS application window. Compensate by adding `safeAreaInsets.top` to `offset`:
```tsx
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const insets = useSafeAreaInsets();
...
;
```
# TagGroup
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/tag-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(collections)/tag-group.mdx
> A compound component for displaying and managing selectable tags with optional removal.
## Import
```tsx
import { TagGroup } from 'heroui-native';
```
## Anatomy
```tsx
...
```
* **TagGroup**: Main container that manages tag selection state, disabled keys, and remove functionality. Provides size and variant context to all child components.
* **TagGroup.List**: Container for rendering the list of tags with optional empty state rendering.
* **TagGroup.Item**: Individual tag within the group. Supports string children (auto-wrapped in TagGroup.ItemLabel), render function children, or custom layouts.
* **TagGroup.ItemLabel**: Text label for the tag. Automatically rendered when string children are provided, or can be used explicitly.
* **TagGroup.ItemRemoveButton**: Remove button for the tag. Must be placed explicitly when removal is needed. Only functional when `onRemove` is provided to TagGroup.
## Usage
### Basic Usage
Display a simple tag group with selectable items.
```tsx
News
Travel
Gaming
```
### Single Selection Mode
Allow only one tag to be selected at a time.
```tsx
News
Travel
Gaming
```
### Multiple Selection Mode
Allow multiple tags to be selected simultaneously.
```tsx
News
Travel
Gaming
```
### Controlled Selection
Control selection state with `selectedKeys` and `onSelectionChange`.
```tsx
const [selected, setSelected] = useState(new Set(['news']));
News
Travel
Gaming
;
```
### Variants
Apply different visual variants to the tags.
```tsx
News
Travel
News
Travel
```
### Sizes
Control the size of all tags in the group.
```tsx
News
News
News
```
### With Remove Button
Add remove buttons to tags by providing `onRemove` and placing `TagGroup.ItemRemoveButton` in each item.
```tsx
const [tags, setTags] = useState([
{ id: 'news', name: 'News' },
{ id: 'travel', name: 'Travel' },
]);
const onRemove = (keys) => {
setTags((prev) => prev.filter((tag) => !keys.has(tag.id)));
};
{tags.map((tag) => (
{tag.name}
))}
;
```
### Render Function Children
Use a render function to access `isSelected` and `isDisabled` for custom layouts.
```tsx
{({ isSelected }) => (
<>
News
>
)}
```
### Empty State
Render custom content when the list has no tags.
```tsx
(
No categories found
)}
>
{tags.map((tag) => (
{tag.name}
))}
```
### Disabled Tags
Disable individual tags or the entire group.
```tsx
News
Travel
Gaming
```
## Example
```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 (
Amenities
Laundry
Fitness center
Parking
Swimming pool
Breakfast
{`Selected: ${Array.from(selected).join(', ')}`}
Please select at least one category
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/tag-group.tsx).
## API Reference
### TagGroup
| prop | type | default | description |
| --------------------- | ---------------------------------- | ----------- | ---------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Child elements to render inside the tag group |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of all tags in the group |
| `variant` | `'default' \| 'surface'` | `'default'` | Visual variant of all tags in the group |
| `selectionMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | The type of selection allowed in the tag group |
| `selectedKeys` | `Iterable` | - | The currently selected keys (controlled) |
| `defaultSelectedKeys` | `Iterable` | - | The initial selected keys (uncontrolled) |
| `disabledKeys` | `Iterable` | - | Keys of tags that should be disabled |
| `isDisabled` | `boolean` | `false` | Whether the entire tag group is disabled |
| `isInvalid` | `boolean` | `false` | Whether the tag group is in an invalid state |
| `isRequired` | `boolean` | `false` | Whether the tag group is required |
| `className` | `string` | - | Additional CSS classes for the tag group container |
| `style` | `StyleProp` | - | Additional styles for the tag group container |
| `animation` | `"disable-all" \| undefined` | - | Use `"disable-all"` to disable all animations including children |
| `onSelectionChange` | `(keys: Set) => void` | - | Handler called when the selection changes |
| `onRemove` | `(keys: Set) => void` | - | Handler called when tags are removed |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### TagKey
`string | number` — Key type for identifying tags within a TagGroup.
#### Animation
Use `animation="disable-all"` to disable all animations including children. Omit or use `undefined` for default animations.
### TagGroup.List
| prop | type | default | description |
| ------------------ | ----------------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Child elements to render inside the list |
| `className` | `string` | - | Additional CSS classes for the list container |
| `style` | `StyleProp` | - | Additional styles for the list container |
| `renderEmptyState` | `() => React.ReactNode` | - | Function to render when the list has no tags |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### TagGroup.Item
| prop | type | default | description |
| ------------------- | ----------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((renderProps: TagRenderProps) => React.ReactNode)` | - | Tag content: string, elements, or a render function receiving TagRenderProps |
| `id` | `TagKey` | - | Unique identifier for this tag |
| `isDisabled` | `boolean` | - | Whether this specific tag is disabled |
| `className` | `string` | - | Additional CSS classes for the tag |
| `style` | `StyleProp` | - | Additional styles for the tag |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### TagRenderProps
| prop | type | description |
| ------------ | --------- | --------------------------------------------------------------------------- |
| `isSelected` | `boolean` | Whether the tag is currently selected |
| `isDisabled` | `boolean` | Whether the tag is disabled (merged from root, disabledKeys, and item prop) |
### TagGroup.ItemLabel
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Text content to render |
| `className` | `string` | - | Additional CSS classes for the label |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### TagGroup.ItemRemoveButton
| prop | type | default | description |
| ------------------- | -------------------------- | ------- | ---------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom icon or content for the remove button. Defaults to close icon when omitted |
| `className` | `string` | - | Additional CSS classes for the remove button |
| `iconProps` | `TagRemoveButtonIconProps` | - | Props for customizing the default close icon. Only applies when no children are provided |
| `hitSlop` | `number` | `8` | Extends the touchable area |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### TagRemoveButtonIconProps
| prop | type | default | description |
| ------- | -------- | ------- | ----------------- |
| `size` | `number` | `12` | Size of the icon |
| `color` | `string` | - | Color of the icon |
## Hooks
### useTagGroup
Hook to access the tag group root context. Must be used within a `TagGroup` component.
```tsx
import { useTagGroup } from 'heroui-native';
const {
selectedKeys,
disabledKeys,
selectionMode,
onSelectionChange,
onRemove,
isDisabled,
isInvalid,
isRequired,
} = useTagGroup();
```
#### Returns
| property | type | description |
| ------------------- | -------------------------------------------- | ---------------------------------------------- |
| `selectionMode` | `'none' \| 'single' \| 'multiple'` | The type of selection allowed in the tag group |
| `selectedKeys` | `Set` | Currently selected tag keys |
| `disabledKeys` | `Set` | Keys of disabled tags |
| `onSelectionChange` | `(keys: Set) => void` | Callback when selection changes |
| `onRemove` | `((keys: Set) => void) \| undefined` | Callback when tags are removed |
| `isDisabled` | `boolean` | Whether the entire tag group is disabled |
| `isInvalid` | `boolean` | Whether the tag group is in an invalid state |
| `isRequired` | `boolean` | Whether the tag group is required |
### useTagGroupItem
Hook to access the tag item context. Must be used within a `TagGroup.Item` component.
```tsx
import { useTagGroupItem } from 'heroui-native';
const { id, isSelected, isDisabled, allowsRemoving } = useTagGroupItem();
```
#### Returns
| property | type | description |
| ---------------- | --------- | --------------------------------------------------------------------------- |
| `id` | `TagKey` | Unique identifier for this tag |
| `isSelected` | `boolean` | Whether the tag is currently selected |
| `isDisabled` | `boolean` | Whether the tag is disabled |
| `allowsRemoving` | `boolean` | Whether the tag can be removed (true when onRemove is provided to TagGroup) |
# Slider
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/slider
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(controls)/slider.mdx
> A draggable input for selecting a value or range within a bounded interval.
## Import
```tsx
import { Slider } from 'heroui-native';
```
## Anatomy
```tsx
```
* **Slider**: Main container that manages slider value state, orientation, and provides context to all sub-components. Supports single value and range modes.
* **Slider.Output**: Optional display of the current value(s). Supports render functions for custom formatting. Shows a formatted value label by default.
* **Slider.Track**: Sizing container for Fill and Thumb elements. Reports its layout size for position calculations. Supports tap-to-position and render-function children for dynamic content (e.g. multiple thumbs for range sliders).
* **Slider.Fill**: Responsive fill bar that stretches the full cross-axis of the Track. Only the main-axis position and size are computed.
* **Slider.Thumb**: Draggable thumb element using react-native-gesture-handler. Centered on the cross-axis by the Track layout. Animates scale on press via react-native-reanimated. Each thumb gets `role="slider"` with full `accessibilityValue`.
## Usage
### Basic Usage
The Slider component uses compound parts to create a draggable value input.
```tsx
```
### With Label and Output
Display a label alongside the current value output.
```tsx
Volume
```
### Vertical Orientation
Render the slider vertically by setting `orientation` to `"vertical"`.
```tsx
```
### Range Slider
Pass an array as the value and use a render function on `Slider.Track` to create multiple thumbs.
```tsx
Price range
{({ state }) => (
<>
{state.values.map((_, i) => (
))}
>
)}
```
### Controlled Value
Use `value` and `onChange` for controlled mode. The `onChangeEnd` callback fires when a drag or tap interaction completes.
```tsx
const [volume, setVolume] = useState(50);
save(v)}>
;
```
### Custom Styling
Apply custom styles using `className`, `classNames`, or `styles` on the thumb and other sub-components.
```tsx
```
### Disabled
Disable the entire slider to prevent interaction.
```tsx
```
## Example
```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) => (
))}
>
)}
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/slider.tsx).
## API Reference
### Slider
| prop | type | default | description |
| --------------- | ------------------------------------- | -------------- | --------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the slider |
| `value` | `number \| number[]` | - | Current slider value (controlled mode) |
| `defaultValue` | `number \| number[]` | `0` | Default slider value (uncontrolled mode) |
| `minValue` | `number` | `0` | Minimum value of the slider |
| `maxValue` | `number` | `100` | Maximum value of the slider |
| `step` | `number` | `1` | Step increment for the slider |
| `formatOptions` | `Intl.NumberFormatOptions` | - | Number format options for value label formatting |
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Orientation of the slider |
| `isDisabled` | `boolean` | `false` | Whether the slider is disabled |
| `className` | `string` | - | Additional CSS classes |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration for the slider |
| `onChange` | `(value: number \| number[]) => void` | - | Callback fired when the slider value changes during interaction |
| `onChangeEnd` | `(value: number \| number[]) => void` | - | Callback fired when an interaction completes (drag end or tap) |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### AnimationRootDisableAll
Animation configuration for the slider root component. Can be:
* `"disable-all"`: Disable all animations including children
* `undefined`: Use default animations
### Slider.Output
| prop | type | default | description |
| -------------- | -------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((props: SliderRenderProps) => React.ReactNode)` | - | Custom content or render function receiving slider state. Defaults to formatted value label |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### SliderRenderProps
| prop | type | description |
| ------------- | ------------------- | ------------------------------ |
| `state` | `SliderState` | Current slider state |
| `orientation` | `SliderOrientation` | Orientation of the slider |
| `isDisabled` | `boolean` | Whether the slider is disabled |
#### SliderState
| prop | type | description |
| -------------------- | --------------------------- | ---------------------------------------------- |
| `values` | `number[]` | Current slider value(s) by thumb index |
| `getThumbValueLabel` | `(index: number) => string` | Returns the formatted string label for a thumb |
### Slider.Track
| prop | type | default | description |
| -------------- | -------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((props: SliderRenderProps) => React.ReactNode)` | - | Content or render function receiving slider state for dynamic thumb rendering |
| `className` | `string` | - | Additional CSS classes |
| `hitSlop` | `number` | `8` | Extra touch area around the track |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Slider.Fill
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Slider.Thumb
| prop | type | default | description |
| -------------- | ---------------------------------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom thumb content. Defaults to an animated knob |
| `index` | `number` | `0` | Index of this thumb within the slider |
| `isDisabled` | `boolean` | - | Whether this individual thumb is disabled |
| `className` | `string` | - | Additional CSS classes for the thumb container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for thumb slots |
| `styles` | `Partial>` | - | Inline styles for thumb slots |
| `hitSlop` | `number` | `12` | Extra touch area around the thumb |
| `animation` | `SliderThumbAnimation` | - | Animation configuration for the thumb knob |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| prop | type | description |
| ---------------- | -------- | ----------------------------------------------- |
| `thumbContainer` | `string` | Custom class name for the outer thumb container |
| `thumbKnob` | `string` | Custom class name for the inner thumb knob |
#### styles
| prop | type | description |
| ---------------- | ----------- | ------------------------------------ |
| `thumbContainer` | `ViewStyle` | Styles for the outer thumb container |
| `thumbKnob` | `ViewStyle` | Styles for the inner thumb knob |
#### SliderThumbAnimation
Animation configuration for the thumb knob scale effect. Can be:
* `false` or `"disabled"`: Disable thumb animation
* `undefined`: Use default animations
* `object`: Custom scale animation configuration
| prop | type | default | description |
| -------------------- | ------------------ | -------------------------------------------- | ----------------------------------------------- |
| `scale.value` | `[number, number]` | `[1, 0.9]` | Scale values \[idle, dragging] |
| `scale.springConfig` | `WithSpringConfig` | `{ damping: 15, stiffness: 200, mass: 0.5 }` | Spring animation configuration for scale effect |
## Hooks
### useSlider
Hook to access the slider context. Must be used within a `Slider` component.
```tsx
import { useSlider } from 'heroui-native';
const { values, orientation, isDisabled, getThumbValueLabel } = useSlider();
```
#### Returns
| property | type | description |
| -------------------- | -------------------------------------------- | -------------------------------------------------------------- |
| `values` | `number[]` | Current slider values (one per thumb) |
| `minValue` | `number` | Minimum value of the slider |
| `maxValue` | `number` | Maximum value of the slider |
| `step` | `number` | Step increment |
| `orientation` | `'horizontal' \| 'vertical'` | Current orientation |
| `isDisabled` | `boolean` | Whether the slider is disabled |
| `formatOptions` | `Intl.NumberFormatOptions \| undefined` | Number format options for labels |
| `getThumbPercent` | `(index: number) => number` | Returns the percentage position (0–1) for a given thumb index |
| `getThumbValueLabel` | `(index: number) => string` | Returns the formatted label for a given thumb index |
| `getThumbMinValue` | `(index: number) => number` | Returns the minimum allowed value for a thumb |
| `getThumbMaxValue` | `(index: number) => number` | Returns the maximum allowed value for a thumb |
| `updateValue` | `(index: number, newValue: number) => void` | Updates a thumb value by index |
| `isThumbDragging` | `(index: number) => boolean` | Returns whether a given thumb is currently being dragged |
| `setThumbDragging` | `(index: number, dragging: boolean) => void` | Sets the dragging state of a thumb |
| `trackSize` | `number` | Track layout width (horizontal) or height (vertical) in pixels |
| `thumbSize` | `number` | Measured thumb size (main-axis dimension) in pixels |
# Switch
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/switch
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(controls)/switch.mdx
> A toggle control that allows users to switch between on and off states.
## Import
```tsx
import { Switch } from 'heroui-native';
```
## Anatomy
```tsx
...
...
...
```
* **Switch**: Main container that handles toggle state and user interaction. Renders default thumb if no children provided. Animates scale (on press) and background color based on selection state. Acts as a pressable area for toggling.
* **Switch.Thumb**: Optional sliding thumb element that moves between positions. Uses spring animation for smooth transitions. Can contain custom content like icons or be customized with different styles and animations.
* **Switch.StartContent**: Optional content displayed on the left side of the switch. Typically used for icons or text that appear when switch is off. Positioned absolutely within the switch container.
* **Switch.EndContent**: Optional content displayed on the right side of the switch. Typically used for icons or text that appear when switch is on. Positioned absolutely within the switch container.
## Usage
### Basic Usage
The Switch component renders with default thumb if no children provided.
```tsx
```
### With Custom Thumb
Replace the default thumb with custom content using the Thumb component.
```tsx
...
```
### With Start and End Content
Add icons or text that appear on each side of the switch.
```tsx
...
...
```
### With Render Function
Use render functions for dynamic content based on switch state.
```tsx
{({ isSelected, isDisabled }) => (
<>
{({ isSelected }) => (isSelected ? : )}
>
)}
```
### With Custom Animations
Customize animations for the switch root and thumb components.
```tsx
```
### Disable Animations
Disable animations entirely or only for specific components.
```tsx
{
/* Disable all animations including children */
}
;
{
/* Disable only root animations, thumb can still animate */
}
;
```
## Example
```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 && (
)}
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/switch.tsx).
## API Reference
### Switch
| prop | type | default | description |
| --------------------------- | -------------------------------------------------------------------- | ----------- | ------------------------------------------------------------ |
| `children` | `React.ReactNode \| ((props: SwitchRenderProps) => React.ReactNode)` | `undefined` | Content to render inside the switch, or a render function |
| `isSelected` | `boolean` | `undefined` | Whether the switch is currently selected |
| `isDisabled` | `boolean` | `false` | Whether the switch is disabled and cannot be interacted with |
| `className` | `string` | `undefined` | Custom class name for the switch |
| `animation` | `SwitchRootAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `onSelectedChange` | `(isSelected: boolean) => void` | - | Callback fired when the switch selection state changes |
| `...AnimatedPressableProps` | `AnimatedProps` | - | All React Native Reanimated Pressable props are supported |
#### SwitchRenderProps
| prop | type | description |
| ------------ | --------- | ------------------------------ |
| `isSelected` | `boolean` | Whether the switch is selected |
| `isDisabled` | `boolean` | Whether the switch is disabled |
#### SwitchRootAnimation
Animation configuration for Switch component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------------------ | ---------------------------------------- | -------------------------------------------------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Disable animations while customizing properties |
| `scale.value` | `[number, number]` | `[1, 0.96]` | Scale values \[unpressed, pressed] |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | Animation timing configuration |
| `backgroundColor.value` | `[string, string]` | Uses theme colors | Background color values \[unselected, selected] |
| `backgroundColor.timingConfig` | `WithTimingConfig` | `{ duration: 175, easing: Easing.bezier(0.25, 0.1, 0.25, 1) }` | Animation timing configuration |
### Switch.Thumb
| prop | type | default | description |
| ----------------------- | -------------------------------------------------------------------- | ----------- | ------------------------------------------------------------ |
| `children` | `React.ReactNode \| ((props: SwitchRenderProps) => React.ReactNode)` | `undefined` | Content to render inside the thumb, or a render function |
| `className` | `string` | `undefined` | Custom class name for the thumb element |
| `animation` | `SwitchThumbAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### SwitchThumbAnimation
Animation configuration for Switch.Thumb component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------------------ | ----------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `left.value` | `number` | `2` | Offset value from the edges (left when unselected, right when selected) |
| `left.springConfig` | `WithSpringConfig` | `{ damping: 120, stiffness: 1600, mass: 2 }` | Spring animation configuration for thumb position |
| `backgroundColor.value` | `[string, string]` | `['white', theme accent-foreground color]` | Background color values \[unselected, selected] |
| `backgroundColor.timingConfig` | `WithTimingConfig` | `{ duration: 175, easing: Easing.bezier(0.25, 0.1, 0.25, 1) }` | Animation timing configuration |
### Switch.StartContent
| prop | type | default | description |
| -------------- | ----------------- | ----------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | `undefined` | Content to render inside the switch content |
| `className` | `string` | `undefined` | Custom class name for the content element |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Switch.EndContent
| prop | type | default | description |
| -------------- | ----------------- | ----------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | `undefined` | Content to render inside the switch content |
| `className` | `string` | `undefined` | Custom class name for the content element |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
## Hooks
### useSwitch
A hook that provides access to the Switch context. This is useful when building custom switch components or when you need to access switch state in child components.
**Returns:**
| Property | Type | Description |
| ------------ | --------- | ------------------------------ |
| `isSelected` | `boolean` | Whether the switch is selected |
| `isDisabled` | `boolean` | Whether the switch is disabled |
**Example:**
```tsx
import { useSwitch } from 'heroui-native';
function CustomSwitchContent() {
const { isSelected, isDisabled } = useSwitch();
return (
Status: {isSelected ? 'On' : 'Off'}
{isDisabled && Disabled }
);
}
// Usage
;
```
## Special Notes
### Border Styling
If you need to apply a border to the switch root, use the `outline` style properties instead of `border`. This ensures the border doesn't affect the internal layout calculations for the thumb position:
```tsx
```
Using `outline` keeps the border visual without impacting the switch's internal width calculations, ensuring the thumb animates correctly.
### Integration with ControlField
The Switch component integrates seamlessly with ControlField for press state sharing:
```tsx
import { Description, ControlField, Label } from 'heroui-native';
Enable notifications
Receive push notifications
```
When wrapped in ControlField, the Switch will automatically respond to press events on the entire ControlField container, creating a larger touch target and better user experience.
# Chip
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/chip
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(data-display)/chip.mdx
> Displays a compact element in a capsule shape.
## Import
```tsx
import { Chip } from 'heroui-native';
```
## Anatomy
```tsx
...
```
* **Chip**: Main container that displays a compact element
* **Chip.Label**: Text content of the chip
## Usage
### Basic Usage
The Chip component displays text or custom content in a capsule shape.
```tsx
Basic Chip
```
### Sizes
Control the chip size with the `size` prop.
```tsx
Small
Medium
Large
```
### Variants
Choose between different visual styles with the `variant` prop.
```tsx
Primary
Secondary
Tertiary
Soft
```
### Colors
Apply different color themes with the `color` prop.
```tsx
Accent
Default
Success
Warning
Danger
```
### With Icons
Add icons or custom content alongside text using compound components.
```tsx
Featured
Close
```
### Custom Styling
Apply custom styles using className or style props.
```tsx
Custom
```
### Disable All Animations
Disable all animations including children by using the `"disable-all"` value for the `animation` prop.
```tsx
{
/* Disable all animations including children */
}
No Animations ;
```
## Example
```tsx
import { Chip } from 'heroui-native';
import { View, Text } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
export default function ChipExample() {
return (
Small
Medium
Large
Primary
Success
Premium
Remove
Custom
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/chip.tsx).
## API Reference
### Chip
| prop | type | default | description |
| ------------------- | ------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to render inside the chip |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the chip |
| `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'soft'` | `'primary'` | Visual variant of the chip |
| `color` | `'accent' \| 'default' \| 'success' \| 'warning' \| 'danger'` | `'accent'` | Color theme of the chip |
| `className` | `string` | - | Additional CSS classes to apply |
| `animation` | `"disable-all" \| undefined` | `undefined` | Animation configuration. Use `"disable-all"` to disable all animations including children |
| `...PressableProps` | `PressableProps` | - | All Pressable props are supported |
### Chip.Label
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------- |
| `children` | `React.ReactNode` | - | Text or content to render as the label |
| `className` | `string` | - | Additional CSS classes to apply |
| `...TextProps` | `TextProps` | - | All standard Text props are supported |
## Hooks
### useChip
Hook to access the Chip context values. Returns the chip's size, variant, and color.
```tsx
import { useChip } from 'heroui-native';
const { size, variant, color } = useChip();
```
#### Return Value
| property | type | description |
| --------- | ------------------------------------------------------------- | -------------------------- |
| `size` | `'sm' \| 'md' \| 'lg'` | Size of the chip |
| `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'soft'` | Visual variant of the chip |
| `color` | `'accent' \| 'default' \| 'success' \| 'warning' \| 'danger'` | Color theme of the chip |
**Note:** This hook must be used within a `Chip` component. It will throw an error if called outside of the chip context.
# Alert
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/alert
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(feedback)/alert.mdx
> Displays important messages and notifications to users with status indicators.
## Import
```tsx
import { Alert } from 'heroui-native';
```
## Anatomy
```tsx
...
...
```
* **Alert**: Main container with `role="alert"` and status-based styling. Provides status context to sub-components via a primitive context.
* **Alert.Indicator**: Renders a status-appropriate icon by default. Accepts custom children to override the default icon. Supports `iconProps` for customising size and color.
* **Alert.Content**: Wrapper for the title and description. Provides layout structure for text content.
* **Alert.Title**: Heading text with status-based color. Connected to root via `aria-labelledby`.
* **Alert.Description**: Body text rendered with muted color. Connected to root via `aria-describedby`.
## Usage
### Basic Usage
The Alert component uses compound parts to display a notification with an icon, title, and description.
```tsx
New features available
Check out our latest updates including dark mode support and improved
accessibility features.
```
### Status Variants
Set the `status` prop to control the icon and title color. Available statuses are `default`, `accent`, `success`, `warning`, and `danger`.
```tsx
Success
...
Scheduled maintenance
...
Unable to connect
...
```
### Title Only
Omit `Alert.Description` for a compact single-line alert.
```tsx
Profile updated successfully
```
### With Action Buttons
Place additional elements like buttons alongside the content.
```tsx
Update available
A new version of the application is available.
Refresh
```
### Custom Indicator
Replace the default status icon by passing custom children to `Alert.Indicator`.
```tsx
Processing your request
Please wait while we sync your data.
```
### Custom Styling
Apply custom styles using the `className` prop on the root and compound parts.
```tsx
...
...
```
## Example
```tsx
import { Alert, Button, CloseButton } from 'heroui-native';
import { View } from 'react-native';
export default function AlertExample() {
return (
Update available
A new version of the application is available. Please refresh to get
the latest features and bug fixes.
Refresh
Unable to connect to server
Unable to connect to the server. Check your internet connection and
try again.
Retry
Profile updated successfully
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/alert.tsx).
## API Reference
### Alert
| prop | type | default | description |
| -------------- | ------------------------------------------------------------- | ----------- | ----------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to render inside the alert |
| `status` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | `'default'` | Status controlling the icon and color treatment |
| `id` | `string \| number` | - | Unique identifier for the alert. Auto-generated when not provided |
| `className` | `string` | - | Additional CSS classes |
| `style` | `ViewStyle` | - | Additional styles applied to the root container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Alert.Indicator
| prop | type | default | description |
| -------------- | ----------------- | ------- | ------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Custom children to render instead of the default status icon |
| `className` | `string` | - | Additional CSS classes |
| `iconProps` | `AlertIconProps` | - | Props passed to the default status icon (size and color overrides) |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### AlertIconProps
| prop | type | default | description |
| ------- | -------- | ------------ | ---------------------- |
| `size` | `number` | `18` | Icon size in pixels |
| `color` | `string` | status color | Icon color as a string |
### Alert.Content
| prop | type | default | description |
| -------------- | ----------------- | ------- | --------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements (typically Alert.Title and Alert.Description) |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Alert.Title
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Title text content |
| `className` | `string` | - | Additional CSS classes |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Alert.Description
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Description text content |
| `className` | `string` | - | Additional CSS classes |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
## Hooks
### useAlert
Hook to access the alert root context. Must be used within an `Alert` component.
```tsx
import { useAlert } from 'heroui-native';
const { status, nativeID } = useAlert();
```
#### Returns
| property | type | description |
| ---------- | ------------------------------------------------------------- | ------------------------------------------------------------ |
| `status` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | Current alert status for sub-component styling |
| `nativeID` | `string` | Unique identifier used for accessibility and ARIA attributes |
# SkeletonGroup
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/skeleton-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(feedback)/skeleton-group.mdx
> Coordinates multiple skeleton loading placeholders with centralized animation control.
## Import
```tsx
import { SkeletonGroup } from 'heroui-native';
```
## Anatomy
```tsx
```
* **SkeletonGroup**: Root container that provides centralized control for all skeleton items
* **SkeletonGroup.Item**: Individual skeleton item that inherits props from the parent group
## Usage
### Basic Usage
The SkeletonGroup component manages multiple skeleton items with shared loading state and animation.
```tsx
```
### With Container Layout
Use className on the group to control layout of skeleton items.
```tsx
```
### With isSkeletonOnly for Pure Skeleton Layouts
Use `isSkeletonOnly` when the group contains only skeleton placeholders with layout wrappers (like View) that have no content to render in the loaded state. This prop hides the entire group when `isLoading` is false, preventing empty containers from affecting your layout.
```tsx
{/* This View is only for layout, no content */}
```
### With Animation Variants
Control animation style for all items in the group.
```tsx
```
### With Custom Animation Configuration
Configure shimmer or pulse animations for the entire group.
```tsx
```
### With Enter/Exit Animations
Apply Reanimated transitions when the group appears or disappears.
```tsx
```
## Example
```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.
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/skeleton-group.tsx).
## API Reference
### SkeletonGroup
| prop | type | default | description |
| ----------------------- | -------------------------------- | ----------- | ---------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | SkeletonGroup.Item components and layout elements |
| `isLoading` | `boolean` | `true` | Whether the skeleton items are currently loading |
| `isSkeletonOnly` | `boolean` | `false` | Hides entire group when isLoading is false (for skeleton-only layouts) |
| `variant` | `'shimmer' \| 'pulse' \| 'none'` | `'shimmer'` | Animation variant for all items in the group |
| `animation` | `SkeletonRootAnimation` | - | Animation configuration |
| `className` | `string` | - | Additional CSS classes for the group container |
| `style` | `StyleProp` | - | Custom styles for the group container |
| `...Animated.ViewProps` | `AnimatedProps` | - | All Reanimated Animated.View props are supported |
#### SkeletonRootAnimation
Animation configuration for SkeletonGroup component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------------ | ---------------------------------------- | --------------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Disable animations while customizing properties |
| `entering.value` | `EntryOrExitLayoutType` | `FadeIn` | Custom entering animation |
| `exiting.value` | `EntryOrExitLayoutType` | `FadeOut` | Custom exiting animation |
| `shimmer.duration` | `number` | `1500` | Animation duration in milliseconds |
| `shimmer.speed` | `number` | `1` | Speed multiplier for the animation |
| `shimmer.highlightColor` | `string` | - | Highlight color for the shimmer effect |
| `shimmer.easing` | `EasingFunction` | `Easing.linear` | Easing function for the animation |
| `pulse.duration` | `number` | `1000` | Animation duration in milliseconds |
| `pulse.minOpacity` | `number` | `0.5` | Minimum opacity value |
| `pulse.maxOpacity` | `number` | `1` | Maximum opacity value |
| `pulse.easing` | `EasingFunction` | `Easing.inOut(Easing.ease)` | Easing function for the animation |
### SkeletonGroup.Item
| prop | type | default | description |
| ----------------------- | -------------------------------- | --------- | ------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to show when not loading |
| `isLoading` | `boolean` | inherited | Whether the skeleton is currently loading (overrides group setting) |
| `variant` | `'shimmer' \| 'pulse' \| 'none'` | inherited | Animation variant (overrides group setting) |
| `animation` | `SkeletonRootAnimation` | inherited | Animation configuration (overrides group setting) |
| `className` | `string` | - | Additional CSS classes for styling the item |
| `...Animated.ViewProps` | `AnimatedProps` | - | All Reanimated Animated.View props are supported |
## Special Notes
### Props Inheritance
SkeletonGroup.Item components inherit all animation-related props from their parent SkeletonGroup:
* `isLoading`
* `variant`
* `animation`
Individual items can override any inherited prop by providing their own value.
# Skeleton
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/skeleton
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(feedback)/skeleton.mdx
> Displays a loading placeholder with shimmer or pulse animation effects.
## Import
```tsx
import { Skeleton } from 'heroui-native';
```
## Anatomy
The Skeleton component is a simple wrapper that renders a placeholder for content that is loading. It does not have any child components.
```tsx
```
## Usage
### Basic Usage
The Skeleton component creates an animated placeholder while content is loading.
```tsx
```
### With Content
Show skeleton while loading, then display content when ready.
```tsx
Loaded Content
```
### Animation Variants
Control the animation style with the `variant` prop.
```tsx
```
### Custom Shimmer Configuration
Customize the shimmer effect with duration, speed, and highlight color.
```tsx
...
```
### Custom Pulse Configuration
Configure pulse animation with duration and opacity range.
```tsx
...
```
### Shape Variations
Create different skeleton shapes using className for styling.
```tsx
```
### Custom Enter/Exit Animations
Apply custom Reanimated transitions when skeleton appears or disappears.
```tsx
...
```
## Example
```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
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/skeleton.tsx).
## API Reference
### Skeleton
| prop | type | default | description |
| ----------------------- | -------------------------------- | ----------- | ------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Content to show when not loading |
| `isLoading` | `boolean` | `true` | Whether the skeleton is currently loading |
| `variant` | `'shimmer' \| 'pulse' \| 'none'` | `'shimmer'` | Animation variant |
| `animation` | `SkeletonRootAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `className` | `string` | - | Additional CSS classes for styling |
| `...Animated.ViewProps` | `AnimatedProps` | - | All Reanimated Animated.View props are supported |
#### SkeletonRootAnimation
Animation configuration for Skeleton component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------------ | ---------------------------------------- | --------------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Disable animations while customizing properties |
| `entering.value` | `EntryOrExitLayoutType` | `FadeIn` | Custom entering animation |
| `exiting.value` | `EntryOrExitLayoutType` | `FadeOut` | Custom exiting animation |
| `shimmer.duration` | `number` | `1500` | Animation duration in milliseconds |
| `shimmer.speed` | `number` | `1` | Speed multiplier for the animation |
| `shimmer.highlightColor` | `string` | - | Highlight color for the shimmer effect |
| `shimmer.easing` | `EasingFunction` | `Easing.linear` | Easing function for the animation |
| `pulse.duration` | `number` | `1000` | Animation duration in milliseconds |
| `pulse.minOpacity` | `number` | `0.5` | Minimum opacity value |
| `pulse.maxOpacity` | `number` | `1` | Maximum opacity value |
| `pulse.easing` | `EasingFunction` | `Easing.inOut(Easing.ease)` | Easing function for the animation |
# Spinner
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/spinner
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(feedback)/spinner.mdx
> Displays an animated loading indicator.
## Import
```tsx
import { Spinner } from 'heroui-native';
```
## Anatomy
```tsx
...
```
* **Spinner**: Main container that controls loading state, size, and color. Renders a default animated indicator if no children provided.
* **Spinner.Indicator**: Optional sub-component for customizing animation configuration and icon appearance. Accepts custom children to replace the default icon.
## Usage
### Basic Usage
The Spinner component displays a rotating loading indicator.
```tsx
```
### Sizes
Control the spinner size with the `size` prop.
```tsx
```
### Colors
Use predefined color variants or custom colors.
```tsx
```
### Loading State
Control the visibility of the spinner with the `isLoading` prop.
```tsx
```
### Animation Speed
Customize the rotation speed using the `animation` prop on the Indicator component.
```tsx
```
### Custom Icon
Replace the default spinner icon with custom content.
```tsx
const themeColorForeground = useThemeColor('foreground')
⏳
```
## Example
```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 (
Loading content...
Processing...
setIsLoading(!isLoading)}>
{isLoading ? 'Tap to stop' : 'Tap to start'}
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/spinner.tsx).
## API Reference
### Spinner
| prop | type | default | description |
| -------------- | ----------------------------------------------------------- | ----------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | `undefined` | Content to render inside the spinner |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the spinner |
| `color` | `'default' \| 'success' \| 'warning' \| 'danger' \| string` | `'default'` | Color theme of the spinner |
| `isLoading` | `boolean` | `true` | Whether the spinner is loading |
| `className` | `string` | `undefined` | Custom class name for the spinner |
| `animation` | `SpinnerRootAnimation` | - | Animation configuration |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### SpinnerRootAnimation
Animation configuration for Spinner component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------------- | ---------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Disable animations while customizing properties |
| `entering.value` | `EntryOrExitLayoutType` | `FadeIn` `.duration(200)` `.easing(Easing.out(Easing.ease))` | Custom entering animation |
| `exiting.value` | `EntryOrExitLayoutType` | `FadeOut` `.duration(100)` | Custom exiting animation |
### Spinner.Indicator
| prop | type | default | description |
| ----------------------- | --------------------------- | ----------- | ------------------------------------------------------------ |
| `children` | `React.ReactNode` | `undefined` | Content to render inside the indicator |
| `iconProps` | `SpinnerIconProps` | `undefined` | Props for the default icon |
| `className` | `string` | `undefined` | Custom class name for the indicator element |
| `animation` | `SpinnerIndicatorAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | All Reanimated Animated.View props are supported |
#### SpinnerIndicatorAnimation
Animation configuration for Spinner.Indicator component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ----------------- | ---------------------------- | --------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `rotation.speed` | `number` | `1.1` | Rotation speed multiplier |
| `rotation.easing` | `WithTimingConfig['easing']` | `Easing.linear` | Animation easing configuration |
### SpinnerIconProps
| prop | type | default | description |
| -------- | ------------------ | ---------------- | ------------------ |
| `width` | `number \| string` | `24` | Width of the icon |
| `height` | `number \| string` | `24` | Height of the icon |
| `color` | `string` | `'currentColor'` | Color of the icon |
# Checkbox
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/checkbox
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(forms)/checkbox.mdx
> A selectable control that allows users to toggle between checked and unchecked states.
## Import
```tsx
import { Checkbox } from 'heroui-native';
```
## Anatomy
```tsx
...
```
* **Checkbox**: Main container that handles selection state and user interaction. Renders default indicator with animated checkmark if no children provided. Automatically detects surface context for proper styling. Features press scale animation that can be customized or disabled. Supports render function children to access state (`isSelected`, `isInvalid`, `isDisabled`).
* **Checkbox.Indicator**: Optional checkmark container with default slide, scale, opacity, and border radius animations when selected. Renders animated check icon with SVG path drawing animation if no children provided. All animations can be individually customized or disabled. Supports render function children to access state.
## Usage
### Basic Usage
The Checkbox component renders with a default animated indicator if no children are provided. It automatically detects whether it's on a surface background for proper styling.
```tsx
```
### With Custom Indicator
Use a render function in the Indicator to show/hide custom icons based on state.
```tsx
{({ isSelected }) => (isSelected ? : null)}
```
### Invalid State
Show validation errors with the `isInvalid` prop, which applies danger color styling.
```tsx
```
### Custom Animations
Customize or disable animations for both the root checkbox and indicator.
```tsx
{
/* Disable all animations (root and indicator) */
}
;
{
/* Disable only root animation */
}
;
{
/* Disable only indicator animation */
}
;
{
/* Custom animation configuration */
}
;
```
## Example
```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 && }
))}
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/checkbox.tsx).
## API Reference
### Checkbox
| prop | type | default | description |
| ----------------------- | ---------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((props: CheckboxRenderProps) => React.ReactNode)` | `undefined` | Child elements or render function to customize the checkbox |
| `isSelected` | `boolean` | `undefined` | Whether the checkbox is currently selected |
| `onSelectedChange` | `(isSelected: boolean) => void` | `undefined` | Callback fired when the checkbox selection state changes |
| `isDisabled` | `boolean` | `false` | Whether the checkbox is disabled and cannot be interacted with |
| `isInvalid` | `boolean` | `false` | Whether the checkbox is invalid (shows danger color) |
| `variant` | `'primary' \| 'secondary'` | `'primary'` | Variant style for the checkbox |
| `hitSlop` | `number` | `6` | Hit slop for the pressable area |
| `animation` | `CheckboxRootAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `className` | `string` | `undefined` | Additional CSS classes to apply |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported (except disabled) |
#### CheckboxRenderProps
| prop | type | description |
| ------------ | --------- | -------------------------------- |
| `isSelected` | `boolean` | Whether the checkbox is selected |
| `isInvalid` | `boolean` | Whether the checkbox is invalid |
| `isDisabled` | `boolean` | Whether the checkbox is disabled |
#### CheckboxRootAnimation
Animation configuration for checkbox root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| -------------------- | ---------------------------------------- | ------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Disable animations while customizing properties |
| `scale.value` | `[number, number]` | `[1, 0.96]` | Scale values \[unpressed, pressed] |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | Animation timing configuration |
### Checkbox.Indicator
| prop | type | default | description |
| ----------------------- | ---------------------------------------------------------------------- | ----------- | ------------------------------------------------------------ |
| `children` | `React.ReactNode \| ((props: CheckboxRenderProps) => React.ReactNode)` | `undefined` | Content or render function for the checkbox indicator |
| `className` | `string` | `undefined` | Additional CSS classes for the indicator |
| `iconProps` | `CheckboxIndicatorIconProps` | `undefined` | Custom props for the default animated check icon |
| `animation` | `CheckboxIndicatorAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `...AnimatedViewProps` | `AnimatedProps` | - | All standard React Native Animated View props are supported |
#### CheckboxIndicatorIconProps
Props for customizing the default animated check icon.
| prop | type | description |
| --------------- | -------- | ------------------------------------------------ |
| `size` | `number` | Icon size |
| `strokeWidth` | `number` | Icon stroke width |
| `color` | `string` | Icon color (defaults to theme accent-foreground) |
| `enterDuration` | `number` | Duration of enter animation (check appearing) |
| `exitDuration` | `number` | Duration of exit animation (check disappearing) |
#### CheckboxIndicatorAnimation
Animation configuration for checkbox indicator component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| --------------------------- | ----------------------- | ------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `opacity.value` | `[number, number]` | `[0, 1]` | Opacity values \[unselected, selected] |
| `opacity.timingConfig` | `WithTimingConfig` | `{ duration: 100 }` | Animation timing configuration |
| `borderRadius.value` | `[number, number]` | `[8, 0]` | Border radius values \[unselected, selected] |
| `borderRadius.timingConfig` | `WithTimingConfig` | `{ duration: 50 }` | Animation timing configuration |
| `translateX.value` | `[number, number]` | `[-4, 0]` | TranslateX values \[unselected, selected] |
| `translateX.timingConfig` | `WithTimingConfig` | `{ duration: 100 }` | Animation timing configuration |
| `scale.value` | `[number, number]` | `[0.8, 1]` | Scale values \[unselected, selected] |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 100 }` | Animation timing configuration |
## Hooks
### useCheckbox
Hook to access checkbox context values within custom components or compound components.
```tsx
import { useCheckbox } from 'heroui-native';
const CustomIndicator = () => {
const { isSelected, isInvalid, isDisabled } = useCheckbox();
// ... your implementation
};
```
**Returns:** `UseCheckboxReturn`
| property | type | description |
| ------------------ | ---------------------------------------------- | -------------------------------------------------------------- |
| `isSelected` | `boolean \| undefined` | Whether the checkbox is currently selected |
| `onSelectedChange` | `((isSelected: boolean) => void) \| undefined` | Callback function to change the checkbox selection state |
| `isDisabled` | `boolean` | Whether the checkbox is disabled and cannot be interacted with |
| `isInvalid` | `boolean` | Whether the checkbox is invalid (shows danger color) |
| `nativeID` | `string \| undefined` | Native ID for the checkbox element |
**Note:** This hook must be used within a `Checkbox` component. It will throw an error if called outside of the checkbox context.
# ControlField
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/control-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(forms)/control-field.mdx
> A field component that combines a label, description (or other content), and a control component (Switch or Checkbox) into a single pressable area.
## Import
```tsx
import { ControlField } from 'heroui-native';
```
## Anatomy
```tsx
...
...
...
...
```
* **ControlField**: Root container that manages layout and state propagation
* **Label**: Primary text label for the control (from [Label](./label) component)
* **Description**: Secondary descriptive helper text (from [Description](./description) component)
* **ControlField.Indicator**: Container for the form control component ([Switch](./switch), [Checkbox](./checkbox), [Radio](./radio))
* **FieldError**: Validation error message display (from [FieldError](./field-error) component)
## Usage
### Basic Usage
ControlField wraps form controls to provide consistent layout and state management.
```tsx
Label text
```
### With Description
Add helper text below the label using the Description component.
```tsx
Enable notifications
Receive push notifications about your account activity
```
### With Error Message
Display validation errors using the ErrorMessage component.
```tsx
I agree to the terms
By checking this box, you agree to our Terms of Service
This field is required
```
### Disabled State
Control interactivity with the disabled prop.
```tsx
Disabled field
This field is disabled
```
### Disabling All Animations
Disable all animations including children by using `"disable-all"`. This cascades down to all child components.
```tsx
Label text
Description text
```
## Example
```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
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/control-field.tsx).
## API Reference
### ControlField
| prop | type | default | description |
| ----------------- | -------------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------- |
| children | `React.ReactNode \| ((props: ControlFieldRenderProps) => React.ReactNode)` | - | Content to render inside the form control, or a render function |
| isSelected | `boolean` | `undefined` | Whether the control is selected/checked |
| isDisabled | `boolean` | `false` | Whether the form control is disabled |
| isInvalid | `boolean` | `false` | Whether the form control is invalid |
| isRequired | `boolean` | `false` | Whether the form control is required |
| className | `string` | - | Custom class name for the root element |
| onSelectedChange | `(isSelected: boolean) => void` | - | Callback when selection state changes |
| animation | `"disable-all" \| undefined` | `undefined` | Animation configuration. Use `"disable-all"` to disable all animations including children |
| ...PressableProps | `PressableProps` | - | All React Native Pressable props are supported |
### Label
The `Label` component automatically consumes form state (`isDisabled`, `isInvalid`) from the ControlField context.
**Note**: For complete prop documentation, see the [Label component documentation](./label).
### Description
The `Description` component automatically consumes form state (`isDisabled`, `isInvalid`) from the ControlField context.
**Note**: For complete prop documentation, see the [Description component documentation](./description).
### ControlField.Indicator
| prop | type | default | description |
| ------------ | ----------------------------------- | ---------- | ---------------------------------------------------------- |
| children | `React.ReactNode` | - | Control component to render (Switch, Checkbox, Radio) |
| variant | `'checkbox' \| 'radio' \| 'switch'` | `'switch'` | Variant of the control to render when no children provided |
| className | `string` | - | Custom class name for the indicator element |
| ...ViewProps | `ViewProps` | - | All React Native View props are supported |
**Note**: When children are provided, the component automatically passes down `isSelected`, `onSelectedChange`, `isDisabled`, and `isInvalid` props from the ControlField context if they are not already present on the child component. When using the `radio` variant, the Radio component renders in standalone mode (outside of a RadioGroup).
### FieldError
The `FieldError` component automatically consumes form state (`isInvalid`) from the ControlField context.
**Note**: For complete prop documentation, see the [FieldError component documentation](./field-error). The error message visibility is controlled by the `isInvalid` state of the parent ControlField.
## Hooks
### useControlField
**Returns:**
| property | type | description |
| ------------------ | ---------------------------------------------- | ---------------------------------------------- |
| `isSelected` | `boolean \| undefined` | Whether the control is selected/checked |
| `onSelectedChange` | `((isSelected: boolean) => void) \| undefined` | Callback when selection state changes |
| `isDisabled` | `boolean` | Whether the form control is disabled |
| `isInvalid` | `boolean` | Whether the form control is invalid |
| `isPressed` | `SharedValue` | Reanimated shared value indicating press state |
# Description
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/description
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(forms)/description.mdx
> Text component for providing accessible descriptions and helper text for form fields and other UI elements.
## Import
```tsx
import { Description } from 'heroui-native';
```
## Anatomy
```tsx
...
```
* **Description**: Text component that displays description or helper text with muted styling. Can be linked to form fields via `nativeID` for accessibility support.
## Usage
### Basic Usage
Display description text with default muted styling.
```tsx
This is a helpful description.
```
### With Form Fields
Provide accessible descriptions for form fields using the `nativeID` prop.
```tsx
Email address
We'll never share your email with anyone else.
```
### Accessibility Linking
Link descriptions to form fields for screen reader support by using `nativeID` and `aria-describedby`.
```tsx
Password
Use at least 8 characters with a mix of letters, numbers, and symbols.
```
### Hiding on Invalid State
Control whether the description should be hidden when the form field is invalid using the `hideOnInvalid` prop.
```tsx
Email
We'll never share your email with anyone else.
Please enter a valid email address
```
When `hideOnInvalid` is `true`, the description will be hidden when the field is invalid. When `false` (default), the description remains visible even when invalid.
## Example
```tsx
import { Description, 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.
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/description.tsx).
## API Reference
### Description
| prop | type | default | description |
| --------------- | ----------------------------------- | ------- | ------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Description text content |
| `className` | `string` | - | Additional CSS classes to apply |
| `nativeID` | `string` | - | Native ID for accessibility. Used to link description to form fields via aria-describedby. |
| `isInvalid` | `boolean` | - | Whether the description is in an invalid state (overrides context) |
| `isDisabled` | `boolean` | - | Whether the description is disabled (overrides context) |
| `hideOnInvalid` | `boolean` | `false` | Whether to hide the description when invalid |
| `animation` | `DescriptionAnimation \| undefined` | - | Animation configuration for description transitions |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
# FieldError
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/field-error
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(forms)/field-error.mdx
> Displays validation error message content with smooth animations.
## Import
```tsx
import { FieldError } from 'heroui-native';
```
## Anatomy
```tsx
Error message content
```
* **FieldError**: Main container that displays error messages with smooth animations. Accepts string children which are automatically wrapped with Text component, or custom React components for more complex layouts. Controls visibility through the `isInvalid` prop and supports custom entering/exiting animations.
## Usage
### Basic Usage
The FieldError component displays error messages when validation fails.
```tsx
This field is required
```
### Controlled Visibility
Control when the error appears using the `isInvalid` prop. When used inside a form field component (like TextField), FieldError automatically consumes the form-item-state context.
```tsx
const [isInvalid, setIsInvalid] = useState(false);
Please enter a valid email address ;
```
### With Form Fields
FieldError automatically consumes form state from TextField via the form-item-state context.
```tsx
import { FieldError, Label, TextField } from 'heroui-native';
Email
Please enter a valid email address
```
### Custom Content
Pass custom React components as children instead of strings.
```tsx
Invalid input
```
### Custom Animations
Override default entering and exiting animations using the `animation` prop.
```tsx
import { SlideInDown, SlideOutUp } from 'react-native-reanimated';
Field validation failed
;
```
Disable animations entirely:
```tsx
Field validation failed
```
### Custom Styling
Apply custom styles to the container and text elements.
```tsx
Password must be at least 8 characters
```
### Custom Text Props
Pass additional props to the Text component when children is a string.
```tsx
This is a very long error message that might need to be truncated
```
## Example
```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 (
Email Address
We'll use this to contact you
Please enter a valid email address
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/field-error.tsx).
## API Reference
### FieldError
| prop | type | default | description |
| ---------------------- | --------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | `undefined` | The content of the error field. String children are wrapped with Text |
| `isInvalid` | `boolean` | `undefined` | Controls the visibility of the error field (overrides form-item-state context). When used inside TextField, automatically consumes form state |
| `animation` | `FieldErrorRootAnimation` | - | Animation configuration |
| `className` | `string` | `undefined` | Additional CSS classes for the container |
| `classNames` | `ElementSlots` | `undefined` | Additional CSS classes for different parts of the component |
| `styles` | `{ container?: ViewStyle; text?: TextStyle }` | `undefined` | Styles for different parts of the field error |
| `textProps` | `TextProps` | `undefined` | Additional props to pass to the Text component when children is a string |
| `...AnimatedViewProps` | `AnimatedProps` | - | All Reanimated Animated.View props are supported |
**classNames prop:** `ElementSlots` provides type-safe CSS classes for different parts of the field error component. Available slots: `container`, `text`.
#### `styles`
| prop | type | description |
| ----------- | ----------- | --------------------------- |
| `container` | `ViewStyle` | Styles for the container |
| `text` | `TextStyle` | Styles for the text content |
#### FieldErrorRootAnimation
Animation configuration for field error root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------------- | ---------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Disable animations while customizing properties |
| `entering.value` | `EntryOrExitLayoutType` | `FadeIn` `.duration(150)` `.easing(Easing.out(Easing.ease))` | Custom entering animation for field error |
| `exiting.value` | `EntryOrExitLayoutType` | `FadeOut` `.duration(100)` `.easing(Easing.out(Easing.ease))` | Custom exiting animation for field error |
# InputGroup
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/input-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(forms)/input-group.mdx
> A compound layout component that groups an input with optional prefix and suffix decorators.
## Import
```tsx
import { InputGroup } from 'heroui-native';
```
## Anatomy
```tsx
...
...
```
* **InputGroup**: Layout container that wraps Prefix, Input, and Suffix. Provides animation settings and a measurement context so Prefix/Suffix widths are automatically applied as padding on the Input.
* **InputGroup.Prefix**: Absolutely positioned View anchored to the left side of the Input. Its measured width is applied as `paddingLeft` on InputGroup.Input automatically.
* **InputGroup.Suffix**: Absolutely positioned View anchored to the right side of the Input. Its measured width is applied as `paddingRight` on InputGroup.Input automatically.
* **InputGroup.Input**: Pass-through to the Input component. Accepts all Input props directly. Automatically receives paddingLeft/paddingRight from measured Prefix/Suffix.
## Usage
### Basic Usage
The InputGroup component uses compound parts to attach prefix and suffix content to an input.
```tsx
...
...
```
### With Prefix Only
Attach leading content such as icons to the input.
```tsx
```
### With Suffix Only
Attach trailing content such as icons to the input.
```tsx
```
### Decorative vs Interactive
Set `isDecorative` on Prefix or Suffix to make touches pass through to the Input and hide the content from screen readers. Omit it when the decorator contains interactive elements.
```tsx
```
### Disabled State
Disable the entire input group. The disabled state cascades to all child components.
```tsx
```
### With TextField Integration
Combine with TextField, Label, and Description for full form field support.
```tsx
Email
We'll never share your email
```
## Example
```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}
>
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/input-group.tsx).
## API Reference
### InputGroup
| prop | type | default | description |
| -------------- | ------------------------- | ------- | ------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the input group |
| `className` | `string` | - | Additional CSS classes |
| `isDisabled` | `boolean` | `false` | Whether the entire input group and its children are disabled |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration for input group |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### AnimationRootDisableAll
Animation configuration for the InputGroup root component. Can be:
* `"disable-all"`: Disable all animations including children (cascades down)
* `undefined`: Use default animations
### InputGroup.Prefix
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to render inside the prefix |
| `className` | `string` | - | Additional CSS classes |
| `isDecorative` | `boolean` | `false` | When true, touches pass through to the Input and content is hidden from screen readers |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### InputGroup.Suffix
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to render inside the suffix |
| `className` | `string` | - | Additional CSS classes |
| `isDecorative` | `boolean` | `false` | When true, touches pass through to the Input and content is hidden from screen readers |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### InputGroup.Input
Pass-through to the [Input](./input) component. Accepts all Input props directly.
# InputOTP
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/input-otp
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(forms)/input-otp.mdx
> Input component for entering one-time passwords (OTP) with individual character slots, animations, and validation support.
## Import
```tsx
import { InputOTP } from 'heroui-native';
```
## Anatomy
```tsx
```
* **InputOTP**: Main container that manages OTP input state, handles text changes, and provides context to child components. Manages focus, validation, and character input.
* **InputOTP.Group**: Container for grouping multiple slots together. Use this to visually group related slots (e.g., groups of 3 digits).
* **InputOTP.Slot**: Individual slot that displays a single character or placeholder. Each slot must have a unique index matching its position in the OTP sequence. When no children are provided, automatically renders SlotPlaceholder, SlotValue, and SlotCaret.
* **InputOTP.SlotPlaceholder**: Text component that displays the placeholder character for a slot when it's empty. Used by default in Slot if no children provided.
* **InputOTP.SlotValue**: Text component that displays the actual character value for a slot with animations. Used by default in Slot if no children provided.
* **InputOTP.SlotCaret**: Animated caret indicator that shows the current input position. Place this inside a Slot to show where the user is currently typing.
* **InputOTP.Separator**: Visual separator between groups of slots. Use this to visually separate different groups of OTP digits.
## Usage
### Basic Usage
Create a 6-digit OTP input with grouped slots and separator.
```tsx
console.log(code)}>
```
### Four Digits
Create a simple 4-digit PIN input.
```tsx
console.log(code)}>
```
### With Placeholder
Provide custom placeholder characters for each slot position.
```tsx
console.log(code)}
>
{({ slots }) => (
<>
{slots.map((slot) => (
))}
>
)}
```
### Controlled Value
Control the OTP value programmatically.
```tsx
const [value, setValue] = useState('');
;
```
### With Validation
Display validation errors when the OTP is invalid.
```tsx
```
### With Pattern
Restrict input to specific character patterns using regex. Three predefined patterns are available: `REGEXP_ONLY_DIGITS` (matches digits 0-9), `REGEXP_ONLY_CHARS` (matches alphabetic characters a-z, A-Z), and `REGEXP_ONLY_DIGITS_AND_CHARS` (matches both digits and alphabetic characters).
```tsx
import { InputOTP, REGEXP_ONLY_CHARS } from 'heroui-native';
console.log(code)}
>
;
```
### Custom Layout
Use render props in Group to create custom slot layouts.
```tsx
{({ slots, isFocused, isInvalid }) => (
<>
{slots.map((slot) => (
))}
>
)}
```
### Inside a Bottom Sheet
When rendering an InputOTP inside a `BottomSheet`, use the `useBottomSheetAwareHandlers` hook to wire keyboard avoidance handlers. Pass the returned `onFocus` and `onBlur` to InputOTP.
> **Note**: `useBottomSheetAwareHandlers` must be used inside a `BottomSheet`. Call it from a child component rendered inside `BottomSheet.Content` — outside of a `BottomSheet` context the returned handlers are no-ops.
```tsx
import { InputOTP, useBottomSheetAwareHandlers } from 'heroui-native';
const BottomSheetOTPInput = () => {
const { onFocus, onBlur } = useBottomSheetAwareHandlers();
return (
);
};
```
## Example
```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 (
Verify account
We've sent a code to a****@gmail.com
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/input-otp.tsx).
## API Reference
### InputOTP
| prop | type | default | description |
| -------------------------- | ----------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------- |
| `maxLength` | `number` | - | Maximum length of the OTP (required) |
| `value` | `string` | - | Controlled value for the OTP input |
| `defaultValue` | `string` | - | Default value for uncontrolled usage |
| `onChange` | `(value: string) => void` | - | Callback when value changes |
| `onComplete` | `(value: string) => void` | - | Handler called when all slots are filled |
| `isDisabled` | `boolean` | `false` | Whether the input is disabled |
| `isInvalid` | `boolean` | `false` | Whether the input is in an invalid state |
| `pattern` | `string` | - | Regex pattern for allowed characters (e.g., REGEXP\_ONLY\_DIGITS, REGEXP\_ONLY\_CHARS) |
| `inputMode` | `TextInputProps['inputMode']` | `'numeric'` | Input mode for the input |
| `placeholder` | `string` | - | Placeholder text for the input. Each character corresponds to a slot position |
| `placeholderTextColor` | `string` | - | Placeholder text color for all slots |
| `placeholderTextClassName` | `string` | - | Placeholder text class name for all slots |
| `pasteTransformer` | `(text: string) => string` | - | Transform pasted text (e.g., remove hyphens). Defaults to removing non-matching characters |
| `onFocus` | `(e: FocusEvent) => void` | - | Handler for focus events |
| `onBlur` | `(e: BlurEvent) => void` | - | Handler for blur events |
| `textInputProps` | `Omit` | - | Additional props to pass to the underlying TextInput component |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the InputOTP |
| `className` | `string` | - | Additional CSS classes to apply |
| `style` | `PressableProps['style']` | - | Style to pass to the container Pressable component |
| `isBottomSheetAware` | `boolean` | `true` | Whether the InputOTP automatically handles keyboard state when rendered inside a BottomSheet. Set to `false` to disable |
| `animation` | `"disable-all" \| undefined` | `undefined` | Animation configuration. Use `"disable-all"` to disable all animations including children |
### InputOTP.Group
| prop | type | default | description |
| -------------- | --------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode \| ((props: InputOTPGroupRenderProps) => React.ReactNode)` | - | Children elements to be rendered inside the group, or a render function that receives slot data and other context values |
| `className` | `string` | - | Additional CSS classes to apply |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### InputOTPGroupRenderProps
| prop | type | description |
| ------------ | ------------ | ---------------------------------------- |
| `slots` | `SlotData[]` | Array of slot data for each position |
| `maxLength` | `number` | Maximum length of the OTP |
| `value` | `string` | Current OTP value |
| `isFocused` | `boolean` | Whether the input is currently focused |
| `isDisabled` | `boolean` | Whether the input is disabled |
| `isInvalid` | `boolean` | Whether the input is in an invalid state |
### InputOTP.Slot
| prop | type | default | description |
| -------------- | ----------------- | ------- | ------------------------------------------------------------------------------------------- |
| `index` | `number` | - | Zero-based index of the slot (required). Must be between 0 and maxLength - 1 |
| `children` | `React.ReactNode` | - | Custom slot content. If not provided, defaults to SlotPlaceholder, SlotValue, and SlotCaret |
| `className` | `string` | - | Additional CSS classes to apply |
| `style` | `ViewStyle` | - | Additional styles to apply |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### InputOTP.SlotPlaceholder
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------------------------- |
| `children` | `string` | - | Text content to display (optional, defaults to slot.placeholderChar) |
| `className` | `string` | - | Additional CSS classes to apply |
| `style` | `TextStyle` | - | Additional styles to apply |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### InputOTP.SlotValue
| prop | type | default | description |
| -------------- | ---------------------------- | ------- | --------------------------------------------------------- |
| `children` | `string` | - | Text content to display (optional, defaults to slot.char) |
| `className` | `string` | - | Additional CSS classes to apply |
| `animation` | `InputOTPSlotValueAnimation` | - | Animation configuration for SlotValue |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
#### InputOTPSlotValueAnimation
Animation configuration for InputOTP.SlotValue component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------ | ----------------------- | ---------------------------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `wrapper.entering` | `EntryOrExitLayoutType` | `FadeIn.duration(250)` | Entering animation for wrapper |
| `wrapper.exiting` | `EntryOrExitLayoutType` | `FadeOut.duration(100)` | Exiting animation for wrapper |
| `text.entering` | `EntryOrExitLayoutType` | `FlipInXDown.duration(250).easing(...)` | Entering animation for text |
| `text.exiting` | `EntryOrExitLayoutType` | `FlipOutXDown.duration(250).easing(...)` | Exiting animation for text |
### InputOTP.SlotCaret
| prop | type | default | description |
| ----------------------- | ---------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `className` | `string` | - | Additional CSS classes to apply |
| `style` | `ViewStyle` | - | Additional styles to apply |
| `animation` | `InputOTPSlotCaretAnimation` | - | Animation configuration for SlotCaret |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active. When `false`, the animated style is removed and you can implement custom logic |
| `pointerEvents` | `'none' \| 'auto' \| ...` | `'none'` | Pointer events configuration |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### InputOTPSlotCaretAnimation
Animation configuration for InputOTP.SlotCaret component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------ | ----------------------- | ---------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `opacity.value` | `[number, number]` | `[0, 1]` | Opacity values \[min, max] |
| `opacity.duration` | `number` | `500` | Animation duration in milliseconds |
| `height.value` | `[number, number]` | `[16, 18]` | Height values \[min, max] in pixels |
| `height.duration` | `number` | `500` | Animation duration in milliseconds |
### InputOTP.Separator
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `className` | `string` | - | Additional CSS classes to apply |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
## Hooks
### useInputOTP
Hook to access the InputOTP root context. Must be used within an `InputOTP` component.
```tsx
const { value, maxLength, isFocused, isDisabled, isInvalid, slots } =
useInputOTP();
```
### useInputOTPSlot
Hook to access the InputOTP.Slot context. Must be used within an `InputOTP.Slot` component.
```tsx
const { slot, isActive, isCaretVisible } = useInputOTPSlot();
```
# Input
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/input
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(forms)/input.mdx
> A text input component with styled border and background for collecting user input.
## Import
```tsx
import { Input } from 'heroui-native';
```
## Usage
### Basic Usage
Input can be used standalone or within a TextField component.
```tsx
import { Input } from 'heroui-native';
;
```
### Within TextField
Input works seamlessly with TextField for complete form structure.
```tsx
import { Input, Label, TextField } from 'heroui-native';
Email
;
```
### With Validation
Display error state when the input is invalid.
```tsx
import { FieldError, Input, Label, TextField } from 'heroui-native';
Email
Please enter a valid email
;
```
### With Local Invalid State Override
Override the context's invalid state for the input.
```tsx
import { FieldError, Input, Label, TextField } from 'heroui-native';
Email
Email format is incorrect
;
```
### Disabled State
Disable the input to prevent interaction.
```tsx
import { Input, Label, TextField } from 'heroui-native';
Disabled Field
;
```
### With Variant
Use different variants to style the input based on context.
```tsx
import { Input, Label, TextField } from 'heroui-native';
Primary Variant
Secondary Variant
```
### Custom Styling
Customize the input appearance using className.
```tsx
import { Input, Label, TextField } from 'heroui-native';
Custom Styled
;
```
### Inside a Bottom Sheet
When rendering an Input inside a `BottomSheet`, use the `useBottomSheetAwareHandlers` hook to wire keyboard avoidance handlers. Pass the returned `onFocus` and `onBlur` to the Input.
> **Note**: `useBottomSheetAwareHandlers` must be used inside a `BottomSheet`. Call it from a child component rendered inside `BottomSheet.Content` — outside of a `BottomSheet` context the returned handlers are no-ops.
```tsx
import { Input, TextField, useBottomSheetAwareHandlers } from 'heroui-native';
const BottomSheetTextInput = () => {
const { onFocus, onBlur } = useBottomSheetAwareHandlers();
return (
);
};
```
## Example
```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 (
Email
We'll never share your email with anyone else.
New password
setIsPasswordVisible(!isPasswordVisible)}
>
Password must be at least 6 characters
);
};
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/input.tsx).
## API Reference
### Input
| prop | type | default | description |
| ------------------------- | -------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------- |
| isInvalid | `boolean` | `undefined` | Whether the input is in an invalid state (overrides context) |
| variant | `'primary' \| 'secondary'` | `'primary'` | Variant style for the input |
| className | `string` | - | Custom class name for the input |
| selectionColorClassName | `string` | `"accent-accent"` | Custom className for the selection color |
| placeholderColorClassName | `string` | `"field-placeholder"` | Custom className for the placeholder text color |
| isBottomSheetAware | `boolean` | `true` | Whether the input automatically handles keyboard state when rendered inside a BottomSheet. Set to `false` to disable |
| animation | `AnimationRoot` | `undefined` | Animation configuration for the input |
| ...TextInputProps | `TextInputProps` | - | All standard React Native TextInput props are supported |
> **Note**: When used within a TextField component, Input automatically consumes form state (isDisabled, isInvalid) from TextField via the form-item-state context.
# Label
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/label
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(forms)/label.mdx
> Text component for labeling form fields and other UI elements with support for required indicators and validation states.
## Import
```tsx
import { Label } from 'heroui-native';
```
## Anatomy
```tsx
...
```
* **Label**: Root container that manages label state and provides context to child components. When string children are provided, automatically renders as Label.Text. Supports disabled, required, and invalid states.
* **Label.Text**: Text content of the label. Displays the label text and automatically shows an asterisk when the label is required. Changes color when invalid or disabled.
## Usage
### Basic Usage
Display a label with text content. String children are automatically rendered as Label.Text.
```tsx
Username
```
### With Form Fields
Use Label with form fields to provide accessible labels.
```tsx
Username
```
### Required Fields
Show an asterisk indicator for required fields using the `isRequired` prop.
```tsx
Password
```
### Invalid State
Display labels in an invalid state to indicate validation errors.
```tsx
import { FieldError, Label, TextField } from 'heroui-native';
Confirm password
Passwords do not match
```
### Disabled State
Disable labels to indicate non-interactive fields.
```tsx
Subscription plan
```
### Custom Layout
Use compound components for custom label layouts.
```tsx
Custom label
```
### Custom Styling
Apply custom styles using className, classNames, or styles props.
```tsx
Custom styled label
```
## Example
```tsx
import { FieldError, Label, TextField } from 'heroui-native';
import { View } from 'react-native';
export default function LabelExample() {
return (
Username
Password
Confirm password
Passwords do not match
Subscription plan
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/label.tsx).
## API Reference
### Label
| prop | type | default | description |
| ------------------- | ---------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Label content. When string is provided, automatically renders as Label.Text. Otherwise renders children as-is |
| `isRequired` | `boolean` | `false` | Whether the label is required. Shows asterisk indicator when true |
| `isInvalid` | `boolean` | `false` | Whether the label is in an invalid state. Changes text color to danger |
| `isDisabled` | `boolean` | `false` | Whether the label is disabled. Applies disabled styling and prevents interaction |
| `className` | `string` | - | Additional CSS classes to apply |
| `animation` | `"disable-all" \| undefined` | `undefined` | Animation configuration. Use `"disable-all"` to disable all animations including children |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### Label.Text
| prop | type | default | description |
| -------------- | ---------------------------------------- | ------- | ---------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Label text content |
| `className` | `string` | - | Additional CSS classes to apply to the text element |
| `classNames` | `ElementSlots` | - | Additional CSS classes for different parts of the label |
| `styles` | `Partial>` | - | Styles for different parts of the label |
| `nativeID` | `string` | - | Native ID for accessibility. Used to link label to form fields via aria-labelledby |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
#### `ElementSlots`
| prop | type | description |
| ---------- | -------- | ------------------------------ |
| `text` | `string` | CSS classes for the label text |
| `asterisk` | `string` | CSS classes for the asterisk |
#### `styles`
| prop | type | description |
| ---------- | ----------- | ------------------------- |
| `text` | `TextStyle` | Styles for the label text |
| `asterisk` | `TextStyle` | Styles for the asterisk |
# RadioGroup
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/radio-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(forms)/radio-group.mdx
> A set of radio buttons where only one option can be selected at a time.
## Import
```tsx
import { RadioGroup } from 'heroui-native';
```
## Anatomy
```tsx
...
...
...
```
* **RadioGroup**: Container that manages the selection state of radio items. Supports both horizontal and vertical orientations.
* **RadioGroup.Item**: Individual radio option within a RadioGroup. Must be used inside RadioGroup. Handles selection state and renders a default ` ` indicator when text children are provided. Supports render function children to access state (`isSelected`, `isInvalid`, `isDisabled`).
* **Label**: Optional clickable text label for the radio option. Linked to the radio for accessibility. Use the [Label](./label) component directly.
* **Description**: Optional secondary text below the label. Provides additional context about the radio option. Use the [Description](./description) component directly.
* **Radio**: The [Radio](./radio) component used inside `RadioGroup.Item` to render the radio indicator. Automatically detects the `RadioGroupItem` context and derives `isSelected`, `isDisabled`, `isInvalid`, and `variant` from it.
* **Radio.Indicator**: Optional container for the radio circle. Renders default thumb if no children provided. Manages the visual selection state. See [Radio](./radio) for full API.
* **Radio.IndicatorThumb**: Optional inner circle that appears when selected. Animates scale based on selection. Can be replaced with custom content. See [Radio](./radio) for full API.
* **FieldError**: Error message displayed when radio group is invalid. Shown with animation below the radio group content. Use the [FieldError](./field-error) component directly.
## Usage
### Basic Usage
RadioGroup with simple string children automatically renders title and indicator.
```tsx
Option 1
Option 2
Option 3
```
### With Descriptions
Add descriptive text below each radio option for additional context.
```tsx
import { RadioGroup, Radio, Label, Description } from 'heroui-native';
import { View } from 'react-native';
Standard Shipping
Delivered in 5-7 business days
Express Shipping
Delivered in 2-3 business days
;
```
### Custom Indicator
Replace the default indicator thumb with custom content using `Radio` sub-components.
```tsx
import { RadioGroup, Radio, Label } from 'heroui-native';
{({ isSelected }) => (
<>
Custom Option
{isSelected && (
)}
>
)}
;
```
### With Render Function
Use a render function on RadioGroup.Item to access state and customize the entire content.
```tsx
import { RadioGroup, Radio, Label } from 'heroui-native';
{({ isSelected, isInvalid, isDisabled }) => (
<>
Option 1
{isSelected && }
>
)}
;
```
### With Error Message
Display validation errors below the radio group.
```tsx
import { RadioGroup, FieldError } from 'heroui-native';
function RadioGroupWithError() {
const [value, setValue] = React.useState(undefined);
return (
I agree to the terms
I do not agree
Please select an option to continue
);
}
```
## Example
```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 (
Standard Shipping
Delivered in 5-7 business days
Express Shipping
Delivered in 2-3 business days
Overnight Shipping
Delivered next business day
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/radio-group.tsx).
## API Reference
### RadioGroup
| prop | type | default | description |
| --------------- | ---------------------------- | ----------- | ----------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | `undefined` | Radio group content |
| `value` | `string \| undefined` | `undefined` | The currently selected value of the radio group |
| `onValueChange` | `(val: string) => void` | `undefined` | Callback fired when the selected value changes |
| `isDisabled` | `boolean` | `false` | Whether the entire radio group is disabled |
| `isInvalid` | `boolean` | `false` | Whether the radio group is invalid |
| `variant` | `'primary' \| 'secondary'` | `undefined` | Variant style for the radio group (inherited by items if not set on item) |
| `animation` | `"disable-all" \| undefined` | `undefined` | Animation configuration. Use `"disable-all"` to disable all animations including children |
| `className` | `string` | `undefined` | Custom class name |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### RadioGroup.Item
| prop | type | default | description |
| ------------------- | ---------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((props: RadioGroupItemRenderProps) => React.ReactNode)` | `undefined` | Radio item content or render function to customize the radio item |
| `value` | `string` | `undefined` | The value associated with this radio item |
| `isDisabled` | `boolean` | `false` | Whether this specific radio item is disabled |
| `isInvalid` | `boolean` | `false` | Whether the radio item is invalid |
| `variant` | `'primary' \| 'secondary'` | `'primary'` | Variant style for the radio item |
| `hitSlop` | `number` | `6` | Hit slop for the pressable area |
| `className` | `string` | `undefined` | Custom class name |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported (except disabled) |
#### RadioGroupItemRenderProps
| prop | type | description |
| ------------ | --------- | ---------------------------------- |
| `isSelected` | `boolean` | Whether the radio item is selected |
| `isInvalid` | `boolean` | Whether the radio item is invalid |
| `isDisabled` | `boolean` | Whether the radio item is disabled |
### Radio (inside RadioGroup.Item)
The `Radio` component is used inside `RadioGroup.Item` to render the radio indicator. When placed inside a `RadioGroup.Item`, the Radio component automatically detects the `RadioGroupItem` context and derives `isSelected`, `isDisabled`, `isInvalid`, and `variant` from it — no manual prop passing is needed.
Use ` ` for the default indicator, or compose with `Radio.Indicator` and `Radio.IndicatorThumb` for custom styling.
| prop | type | default | description |
| ------------------- | ------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((props: RadioRenderProps) => React.ReactNode)` | `undefined` | Child elements or render function to customize the radio |
| `variant` | `'primary' \| 'secondary'` | `'primary'` | Variant style for the radio |
| `isSelected` | `boolean` | `undefined` | Whether the radio is currently selected |
| `isDisabled` | `boolean` | `undefined` | Whether the radio is disabled and cannot be interacted with |
| `isInvalid` | `boolean` | `false` | Whether the radio is invalid (shows danger color) |
| `className` | `string` | `undefined` | Additional CSS classes to apply |
| `animation` | `RadioRootAnimation` | - | Animation configuration for radio |
| `onSelectedChange` | `(isSelected: boolean) => void` | `undefined` | Callback fired when the radio selection state changes |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported (except disabled) |
#### RadioRenderProps
| prop | type | description |
| ------------ | --------- | ----------------------------- |
| `isSelected` | `boolean` | Whether the radio is selected |
| `isDisabled` | `boolean` | Whether the radio is disabled |
| `isInvalid` | `boolean` | Whether the radio is invalid |
#### RadioRootAnimation
Animation configuration for radio root component. Can be:
* `"disable-all"`: Disable all animations including children (Indicator, IndicatorThumb)
* `undefined`: Use default animations
### Radio.Indicator
| prop | type | default | description |
| ---------------------- | -------------------------- | ----------- | ------------------------------------------------ |
| `children` | `React.ReactNode` | `undefined` | Content for the radio indicator |
| `className` | `string` | `undefined` | Additional CSS classes for the indicator |
| `...AnimatedViewProps` | `AnimatedProps` | - | All Reanimated Animated.View props are supported |
### Radio.IndicatorThumb
| prop | type | default | description |
| ----------------------- | ------------------------------ | ----------- | ------------------------------------------------------------ |
| `className` | `string` | `undefined` | Additional CSS classes for the thumb |
| `animation` | `RadioIndicatorThumbAnimation` | - | Animation configuration for the thumb |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `...AnimatedViewProps` | `AnimatedProps` | - | All Reanimated Animated.View props are supported |
#### RadioIndicatorThumbAnimation
Animation configuration for radio indicator thumb component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| -------------------- | ----------------------- | ---------------------------------------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `scale.value` | `[number, number]` | `[1.5, 1]` | Scale values \[unselected, selected] |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 300, easing: Easing.out(Easing.ease) }` | Animation timing configuration |
**Note:** For labels, descriptions, and error messages, use the base components directly:
* Use [Label](../label/label.md) component for labels
* Use [Description](../description/description.md) component for descriptions
* Use [FieldError](../field-error/field-error.md) component for error messages
## Hooks
### useRadioGroup
**Returns:**
| Property | Type | Description |
| --------------- | -------------------------- | ---------------------------------------------- |
| `value` | `string \| undefined` | Currently selected value |
| `isDisabled` | `boolean` | Whether the radio group is disabled |
| `isInvalid` | `boolean` | Whether the radio group is in an invalid state |
| `variant` | `'primary' \| 'secondary'` | Variant style for the radio group |
| `onValueChange` | `(value: string) => void` | Function to change the selected value |
### useRadioGroupItem
**Returns:**
| Property | Type | Description |
| ------------------ | ---------------------------------------------- | ----------------------------------------------------------------------- |
| `isSelected` | `boolean` | Whether the radio item is selected |
| `isDisabled` | `boolean \| undefined` | Whether the radio item is disabled |
| `isInvalid` | `boolean \| undefined` | Whether the radio item is invalid |
| `variant` | `'primary' \| 'secondary' \| undefined` | Variant style for the radio item |
| `onSelectedChange` | `((isSelected: boolean) => void) \| undefined` | Callback to change the selection state (selects this item in the group) |
# SearchField
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/search-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(forms)/search-field.mdx
> A compound search input for filtering and querying content.
## Import
```tsx
import { SearchField } from 'heroui-native';
```
## Anatomy
```tsx
```
* **SearchField**: Root container that accepts `value` and `onChange`, providing them to children via context. Also provides form field state (isDisabled, isInvalid, isRequired) and animation settings.
* **SearchField.Group**: Flex-row container that positions the search icon, input, and clear button horizontally.
* **SearchField.SearchIcon**: Magnifying glass icon positioned absolutely on the left side of the input. Supports custom children to replace the default icon.
* **SearchField.Input**: Wraps the Input component with search-specific defaults. Reads `value` and `onChangeText` from the SearchField context automatically.
* **SearchField.ClearButton**: Small icon-only button to clear the search input. Automatically hidden when value is empty. Calls `onChange("")` from context on press.
## Usage
### Basic Usage
The SearchField component uses compound parts to create a search input. Pass `value` and `onChange` to the root; the Input and ClearButton consume them via context.
```tsx
```
### With Label and Description
Add a Label and Description outside the Group to provide context for the search field.
```tsx
Find products
Search by name, category, or SKU
```
### With Validation
Use `isInvalid` and `isRequired` on the root to control validation state. Pair with FieldError to display error messages.
```tsx
Search users
Enter at least 3 characters to search
No results found. Please try a different search term.
```
### Custom Search Icon
Replace the default magnifying glass icon by passing children to `SearchField.SearchIcon`.
```tsx
🔍
```
### Disabled
Set `isDisabled` on the root to disable all child components via context.
```tsx
Disabled search
Search is temporarily unavailable
```
## Example
```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 (
Find products
Search by name, category, or SKU
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/search-field.tsx).
## API Reference
### SearchField
| prop | type | default | description |
| -------------- | ------------------------- | ------- | -------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the search field |
| `value` | `string` | - | Controlled search text value |
| `onChange` | `(value: string) => void` | - | Callback fired when the search text changes |
| `isDisabled` | `boolean` | `false` | Whether the search field is disabled |
| `isInvalid` | `boolean` | `false` | Whether the search field is in an invalid state |
| `isRequired` | `boolean` | `false` | Whether the search field is required |
| `className` | `string` | - | Additional CSS classes |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration for the search field |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### AnimationRootDisableAll
Animation configuration for the SearchField root component. Can be:
* `"disable-all"`: Disable all animations including children (cascades down)
* `undefined`: Use default animations
### SearchField.Group
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the group |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### SearchField.SearchIcon
| prop | type | default | description |
| -------------- | -------------------------------- | ------- | ---------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content to replace the default search icon |
| `className` | `string` | - | Additional CSS classes |
| `iconProps` | `SearchFieldSearchIconIconProps` | - | Props for customizing the default search icon (ignored when children are provided) |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### SearchFieldSearchIconIconProps
| prop | type | default | description |
| ------- | -------- | ------------------- | ----------------- |
| `size` | `number` | `16` | Size of the icon |
| `color` | `string` | Theme `muted` color | Color of the icon |
### SearchField.Input
Extends [Input](./input) props with search-specific defaults (`placeholder="Search..."`, `returnKeyType="search"`, `accessibilityRole="search"`). Omits `value` and `onChangeText` because they are provided by the SearchField context.
### SearchField.ClearButton
Automatically hidden when the controlled `value` is an empty string. Calls `onChange("")` from context on press. Additional `onPress` handlers passed via props are called after clearing.
| prop | type | default | description |
| ---------------- | --------------------------------- | ------- | ------------------------------------------------ |
| `children` | `React.ReactNode` | - | Custom content to replace the default close icon |
| `iconProps` | `SearchFieldClearButtonIconProps` | - | Props for customizing the clear button icon |
| `className` | `string` | - | Additional CSS classes |
| `...ButtonProps` | `ButtonRootProps` | - | All Button root props are supported |
#### SearchFieldClearButtonIconProps
| prop | type | default | description |
| ------- | -------- | ------------------- | ----------------- |
| `size` | `number` | `14` | Size of the icon |
| `color` | `string` | Theme `muted` color | Color of the icon |
## Hooks
### useSearchField
Hook to access the search field state from context. Must be used within a `SearchField` component.
```tsx
import { useSearchField } from 'heroui-native';
const { value, onChange, isDisabled, isInvalid, isRequired } = useSearchField();
```
#### Returns
| property | type | description |
| ------------ | ---------------------------------------- | ----------------------------------------------- |
| `value` | `string \| undefined` | Current controlled search text value |
| `onChange` | `((value: string) => void) \| undefined` | Callback to update the search text |
| `isDisabled` | `boolean` | Whether the search field is disabled |
| `isInvalid` | `boolean` | Whether the search field is in an invalid state |
| `isRequired` | `boolean` | Whether the search field is required |
# Select
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/select
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(forms)/select.mdx
> Displays a list of options for the user to pick from — triggered by a button.
## Import
```tsx
import { Select } from 'heroui-native';
```
## Anatomy
```tsx
...
...
```
* **Select**: Main container that manages open/close state, value selection and provides context to child components.
* **Select.Trigger**: Clickable element that toggles the select visibility. Wraps any child element with press handlers. Supports `variant` prop (`'default'` or `'unstyled'`).
* **Select.Value**: Displays the selected value or placeholder text. Automatically updates when selection changes. Styling changes based on selection state.
* **Select.TriggerIndicator**: Optional visual indicator showing open/close state. Renders an animated chevron icon by default that rotates when the select opens/closes.
* **Select.Portal**: Renders select content in a portal layer above other content. Ensures proper stacking and positioning.
* **Select.Overlay**: Optional background overlay. Can be transparent or semi-transparent to capture outside clicks.
* **Select.Content**: Container for select content with three presentation modes: popover (floating with positioning), bottom sheet modal, or dialog modal.
* **Select.Close**: Close button for the select. Can accept custom children or uses default close icon.
* **Select.ListLabel**: Label for the list of items with pre-styled typography.
* **Select.Item**: Selectable option item. Handles selection state and press events.
* **Select.ItemLabel**: Displays the label text for an item.
* **Select.ItemDescription**: Optional description text for items with muted styling.
* **Select.ItemIndicator**: Optional indicator shown for selected items. Renders a check icon by default.
## Usage
### Basic Usage
The Select component uses compound parts to create dropdown selection interfaces.
```tsx
...
```
### With Value Display
Display the selected value in the trigger using the Value component.
```tsx
```
### Popover Presentation
Use popover presentation for floating content with automatic positioning.
```tsx
...
```
### Width Control
Control the width of the select content using the `width` prop. This only works with popover presentation.
```tsx
{
/* Fixed width in pixels */
}
...
;
{
/* Match trigger width */
}
...
;
{
/* Full width (100%) */
}
...
;
{
/* Auto-size to content (default) */
}
...
;
```
### Bottom Sheet Presentation
Use bottom sheet for mobile-optimized selection experience.
```tsx
...
```
### Dialog Presentation
Use dialog presentation for centered modal-style selection.
```tsx
...
Choose an option
```
### Custom Item Content
Customize item appearance with custom content and indicators.
```tsx
...
🇺🇸
🇬🇧
```
### With Render Function
Use a render function on `Select.Item` to access state and customize content based on selection.
```tsx
...
{({ isSelected, value, isDisabled }) => (
<>
🇺🇸
>
)}
{({ isSelected }) => (
<>
🇬🇧
>
)}
```
### With Item Description
Add descriptions to items for additional context.
```tsx
...
Essential features for personal use
```
### With Trigger Indicator
Add a visual indicator to show the open/close state of the select. The indicator rotates when the select opens/closes.
```tsx
```
### Custom Trigger with Unstyled Variant
Use the `unstyled` variant when composing a custom trigger with other components like Button.
```tsx
```
### Controlled Mode
Control the select state programmatically.
```tsx
const [value, setValue] = useState();
const [isOpen, setIsOpen] = useState(false);
;
```
## Example
```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: 'California' },
{ value: 'NY', label: 'New York' },
{ value: 'TX', label: 'Texas' },
{ value: 'FL', label: 'Florida' },
];
export default function SelectExample() {
const [value, setValue] = useState();
return (
Choose a state
{US_STATES.map((state, index) => (
{index < US_STATES.length - 1 && }
))}
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/select.tsx).
## API Reference
### Select
| prop | type | default | description |
| --------------- | ------------------------------------------------- | ----------- | ---------------------------------------------------------------------- |
| `children` | `ReactNode` | - | The content of the select |
| `value` | `SelectOption \| SelectOption[]` | - | The selected value(s) (controlled mode) |
| `onValueChange` | `(value: SelectOption \| SelectOption[]) => void` | - | Callback when the value changes |
| `defaultValue` | `SelectOption \| SelectOption[]` | - | The default selected value(s) (uncontrolled mode) |
| `isOpen` | `boolean` | - | Whether the select is open (controlled mode) |
| `isDefaultOpen` | `boolean` | - | Whether the select is open when initially rendered (uncontrolled mode) |
| `onOpenChange` | `(isOpen: boolean) => void` | - | Callback when the select open state changes |
| `isDisabled` | `boolean` | `false` | Whether the select is disabled |
| `presentation` | `'popover' \| 'bottom-sheet' \| 'dialog'` | `'popover'` | Presentation mode for the select content |
| `animation` | `SelectRootAnimation` | - | Animation configuration |
| `asChild` | `boolean` | `false` | Whether to render as a child element |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### SelectRootAnimation
Animation configuration for Select component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------------- | ------------------------------------------------ | ------- | ----------------------------------------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Disable animations while customizing properties |
| `entering.value` | `SpringAnimationConfig \| TimingAnimationConfig` | - | Animation configuration for when select opens |
| `exiting.value` | `SpringAnimationConfig \| TimingAnimationConfig` | - | Animation configuration for when select closes |
#### SpringAnimationConfig
| prop | type | default | description |
| -------- | ------------------ | ------- | ----------------------------------------- |
| `type` | `'spring'` | - | Animation type (must be `'spring'`) |
| `config` | `WithSpringConfig` | - | Reanimated spring animation configuration |
#### TimingAnimationConfig
| prop | type | default | description |
| -------- | ------------------ | ------- | ----------------------------------------- |
| `type` | `'timing'` | - | Animation type (must be `'timing'`) |
| `config` | `WithTimingConfig` | - | Reanimated timing animation configuration |
### Select.Trigger
| prop | type | default | description |
| ------------------- | ------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------- |
| `variant` | `'default' \| 'unstyled'` | `'default'` | The variant of the trigger. `'default'` applies pre-styled container styles, `'unstyled'` removes default styling |
| `children` | `ReactNode` | - | The trigger element content |
| `className` | `string` | - | Additional CSS classes for the trigger |
| `asChild` | `boolean` | `true` | Whether to render as a child element |
| `isDisabled` | `boolean` | - | Whether the trigger is disabled |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### Select.Value
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `placeholder` | `string` | - | Placeholder text when no value is selected |
| `className` | `string` | - | Additional CSS classes for the value |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
**Note:** The value component automatically applies different text colors based on selection state:
* When a value is selected: `text-foreground`
* When no value is selected (placeholder): `text-field-placeholder`
### Select.TriggerIndicator
| prop | type | default | description |
| ----------------------- | --------------------------------- | ------- | ------------------------------------------------------------ |
| `children` | `ReactNode` | - | Custom indicator content. Defaults to animated chevron icon |
| `className` | `string` | - | Additional CSS classes for the trigger indicator |
| `style` | `ViewStyle` | - | Custom styles for the trigger indicator |
| `iconProps` | `SelectTriggerIndicatorIconProps` | - | Chevron icon configuration |
| `animation` | `SelectTriggerIndicatorAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
**Note:** The following style properties are occupied by animations and cannot be set via className:
* `transform` (specifically `rotate`) - Animated for open/close rotation transitions
To customize this property, use the `animation` prop. To completely disable animated styles and use your own via className or style prop, set `isAnimatedStyleActive={false}`.
#### SelectTriggerIndicatorIconProps
| prop | type | default | description |
| ------- | -------- | ------- | ------------------------------------------------------ |
| `size` | `number` | `16` | Size of the icon |
| `color` | `string` | - | Color of the icon (defaults to foreground theme color) |
#### SelectTriggerIndicatorAnimation
Animation configuration for Select.TriggerIndicator component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations (rotation from 0° to -180°)
* `object`: Custom animation configuration
| prop | type | default | description |
| ----------------------- | ----------------------- | -------------------------------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `rotation.value` | `[number, number]` | `[0, -180]` | Rotation values \[closed, open] in degrees |
| `rotation.springConfig` | `WithSpringConfig` | `{ damping: 140, stiffness: 1000, mass: 4 }` | Spring animation configuration for rotation |
### Select.Portal
| prop | type | default | description |
| -------------------------------------------- | ----------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `ReactNode` | - | The portal content (required) |
| `disableFullWindowOverlay` | `boolean` | `false` | When true on iOS, uses View instead of FullWindowOverlay. Enables element inspector; overlay won't appear above native modals |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | Controls whether VoiceOver treats the overlay window as a modal container. When `true`, VoiceOver is restricted to elements inside the overlay. iOS only. Unstable: may change with react-native-screens updates |
| `className` | `string` | - | Additional CSS classes for the portal container |
| `hostName` | `string` | - | Optional name of the host element for the portal |
| `forceMount` | `boolean` | - | Whether to force mount the component in the DOM |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Select.Overlay
| prop | type | default | description |
| ----------------------- | ------------------------ | ------- | ------------------------------------------------------------ |
| `className` | `string` | - | Additional CSS classes for the overlay |
| `animation` | `SelectOverlayAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `closeOnPress` | `boolean` | `true` | Whether to close the select when overlay is pressed |
| `forceMount` | `boolean` | - | Whether to force mount the component in the DOM |
| `asChild` | `boolean` | `false` | Whether to render as a child element |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | All Reanimated Animated.View props are supported |
#### SelectOverlayAnimation
Animation configuration for Select.Overlay component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations (progress-based opacity for bottom-sheet/dialog, Keyframe animations for popover)
* `object`: Custom animation configuration
| prop | type | default | description |
| --------------- | -------------------------- | ----------- | ---------------------------------------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `opacity.value` | `[number, number, number]` | `[0, 1, 0]` | Opacity values \[idle, open, close] (for bottom-sheet/dialog presentation) |
| `entering` | `EntryOrExitLayoutType` | - | Custom Keyframe animation for entering transition (for popover presentation) |
| `exiting` | `EntryOrExitLayoutType` | - | Custom Keyframe animation for exiting transition (for popover presentation) |
### Select.Content (Popover Presentation)
| prop | type | default | description |
| ----------------------- | ------------------------------------------------ | --------------- | ------------------------------------------------------ |
| `children` | `ReactNode` | - | The select content |
| `width` | `number \| 'trigger' \| 'content-fit' \| 'full'` | `'content-fit'` | Width sizing strategy for the content |
| `presentation` | `'popover'` | `'popover'` | Presentation mode for the select |
| `placement` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | Placement of the content relative to trigger |
| `align` | `'start' \| 'center' \| 'end'` | `'center'` | Alignment along the placement axis |
| `avoidCollisions` | `boolean` | `true` | Whether to flip placement when close to viewport edges |
| `offset` | `number` | `8` | Distance from trigger element in pixels |
| `alignOffset` | `number` | `0` | Offset along the alignment axis in pixels |
| `className` | `string` | - | Additional CSS classes for the content container |
| `animation` | `SelectContentPopoverAnimation` | - | Animation configuration |
| `forceMount` | `boolean` | - | Whether to force mount the component in the DOM |
| `insets` | `Insets` | - | Screen edge insets to respect when positioning |
| `asChild` | `boolean` | `false` | Whether to render as a child element |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | All Reanimated Animated.View props are supported |
#### SelectContentPopoverAnimation
Animation configuration for Select.Content component (popover presentation). Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default Keyframe animations (translateY/translateX, scale, opacity based on placement)
* `object`: Custom animation configuration with `entering` and/or `exiting` Keyframe animations
| prop | type | default | description |
| ---------- | ----------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `entering` | `EntryOrExitLayoutType` | - | Custom Keyframe animation for entering transition (default: Keyframe with translateY/translateX, scale, opacity based on placement, 200ms) |
| `exiting` | `EntryOrExitLayoutType` | - | Custom Keyframe animation for exiting transition (default: Keyframe mirroring entering animation, 150ms) |
### Select.Content (Bottom Sheet Presentation)
| prop | type | default | description |
| --------------------------- | ------------------ | ------- | ------------------------------------------------ |
| `children` | `ReactNode` | - | The bottom sheet content |
| `presentation` | `'bottom-sheet'` | - | Presentation mode for the select |
| `contentContainerClassName` | `string` | - | Additional CSS classes for the content container |
| `...BottomSheetProps` | `BottomSheetProps` | - | All @gorhom/bottom-sheet props are supported |
### Select.Content (Dialog Presentation)
| prop | type | default | description |
| -------------- | -------------------------------------------------------- | ------- | --------------------------------------------------- |
| `children` | `ReactNode` | - | The dialog content |
| `presentation` | `'dialog'` | - | Presentation mode for the select |
| `classNames` | `{ wrapper?: string; content?: string }` | - | Additional CSS classes for wrapper and content |
| `styles` | `Partial>` | - | Styles for different parts of the dialog content |
| `animation` | `SelectContentAnimation` | - | Animation configuration |
| `isSwipeable` | `boolean` | `true` | Whether the dialog content can be swiped to dismiss |
| `forceMount` | `boolean` | - | Whether to force mount the component in the DOM |
| `asChild` | `boolean` | `false` | Whether to render as a child element |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### `styles`
| prop | type | description |
| --------- | ----------- | -------------------------------- |
| `wrapper` | `ViewStyle` | Styles for the wrapper container |
| `content` | `ViewStyle` | Styles for the dialog content |
#### SelectContentAnimation
Animation configuration for Select.Content component (dialog presentation). Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default Keyframe animations (scale and opacity transitions)
* `object`: Custom animation configuration with `entering` and/or `exiting` Keyframe animations
| prop | type | default | description |
| ---------- | ----------------------- | ------- | -------------------------------------------------------------------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `entering` | `EntryOrExitLayoutType` | - | Custom Keyframe animation for entering transition (default: Keyframe with scale and opacity, 200ms) |
| `exiting` | `EntryOrExitLayoutType` | - | Custom Keyframe animation for exiting transition (default: Keyframe mirroring entering animation, 150ms) |
### Select.Close
Select.Close extends [CloseButton](./close-button) and automatically handles select dismissal when pressed.
### Select.ListLabel
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `children` | `ReactNode` | - | The label text content |
| `className` | `string` | - | Additional CSS classes for the list label |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Select.Item
| prop | type | default | description |
| ------------------- | ------------------------------------------------------------ | ------- | -------------------------------------------------------------------------- |
| `children` | `ReactNode \| ((props: SelectItemRenderProps) => ReactNode)` | - | Custom item content. Defaults to label and indicator, or a render function |
| `value` | `any` | - | The value associated with this item (required) |
| `label` | `string` | - | The label text for this item (required) |
| `isDisabled` | `boolean` | `false` | Whether this item is disabled |
| `className` | `string` | - | Additional CSS classes for the item |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### SelectItemRenderProps
When using a render function for `children`, the following props are provided:
| property | type | description |
| ------------ | --------- | --------------------------------------- |
| `isSelected` | `boolean` | Whether this item is currently selected |
| `value` | `string` | The value of the item |
| `isDisabled` | `boolean` | Whether the item is disabled |
### Select.ItemLabel
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `className` | `string` | - | Additional CSS classes for the item label |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Select.ItemDescription
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `children` | `ReactNode` | - | The description text content |
| `className` | `string` | - | Additional CSS classes for the item description |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Select.ItemIndicator
| prop | type | default | description |
| -------------- | ------------------------------ | ------- | -------------------------------------------------- |
| `children` | `ReactNode` | - | Custom indicator content. Defaults to check icon |
| `className` | `string` | - | Additional CSS classes for the item indicator |
| `iconProps` | `SelectItemIndicatorIconProps` | - | Check icon configuration |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### SelectItemIndicatorIconProps
| prop | type | default | description |
| ------- | -------- | ---------------- | ----------------- |
| `size` | `number` | `16` | Size of the icon |
| `color` | `string` | `--colors-muted` | Color of the icon |
## Hooks
### useSelect
Hook to access the Select root context. Returns the select state and control functions.
```tsx
import { useSelect } from 'heroui-native';
const {
isOpen,
onOpenChange,
isDefaultOpen,
isDisabled,
presentation,
triggerPosition,
setTriggerPosition,
contentLayout,
setContentLayout,
nativeID,
value,
onValueChange,
} = useSelect();
```
#### Return Value
| property | type | description |
| -------------------- | -------------------------------------------------- | --------------------------------------------------------- |
| `isOpen` | `boolean` | Whether the select is currently open |
| `onOpenChange` | `(open: boolean) => void` | Callback to change the open state |
| `isDefaultOpen` | `boolean \| undefined` | Whether the select is open by default (uncontrolled mode) |
| `isDisabled` | `boolean \| undefined` | Whether the select is disabled |
| `presentation` | `'popover' \| 'bottom-sheet' \| 'dialog'` | Presentation mode for the select content |
| `triggerPosition` | `LayoutPosition \| null` | Position of the trigger element relative to viewport |
| `setTriggerPosition` | `(position: LayoutPosition \| null) => void` | Updates the trigger element's position |
| `contentLayout` | `LayoutRectangle \| null` | Layout measurements of the select content |
| `setContentLayout` | `(layout: LayoutRectangle \| null) => void` | Updates the content layout measurements |
| `nativeID` | `string` | Unique identifier for the select instance |
| `value` | `SelectOption \| SelectOption[]` | Currently selected option |
| `onValueChange` | `(option: SelectOption \| SelectOption[]) => void` | Callback fired when the selected value changes |
**Note:** This hook must be used within a `Select` component. It will throw an error if called outside of the select context.
### useSelectAnimation
Hook to access the Select animation state values within custom components or compound components.
```tsx
import { useSelectAnimation } from 'heroui-native';
const { selectState, progress, isDragging, isGestureReleaseAnimationRunning } =
useSelectAnimation();
```
#### Return Value
| property | type | description |
| ---------------------------------- | ---------------------- | ---------------------------------------------------------- |
| `progress` | `SharedValue` | Progress value for animations (0=idle, 1=open, 2=close) |
| `isDragging` | `SharedValue` | Whether the select content is currently being dragged |
| `isGestureReleaseAnimationRunning` | `SharedValue` | Whether the gesture release animation is currently running |
**Note:** This hook must be used within a `Select` component. It will throw an error if called outside of the select animation context.
#### SelectOption
| property | type | description |
| -------- | -------- | ---------------------------- |
| `value` | `string` | The value of the option |
| `label` | `string` | The label text of the option |
### useSelectItem
Hook to access the Select Item context. Returns the item's value and label.
```tsx
import { useSelectItem } from 'heroui-native';
const { itemValue, label } = useSelectItem();
```
#### Return Value
| property | type | description |
| ----------- | -------- | ---------------------------------- |
| `itemValue` | `string` | The value of the current item |
| `label` | `string` | The label text of the current item |
## Special Notes
### Element Inspector (iOS)
Select uses FullWindowOverlay on iOS. To enable the React Native element inspector during development, set `disableFullWindowOverlay={true}` on `Select.Portal`. Tradeoff: the select dropdown will not appear above native modals when disabled.
### Native Modal (iOS)
When a `Select` is opened inside a screen presented as a native modal (`presentation: 'modal' | 'formSheet' | 'pageSheet'`), the dropdown may render shifted upward. In the new architecture (Fabric), `react-native-screens` marks `RNSModalScreen` as a Fabric root, so the trigger's position is reported relative to the modal's origin while `FullWindowOverlay` (where the dropdown is mounted) is anchored to the iOS application window. Compensate by adding `safeAreaInsets.top` to `offset`:
```tsx
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const insets = useSafeAreaInsets();
...
;
```
# TextArea
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/text-area
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(forms)/text-area.mdx
> A multiline text input component with styled border and background for collecting longer user input.
## Import
```tsx
import { TextArea } from 'heroui-native';
```
## Usage
### Basic Usage
TextArea can be used standalone or within a TextField component.
```tsx
import { TextArea } from 'heroui-native';
```
### Within TextField
TextArea works seamlessly with TextField for complete form structure.
```tsx
import { Description, Label, TextArea, TextField } from 'heroui-native';
Message
Please provide as much detail as possible.
```
### With Validation
Display error state when the text area is invalid.
```tsx
import { FieldError, Label, TextArea, TextField } from 'heroui-native';
Message
Please enter a valid message
```
### Disabled State
Disable the text area to prevent interaction.
```tsx
import { Label, TextArea, TextField } from 'heroui-native';
Disabled Field
```
### With Variant
Use different variants to style the text area based on context.
```tsx
import { Label, TextArea, TextField } from 'heroui-native';
Primary Variant
Secondary Variant
```
### Custom Styling
Customize the text area appearance using className.
```tsx
import { Label, TextArea, TextField } from 'heroui-native';
Custom Styled
```
## Example
```tsx
import { Description, FieldError, Label, TextArea, TextField } from 'heroui-native';
import { View } from 'react-native';
export default function TextAreaExample() {
return (
Primary Variant
Default variant with primary styling
Secondary Variant
Secondary variant for surfaces
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/text-area.tsx).
## API Reference
TextArea extends [Input](./input) component and inherits all its props. The only differences are default values: `multiline` defaults to `true` and `textAlignVertical` defaults to `'top'`.
# TextField
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/text-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(forms)/text-field.mdx
> A text input component with label, description, and error handling for collecting user input.
## Import
```tsx
import { TextField } from 'heroui-native';
```
## Anatomy
```tsx
...
...
...
```
* **TextField**: Root container that provides spacing and state management
* **Label**: Label with optional asterisk for required fields (from [Label](./label) component)
* **Input**: Input container with animated border and background (from [Input](./input) component)
* **Description**: Secondary descriptive helper text (from [Description](./description) component)
* **FieldError**: Validation error message display (from [FieldError](./field-error) component)
## Usage
### Basic Usage
TextField provides a complete form input structure with label and description.
```tsx
Email
We'll never share your email
```
### With Required Field
Mark fields as required to show an asterisk in the label.
```tsx
Username
```
### With Validation
Display error messages when the field is invalid.
```tsx
import { FieldError, Input, Label, TextField } from 'heroui-native';
Email
Please enter a valid email
;
```
### With Local Invalid State Override
Override the context's invalid state for individual components.
```tsx
import {
Description,
FieldError,
Input,
Label,
TextField,
} from 'heroui-native';
Email
This shows despite input being invalid
Email format is incorrect
;
```
### Multiline Input
Create text areas for longer content.
```tsx
Message
Maximum 500 characters
```
### Disabled State
Disable the entire field to prevent interaction.
```tsx
Disabled Field
```
### With Variant
Use different variants to style the input based on context.
```tsx
Primary Variant
Secondary Variant
```
### Custom Styling
Customize the input appearance using className.
```tsx
Custom Styled
```
## Example
```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 (
Email
We'll never share your email with anyone else.
New password
setIsPasswordVisible(!isPasswordVisible)}
>
Password must be at least 6 characters
);
};
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/text-field.tsx).
## API Reference
### TextField
| prop | type | default | description |
| ------------ | ---------------------------- | ----------- | ----------------------------------------------------------------------------------------- |
| children | `React.ReactNode` | - | Content to render inside the text field |
| isDisabled | `boolean` | `false` | Whether the entire text field is disabled |
| isInvalid | `boolean` | `false` | Whether the text field is in an invalid state |
| isRequired | `boolean` | `false` | Whether the text field is required (shows asterisk) |
| className | `string` | - | Custom class name for the root element |
| animation | `"disable-all" \| undefined` | `undefined` | Animation configuration. Use `"disable-all"` to disable all animations including children |
| ...ViewProps | `ViewProps` | - | All standard React Native View props are supported |
> **Note**: For Label, Input, Description, and FieldError components, see their respective documentation:
>
> * [Label documentation](./label)
> * [Input documentation](./input)
> * [Description documentation](./description)
> * [FieldError documentation](./field-error)
>
> These components automatically consume form state from TextField via the form-item-state context.
## Hooks
### useTextField
Hook to access the TextField context values. Must be used within a `TextField` component.
```tsx
import { TextField, useTextField } from 'heroui-native';
function CustomComponent() {
const { isDisabled, isInvalid, isRequired } = useTextField();
// Use the context values...
}
```
#### Returns
| property | type | description |
| ---------- | --------- | --------------------------------------------- |
| isDisabled | `boolean` | Whether the entire text field is disabled |
| isInvalid | `boolean` | Whether the text field is in an invalid state |
| isRequired | `boolean` | Whether the text field is required |
# Card
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/card
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(layout)/card.mdx
> Displays a card container with flexible layout sections for structured content.
## Import
```tsx
import { Card } from 'heroui-native';
```
## Anatomy
```tsx
...
...
...
...
```
* **Card**: Main container that extends Surface component. Provides base card structure with configurable surface variants and handles overall layout.
* **Card.Header**: Header section for top-aligned content like icons or badges.
* **Card.Body**: Main content area with flex-1 that expands to fill all available space between Card.Header and Card.Footer.
* **Card.Title**: Title text with foreground color and medium font weight.
* **Card.Description**: Description text with muted color and smaller font size.
* **Card.Footer**: Footer section for bottom-aligned actions like buttons.
## Usage
### Basic Usage
The Card component creates a container with built-in sections for organized content.
```tsx
...
```
### With Title and Description
Combine title and description components for structured text content.
```tsx
...
...
```
### With Header and Footer
Add header and footer sections for icons, badges, or actions.
```tsx
...
...
...
```
### Variants
Control the card's background appearance using different variants.
```tsx
...
...
...
...
```
### Horizontal Layout
Create horizontal cards by using flex-row styling.
```tsx
```
### Background Image
Use an image as an absolute positioned background.
```tsx
...
```
## Example
```tsx
import { Button, Card } from 'heroui-native';
import { Ionicons } from '@expo/vector-icons';
import { View } from 'react-native';
export default function CardExample() {
return (
$450
Living room Sofa • Collection 2025
This sofa is perfect for modern tropical spaces, baroque inspired
spaces.
Buy now
Add to cart
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/card.tsx).
## API Reference
### Card
| prop | type | default | description |
| -------------- | --------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to be rendered inside the card |
| `variant` | `'default' \| 'secondary' \| 'tertiary' \| 'transparent'` | `'default'` | Visual variant of the card surface |
| `className` | `string` | - | Additional CSS classes to apply |
| `animation` | `"disable-all" \| undefined` | `undefined` | Animation configuration. Use `"disable-all"` to disable all animations including children |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Card.Header
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the header |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Card.Body
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the body |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Card.Footer
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the footer |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Card.Title
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered as the title text |
| `className` | `string` | - | Additional CSS classes |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Card.Description
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered as the description text |
| `className` | `string` | - | Additional CSS classes |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
# Separator
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/separator
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(layout)/separator.mdx
> A simple line to separate content visually.
## Import
```tsx
import { Separator } from "heroui-native";
```
## Anatomy
```tsx
```
* **Separator**: A simple line component that separates content visually. Can be oriented horizontally or vertically, with customizable thickness and variant styles.
## Usage
### Basic Usage
The Separator component creates a visual separation between content sections.
```tsx
```
### Orientation
Control the direction of the separator with the `orientation` prop.
```tsx
Horizontal separator
Content below
Left
Right
```
### Variants
Choose between thin and thick variants for different visual emphasis.
```tsx
```
### Custom Thickness
Set a specific thickness value for precise control.
```tsx
```
## Example
```tsx
import { Separator, Surface } from 'heroui-native';
import { Text, View } from 'react-native';
export default function SeparatorExample() {
return (
HeroUI Native
A modern React Native component library.
Components
Themes
Examples
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/separator.tsx).
## API Reference
### Separator
| prop | type | default | description |
| -------------- | ---------------------------- | -------------- | -------------------------------------------------------------------------------------------- |
| `variant` | `'thin' \| 'thick'` | `'thin'` | Variant style of the separator |
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Orientation of the separator |
| `thickness` | `number` | `undefined` | Custom thickness in pixels. Controls height for horizontal or width for vertical orientation |
| `className` | `string` | `undefined` | Additional CSS classes to apply |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
# Surface
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/surface
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(layout)/surface.mdx
> Container component that provides elevation and background styling.
## Import
```tsx
import { Surface } from 'heroui-native';
```
## Anatomy
The Surface component is a container that provides elevation and background styling. It accepts children and can be customized with variants and styling props.
```tsx
...
```
* **Surface**: Main container component that provides consistent padding, background styling, and elevation through variants.
## Usage
### Basic Usage
The Surface component creates a container with consistent padding and styling.
```tsx
...
```
### Variants
Control the visual appearance with different surface levels.
```tsx
...
...
...
```
### Nested Surfaces
Create visual hierarchy by nesting surfaces with different variants.
```tsx
...
...
...
```
### Custom Styling
Apply custom styles using className or style props.
```tsx
...
...
```
### Disable All Animations
Disable all animations including children by using the `"disable-all"` value for the `animation` prop.
```tsx
{
/* Disable all animations including children */
}
No Animations ;
```
## Example
```tsx
import { Surface } from 'heroui-native';
import { Text, View } from 'react-native';
export default function SurfaceExample() {
return (
Surface Content
This is a default surface variant. It uses bg-surface styling.
Surface Content
This is a secondary surface variant. It uses bg-surface-secondary
styling.
Surface Content
This is a tertiary surface variant. It uses bg-surface-tertiary
styling.
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/surface.tsx).
## API Reference
### Surface
| prop | type | default | description |
| -------------- | --------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------- |
| `variant` | `'default' \| 'secondary' \| 'tertiary' \| 'transparent'` | `'default'` | Visual variant controlling background color and border |
| `children` | `React.ReactNode` | - | Content to be rendered inside the surface |
| `className` | `string` | - | Additional CSS classes to apply |
| `animation` | `"disable-all" \| undefined` | `undefined` | Animation configuration. Use `"disable-all"` to disable all animations including children |
| `asChild` | `boolean` | `false` | Whether to render as a child element |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
# Avatar
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/avatar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(media)/avatar.mdx
> Displays a user avatar with support for images, text initials, or fallback icons.
## Import
```tsx
import { Avatar } from 'heroui-native';
```
## Anatomy
```tsx
```
* **Avatar**: Main container that manages avatar display state. Provides size and color context to child components. Supports animation configuration to control all child animations.
* **Avatar.Image**: Optional image component that displays the avatar image. Handles loading states and errors automatically with opacity-based fade-in animation.
* **Avatar.Fallback**: Optional fallback component shown when image fails to load or is unavailable. Displays a default person icon when no children are provided. Supports configurable entering animations with delay support.
## Usage
### Basic Usage
The Avatar component displays a default person icon when no image or text is provided.
```tsx
```
### With Image
Display an avatar image with automatic fallback handling.
```tsx
JD
```
### With Text Initials
Show text initials as the avatar content.
```tsx
AB
```
### With Custom Icon
Provide a custom icon as fallback content.
```tsx
```
### Sizes
Control the avatar size with the size prop.
```tsx
```
### Variants
Choose between different visual styles with the `variant` prop.
```tsx
DF
SF
```
### Colors
Apply different color variants to the avatar.
```tsx
DF
AC
SC
WR
DG
```
### Delayed Fallback
Show fallback after a delay to prevent flashing during image load.
```tsx
NA
```
### Custom Image Component
Use a custom image component with the asChild prop.
```tsx
import { Image } from 'expo-image';
EI
;
```
### Animation Control
Control animations at different levels of the Avatar component.
#### Disable All Animations
Disable all animations including children from the root component:
```tsx
JD
```
#### Custom Image Animation
Customize the image opacity animation:
```tsx
JD
```
#### Custom Fallback Animation
Customize the fallback entering animation:
```tsx
import { FadeInDown } from 'react-native-reanimated';
JD
;
```
#### Disable Individual Animations
Disable animations for specific components:
```tsx
JD
```
## Example
```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: 'John Doe' },
{ id: 2, image: 'https://example.com/user2.jpg', name: 'Jane Smith' },
{ id: 3, image: 'https://example.com/user3.jpg', name: 'Bob Johnson' },
];
return (
{users.map((user) => (
{user.name
.split(' ')
.map((n) => n[0])
.join('')}
))}
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/avatar.tsx).
## API Reference
### Avatar
| prop | type | default | description |
| -------------- | ------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Avatar content (Image and/or Fallback components) |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the avatar |
| `variant` | `'default' \| 'soft'` | `'default'` | Visual variant of the avatar |
| `color` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | `'accent'` | Color variant of the avatar |
| `className` | `string` | - | Additional CSS classes to apply |
| `animation` | `"disable-all"` \| `undefined` | `undefined` | Animation configuration. Use `"disable-all"` to disable all animations including children |
| `alt` | `string` | `'Avatar'` | Alternative text description for accessibility |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Avatar.Image
Props extend different base types depending on the `asChild` prop value:
* When `asChild={false}` (default): extends `AnimatedProps` from React Native Reanimated
* When `asChild={true}`: extends primitive image props for custom image components
**Note:** When using `asChild={true}` with custom image components, the `className` prop may not be applied in some cases depending on the custom component's implementation. Ensure your custom component properly handles style props.
| prop | type | default | description |
| ----------------------- | ---------------------------------------------- | ------- | ------------------------------------------------------------ |
| `source` | `ImageSourcePropType` | - | Image source (required when `asChild={false}`) |
| `asChild` | `boolean` | `false` | Whether to use a custom image component as child |
| `className` | `string` | - | Additional CSS classes to apply |
| `animation` | `AvatarImageAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `...AnimatedProps` | `AnimatedProps` or primitive props | - | Additional props based on `asChild` value |
#### AvatarImageAnimation
Animation configuration for avatar image component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------------------- | ----------------------- | --------------------------------------------------- | ----------------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `opacity.value` | `[number, number]` | `[0, 1]` | Opacity values \[initial, loaded] for image animation |
| `opacity.timingConfig` | `WithTimingConfig` | `{ duration: 200, easing: Easing.in(Easing.ease) }` | Animation timing configuration |
**Note:** Animation is automatically disabled when `asChild={true}`
### Avatar.Fallback
| prop | type | default | description |
| ----------------------- | ------------------------------------------------------------- | --------------------- | --------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Fallback content (text, icon, or custom element) |
| `delayMs` | `number` | `0` | Delay in milliseconds before showing the fallback (applied to entering animation) |
| `color` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | inherited from parent | Color variant of the fallback |
| `className` | `string` | - | Additional CSS classes for the container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for different parts |
| `styles` | `{ container?: ViewStyle; text?: TextStyle }` | - | Styles for different parts of the avatar fallback |
| `textProps` | `TextProps` | - | Props to pass to Text component when children is a string |
| `iconProps` | `PersonIconProps` | - | Props to customize the default person icon |
| `animation` | `AvatarFallbackAnimation` | - | Animation configuration |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | All Reanimated Animated.View props are supported |
**classNames prop:** `ElementSlots` provides type-safe CSS classes for different parts of the fallback component. Available slots: `container`, `text`.
#### `styles`
| prop | type | description |
| ----------- | ----------- | --------------------------- |
| `container` | `ViewStyle` | Styles for the container |
| `text` | `TextStyle` | Styles for the text content |
#### AvatarFallbackAnimation
Animation configuration for avatar fallback component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------------- | ----------------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `entering.value` | `EntryOrExitLayoutType` | `FadeIn` `.duration(200)` `.easing(Easing.in(Easing.ease))` `.delay(0)` | Custom entering animation for fallback |
#### PersonIconProps
| prop | type | description |
| ------- | -------- | ------------------------------------- |
| `size` | `number` | Size of the icon in pixels (optional) |
| `color` | `string` | Color of the icon (optional) |
## Hooks
### useAvatar Hook
Hook to access Avatar primitive root context. Provides access to avatar status.
**Note:** The `status` property is particularly useful for adding a skeleton loader while the image is loading.
```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'` | Current loading state of the avatar image. |
| `setStatus` | `(status: 'loading' \| 'loaded' \| 'error') => void` | Function to manually set the avatar status (advanced usage) |
**Status Values:**
* `'loading'`: Image is currently being loaded. Use this state to show a skeleton loader.
* `'loaded'`: Image has successfully loaded.
* `'error'`: Image failed to load or source is invalid. The fallback component is automatically shown in this state.
# Accordion
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/accordion
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(navigation)/accordion.mdx
> A collapsible content panel for organizing information in a compact space
## Import
```tsx
import { Accordion } from 'heroui-native';
```
## Anatomy
```tsx
...
...
...
```
* **Accordion**: Main container that manages the accordion state and behavior. Controls expansion/collapse of items, supports single or multiple selection modes, and provides variant styling (default or surface).
* **Accordion.Item**: Container for individual accordion items. Wraps the trigger and content, managing the expanded state for each item.
* **Accordion.Trigger**: Interactive element that toggles item expansion. Built on Header and Trigger primitives.
* **Accordion.Indicator**: Optional visual indicator showing expansion state. Defaults to an animated chevron icon that rotates based on item state.
* **Accordion.Content**: Container for expandable content. Animated with layout transitions for smooth expand/collapse effects.
## Usage
### Basic Usage
The Accordion component uses compound parts to create expandable content sections.
```tsx
...
...
```
### Single Selection Mode
Allow only one item to be expanded at a time.
```tsx
...
...
...
...
```
### Multiple Selection Mode
Allow multiple items to be expanded simultaneously.
```tsx
...
...
...
...
...
...
```
### Surface Variant
Apply a surface container style to the accordion.
```tsx
...
...
```
### Custom Indicator
Replace the default chevron indicator with custom content.
```tsx
...
...
```
### Without Separators
Hide the separators between accordion items.
```tsx
...
...
...
...
```
### Custom Styling
Apply custom styles using className, classNames, or styles props.
```tsx
...
...
```
### With PressableFeedback
Use `Accordion.Trigger` with `asChild` prop and wrap content with `PressableFeedback` to add custom press feedback animations.
```tsx
import { Accordion, PressableFeedback } from 'heroui-native';
import { View } from 'react-native';
Item Title
...
;
```
## Example
```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: 'How do I place an order?',
icon: ,
content:
'Lorem ipsum dolor sit amet consectetur. Netus nunc mauris risus consequat. Libero placerat dignissim consectetur nisl.',
},
{
id: '2',
title: 'What payment methods do you accept?',
icon: ,
content:
'Lorem ipsum dolor sit amet consectetur. Netus nunc mauris risus consequat. Libero placerat dignissim consectetur nisl.',
},
{
id: '3',
title: 'How much does shipping cost?',
icon: ,
content:
'Lorem ipsum dolor sit amet consectetur. Netus nunc mauris risus consequat. Libero placerat dignissim consectetur nisl.',
},
];
return (
{accordionData.map((item) => (
{item.icon}
{item.title}
{item.content}
))}
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/accordion.tsx).
## API Reference
### Accordion
| prop | type | default | description |
| ----------------------- | -------------------------------------------------- | ----------- | -------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the accordion |
| `selectionMode` | `'single' \| 'multiple'` | - | Whether the accordion allows single or multiple expanded items |
| `variant` | `'default' \| 'surface'` | `'default'` | Visual variant of the accordion |
| `hideSeparator` | `boolean` | `false` | Whether to hide the separator between accordion items |
| `defaultValue` | `string \| string[] \| undefined` | - | Default expanded item(s) in uncontrolled mode |
| `value` | `string \| string[] \| undefined` | - | Controlled expanded item(s) |
| `isDisabled` | `boolean` | - | Whether all accordion items are disabled |
| `isCollapsible` | `boolean` | `true` | Whether expanded items can be collapsed |
| `animation` | `AccordionRootAnimation` | - | Animation configuration for accordion |
| `className` | `string` | - | Additional CSS classes for the container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for the slots |
| `styles` | `Partial>` | - | Styles for different parts of the accordion root |
| `onValueChange` | `(value: string \| string[] \| undefined) => void` | - | Callback when expanded items change |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | All Reanimated Animated.View props are supported |
#### `ElementSlots`
| prop | type | description |
| ----------- | -------- | ------------------------------------------------- |
| `container` | `string` | Custom class name for the accordion container |
| `separator` | `string` | Custom class name for the separator between items |
#### `styles`
| prop | type | description |
| ----------- | ----------- | -------------------------------------- |
| `container` | `ViewStyle` | Styles for the accordion container |
| `separator` | `ViewStyle` | Styles for the separator between items |
#### AccordionRootAnimation
Animation configuration for accordion root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| -------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Disable animations while customizing properties |
| `layout.value` | `LayoutTransition` | `LinearTransition` `.springify()` `.damping(140)` `.stiffness(1600)` `.mass(4)` | Custom layout animation for accordion transitions |
### Accordion.Item
| prop | type | default | description |
| ----------------------- | --------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((props: AccordionItemRenderProps) => React.ReactNode)` | - | Children elements to be rendered inside the accordion item, or a render function |
| `value` | `string` | - | Unique value to identify this item |
| `isDisabled` | `boolean` | - | Whether this specific item is disabled |
| `className` | `string` | - | Additional CSS classes |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | All Reanimated Animated.View props are supported |
#### AccordionItemRenderProps
| prop | type | description |
| ------------ | --------- | ------------------------------------------------ |
| `isExpanded` | `boolean` | Whether the accordion item is currently expanded |
| `value` | `string` | Unique value identifier for this accordion item |
### Accordion.Trigger
| prop | type | default | description |
| ------------------- | ----------------- | ------- | ------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the trigger |
| `className` | `string` | - | Additional CSS classes |
| `isDisabled` | `boolean` | - | Whether the trigger is disabled |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### Accordion.Indicator
| prop | type | default | description |
| ----------------------- | ----------------------------- | ------- | ---------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom indicator content, if not provided defaults to animated chevron |
| `className` | `string` | - | Additional CSS classes |
| `iconProps` | `AccordionIndicatorIconProps` | - | Icon configuration |
| `animation` | `AccordionIndicatorAnimation` | - | Animation configuration for indicator |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | All Reanimated Animated.View props are supported |
#### AccordionIndicatorIconProps
| prop | type | default | description |
| ------- | -------- | ------------ | ----------------- |
| `size` | `number` | `16` | Size of the icon |
| `color` | `string` | `foreground` | Color of the icon |
#### AccordionIndicatorAnimation
Animation configuration for accordion indicator component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ----------------------- | ----------------------- | -------------------------------------------- | ------------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `rotation.value` | `[number, number]` | `[0, -180]` | Rotation values \[collapsed, expanded] in degrees |
| `rotation.springConfig` | `WithSpringConfig` | `{ damping: 140, stiffness: 1000, mass: 4 }` | Spring animation configuration for rotation |
### Accordion.Content
| prop | type | default | description |
| -------------- | --------------------------- | ------- | --------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the content |
| `className` | `string` | - | Additional CSS classes |
| `animation` | `AccordionContentAnimation` | - | Animation configuration for content |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### AccordionContentAnimation
Animation configuration for accordion content component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------------- | ----------------------- | ---------------------------------------------------------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `entering.value` | `EntryOrExitLayoutType` | `FadeIn` `.duration(200)` `.easing(Easing.out(Easing.ease))` | Custom entering animation for content |
| `exiting.value` | `EntryOrExitLayoutType` | `FadeOut` `.duration(200)` `.easing(Easing.in(Easing.ease))` | Custom exiting animation for content |
## Hooks
### useAccordion
Hook to access the accordion root context. Must be used within an `Accordion` component.
```tsx
import { useAccordion } from 'heroui-native';
const { value, onValueChange, selectionMode, isCollapsible, isDisabled } =
useAccordion();
```
#### Returns
| property | type | description |
| --------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `selectionMode` | `'single' \| 'multiple' \| undefined` | Whether the accordion allows single or multiple expanded items |
| `value` | `(string \| undefined) \| string[]` | Currently expanded item(s) - string for single mode, array for multiple mode |
| `onValueChange` | `(value: string \| undefined) => void \| ((value: string[]) => void)` | Callback function to update expanded items |
| `isCollapsible` | `boolean` | Whether expanded items can be collapsed |
| `isDisabled` | `boolean \| undefined` | Whether all accordion items are disabled |
### useAccordionItem
Hook to access the accordion item context. Must be used within an `Accordion.Item` component.
```tsx
import { useAccordionItem } from 'heroui-native';
const { value, isExpanded, isDisabled, nativeID } = useAccordionItem();
```
#### Returns
| property | type | description |
| ------------ | ---------------------- | ---------------------------------------------------- |
| `value` | `string` | Unique value identifier for this accordion item |
| `isExpanded` | `boolean` | Whether the accordion item is currently expanded |
| `isDisabled` | `boolean \| undefined` | Whether this specific item is disabled |
| `nativeID` | `string` | Native ID used for accessibility and ARIA attributes |
## Special Notes
When using the Accordion component alongside other components in the same view, you should import and apply `AccordionLayoutTransition` to those components to ensure smooth and consistent layout animations across the entire screen.
```jsx
import { Accordion, AccordionLayoutTransition } from 'heroui-native';
import Animated from 'react-native-reanimated';
{/* Other content */}
{/* Accordion items */}
;
```
This ensures that when the accordion expands or collapses, all components on the screen animate with the same timing and easing, creating a cohesive user experience.
# ListGroup
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/list-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(navigation)/list-group.mdx
> A Surface-based container that groups related list items with consistent layout and spacing.
## Import
```tsx
import { ListGroup } from 'heroui-native';
```
## Anatomy
```tsx
...
...
...
```
* **ListGroup**: Surface-based root container that groups related list items. Supports all Surface variants (default, secondary, tertiary, transparent).
* **ListGroup.Item**: Pressable horizontal flex-row container for a single item, providing consistent spacing and alignment.
* **ListGroup.ItemPrefix**: Optional leading content slot for icons, avatars, or other visual elements.
* **ListGroup.ItemContent**: Flex-1 wrapper for title and description, occupying the remaining horizontal space.
* **ListGroup.ItemTitle**: Primary text label styled with foreground color and medium font weight.
* **ListGroup.ItemDescription**: Secondary text styled with muted color and smaller font size.
* **ListGroup.ItemSuffix**: Optional trailing content slot. Renders a chevron-right icon by default; accepts children to override the default icon.
## Usage
### Basic Usage
The ListGroup component uses compound parts to create grouped list items with title and description.
```tsx
Personal Info
Name, email, phone number
Payment Methods
Visa ending in 4829
```
### With Icons
Add leading icons using the `ListGroup.ItemPrefix` slot.
```tsx
Profile
Name, photo, bio
Security
Password, 2FA
```
### Title Only
Omit `ListGroup.ItemDescription` to display title-only items.
```tsx
Wi-Fi
Bluetooth
```
### Surface Variant
Apply a different visual variant to the root container.
```tsx
Wi-Fi
```
### Custom Suffix
Override the default chevron icon by passing children to `ListGroup.ItemSuffix`.
```tsx
Language
English
Notifications
7
```
### Custom Suffix Icon Props
Customise the default chevron icon size and color using `iconProps`.
```tsx
Storage
12.4 GB of 50 GB used
```
### With PressableFeedback
Wrap items with `PressableFeedback` to add scale and ripple press feedback animations. When using this pattern, pass `onPress` on `PressableFeedback` instead of `ListGroup.Item` and disable the item with `disabled` prop.
```tsx
import { ListGroup, PressableFeedback, Separator } from 'heroui-native';
{}}>
Appearance
Theme, font size, display
{}}>
Notifications
Alerts, sounds, badges
```
## Example
```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 (
Account
Personal Info
Name, email, phone number
Payment Methods
Visa ending in 4829
Preferences
Appearance
Theme, font size, display
Notifications
Alerts, sounds, badges
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/list-group.tsx).
## API Reference
### ListGroup
| prop | type | default | description |
| -------------- | --------------------------------------------------------- | ----------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the group |
| `variant` | `'default' \| 'secondary' \| 'tertiary' \| 'transparent'` | `'default'` | Visual variant of the underlying Surface container |
| `className` | `string` | - | Additional CSS classes for the root container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### ListGroup.Item
| prop | type | default | description |
| ------------------- | ----------------- | ------- | ------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the item |
| `className` | `string` | - | Additional CSS classes for the item |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### ListGroup.ItemPrefix
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Leading content such as icons or avatars |
| `className` | `string` | - | Additional CSS classes for the prefix |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### ListGroup.ItemContent
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content area, typically title and description |
| `className` | `string` | - | Additional CSS classes for the content area |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### ListGroup.ItemTitle
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Title text or custom content |
| `className` | `string` | - | Additional CSS classes for the title |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### ListGroup.ItemDescription
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Description text or custom content |
| `className` | `string` | - | Additional CSS classes for the description |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### ListGroup.ItemSuffix
| prop | type | default | description |
| -------------- | -------------------- | ------- | -------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom trailing content; overrides the default chevron-right icon when provided |
| `className` | `string` | - | Additional CSS classes for the suffix |
| `iconProps` | `ListGroupIconProps` | - | Props to customise the default chevron-right icon. Only applies when no children |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ListGroupIconProps
| prop | type | default | description |
| ------- | -------- | ------------------- | ---------------------------------- |
| `size` | `number` | `16` | Size of the chevron icon in pixels |
| `color` | `string` | theme `muted` color | Color of the chevron icon |
# Tabs
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/tabs
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(navigation)/tabs.mdx
> Organize content into tabbed views with animated transitions and indicators.
## Import
```tsx
import { Tabs } from 'heroui-native';
```
## Anatomy
```tsx
...
...
...
```
* **Tabs**: Main container that manages tab state and selection. Controls active tab, handles value changes, and provides context to child components.
* **Tabs.List**: Container for tab triggers. Groups triggers together with optional styling variants (primary or secondary).
* **Tabs.ScrollView**: Optional scrollable wrapper for tab triggers. Enables horizontal scrolling when tabs overflow with automatic centering of active tab.
* **Tabs.Trigger**: Interactive button for each tab. Handles press events to change active tab and measures its position for indicator animation.
* **Tabs.Label**: Text content for tab triggers. Displays the tab title with appropriate styling.
* **Tabs.Indicator**: Animated visual indicator for active tab. Smoothly transitions between tabs using spring or timing animations.
* **Tabs.Separator**: Visual separator between tabs. Shows when the current tab value is not in the `betweenValues` array, with animated opacity transitions.
* **Tabs.Content**: Container for tab panel content. Shows content when its value matches the active tab.
## Usage
### Basic Usage
The Tabs component uses compound parts to create navigable content sections.
```tsx
Tab 1
Tab 2
...
...
```
### Primary Variant
Default rounded primary style for tab triggers.
```tsx
Settings
Profile
...
...
```
### Secondary Variant
Underline style indicator for a more minimal appearance.
```tsx
Overview
Analytics
...
...
```
### Scrollable Tabs
Handle many tabs with horizontal scrolling.
```tsx
First Tab
Second Tab
Third Tab
Fourth Tab
Fifth Tab
...
...
...
...
...
```
### Disabled Tabs
Disable specific tabs to prevent interaction.
```tsx
Active
Disabled
Another
...
...
```
### With Icons
Combine icons with labels for enhanced visual context.
```tsx
Home
Search
...
...
```
### With Render Function
Use a render function on `Tabs.Trigger` to access state and customize content based on selection.
```tsx
{({ isSelected, value, isDisabled }) => (
Settings
)}
{({ isSelected }) => (
<>
Profile
>
)}
...
...
```
### With Separators
Add visual separators between tabs that show when the active tab is not between specified values.
```tsx
General
Notifications
Profile
...
...
...
```
## Example
```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 (
General
Notifications
Profile
Show sidebar
Display the sidebar navigation panel
Account activity
Notifications about your account activity
Name
Update profile
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/tabs.tsx).
## API Reference
### Tabs
| prop | type | default | description |
| --------------- | ---------------------------- | ----------- | ----------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside tabs |
| `value` | `string` | - | Currently active tab value |
| `variant` | `'primary' \| 'secondary'` | `'primary'` | Visual variant of the tabs |
| `className` | `string` | - | Additional CSS classes for the container |
| `animation` | `"disable-all" \| undefined` | `undefined` | Animation configuration. Use `"disable-all"` to disable all animations including children |
| `onValueChange` | `(value: string) => void` | - | Callback when the active tab changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Tabs.List
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the list |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Tabs.ScrollView
| prop | type | default | description |
| --------------------------- | ---------------------------------------- | ---------- | -------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the scroll view |
| `scrollAlign` | `'start' \| 'center' \| 'end' \| 'none'` | `'center'` | Scroll alignment variant for the selected item |
| `className` | `string` | - | Additional CSS classes for the scroll view |
| `contentContainerClassName` | `string` | - | Additional CSS classes for the content container |
| `...ScrollViewProps` | `ScrollViewProps` | - | All standard React Native ScrollView props are supported |
### Tabs.Trigger
| prop | type | default | description |
| ------------------- | ------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((props: TabsTriggerRenderProps) => React.ReactNode)` | - | Children elements to be rendered inside the trigger, or a render function |
| `value` | `string` | - | The unique value identifying this tab |
| `isDisabled` | `boolean` | `false` | Whether the trigger is disabled |
| `className` | `string` | - | Additional CSS classes |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### TabsTriggerRenderProps
When using a render function for `children`, the following props are provided:
| property | type | description |
| ------------ | --------- | ------------------------------------------ |
| `isSelected` | `boolean` | Whether this trigger is currently selected |
| `value` | `string` | The value of the trigger |
| `isDisabled` | `boolean` | Whether the trigger is disabled |
### Tabs.Label
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Text content to be rendered as label |
| `className` | `string` | - | Additional CSS classes |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Tabs.Indicator
| prop | type | default | description |
| ----------------------- | ------------------------ | ------- | ------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Custom indicator content |
| `className` | `string` | - | Additional CSS classes |
| `animation` | `TabsIndicatorAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | All Reanimated Animated.View props are supported |
#### TabsIndicatorAnimation
Animation configuration for Tabs.Indicator component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------- | -------------------------------------- | ---------------------------------------------------------------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `width.type` | `'spring' \| 'timing'` | `'spring'` | Type of animation to use |
| `width.config` | `WithSpringConfig \| WithTimingConfig` | `{ stiffness: 1200, damping: 120 }` (spring) or `{ duration: 200 }` (timing) | Reanimated animation configuration |
| `height.type` | `'spring' \| 'timing'` | `'spring'` | Type of animation to use |
| `height.config` | `WithSpringConfig \| WithTimingConfig` | `{ stiffness: 1200, damping: 120 }` (spring) or `{ duration: 200 }` (timing) | Reanimated animation configuration |
| `translateX.type` | `'spring' \| 'timing'` | `'spring'` | Type of animation to use |
| `translateX.config` | `WithSpringConfig \| WithTimingConfig` | `{ stiffness: 1200, damping: 120 }` (spring) or `{ duration: 200 }` (timing) | Reanimated animation configuration |
### Tabs.Separator
| prop | type | default | description |
| ----------------------- | ------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `betweenValues` | `string[]` | - | Array of tab values between which the separator should be visible. The separator shows when the current tab value is NOT in this array |
| `isAlwaysVisible` | `boolean` | `false` | If true, opacity is always 1 regardless of the current tab value |
| `className` | `string` | - | Additional CSS classes |
| `animation` | `TabsSeparatorAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `children` | `React.ReactNode` | - | Custom separator content |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | All Reanimated Animated.View props are supported |
**Note:** The following style properties are occupied by animations and cannot be set via className:
* `opacity` - Animated for separator visibility transitions (0 when current tab is in `betweenValues`, 1 when not)
To customize these properties, use the `animation` prop. To completely disable animated styles and use your own via className or style prop, set `isAnimatedStyleActive={false}`.
#### TabsSeparatorAnimation
Animation configuration for Tabs.Separator component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------------------- | ----------------------- | ------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `opacity.value` | `[number, number]` | `[0, 1]` | Opacity values \[hidden, visible] |
| `opacity.timingConfig` | `WithTimingConfig` | `{ duration: 200 }` | Animation timing configuration |
### Tabs.Content
| prop | type | default | description |
| -------------- | ----------------- | ------- | --------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements to be rendered inside the content |
| `value` | `string` | - | The value of the tab this content belongs to |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
## Hooks
### useTabs
Hook to access tabs root context values within custom components or compound components.
```tsx
import { useTabs } from 'heroui-native';
const CustomComponent = () => {
const { value, onValueChange, nativeID } = useTabs();
// ... your implementation
};
```
**Returns:** `UseTabsReturn`
| property | type | description |
| --------------- | ------------------------- | ------------------------------------------ |
| `value` | `string` | Currently active tab value |
| `onValueChange` | `(value: string) => void` | Callback function to change the active tab |
| `nativeID` | `string` | Unique identifier for the tabs instance |
**Note:** This hook must be used within a `Tabs` component. It will throw an error if called outside of the tabs context.
### useTabsMeasurements
Hook to access tab measurements context values for managing tab trigger positions and dimensions.
```tsx
import { useTabsMeasurements } from 'heroui-native';
const CustomIndicator = () => {
const { measurements, variant } = useTabsMeasurements();
// ... your implementation
};
```
**Returns:** `UseTabsMeasurementsReturn`
| property | type | description |
| ----------------- | ------------------------------------------------------- | ------------------------------------------------- |
| `measurements` | `Record` | Record of measurements for each tab trigger |
| `setMeasurements` | `(key: string, measurements: ItemMeasurements) => void` | Function to update measurements for a tab trigger |
| `variant` | `'primary' \| 'secondary'` | Visual variant of the tabs |
#### ItemMeasurements
| property | type | description |
| -------- | -------- | ----------------------------------- |
| `width` | `number` | Width of the tab trigger in pixels |
| `height` | `number` | Height of the tab trigger in pixels |
| `x` | `number` | X position of the tab trigger |
**Note:** This hook must be used within a `Tabs` component. It will throw an error if called outside of the tabs context.
### useTabsTrigger
Hook to access tab trigger context values within custom components or compound components.
```tsx
import { useTabsTrigger } from 'heroui-native';
const CustomLabel = () => {
const { value, isSelected, nativeID } = useTabsTrigger();
// ... your implementation
};
```
**Returns:** `UseTabsTriggerReturn`
| property | type | description |
| ------------ | --------- | ------------------------------------------ |
| `value` | `string` | The value of this trigger |
| `nativeID` | `string` | Unique identifier for this trigger |
| `isSelected` | `boolean` | Whether this trigger is currently selected |
**Note:** This hook must be used within a `Tabs.Trigger` component. It will throw an error if called outside of the trigger context.
# BottomSheet
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/bottom-sheet
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(overlays)/bottom-sheet.mdx
> Displays a bottom sheet that slides up from the bottom with animated transitions and swipe-to-dismiss gestures.
## Import
```tsx
import { BottomSheet } from 'heroui-native';
```
## Anatomy
```tsx
...
...
...
...
```
* **BottomSheet**: Root component that manages open state and provides context to child components.
* **BottomSheet.Trigger**: Pressable element that opens the bottom sheet when pressed.
* **BottomSheet.Portal**: Renders bottom sheet content in a portal with full window overlay.
* **BottomSheet.Overlay**: Background overlay that covers the screen, typically closes bottom sheet when pressed.
* **BottomSheet.Content**: Main bottom sheet container using @gorhom/bottom-sheet for rendering with gesture support.
* **BottomSheet.Close**: Close button for the bottom sheet. Can accept custom children or uses default close icon.
* **BottomSheet.Title**: Bottom sheet title text with semantic heading role and accessibility linking.
* **BottomSheet.Description**: Bottom sheet description text that provides additional context with accessibility linking.
## Usage
### Basic Bottom Sheet
Simple bottom sheet with title, description, and close button.
```tsx
Open Bottom Sheet
...
...
```
### Detached Bottom Sheet
Bottom sheet that appears detached from the bottom edge with custom spacing.
```tsx
...
...
```
### Scrollable with Snap Points
Bottom sheet with multiple snap points and scrollable content.
To make scrollable content work correctly inside `BottomSheet.Content`, follow these base principles:
* Use a scrollable from [`@gorhom/bottom-sheet`](https://gorhom.dev/react-native-bottom-sheet/components/bottomsheetscrollview) (e.g. `BottomSheetScrollView`, `BottomSheetFlatList`, `BottomSheetSectionList`, `BottomSheetVirtualizedList`). A plain `ScrollView`/`FlatList` from `react-native` will let the sheet intercept the scroll gesture.
* On `BottomSheet.Content`, disable over-drag and dynamic sizing so the sheet does not grow with its content or absorb the scroll: `enableOverDrag={false}` and `enableDynamicSizing={false}`.
* Give `BottomSheet.Content` a fixed height via `contentContainerClassName="h-full"` (or any other fixed height). The constraint must be on `BottomSheet.Content`, not on the scrollable child — the scrollable needs a bounded parent to scroll inside.
```tsx
import { BottomSheetScrollView } from '@gorhom/bottom-sheet';
...
...
;
```
See the full example with a sticky footer (`BottomSheetFooter`) in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/components/bottom-sheet/scrollable-with-snap-points.tsx).
### Custom Overlay
Replace the default overlay with custom content like blur effects.
```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
...
...
```
### With Keyboard-Aware Input
When rendering an `Input` or `InputOTP` inside `BottomSheet.Content`, use the `useBottomSheetAwareHandlers` hook to wire keyboard avoidance handlers. Pass the returned `onFocus` / `onBlur` to your input.
> **Note**: `useBottomSheetAwareHandlers` must be used inside a `BottomSheet`. Call it from a child component rendered inside `BottomSheet.Content` — outside of a `BottomSheet` context the returned handlers are no-ops.
For scrollable content, also configure `BottomSheet.Content` with `keyboardBehavior="extend"` (or `"interactive"`) and `keyboardShouldPersistTaps="handled"` on the scrollable so taps don't dismiss the keyboard before reaching their target.
```tsx
import { BottomSheet, Input, useBottomSheetAwareHandlers } from 'heroui-native';
const BottomSheetTextInput = () => {
const { onFocus, onBlur } = useBottomSheetAwareHandlers();
return ;
};
...
;
```
See full examples for [`Input`](https://github.com/heroui-inc/heroui-native/blob/main/example/src/components/bottom-sheet/with-text-input.tsx) and [`InputOTP`](https://github.com/heroui-inc/heroui-native/blob/main/example/src/components/bottom-sheet/with-otp-input.tsx) inside a bottom sheet.
## Example
```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 (
Open Bottom Sheet
Keep yourself safe
Update your software to the latest version for better security and
performance.
setIsOpen(false)}>Update Now
setIsOpen(false)}>
Later
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/bottom-sheet.tsx).
## API Reference
### BottomSheet
| prop | type | default | description |
| --------------- | -------------------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Bottom sheet content and trigger elements |
| `isOpen` | `boolean` | - | Controlled open state of the bottom sheet |
| `isDefaultOpen` | `boolean` | `false` | Initial open state when uncontrolled |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration |
| `onOpenChange` | `(value: boolean) => void` | - | Callback when open state changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Animation Configuration
Animation configuration for bottom sheet root component. Can be:
* `"disable-all"`: Disable all animations including children
* `undefined`: Use default animations
### BottomSheet.Trigger
| prop | type | default | description |
| -------------------------- | ----------------------- | ------- | -------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Trigger element content |
| `asChild` | `boolean` | - | Render as child element without wrapper |
| `...TouchableOpacityProps` | `TouchableOpacityProps` | - | All standard React Native TouchableOpacity props are supported |
### BottomSheet.Portal
| prop | type | default | description |
| -------------------------------------------- | ---------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Portal content (overlay and bottom sheet) |
| `disableFullWindowOverlay` | `boolean` | `false` | When true on iOS, uses View instead of FullWindowOverlay. Enables element inspector; overlay won't appear above native modals |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | Controls whether VoiceOver treats the overlay window as a modal container. When `true`, VoiceOver is restricted to elements inside the overlay. iOS only. Unstable: may change with react-native-screens updates |
| `className` | `string` | - | Additional CSS classes for portal container |
| `style` | `StyleProp` | - | Additional styles for portal container |
| `hostName` | `string` | - | Optional portal host name for specific container |
| `forceMount` | `boolean` | - | Force mount when closed for animation purposes |
### BottomSheet.Overlay
| prop | type | default | description |
| ----------------------- | ------------------------------------------------------ | ------- | ------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Custom overlay content |
| `className` | `string` | - | Additional CSS classes for overlay |
| `style` | `ViewStyle` | - | Additional styles for overlay container |
| `animation` | `Omit` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `isCloseOnPress` | `boolean` | `true` | Whether pressing overlay closes bottom sheet |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### Animation Configuration
Animation configuration for bottom sheet overlay component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration (excluding `entering` and `exiting` properties)
| prop | type | default | description |
| --------------- | -------------------------- | ----------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `opacity.value` | `[number, number, number]` | `[0, 1, 0]` | Opacity values \[idle, open, close] |
### BottomSheet.Content
| prop | type | default | description |
| --------------------------- | ---------------------------------------- | ------- | -------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Bottom sheet content |
| `className` | `string` | - | Additional CSS classes for bottom sheet container |
| `containerClassName` | `string` | - | Additional CSS classes for container |
| `contentContainerClassName` | `string` | - | Additional CSS classes for content container |
| `backgroundClassName` | `string` | - | Additional CSS classes for background |
| `handleClassName` | `string` | - | Additional CSS classes for handle |
| `handleIndicatorClassName` | `string` | - | Additional CSS classes for handle indicator |
| `contentContainerProps` | `Omit` | - | Props for the content container |
| `animation` | `AnimationDisabled` | - | Animation configuration |
| `...GorhomBottomSheetProps` | `Partial` | - | All [@gorhom/bottom-sheet props](https://gorhom.dev/react-native-bottom-sheet/props) are supported |
**Note**: You can use all components from [@gorhom/bottom-sheet](https://gorhom.dev/react-native-bottom-sheet/components/bottomsheetview) inside the content, such as `BottomSheetView`, `BottomSheetScrollView`, `BottomSheetFlatList`, etc.
### BottomSheet.Close
BottomSheet.Close extends [CloseButton](./close-button) and automatically handles bottom sheet dismissal when pressed.
### BottomSheet.Title
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Title content |
| `className` | `string` | - | Additional CSS classes for title |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### BottomSheet.Description
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Description content |
| `className` | `string` | - | Additional CSS classes for description |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
## Hooks
### useBottomSheet
Hook to access bottom sheet primitive context.
```tsx
const { isOpen, onOpenChange } = useBottomSheet();
```
| property | type | description |
| -------------- | -------------------------- | ----------------------------- |
| `isOpen` | `boolean` | Current open state |
| `onOpenChange` | `(value: boolean) => void` | Function to change open state |
### useBottomSheetAnimation
Hook to access bottom sheet animation context for advanced customization.
```tsx
const { progress } = useBottomSheetAnimation();
```
| property | type | description |
| ---------- | --------------------- | -------------------------------------------- |
| `progress` | `SharedValue` | Animation progress (0=idle, 1=open, 2=close) |
### useBottomSheetAwareHandlers
Hook that returns `onFocus` and `onBlur` handlers for keyboard avoidance when an `Input` or `InputOTP` is rendered inside `BottomSheet.Content`. Must be used inside a `BottomSheet` — outside of a `BottomSheet` context, the returned handlers are no-ops.
```tsx
const { onFocus, onBlur } = useBottomSheetAwareHandlers();
```
| property | type | description |
| --------- | ------------------------- | ------------------------------------------------------------------------------------ |
| `onFocus` | `(e: FocusEvent) => void` | Focus handler that notifies the bottom sheet about the keyboard target |
| `onBlur` | `(e: BlurEvent) => void` | Blur handler that conditionally clears the keyboard target in the bottom sheet state |
## Special Notes
### Element Inspector (iOS)
BottomSheet uses FullWindowOverlay on iOS, which renders in a separate native window. This breaks the React Native element inspector. To enable the inspector during development, set `disableFullWindowOverlay={true}` on `BottomSheet.Portal`. Tradeoff: the bottom sheet will not appear above native modals when disabled.
### Handling Close Callbacks
It's recommended to use `BottomSheet`'s `onOpenChange` prop for handling close callbacks, as it reliably fires for all close scenarios (swiping down, pressing overlay, pressing close button, programmatic close, etc.).
```tsx
{
setIsOpen(value);
if (!value) {
// This callback runs whenever the bottom sheet closes
// regardless of how it was closed
yourCallbackOnClose();
}
}}
>
...
```
**Note**: `BottomSheet.Content`'s `onClose` prop (from @gorhom/bottom-sheet) has limitations and will only fire when the bottom sheet is closed by swiping down. It won't fire when closing via overlay press, close button, or programmatic close. For reliable close callbacks, always use `BottomSheet`'s `onOpenChange` prop instead.
# Dialog
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/dialog
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(overlays)/dialog.mdx
> Displays a modal overlay with animated transitions and gesture-based dismissal.
## Import
```tsx
import { Dialog } from 'heroui-native';
```
## Anatomy
```tsx
...
...
...
...
...
```
* **Dialog**: Root component that manages open state and provides context to child components.
* **Dialog.Trigger**: Pressable element that opens the dialog when pressed.
* **Dialog.Portal**: Renders dialog content in a portal with centered layout and animation control.
* **Dialog.Overlay**: Background overlay that appears behind the dialog content, typically closes dialog when pressed.
* **Dialog.Content**: Main dialog container with gesture support for drag-to-dismiss.
* **Dialog.Close**: Close button for the dialog. Can accept custom children or uses default close icon.
* **Dialog.Title**: Dialog title text with semantic heading role.
* **Dialog.Description**: Dialog description text that provides additional context.
## Usage
### Basic Dialog
Simple dialog with title, description, and close button.
```tsx
Open Dialog
...
...
```
### Scrollable Content
Handle long content with scroll views.
```tsx
...
...
...
```
### Form Dialog
Dialog with text inputs and keyboard handling.
```tsx
...
...
...
Submit
```
## Example
```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 (
Open Dialog
Confirm Action
Are you sure you want to proceed with this action? This cannot be
undone.
setIsOpen(false)}>
Cancel
Confirm
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/dialog.tsx).
## API Reference
### Dialog
| prop | type | default | description |
| --------------- | -------------------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Dialog content and trigger elements |
| `isOpen` | `boolean` | - | Controlled open state of the dialog |
| `isDefaultOpen` | `boolean` | `false` | Initial open state when uncontrolled |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration |
| `onOpenChange` | `(value: boolean) => void` | - | Callback when open state changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### AnimationRootDisableAll
Animation configuration for dialog root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
### Dialog.Trigger
| prop | type | default | description |
| -------------------------- | ----------------------- | ------- | -------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Trigger element content |
| `asChild` | `boolean` | - | Render as child element without wrapper |
| `...TouchableOpacityProps` | `TouchableOpacityProps` | - | All standard React Native TouchableOpacity props are supported |
### Dialog.Portal
| prop | type | default | description |
| -------------------------------------------- | ---------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Portal content (overlay and dialog) |
| `disableFullWindowOverlay` | `boolean` | `false` | When true on iOS, uses View instead of FullWindowOverlay. Enables element inspector; overlay won't appear above native modals |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | Controls whether VoiceOver treats the overlay window as a modal container. When `true`, VoiceOver is restricted to elements inside the overlay. iOS only. Unstable: may change with react-native-screens updates |
| `className` | `string` | - | Additional CSS classes for portal container |
| `style` | `StyleProp` | - | Additional styles for portal container |
| `hostName` | `string` | - | Optional portal host name for specific container |
| `forceMount` | `boolean` | - | Force mount when closed for animation purposes |
### Dialog.Overlay
| prop | type | default | description |
| ----------------------- | ------------------------ | ------- | ------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Custom overlay content |
| `className` | `string` | - | Additional CSS classes for overlay |
| `style` | `ViewStyle` | - | Additional styles for overlay container |
| `animation` | `DialogOverlayAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `isCloseOnPress` | `boolean` | `true` | Whether pressing overlay closes dialog |
| `forceMount` | `boolean` | - | Force mount when closed for animation purposes |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### DialogOverlayAnimation
Animation configuration for dialog overlay component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| --------------- | -------------------------- | ----------------------- | ----------------------------------------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `opacity.value` | `[number, number, number]` | `[0, 1, 0]` | Opacity values \[idle, open, close] (progress-based, for dialog presentation) |
| `entering` | `EntryOrExitLayoutType` | `FadeIn.duration(200)` | Custom entering animation (for popover presentation) |
| `exiting` | `EntryOrExitLayoutType` | `FadeOut.duration(150)` | Custom exiting animation (for popover presentation) |
### Dialog.Content
| prop | type | default | description |
| ----------------------- | ------------------------ | ------- | --------------------------------------------------- |
| `children` | `React.ReactNode` | - | Dialog content |
| `className` | `string` | - | Additional CSS classes for content container |
| `style` | `StyleProp` | - | Additional styles for content container |
| `animation` | `DialogContentAnimation` | - | Animation configuration |
| `isSwipeable` | `boolean` | `true` | Whether the dialog content can be swiped to dismiss |
| `forceMount` | `boolean` | - | Force mount when closed for animation purposes |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | All Reanimated Animated.View props are supported |
#### DialogContentAnimation
Animation configuration for dialog content component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------- | ----------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `entering` | `EntryOrExitLayoutType` | Keyframe with `scale: 0.96→1` and `opacity: 0→1` (200ms, easing: `Easing.out(Easing.ease)`) | Custom entering animation |
| `exiting` | `EntryOrExitLayoutType` | Keyframe with `scale: 1→0.96` and `opacity: 1→0` (150ms, easing: `Easing.in(Easing.ease)`) | Custom exiting animation |
### Dialog.Close
Dialog.Close extends [CloseButton](./close-button) and automatically handles dialog dismissal when pressed.
### Dialog.Title
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Title content |
| `className` | `string` | - | Additional CSS classes for title |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Dialog.Description
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Description content |
| `className` | `string` | - | Additional CSS classes for description |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
## Hooks
### useDialog
Hook to access dialog primitive context.
```tsx
const { isOpen, onOpenChange } = useDialog();
```
| property | type | description |
| -------------- | -------------------------- | ----------------------------- |
| `isOpen` | `boolean` | Current open state |
| `onOpenChange` | `(value: boolean) => void` | Function to change open state |
### useDialogAnimation
Hook to access dialog animation context for advanced customization.
```tsx
const { progress, isDragging, isGestureReleaseAnimationRunning } =
useDialogAnimation();
```
| property | type | description |
| ---------------------------------- | ---------------------- | -------------------------------------------- |
| `progress` | `SharedValue` | Animation progress (0=idle, 1=open, 2=close) |
| `isDragging` | `SharedValue` | Whether dialog is being dragged |
| `isGestureReleaseAnimationRunning` | `SharedValue` | Whether gesture release animation is running |
## Special Notes
### Element Inspector (iOS)
Dialog uses FullWindowOverlay on iOS. To enable the React Native element inspector during development, set `disableFullWindowOverlay={true}` on `Dialog.Portal`. Tradeoff: the dialog will not appear above native modals when disabled.
# Popover
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/popover
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(overlays)/popover.mdx
> Displays a floating content panel anchored to a trigger element with placement and alignment options.
## Import
```tsx
import { Popover } from 'heroui-native';
```
## Anatomy
```tsx
...
...
...
```
* **Popover**: Main container that manages open/close state, positioning, and provides context to child components.
* **Popover.Trigger**: Clickable element that toggles popover visibility. Wraps any child element with press handlers.
* **Popover.Portal**: Renders popover content in a portal layer above other content. Ensures proper stacking and positioning.
* **Popover.Overlay**: Optional background overlay. Can be transparent or semi-transparent to capture outside clicks.
* **Popover.Content**: Container for popover content with positioning, styling, and collision detection. Supports both popover and bottom-sheet presentations.
* **Popover.Arrow**: Optional arrow element pointing to the trigger. Automatically positioned based on placement.
* **Popover.Close**: Close button for the popover. Can accept custom children or uses default close icon.
* **Popover.Title**: Optional title text with pre-styled typography.
* **Popover.Description**: Optional description text with muted styling.
## Usage
### Basic Usage
The Popover component uses compound parts to create floating content panels.
```tsx
...
...
```
### With Title and Description
Structure popover content with title and description for better information hierarchy.
```tsx
...
...
...
```
### With Arrow
Add an arrow pointing to the trigger element for better visual connection.
```tsx
...
...
```
> **Note:** When using ` `, you need to apply a border to `Popover.Content`, for instance using the `border border-border` class. This ensures the arrow visually connects properly with the content border.
### Width Control
Control the width of the popover content using the `width` prop.
```tsx
{
/* Fixed width in pixels */
}
...
...
;
{
/* Match trigger width */
}
...
...
;
{
/* Full width (100%) */
}
...
...
;
{
/* Auto-size to content (default) */
}
...
...
;
```
### Bottom Sheet Presentation
Use bottom sheet presentation for mobile-optimized interaction patterns.
> **Important:** The `presentation` prop on `Popover.Content` must match the `presentation` prop on `Popover.Root`. In development mode, a mismatch will throw an error.
```tsx
...
...
...
Close
```
### Placement Options
Control where the popover appears relative to the trigger element.
```tsx
...
...
```
### Alignment Options
Fine-tune content alignment along the placement axis.
```tsx
...
...
```
### Custom Animation
Configure custom animations for open and close transitions using the `animation` prop on `Popover.Root`.
```tsx
...
...
```
### Programmatic control
```tsx
// Open or close popover programmatically using ref
const popoverRef = useRef(null);
// Open programmatically
popoverRef.current?.open();
// Close programmatically
popoverRef.current?.close();
// Full example
Trigger
Content
popoverRef.current?.close()}>Close
;
```
## Example
```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 (
Show Info
Information
This popover includes a title and description to provide more
structured information to users.
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/popover.tsx).
## API Reference
### Popover
| prop | type | default | description |
| --------------- | ----------------------------- | ----------- | ---------------------------------------------------------------------------------------------- |
| `children` | `ReactNode` | - | Children elements to be rendered inside the popover |
| `isOpen` | `boolean` | - | Whether the popover is open (controlled mode) |
| `isDefaultOpen` | `boolean` | - | The open state of the popover when initially rendered (uncontrolled mode) |
| `onOpenChange` | `(isOpen: boolean) => void` | - | Callback when the popover open state changes |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration. Can be `false`, `"disabled"`, `"disable-all"`, `true`, or `undefined` |
| `presentation` | `'popover' \| 'bottom-sheet'` | `'popover'` | Presentation mode for the popover content |
| `asChild` | `boolean` | `false` | Whether to render as a child element |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### AnimationRootDisableAll
Animation configuration for popover root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
### Popover.Trigger
| prop | type | default | description |
| ------------------- | ---------------- | ------- | ------------------------------------------------------- |
| `children` | `ReactNode` | - | The trigger element content |
| `className` | `string` | - | Additional CSS classes for the trigger |
| `asChild` | `boolean` | `true` | Whether to render as a child element |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### Popover.Portal
| prop | type | default | description |
| -------------------------------------------- | ----------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `ReactNode` | - | The portal content (required) |
| `disableFullWindowOverlay` | `boolean` | `false` | When true on iOS, uses View instead of FullWindowOverlay. Enables element inspector; overlay won't appear above native modals |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | Controls whether VoiceOver treats the overlay window as a modal container. When `true`, VoiceOver is restricted to elements inside the overlay. iOS only. Unstable: may change with react-native-screens updates |
| `hostName` | `string` | - | Optional name of the host element for the portal |
| `forceMount` | `boolean` | - | Whether to force mount the component in the DOM |
| `className` | `string` | - | Additional CSS classes for the portal container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Popover.Overlay
| prop | type | default | description |
| ----------------------- | ------------------------- | ------- | ------------------------------------------------------------ |
| `className` | `string` | - | Additional CSS classes for the overlay |
| `closeOnPress` | `boolean` | `true` | Whether to close the popover when overlay is pressed |
| `forceMount` | `boolean` | - | Whether to force mount the component in the DOM |
| `animation` | `PopoverOverlayAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `asChild` | `boolean` | `false` | Whether to render as a child element |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | All Reanimated Animated.View props are supported |
#### PopoverOverlayAnimation
Animation configuration for popover overlay component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| --------------- | -------------------------- | --------------------------- | ----------------------------------------------------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `opacity.value` | `[number, number, number]` | `[0, 1, 0]` | Opacity values \[idle, open, close] - Takes effect for bottom-sheet/dialog presentation |
| `entering` | `EntryOrExitLayoutType` | FadeIn with duration 200ms | Custom Keyframe animation for entering transition - Takes effect for popover presentation |
| `exiting` | `EntryOrExitLayoutType` | FadeOut with duration 150ms | Custom Keyframe animation for exiting transition - Takes effect for popover presentation |
### Popover.Content (Popover Presentation)
| prop | type | default | description |
| ------------------------- | ------------------------------------------------ | --------------- | ------------------------------------------------------------------------------------------------------- |
| `children` | `ReactNode` | - | The popover content |
| `presentation` | `'popover'` | `'popover'` | Presentation mode - must match Popover.Root presentation prop. When not provided, defaults to 'popover' |
| `width` | `number \| 'trigger' \| 'content-fit' \| 'full'` | `'content-fit'` | Width sizing strategy for the content |
| `placement` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | Placement of the popover relative to trigger |
| `align` | `'start' \| 'center' \| 'end'` | `'center'` | Alignment along the placement axis |
| `avoidCollisions` | `boolean` | `true` | Whether to flip placement when close to viewport edges |
| `offset` | `number` | `9` | Distance from trigger element in pixels |
| `alignOffset` | `number` | `0` | Offset along the alignment axis in pixels |
| `disablePositioningStyle` | `boolean` | `false` | Whether to disable automatic positioning styles |
| `forceMount` | `boolean` | - | Whether to force mount the component in the DOM |
| `insets` | `Insets` | - | Screen edge insets to respect when positioning |
| `className` | `string` | - | Additional CSS classes for the content container |
| `animation` | `PopupPopoverContentAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `asChild` | `boolean` | `false` | Whether to render as a child element |
| `...Animated.ViewProps` | `Animated.ViewProps` | - | All Reanimated Animated.View props are supported |
### Popover.Content (Bottom Sheet Presentation)
| prop | type | default | description |
| --------------------------- | ---------------------- | ------- | ---------------------------------------------------------------------------------------------- |
| `children` | `ReactNode` | - | The bottom sheet content |
| `presentation` | `'bottom-sheet'` | - | Presentation mode - must be 'bottom-sheet' and match Popover.Root presentation prop (required) |
| `contentContainerClassName` | `string` | - | Additional CSS classes for the content container |
| `contentContainerProps` | `BottomSheetViewProps` | - | Props for the content container |
| `enablePanDownToClose` | `boolean` | `true` | Whether pan down gesture closes the sheet |
| `backgroundStyle` | `ViewStyle` | - | Style for the bottom sheet background |
| `handleIndicatorStyle` | `ViewStyle` | - | Style for the bottom sheet handle indicator |
| `...BottomSheetProps` | `BottomSheetProps` | - | All @gorhom/bottom-sheet props are supported |
#### PopupPopoverContentAnimation
Animation configuration for popover content component (popover presentation). Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------- | ----------------------- | --------------------------------------------------------------- | ------------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `entering` | `EntryOrExitLayoutType` | Keyframe with translateY/translateX, scale, and opacity (200ms) | Custom Keyframe animation for entering transition |
| `exiting` | `EntryOrExitLayoutType` | Keyframe mirroring entering animation (150ms) | Custom Keyframe animation for exiting transition |
### Popover.Arrow
| prop | type | default | description |
| --------------------- | ---------------------------------------- | ------- | --------------------------------------------------------------------- |
| `className` | `string` | - | Additional CSS classes for the arrow |
| `height` | `number` | `12` | Height of the arrow in pixels |
| `width` | `number` | `20` | Width of the arrow in pixels |
| `fill` | `string` | - | Fill color of the arrow (defaults to content background) |
| `stroke` | `string` | - | Stroke (border) color of the arrow (defaults to content border color) |
| `strokeWidth` | `number` | `1` | Stroke width of the arrow border in pixels |
| `strokeBaselineInset` | `number` | `1` | Baseline inset in pixels for stroke alignment |
| `placement` | `'top' \| 'bottom' \| 'left' \| 'right'` | - | Placement of the popover (inherited from content) |
| `children` | `ReactNode` | - | Custom arrow content (replaces default SVG arrow) |
| `style` | `StyleProp` | - | Additional styles for the arrow container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Popover.Close
Popover.Close extends [CloseButton](./close-button) and automatically handles popover dismissal when pressed.
### Popover.Title
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `children` | `ReactNode` | - | The title text content |
| `className` | `string` | - | Additional CSS classes for the title |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Popover.Description
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `children` | `ReactNode` | - | The description text content |
| `className` | `string` | - | Additional CSS classes for the description |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
## Hooks
### usePopover
Hook to access popover context values within custom components or compound components.
```tsx
import { usePopover } from 'heroui-native';
const CustomContent = () => {
const { isOpen, onOpenChange, triggerPosition } = usePopover();
// ... your implementation
};
```
**Returns:** `UsePopoverReturn`
| property | type | description |
| -------------------- | --------------------------------------------------- | ----------------------------------------------------------------- |
| `isOpen` | `boolean` | Whether the popover is currently open |
| `onOpenChange` | `(open: boolean) => void` | Callback function to change the popover open state |
| `isDefaultOpen` | `boolean \| undefined` | Whether the popover should be open by default (uncontrolled mode) |
| `isDisabled` | `boolean \| undefined` | Whether the popover is disabled |
| `triggerPosition` | `LayoutPosition \| null` | The position of the trigger element relative to the viewport |
| `setTriggerPosition` | `(triggerPosition: LayoutPosition \| null) => void` | Function to update the trigger element's position |
| `contentLayout` | `LayoutRectangle \| null` | The layout measurements of the popover content |
| `setContentLayout` | `(contentLayout: LayoutRectangle \| null) => void` | Function to update the content layout measurements |
| `nativeID` | `string` | Unique identifier for the popover instance |
**Note:** This hook must be used within a `Popover` component. It will throw an error if called outside of the popover context.
### usePopoverAnimation
Hook to access popover animation state values within custom components or compound components.
```tsx
import { usePopoverAnimation } from 'heroui-native';
const CustomContent = () => {
const { progress, isDragging } = usePopoverAnimation();
// ... your implementation
};
```
**Returns:** `UsePopoverAnimationReturn`
| property | type | description |
| ------------ | ---------------------- | ------------------------------------------------------------------ |
| `progress` | `SharedValue` | Progress value for the popover animation (0=idle, 1=open, 2=close) |
| `isDragging` | `SharedValue` | Dragging state shared value |
**Note:** This hook must be used within a `Popover` component. It will throw an error if called outside of the popover animation context.
## Special Notes
### Element Inspector (iOS)
Popover uses FullWindowOverlay on iOS. To enable the React Native element inspector during development, set `disableFullWindowOverlay={true}` on `Popover.Portal`. Tradeoff: the popover will not appear above native modals when disabled.
### Native Modal (iOS)
When a `Popover` is opened inside a screen presented as a native modal (`presentation: 'modal' | 'formSheet' | 'pageSheet'`), the popover content may render shifted upward. In the new architecture (Fabric), `react-native-screens` marks `RNSModalScreen` as a Fabric root, so the trigger's position is reported relative to the modal's origin while `FullWindowOverlay` (where the popover is mounted) is anchored to the iOS application window. Compensate by adding `safeAreaInsets.top` to `offset`:
```tsx
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const insets = useSafeAreaInsets();
...
;
```
# Toast
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/toast
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(overlays)/toast.mdx
> Displays temporary notification messages that appear at the top or bottom of the screen.
## Import
```tsx
import { Toast, useToast } from 'heroui-native';
```
## Anatomy
```tsx
...
...
...
```
* **Toast**: Main container that displays notification messages. Handles positioning, animations, and swipe gestures.
* **Toast.Title**: Title text of the toast notification. Inherits variant styling from parent Toast context.
* **Toast.Description**: Descriptive text content displayed below the title.
* **Toast.Action**: Action button within the toast. Button variant is automatically determined based on toast variant but can be overridden.
* **Toast.Close**: Close button for dismissing the toast. Renders as an icon-only button that calls hide when pressed.
## Usage
### Usage Pattern 1: Simple String
Show a toast with a simple string message.
```tsx
const { toast } = useToast();
toast.show('This is a toast message');
```
### Usage Pattern 2: Config Object
Show a toast with label, description, variant, and action button using a config object.
```tsx
const { toast } = useToast();
toast.show({
variant: 'success',
label: 'You have upgraded your plan',
description: 'You can continue using HeroUI Chat',
icon: ,
actionLabel: 'Close',
onActionPress: ({ hide }) => hide(),
});
```
### Usage Pattern 3: Custom Component
Show a toast with a fully custom component for complete control over styling and layout.
```tsx
const { toast } = useToast();
toast.show({
component: (props) => (
Custom Toast
This is a custom toast component
),
});
```
**Note**: Toast items are memoized for performance. If you need to pass external state (like loading state) to a custom toast component, it will not update automatically. Use shared state techniques instead, such as React Context, state management libraries, or refs to ensure state updates propagate to the toast component.
### Disabling All Animations
Disable all animations including children by using `"disable-all"`. This cascades down to all child components (like Button in Toast.Action).
```tsx
const { toast } = useToast();
toast.show({
variant: 'success',
label: 'Operation completed',
description: 'All animations are disabled',
animation: 'disable-all',
});
```
Or with a custom component:
```tsx
const { toast } = useToast();
toast.show({
component: (props) => (
No animations
This toast has all animations disabled
Action
),
});
```
## Example
```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: 'You have upgraded your plan',
description: 'You can continue using HeroUI Chat',
actionLabel: 'Close',
onActionPress: ({ hide }) => hide(),
})
}
>
Show Success Toast
toast.show({
component: (props) => (
Custom Toast
This uses a custom component
props.hide()}>Undo
),
})
}
>
Show Custom Toast
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/toast.tsx).
## Global Configuration
Configure toast behavior globally using `HeroUINativeProvider` config prop. Global configs serve as defaults for all toasts unless overridden locally.
> **Note**: For complete provider configuration options, see the [Provider documentation](/docs/native/getting-started/handbook/provider#toast-configuration).
### Insets
Insets control the distance of toast sides from screen edges. Insets are added to safe area insets. To set all toasts to have a side distance of 20px from screen edges, configure insets:
```tsx
{children}
```
### Content Wrapper with KeyboardAvoidingView
Wrap toast content with KeyboardAvoidingView to ensure toasts adjust when the keyboard appears:
```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}
);
}
```
### Default Props
Set global defaults for variant, placement, animation, and swipe behavior:
```tsx
{children}
```
## API Reference
### Toast
| prop | type | default | description |
| ----------------------- | ------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------- |
| `variant` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | `'default'` | Visual variant of the toast |
| `placement` | `'top' \| 'bottom'` | `'top'` | Placement of the toast on screen |
| `isSwipeable` | `boolean` | `true` | Whether the toast can be swiped to dismiss and dragged with rubber effect |
| `animation` | `ToastRootAnimation` | - | Animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `className` | `string` | - | Additional CSS class for the toast container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ToastRootAnimation
Animation configuration for Toast component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Disable animations while customizing properties |
| `opacity.value` | `[number, number]` | `[1, 0]` | Opacity interpolation values for fade effect as toasts move beyond visible stack |
| `opacity.timingConfig` | `WithTimingConfig` | `{ duration: 300 }` | Animation timing configuration for opacity transitions |
| `translateY.value` | `[number, number]` | `[0, 10]` | Translate Y interpolation values for peek effect of stacked toasts |
| `translateY.timingConfig` | `WithTimingConfig` | `{ duration: 300 }` | Animation timing configuration for translateY transitions |
| `scale.value` | `[number, number]` | `[1, 0.97]` | Scale interpolation values for depth effect of stacked toasts |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 300 }` | Animation timing configuration for scale transitions |
| `entering.top` | `EntryOrExitLayoutType` | `FadeInUp` `.springify()` `.withInitialValues({ opacity: 1, transform: [{ translateY: -100 }] })` `.mass(3)` | Custom entering animation for top placement |
| `entering.bottom` | `EntryOrExitLayoutType` | `FadeInDown` `.springify()` `.withInitialValues({ opacity: 1, transform: [{ translateY: 100 }] })` `.mass(3)` | Custom entering animation for bottom placement |
| `exiting.top` | `EntryOrExitLayoutType` | Keyframe animation with `translateY: -100, scale: 0.97, opacity: 0.5` | Custom exiting animation for top placement |
| `exiting.bottom` | `EntryOrExitLayoutType` | Keyframe animation with `translateY: 100, scale: 0.97, opacity: 0.5` | Custom exiting animation for bottom placement |
### Toast.Title
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to be rendered as title |
| `className` | `string` | - | Additional CSS classes |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Toast.Description
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to be rendered as description |
| `className` | `string` | - | Additional CSS classes |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Toast.Action
Toast.Action extends all props from [Button](button) component. Button variant is automatically determined based on toast variant but can be overridden.
| prop | type | default | description |
| ----------- | ---------------------- | ------- | ---------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to be rendered as action button label |
| `variant` | `ButtonVariant` | - | Button variant. If not provided, automatically determined from toast variant |
| `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` | Size of the action button |
| `className` | `string` | - | Additional CSS classes |
For inherited props including `onPress`, `isDisabled`, and all Button props, see [Button API Reference](button#api-reference).
### Toast.Close
Toast.Close extends all props from [Button](button) component.
| prop | type | default | description |
| ----------- | ----------------------------------- | ------- | ---------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom close icon. Defaults to CloseIcon |
| `iconProps` | `{ size?: number; color?: string }` | - | Props for the default close icon |
| `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` | Size of the close button |
| `className` | `string` | - | Additional CSS classes |
| `onPress` | `(event: any) => void` | - | Custom press handler. Defaults to hiding toast |
For inherited props including `isDisabled` and all Button props, see [Button API Reference](button#api-reference).
### ToastProviderProps
Props for configuring toast behavior globally via `HeroUINativeProvider` config prop.
| prop | type | default | description |
| -------------------------------------------- | --------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `defaultProps` | `ToastGlobalConfig` | - | Global toast configuration used as defaults for all toasts |
| `disableFullWindowOverlay` | `boolean` | `false` | When true on iOS, uses View instead of FullWindowOverlay. Enables element inspector; toasts won't appear above native modals |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | Controls whether VoiceOver treats the overlay window as a modal container. When `true`, VoiceOver is restricted to elements inside the overlay. iOS only. Unstable: may change with react-native-screens updates |
| `insets` | `ToastInsets` | - | Insets for spacing from screen edges (added to safe area insets) |
| `maxVisibleToasts` | `number` | `3` | Maximum number of visible toasts before opacity starts fading |
| `contentWrapper` | `(children: React.ReactNode) => React.ReactElement` | - | Custom wrapper function to wrap toast content |
| `children` | `React.ReactNode` | - | Children to render |
#### ToastGlobalConfig
Global toast configuration used as defaults for all toasts unless overridden locally.
| prop | type | description |
| ------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `variant` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | Visual variant of the toast |
| `placement` | `'top' \| 'bottom'` | Placement of the toast on screen |
| `isSwipeable` | `boolean` | Whether the toast can be swiped to dismiss and dragged with rubber effect |
| `animation` | `ToastRootAnimation` | Animation configuration for toast |
#### ToastInsets
Insets for spacing from screen edges. Values are added to safe area insets.
| prop | type | default | description |
| -------- | -------- | ------- | --------------------------------------------------------------------------------------------------------- |
| `top` | `number` | - | Inset from the top edge in pixels (added to safe area inset). Platform-specific: iOS = 0, Android = 12 |
| `bottom` | `number` | - | Inset from the bottom edge in pixels (added to safe area inset). Platform-specific: iOS = 6, Android = 12 |
| `left` | `number` | - | Inset from the left edge in pixels (added to safe area inset). Default: 12 |
| `right` | `number` | - | Inset from the right edge in pixels (added to safe area inset). Default: 12 |
## Hooks
### useToast
Hook to access toast functionality. Must be used within a `ToastProvider` (provided by `HeroUINativeProvider`).
| return value | type | description |
| ---------------- | -------------- | ---------------------------------------- |
| `toast` | `ToastManager` | Toast manager with show and hide methods |
| `isToastVisible` | `boolean` | Whether any toast is currently visible |
#### ToastManager
| method | type | description |
| ------ | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `show` | `(options: string \| ToastShowOptions) => string` | Show a toast. Returns the ID of the shown toast. Supports three usage patterns: simple string, config object, or custom component |
| `hide` | `(ids?: string \| string[] \| 'all') => void` | Hide one or more toasts. No argument hides the last toast, 'all' hides all toasts, single ID or array of IDs hides specific toast(s) |
#### ToastShowOptions
Options for showing a toast. Can be either a config object with default styling or a custom component.
**When using config object (without component):**
| prop | type | default | description |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------- |
| `variant` | `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | - | Visual variant of the toast |
| `placement` | `'top' \| 'bottom'` | - | Placement of the toast on screen |
| `isSwipeable` | `boolean` | - | Whether the toast can be swiped to dismiss |
| `animation` | `ToastRootAnimation \| false \| "disabled" \| "disable-all"` | - | Animation configuration for toast |
| `duration` | `number \| 'persistent'` | `4000` | Duration in milliseconds before auto-hide. Set to 'persistent' to prevent auto-hide |
| `id` | `string` | - | Optional ID for the toast. If not provided, one will be generated |
| `label` | `string` | - | Label text for the toast |
| `description` | `string` | - | Description text for the toast |
| `actionLabel` | `string` | - | Action button label text |
| `onActionPress` | `(helpers: { show: (options: string \| ToastShowOptions) => string; hide: (ids?: string \| string[] \| 'all') => void }) => void` | - | Callback function called when the action button is pressed |
| `icon` | `React.ReactNode` | - | Icon element to display in the toast |
| `onShow` | `() => void` | - | Callback function called when the toast is shown |
| `onHide` | `() => void` | - | Callback function called when the toast is hidden |
**When using custom component:**
| prop | type | default | description |
| ----------- | ---------------------------------------------------- | ------- | ----------------------------------------------------------------------------------- |
| `id` | `string` | - | Optional ID for the toast. If not provided, one will be generated |
| `component` | `(props: ToastComponentProps) => React.ReactElement` | - | A function that receives toast props and returns a React element |
| `duration` | `number \| 'persistent'` | `4000` | Duration in milliseconds before auto-hide. Set to 'persistent' to prevent auto-hide |
| `onShow` | `() => void` | - | Callback function called when the toast is shown |
| `onHide` | `() => void` | - | Callback function called when the toast is hidden |
## Special Notes
### Element Inspector (iOS)
Toast uses FullWindowOverlay on iOS. To enable the React Native element inspector during development, set `disableFullWindowOverlay={true}` on `ToastProvider` (via `config.toast` when using HeroUINativeProvider). Tradeoff: toasts will not appear above native modals when disabled.
# Typography
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/text
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(typography)/text.mdx
> Primitive typography component for rendering styled text with semantic type variants.
## Import
```tsx
import { Typography } from 'heroui-native';
```
## Anatomy
```tsx
...
{/* Sub-components */}
...
...
...
```
* **Typography**: Root text element. Selects a typography preset via `type` and exposes orthogonal `align`, `color`, `weight`, and `truncate` props.
* **Typography.Heading**: Convenience wrapper restricted to heading types (`h1`–`h6`). Adds `accessibilityRole="header"` automatically.
* **Typography.Paragraph**: Convenience wrapper restricted to body types (`body`, `body-sm`, `body-xs`).
* **Typography.Code**: Chip-styled inline monospaced text. Uses a platform-appropriate monospace font family.
## Usage
### Basic Usage
The Typography component renders body text by default.
```tsx
Hello, world!
```
### Type Variants
Use the `type` prop to select a semantic typography preset.
```tsx
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Body text
Small body text
Extra-small body text
Code snippet
```
### Headings
Use `Typography.Heading` for heading text with automatic header accessibility role.
```tsx
Page Title
Section Title
Subsection Title
```
### Paragraphs
Use `Typography.Paragraph` for body text.
```tsx
This is a paragraph of body text with the default size.
This is smaller body text.
```
### Code
Use `Typography.Code` (or equivalently ``) for inline code snippets. Both render a chip-styled, monospaced inline element with a subtle background, rounded corners, and a `self-start` layout so it does not stretch in flex containers. The platform monospace `fontFamily` is applied at the `Typography` root, so the two forms are interchangeable.
```tsx
console.log('hello')
console.log('hello')
```
### Alignment
Use the `align` prop to control horizontal alignment. `start` and `end` are RTL-aware (they flip under right-to-left layouts).
```tsx
Start-aligned
Center-aligned
End-aligned
Justified text spreads across the line.
```
> **Note:** `text-justify` is iOS-only on React Native; Android falls back to left alignment.
### Color
Use the `color` prop to apply a semantic foreground color preset.
```tsx
Default foreground
Muted secondary text
```
For other theme colors, pass the corresponding utility through `className` (e.g. `className="text-accent"`, `className="text-danger"`).
### Weight
Use the `weight` prop to override the font weight implied by `type`. The override merges via `tailwind-merge`, so it always wins over the type variant's default weight.
```tsx
Bold H1
Medium body
Semibold body
```
### Truncation
Use the `truncate` boolean prop to limit the text to a single line with an ellipsis. It is mapped to React Native's `numberOfLines={1}`. An explicit `numberOfLines` prop, if provided, takes precedence.
```tsx
A long line of text that will be cut off with an ellipsis when it overflows
the container.
;
{
/* Multi-line truncation via the underlying RN prop */
}
Two-line truncation works through React Native's standard `numberOfLines`
prop.
;
```
## Example
```tsx
import { Typography } from 'heroui-native';
import { View } from 'react-native';
export default function TypographyExample() {
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
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/text.tsx).
## API Reference
### Typography
`Typography` extends all standard React Native `TextProps` with additional typography props.
| prop | type | default | description |
| -------------- | -------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `type` | `'h1' \| 'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6' \| 'body' \| 'body-sm' \| 'body-xs' \| 'code'` | `'body'` | Semantic typography variant (size, default weight, line-height) |
| `align` | `'start' \| 'center' \| 'end' \| 'justify'` | `'start'` | Horizontal alignment. `start` and `end` are RTL-aware. `justify` is iOS-only. |
| `color` | `'default' \| 'muted'` | `'default'` | Semantic foreground color preset |
| `weight` | `'normal' \| 'medium' \| 'semibold' \| 'bold'` | - | Font weight override. When set, overrides the weight implied by `type`. |
| `truncate` | `boolean` | `false` | Truncates the text to a single line with an ellipsis (sets `numberOfLines={1}`). An explicit `numberOfLines` takes precedence. |
| `children` | `React.ReactNode` | - | Content to render |
| `className` | `string` | - | Additional CSS classes |
| `...TextProps` | `TextProps` | - | All standard React Native `Text` props are supported |
### Typography.Heading
Inherits all `Typography` root props (`align`, `color`, `weight`, `truncate`, `className`, and React Native `TextProps`). Sets `accessibilityRole="header"` automatically and narrows `type` to heading variants.
| prop | type | default | description |
| -------------- | ---------------------------------------------- | ------- | ---------------------------------------------------- |
| `type` | `'h1' \| 'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6'` | `'h1'` | Heading level |
| `children` | `React.ReactNode` | - | Content to render |
| `className` | `string` | - | Additional CSS classes |
| `...TextProps` | `TextProps` | - | All standard React Native `Text` props are supported |
### Typography.Paragraph
Inherits all `Typography` root props (`align`, `color`, `weight`, `truncate`, `className`, and React Native `TextProps`). Narrows `type` to body variants.
| prop | type | default | description |
| -------------- | ---------------------------------- | -------- | ---------------------------------------------------- |
| `type` | `'body' \| 'body-sm' \| 'body-xs'` | `'body'` | Paragraph text size |
| `children` | `React.ReactNode` | - | Content to render |
| `className` | `string` | - | Additional CSS classes |
| `...TextProps` | `TextProps` | - | All standard React Native `Text` props are supported |
### Typography.Code
Inherits all `Typography` root props (`align`, `color`, `weight`, `truncate`, `className`, `style`, and React Native `TextProps`). Thin wrapper that forces `type="code"`; the platform monospace `fontFamily` is merged in at the `Typography` root, so `` and `` render identically.
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to render |
| `className` | `string` | - | Additional CSS classes |
| `...TextProps` | `TextProps` | - | All standard React Native `Text` props are supported |
# PressableFeedback
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/pressable-feedback
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(utilities)/pressable-feedback.mdx
> Container component that provides visual feedback for press interactions with automatic scale animation.
## Import
```tsx
import { PressableFeedback } from 'heroui-native';
```
## Anatomy
```tsx
...
```
* **PressableFeedback**: Pressable container with built-in scale animation. Manages press state and container dimensions, providing them to child compound parts via context. Use `animation={false}` to disable the built-in scale when using `PressableFeedback.Scale` instead.
* **PressableFeedback.Scale**: Scale animation wrapper for applying scale to a specific child element. Use this instead of the root's built-in scale when you need control over which element scales or need to apply `className` / `style` to the scale wrapper.
* **PressableFeedback.Highlight**: Highlight overlay for iOS-style press feedback. Renders an absolute-positioned layer that fades in on press.
* **PressableFeedback.Ripple**: Ripple overlay for Android-style press feedback. Renders a radial gradient circle that expands from the touch point.
## Usage
### Basic
PressableFeedback provides press-down scale feedback out of the box. This is the recommended way to use it in most cases.
```tsx
...
```
### With Highlight
Add a highlight overlay for iOS-style feedback effect alongside the built-in scale.
```tsx
...
```
### With Ripple
Add a ripple overlay for Android-style feedback effect alongside the built-in scale.
```tsx
...
```
### Custom Scale Animation
Customize the built-in scale animation via the `animation.scale` prop. Accepts `value`, `timingConfig`, and `ignoreScaleCoefficient`.
```tsx
...
```
### Custom Highlight Animation
Configure highlight overlay opacity and background color.
```tsx
...
```
### Custom Ripple Animation
Configure ripple effect color, opacity, and duration.
```tsx
...
```
### Scale on a Specific Child (PressableFeedback.Scale)
When you need to apply the scale animation to a specific element inside the container rather than the root itself, disable the root's built-in scale with `animation={false}` and use the `PressableFeedback.Scale` compound part. This gives you full control over which element scales and lets you apply `className` / `style` directly to the scale wrapper.
```tsx
...
```
You can combine it with Highlight or Ripple inside the Scale wrapper:
```tsx
...
```
### Disable All Animations
Set `animation="disable-all"` on the root to cascade-disable all animations including the built-in scale and any child compound parts (Scale, Highlight, Ripple).
```tsx
...
```
You can also disable all animations while keeping a scale config (e.g. for toggling at runtime):
```tsx
...
```
## Example
```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
Home robot
Available soon
Get notified
Notify me
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/pressable-feedback.tsx).
## API Reference
### PressableFeedback
| prop | type | default | description |
| ----------------------- | -------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to be wrapped with press feedback |
| `isDisabled` | `boolean` | `false` | Whether the pressable component is disabled |
| `className` | `string` | - | Additional CSS classes |
| `animation` | `PressableFeedbackRootAnimation` | - | Customize scale via `{ scale: ... }`, `false` to disable root scale, `'disable-all'` to cascade-disable all |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether the root's built-in animated styles are active |
| `asChild` | `boolean` | `false` | Whether to render as a child element |
| `...rest` | `AnimatedProps` | - | All Reanimated Animated Pressable props are supported |
#### PressableFeedbackRootAnimation
The root animation prop supports the standard `AnimationRoot` control flow:
* `true` or `undefined`: Use the default built-in scale animation
* `false` or `"disabled"`: Disable the root's built-in scale (use this when applying scale via `PressableFeedback.Scale` instead)
* `"disable-all"`: Cascade-disable all animations including the built-in scale and children (Scale, Highlight, Ripple)
* `object`: Custom configuration for the built-in scale
| prop | type | default | description |
| ------- | ---------------------------------------- | ------- | ------------------------------------------------------------------------------- |
| `scale` | `PressableFeedbackScaleAnimation` | - | Customize the built-in scale animation (value, timingConfig, etc.) |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Control animation state while keeping configuration (e.g. for runtime toggling) |
### PressableFeedback.Scale
Use this compound part when you need to apply scale to a specific child element inside the container, instead of scaling the root itself. Set `animation={false}` on the root to disable its built-in scale when using this component.
| prop | type | default | description |
| ----------------------- | --------------------------------- | ------- | ------------------------------------------------------------ |
| `className` | `string` | - | Additional CSS classes |
| `animation` | `PressableFeedbackScaleAnimation` | - | Animation configuration for scale effect |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `style` | `ViewStyle` | - | Additional styles |
| `...AnimatedProps` | `AnimatedProps` | - | All Reanimated Animated View props are supported |
#### PressableFeedbackScaleAnimation
Animation configuration for scale effect. Can be:
* `false` or `"disabled"`: Disable scale animation
* `true` or `undefined`: Use default scale animation
* `object`: Custom scale configuration
| prop | type | default | description |
| ------------------------ | ----------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `value` | `number` | `0.985` | Scale value when pressed (automatically adjusted based on container width) |
| `timingConfig` | `WithTimingConfig` | `{ duration: 300, easing: Easing.out(Easing.ease) }` | Animation timing configuration |
| `ignoreScaleCoefficient` | `boolean` | `false` | Ignore automatic scale coefficient and use the scale value directly |
### PressableFeedback.Highlight
| prop | type | default | description |
| ----------------------- | ------------------------------------- | ------- | ------------------------------------------------------------ |
| `className` | `string` | - | Additional CSS classes |
| `animation` | `PressableFeedbackHighlightAnimation` | - | Animation configuration for highlight overlay |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `style` | `ViewStyle` | - | Additional styles |
| `...AnimatedProps` | `AnimatedProps` | - | All Reanimated Animated View props are supported |
#### PressableFeedbackHighlightAnimation
Animation configuration for highlight overlay. Can be:
* `false` or `"disabled"`: Disable highlight animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ----------------------- | ----------------------- | ------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `opacity.value` | `[number, number]` | `[0, 0.1]` | Opacity values \[unpressed, pressed] |
| `opacity.timingConfig` | `WithTimingConfig` | `{ duration: 200 }` | Animation timing configuration |
| `backgroundColor.value` | `string` | Theme-aware gray | Background color of highlight overlay |
### PressableFeedback.Ripple
| prop | type | default | description |
| ----------------------- | ----------------------------------------- | ------- | ------------------------------------------------------------ |
| `className` | `string` | - | Additional CSS classes for container slot |
| `classNames` | `ElementSlots` | - | Additional CSS classes for slots (container, ripple) |
| `styles` | `Partial>` | - | Styles for different parts of the ripple overlay |
| `animation` | `PressableFeedbackRippleAnimation` | - | Animation configuration for ripple overlay |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `...ViewProps` | `Omit` | - | All View props except style are supported |
#### `styles`
| prop | type | description |
| ----------- | ----------- | ----------------------------- |
| `container` | `ViewStyle` | Styles for the container slot |
| `ripple` | `ViewStyle` | Styles for the ripple slot |
#### PressableFeedbackRippleAnimation
Animation configuration for ripple overlay. Can be:
* `false` or `"disabled"`: Disable ripple animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------------------------ | -------------------------- | ----------------------- | ---------------------------------------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `backgroundColor.value` | `string` | Computed based on theme | Background color of ripple effect |
| `progress.baseDuration` | `number` | `1000` | Base duration for ripple progress (automatically adjusted based on diagonal) |
| `progress.minBaseDuration` | `number` | `750` | Minimum base duration for the ripple progress animation |
| `progress.ignoreDurationCoefficient` | `boolean` | `false` | Ignore automatic duration coefficient and use base duration directly |
| `opacity.value` | `[number, number, number]` | `[0, 0.1, 0]` | Opacity values \[start, peak, end] for ripple animation |
| `opacity.timingConfig` | `WithTimingConfig` | `{ duration: 200 }` | Animation timing configuration |
| `scale.value` | `[number, number, number]` | `[0, 1, 1]` | Scale values \[start, peak, end] for ripple animation |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 200 }` | Animation timing configuration |
#### `ElementSlots`
Additional CSS classes for ripple slots:
| slot | description |
| ----------- | ------------------------------------------------------------------------------------------------------------------- |
| `container` | Outer container slot (`absolute inset-0`) - styles can be fully customized |
| `ripple` | Inner ripple slot (`absolute top-0 left-0 rounded-full`) - has animated properties that cannot be set via className |
# ScrollShadow
**Category**: native
**URL**: https://v3.heroui.com/en/docs/native/components/scroll-shadow
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/en/native/components/(utilities)/scroll-shadow.mdx
> Adds dynamic gradient shadows to scrollable content based on scroll position and overflow.
## Import
```tsx
import { ScrollShadow } from 'heroui-native';
```
## Anatomy
```tsx
...
```
* **ScrollShadow**: Main container that wraps scrollable components and adds dynamic gradient shadows at the edges based on scroll position and content overflow. Automatically detects scroll orientation (horizontal/vertical) and manages shadow visibility.
* **LinearGradientComponent**: Required prop that accepts a LinearGradient component from compatible libraries (expo-linear-gradient, react-native-linear-gradient, etc.) to render the gradient shadows.
## Usage
### Basic Usage
Wrap any scrollable component to automatically add edge shadows.
```tsx
...
```
### Horizontal Scrolling
The component auto-detects horizontal scrolling from the child's `horizontal` prop.
```tsx
```
### Custom Shadow Size
Control the gradient shadow height/width with the `size` prop.
```tsx
...
```
### Visibility Control
Specify which shadows to display using the `visibility` prop.
```tsx
...
...
...
```
### Custom Shadow Color
Override the default shadow color which uses the theme's background.
```tsx
...
```
### With Custom Scroll Handler
**Important:** ScrollShadow internally converts the child to a Reanimated animated component. If you need to use the `onScroll` prop, you must use `useAnimatedScrollHandler` from react-native-reanimated.
```tsx
import { LinearGradient } from 'expo-linear-gradient';
import Animated, { useAnimatedScrollHandler } from 'react-native-reanimated';
const scrollHandler = useAnimatedScrollHandler({
onScroll: (event) => {
console.log(event.contentOffset.y);
},
});
...
;
```
## Example
```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.
);
}
```
You can find more examples in the [GitHub repository](https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/\(home\)/components/scroll-shadow.tsx).
## API Reference
### ScrollShadow
| prop | type | default | description |
| ------------------------- | ---------------------------------------------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactElement` | - | The scrollable component to enhance with shadows. Must be a single React element (ScrollView, FlatList, etc.) |
| `LinearGradientComponent` | `ComponentType<` `LinearGradientProps>` | **required** | LinearGradient component from any compatible library (expo-linear-gradient, react-native-linear-gradient, etc.) |
| `size` | `number` | `50` | Size (height/width) of the gradient shadow in pixels |
| `orientation` | `'horizontal' \| 'vertical'` | auto-detect | Orientation of the scroll shadow. If not provided, will auto-detect from child's `horizontal` prop |
| `visibility` | `'auto' \| 'top' \| 'bottom' \| 'left' \| 'right' \| 'both' \| 'none'` | `'auto'` | Visibility mode for the shadows. 'auto' shows shadows based on scroll position and content overflow |
| `color` | `string` | theme color | Custom color for the gradient shadow. If not provided, uses the theme's background color |
| `isEnabled` | `boolean` | `true` | Whether the shadow effect is enabled |
| `animation` | `ScrollShadowRootAnimation` | - | Animation configuration |
| `className` | `string` | - | Additional CSS classes to apply to the container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ScrollShadowRootAnimation
Animation configuration for ScrollShadow component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| --------------- | ---------------------------------------- | -------- | ------------------------------------------------------------------------------------ |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Disable animations while customizing properties |
| `opacity.value` | `[number, number]` | `[0, 1]` | `Opacity values [initial, active].` `For bottom/right shadow, this is reversed` |
### LinearGradientProps
The `LinearGradientComponent` prop expects a component that accepts these props:
| prop | type | description |
| ----------- | --------------------------------- | ------------------------------------------------------------------ |
| `colors` | `any` | Array of colors for the gradient |
| `locations` | `any` (optional) | Array of numbers defining the location of each gradient color stop |
| `start` | `any` (optional) | Start point of the gradient (e.g., `{ x: 0, y: 0 }`) |
| `end` | `any` (optional) | End point of the gradient (e.g., `{ x: 1, y: 0 }`) |
| `style` | `StyleProp` (optional) | Style to apply to the gradient view |
## Special Notes
**Important:** ScrollShadow internally converts the child to a Reanimated animated component. If you need to use the `onScroll` prop on your scrollable component, you must use `useAnimatedScrollHandler` from react-native-reanimated instead of the standard `onScroll` prop.
# 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 类 |
# 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)。
# 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 元素。 |
# 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` | - | 可滚动的子内容 |
# 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` | - | 文本内容。 |
# 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`。