v3.0.0-beta.2
Five new components (AlertDialog, ComboBox, Dropdown, Modal, NumberField), Select API improvements, and component refinements.
This release introduces five essential new components, improves the Select component API, and includes various refinements and bug fixes.
Installation
Update to the latest version:
npm i @heroui/styles@beta @heroui/react@betapnpm add @heroui/styles@beta @heroui/react@betayarn add @heroui/styles@beta @heroui/react@betabun add @heroui/styles@beta @heroui/react@betaUsing AI assistants? Simply prompt "Hey Cursor, update HeroUI to the latest version" and your AI assistant will automatically compare versions and apply the necessary changes. Learn more about the HeroUI MCP Server.
What's New
New Components
This release introduces 5 new essential components:
- AlertDialog: Modal dialog for important decisions that require user confirmation. (Documentation)
- ComboBox: Combines a text input with a listbox, allowing users to filter a list of options. (Documentation)
- Dropdown: Displays a list of actions or options that a user can choose. (Documentation)
- Modal: Dialog overlay for focused user interactions and important content. (Documentation)
- NumberField: Number input with increment/decrement buttons, validation, and internationalized formatting. (Documentation)
AlertDialog
"use client";
import {AlertDialog, Button} from "@heroui/react";
export function Default() {
return (
<AlertDialog>
<Button variant="danger">Delete Project</Button>
<AlertDialog.Container>
<AlertDialog.Dialog className="sm:max-w-[400px]">
{({close}) => (
<>
<AlertDialog.Header>
<AlertDialog.Icon status="danger" />
<AlertDialog.Heading>Delete project permanently?</AlertDialog.Heading>
</AlertDialog.Header>
<AlertDialog.Body>
<p>
This will permanently delete <strong>My Awesome Project</strong> and all of its
data. This action cannot be undone.
</p>
</AlertDialog.Body>
<AlertDialog.Footer>
<Button variant="tertiary" onPress={close}>
Cancel
</Button>
<Button variant="danger" onPress={close}>
Delete Project
</Button>
</AlertDialog.Footer>
</>
)}
</AlertDialog.Dialog>
</AlertDialog.Container>
</AlertDialog>
);
}ComboBox
"use client";
import {ComboBox, Input, Label, ListBox} from "@heroui/react";
export function Default() {
return (
<ComboBox className="w-[256px]">
<Label>Favorite Animal</Label>
<ComboBox.InputGroup>
<Input placeholder="Search animals..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Item id="aardvark" textValue="Aardvark">
Aardvark
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="cat" textValue="Cat">
Cat
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
Dog
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="kangaroo" textValue="Kangaroo">
Kangaroo
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="panda" textValue="Panda">
Panda
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="snake" textValue="Snake">
Snake
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</ComboBox.Popover>
</ComboBox>
);
}Dropdown
"use client";
import {Button, Dropdown, Label} from "@heroui/react";
export function Default() {
return (
<Dropdown>
<Button aria-label="Menu" variant="secondary">
Actions
</Button>
<Dropdown.Popover>
<Dropdown.Menu onAction={(key) => console.log(`Selected: ${key}`)}>
<Dropdown.Item id="new-file" textValue="New file">
<Label>New file</Label>
</Dropdown.Item>
<Dropdown.Item id="copy-link" textValue="Copy link">
<Label>Copy link</Label>
</Dropdown.Item>
<Dropdown.Item id="edit-file" textValue="Edit file">
<Label>Edit file</Label>
</Dropdown.Item>
<Dropdown.Item id="delete-file" textValue="Delete file" variant="danger">
<Label>Delete file</Label>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown.Popover>
</Dropdown>
);
}Modal
<Modal.Header>
<Modal.Icon className="bg-default text-foreground">
<Icon className="size-5" icon="gravity-ui:rocket" />
</Modal.Icon>
<Modal.Heading>Welcome to HeroUI</Modal.Heading>
</Modal.Header>"use client";
import {Button, Modal} from "@heroui/react";
import {Icon} from "@iconify/react";
export function Default() {
return (
<Modal>
<Button variant="secondary">Open Modal</Button>
<Modal.Container>
<Modal.Dialog className="sm:max-w-[360px]">
{({close}) => (
<>
<Modal.CloseTrigger />
<Modal.Header>
<Modal.Icon className="bg-default text-foreground">
<Icon className="size-5" icon="gravity-ui:rocket" />
</Modal.Icon>
<Modal.Heading>Welcome to HeroUI</Modal.Heading>
</Modal.Header>
<Modal.Body>
<p>
A beautiful, fast, and modern React UI library for building accessible and
customizable web applications with ease.
</p>
</Modal.Body>
<Modal.Footer>
<Button className="w-full" onPress={close}>
Continue
</Button>
</Modal.Footer>
</>
)}
</Modal.Dialog>
</Modal.Container>
</Modal>
);
}NumberField
import {Label, NumberField} from "@heroui/react";
export function Basic() {
return (
<NumberField className="w-full max-w-64" defaultValue={1024} minValue={0} name="width">
<Label>Width</Label>
<NumberField.Group>
<NumberField.DecrementButton />
<NumberField.Input className="w-[120px]" />
<NumberField.IncrementButton />
</NumberField.Group>
</NumberField>
);
}Style Improvements
Custom Variants and Theme Compatibility
Enhanced CSS variants and theme system for better customization:
Motion Preferences:
- New
motion-safevariant withdata-reduce-motion="true"attribute matching - Enhanced
motion-reducenow supports ancestor elements and pseudo-elements
Dark Mode:
- Class and
data-theme="dark"attribute selectors now take precedence overprefers-color-scheme - Full support for pseudo-elements in dark mode
Theme Variables:
- Expanded light theme scope to support nested themes (
:root,.light,.default,[data-theme="light"],[data-theme="default"])
Component Improvements
Select Component API Update
The Select component's API has been improved for better consistency with other components. The Content subcomponent has been renamed to Popover.
Before:
<Select>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Content>
<ListBox>
{/* items */}
</ListBox>
</Select.Content>
</Select>After:
<Select>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Popover>
<ListBox>
{/* items */}
</ListBox>
</Select.Popover>
</Select>Chip Component Refinements
Chip component sizes have been updated for better consistency:
- Small (
sm):px-1 py-0 text-xs - Medium (
md):text-xs(now explicitly set) - Large (
lg):px-3 py-1 text-sm font-medium
Separator Component Enhancement
The Separator component now automatically detects when it's placed inside a surface component (one that uses bg-surface) and applies the appropriate divider color for better visibility. A new isOnSurface prop is also available for manual control.
New Calculated Variable:
--color-separator-on-surface: A calculated variable (automatically generated usingcolor-mix) that ensures the separator is visible when placed on a surface background. Like other calculated variables, it can be overridden in your theme.
Usage:
<div className="bg-surface">
<Separator isOnSurface />
</div>The isOnSurface prop is automatically applied when the Separator detects a SurfaceContext provider (used by components like Card, Alert, Popover, Modal, etc.).
You can also use the calculated variable directly with Tailwind classes:
<div className="bg-surface">
<div className="h-px w-full bg-separator-on-surface" />
</div>Animation Improvements
- Loading state spinner color updated for better visibility
- Select and Slider component styles adjusted for improved animations
- Checkbox animation improved (faster transition)
- Better support for
prefers-reduced-motionwith pseudo elements
⚠️ Breaking Changes
Select Component
The Select.Content subcomponent has been renamed to Select.Popover for consistency with other components like ComboBox and Dropdown.
Migration:
Replace all instances of Select.Content with Select.Popover:
// Before
<Select.Content>
<ListBox>...</ListBox>
</Select.Content>
// After
<Select.Popover>
<ListBox>...</ListBox>
</Select.Popover>Type imports:
// Before
import type { SelectContentProps } from "@heroui/react"
// After
import type { SelectPopoverProps } from "@heroui/react"Named exports:
// Before
import { SelectContent } from "@heroui/react"
// After
import { SelectPopover } from "@heroui/react"CSS Variables and Utilities: Divider → Separator
All CSS variables and utility classes related to divider have been renamed to separator for consistency with the Separator component name.
CSS Variables:
/* Before */
border-bottom: 1px solid var(--divider);
/* After */
border-bottom: 1px solid var(--separator);Tailwind Utility Classes:
// Before
<div className="bg-divider" />
<div className="border-divider" />
// After
<div className="bg-separator" />
<div className="border-separator" />Theme Overrides:
If you have custom themes that override the divider variable, update them:
/* Before */
:root {
--divider: oklch(92% 0.004 286.32);
}
.dark {
--divider: oklch(22% 0.006 286.033);
}
/* After */
:root {
--separator: oklch(92% 0.004 286.32);
}
.dark {
--separator: oklch(22% 0.006 286.033);
}Bug Fixes
- Fixed loading state spinner color for better visibility
- Fixed bordered focus styles taking precedence over hover states
- Fixed animation stuttering in documentation
- Improved modal form styling
- Enhanced motion reduce support for pseudo elements
- Fixed mobile hover states sticking after touch interactions by wrapping hover styles in
@media (hover: hover)media queries. Also simplified data attribute selectors by removing unnecessary="true"value checks.
Links
Contributors
Thanks to everyone who contributed to this release!