`)
* `.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
#### 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.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 |
# Calendar
**Category**: react
**URL**: https://v3.heroui.com/docs/react/components/calendar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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 {DateValue} 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
);
}
```
### 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.
```tsx
"use client";
import {Calendar} from "@heroui/react";
import {getLocalTimeZone} from "@internationalized/date";
import React from "react";
import {CalendarStateContext, useLocale} from "react-aria-components";
function CalendarMonthHeading({offset = 0}: {offset?: number}) {
const state = React.useContext(CalendarStateContext)!;
const {locale} = useLocale();
const startDate = state.visibleRange.start;
const monthDate = startDate.add({months: offset});
const dateObj = monthDate.toDate(getLocalTimeZone());
const monthYear = new Intl.DateTimeFormat(locale, {month: "long", year: "numeric"}).format(
dateObj,
);
return {monthYear} ;
}
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 {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 |
| ------------------------ | ------------------------------ | --------------------------- | ------------------------------------------------------ |
| `value` | `DateValue \| null` | - | Controlled selected date. |
| `defaultValue` | `DateValue \| null` | - | Initial selected date (uncontrolled). |
| `onChange` | `(value: DateValue) => 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. |
| `isDateUnavailable` | `(date: DateValue) => boolean` | - | Marks dates as unavailable. |
| `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}` | `{months: 1}` | Number of visible months. |
| `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` | Current month/year heading. |
| `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. |
### 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/docs/react/components/date-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/date-picker
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/date-range-picker
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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,
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"}
>
)}
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/docs/react/components/range-calendar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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
);
}
```
### 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.
```tsx
"use client";
import {RangeCalendar} from "@heroui/react";
import {getLocalTimeZone} from "@internationalized/date";
import React from "react";
import {RangeCalendarStateContext, useLocale} from "react-aria-components";
function RangeCalendarMonthHeading({offset = 0}: {offset?: number}) {
const state = React.useContext(RangeCalendarStateContext)!;
const {locale} = useLocale();
const startDate = state.visibleRange.start;
const monthDate = startDate.add({months: offset});
const dateObj = monthDate.toDate(getLocalTimeZone());
const monthYear = new Intl.DateTimeFormat(locale, {month: "long", year: "numeric"}).format(
dateObj,
);
return {monthYear} ;
}
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. |
| `isDateUnavailable` | `(date: DateValue) => boolean` | - | Marks dates as unavailable. |
| `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}` | `{months: 1}` | Number of visible months. |
| `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` | Current month/year heading. |
| `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. |
### 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/docs/react/components/time-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/alert
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/meter
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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 |
# Skeleton
**Category**: react
**URL**: https://v3.heroui.com/docs/react/components/skeleton
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/spinner
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/checkbox-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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 */}
{/* Optional */}
{/* 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, Label} 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/docs/react/components/checkbox
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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, Label } from '@heroui/react';
```
### Usage
```tsx
import {Checkbox, Label} 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, Label, Description } from '@heroui/react';
export default () => (
{/* Optional */}
);
```
### Disabled
```tsx
import {Checkbox, Description, Label} from "@heroui/react";
export function Disabled() {
return (
Premium Feature
This feature is coming soon
);
}
```
### Default Selected
```tsx
import {Checkbox, Label} from "@heroui/react";
export function DefaultSelected() {
return (
Enable email notifications
);
}
```
### Controlled
```tsx
"use client";
import {Checkbox, Label} 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, Label} 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)
);
}
```
### With Label
```tsx
import {Checkbox, Label} from "@heroui/react";
export function WithLabel() {
return (
Send me marketing emails
);
}
```
### With Description
```tsx
import {Checkbox, Description, Label} 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, Label} 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, Label} 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, Description, Label} 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, Label} 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, Label} 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, Label } 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 flex flex-col 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 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
* `.checkbox__control` - Checkbox control box
* `.checkbox__indicator` - Checkbox checkmark indicator
* `.checkbox__content` - Optional content container
### 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"]`
* **Focus**: `:focus-visible` or `[data-focus-visible="true"]` (shows focus ring)
* **Disabled**: `:disabled` or `[aria-disabled="true"]` (reduced opacity, no pointer events)
* **Pressed**: `:active` or `[data-pressed="true"]`
## API Reference
### Checkbox Props
Inherits from [React Aria Checkbox](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 |
| `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: CheckboxRenderProps) => React.ReactNode` | - | Checkbox content or render prop |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. |
### CheckboxRenderProps
When using the render prop pattern, these values are provided:
| Prop | Type | Description |
| ----------------- | --------- | ------------------------------------------------- |
| `isSelected` | `boolean` | Whether the checkbox is currently checked |
| `isIndeterminate` | `boolean` | Whether the checkbox is in an indeterminate state |
| `isHovered` | `boolean` | Whether the checkbox is hovered |
| `isPressed` | `boolean` | Whether the checkbox is currently pressed |
| `isFocused` | `boolean` | Whether the checkbox is focused |
| `isFocusVisible` | `boolean` | Whether the checkbox is keyboard focused |
| `isDisabled` | `boolean` | Whether the checkbox is disabled |
| `isReadOnly` | `boolean` | Whether the checkbox is read only |
# Description
**Category**: react
**URL**: https://v3.heroui.com/docs/react/components/description
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/error-message
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/field-error
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/fieldset
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/form
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/input-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/input-otp
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/input
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/label
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/number-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/radio-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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 () => (
✓ {/* Custom indicator (optional) */}
)
```
### 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, Label, 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__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: RadioRenderProps) => React.ReactNode` | - | Radio content or 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: RadioRenderProps) => React.ReactNode` | - | Optional content or render prop that receives the current radio state. |
### Radio.Content Props
Extends `React.HTMLAttributes`.
| Prop | Type | Default | Description |
| ---------- | ----------------- | ------- | ---------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | The content to render inside the content wrapper (typically Label and Description) |
### RadioRenderProps
When using the render prop pattern, these values are provided:
| Prop | Type | Description |
| ---------------- | --------- | ---------------------------------------- |
| `isSelected` | `boolean` | Whether the radio is currently selected |
| `isHovered` | `boolean` | Whether the radio is hovered |
| `isPressed` | `boolean` | Whether the radio is currently pressed |
| `isFocused` | `boolean` | Whether the radio is focused |
| `isFocusVisible` | `boolean` | Whether the radio is keyboard focused |
| `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 |
# SearchField
**Category**: react
**URL**: https://v3.heroui.com/docs/react/components/search-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/text-area
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/text-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/card
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/separator
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/surface
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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
}
```
# Avatar
**Category**: react
**URL**: https://v3.heroui.com/docs/react/components/avatar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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 |
# Accordion
**Category**: react
**URL**: https://v3.heroui.com/docs/react/components/accordion
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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.
);
}
```
### 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/docs/react/components/breadcrumbs
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/disclosure-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/disclosure
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/link
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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
```tsx
import {Link} from "@heroui/react";
export function LinkUnderlineAndOffset() {
return (
Always visible underline
Underline always visible
Underline visible on hover
Hover to see the underline
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
* `hover:underline` - 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 no-underline hover:underline;
}
}
```
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"]`
* **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/docs/react/components/pagination
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/tabs
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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. |
# AlertDialog
**Category**: react
**URL**: https://v3.heroui.com/docs/react/components/alert-dialog
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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)
# Modal
**Category**: react
**URL**: https://v3.heroui.com/docs/react/components/modal
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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, Label, 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/docs/react/components/popover
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/toast
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/tooltip
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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
### 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` | `700` | Delay in milliseconds before showing tooltip |
| `closeDelay` | `number` | `0` | Delay in milliseconds before hiding tooltip |
| `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/docs/react/components/autocomplete
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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}
)}
);
}
```
### 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/docs/react/components/combo-box
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/docs/react/components/select
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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).
# Kbd
**Category**: react
**URL**: https://v3.heroui.com/docs/react/components/kbd
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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` | |
# ScrollShadow
**Category**: react
**URL**: https://v3.heroui.com/docs/react/components/scroll-shadow
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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
### 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 |
# Button
**Category**: native
**URL**: https://v3.heroui.com/docs/native/components/button
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/close-button
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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 |
# Menu
**Category**: native
**URL**: https://v3.heroui.com/docs/native/components/menu
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/native/components/(collections)/menu.mdx
> A floating context menu with positioning, selection groups, and multiple presentation modes.
## Import
```tsx
import { Menu } 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.
## 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
```
### 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/rc/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 |
| `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 |
## 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 |
# TagGroup
**Category**: native
**URL**: https://v3.heroui.com/docs/native/components/tag-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/slider
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/switch
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/chip
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/alert
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/skeleton-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/skeleton
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/spinner
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/checkbox
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/control-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/description
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/field-error
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/input-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/input-otp
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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) => (
))}
>
)}
```
## 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/rc/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/docs/native/components/input
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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
;
```
## 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/rc/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/docs/native/components/label
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/radio-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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](./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.
**Note:** For complete Radio prop documentation (including `Radio.Indicator` and `Radio.IndicatorThumb`), see the [Radio component documentation](./radio).
**Note:** For labels, descriptions, and error messages, use the base components directly:
* Use [Label](./label) component for labels
* Use [Description](./description) component for descriptions
* Use [FieldError](./field-error) 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/docs/native/components/search-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/select
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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 |
| `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.
# TextArea
**Category**: native
**URL**: https://v3.heroui.com/docs/native/components/text-area
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/text-field
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/card
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/separator
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/surface
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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 |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
# Avatar
**Category**: native
**URL**: https://v3.heroui.com/docs/native/components/avatar
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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` | - | 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/docs/native/components/accordion
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/list-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/tabs
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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/docs/native/components/bottom-sheet
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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.
```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
...
...
```
## 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/rc/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 |
| `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) |
## 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/docs/native/components/dialog
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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 |
| `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/docs/native/components/popover
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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 |
| `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.
# Toast
**Category**: native
**URL**: https://v3.heroui.com/docs/native/components/toast
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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 |
| `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.
# PressableFeedback
**Category**: native
**URL**: https://v3.heroui.com/docs/native/components/pressable-feedback
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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 |
| `...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/docs/native/components/scroll-shadow
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/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/rc/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.