v3.0.0-beta.1
Major redesign with new design system, 8 new components, and improved developer experience.
This release introduces a comprehensive redesign of HeroUI v3, merging v2's beauty and animations with v3's simplicity. All components redesigned, 8 new components, and improved design system with better color tokens, shadows, and architecture.
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 Design System
We've spent weeks crafting a new design system that merges the soul of HeroUI v2 with the simplicity of v3. Every component has been redesigned with attention to detail, smooth animations, and improved developer experience. The new design system is available in our Figma Kit V3.
The redesign brings:
- New color system that brings v3's vision to life and stands out for its uniqueness
- Refined shadow system for better depth perception
- New variables and tokens for better customization
- Automatic
isOnSurfacesupport for form-based components - Enhanced border and spacing tokens
- Better contrast and accessibility
- Consistent component patterns across web and native
New Components
This release introduces 8 new essential components:
- Alert: Display important messages and notifications with status indicators.
- Checkbox & CheckboxGroup: Select multiple items from a list.
- InputOTP: One-time password input for authentication flows.
- Listbox: Display a list of options and allow single or multiple selection.
- Select: Dropdown selection component built on top of Listbox.
- Slider: Select a value from a range with custom marks and labels.
- Surface: Base surface component for creating elevated containers.
Alert
New features available
Check out our latest updates including dark mode support and improved accessibility features.Update available
A new version of the application is available. Please refresh to get the latest features and bug fixes.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
Profile updated successfully
Processing your request
Please wait while we sync your data. This may take a few moments.Scheduled maintenance
Our services will be unavailable on Sunday, March 15th from 2:00 AM to 6:00 AM UTC for scheduled maintenance.import {Alert, Button, CloseButton, Spinner} from "@heroui/react";
import React from "react";
export function Basic() {
return (
<div className="grid w-full max-w-xl gap-4">
{/* Default - General information */}
<Alert>
<Alert.Indicator />
<Alert.Content>
<Alert.Title>New features available</Alert.Title>
<Alert.Description>
Check out our latest updates including dark mode support and improved accessibility
features.
</Alert.Description>
</Alert.Content>
</Alert>
{/* Accent - Important information with action */}
<Alert status="accent">
<Alert.Indicator />
<Alert.Content>
<Alert.Title>Update available</Alert.Title>
<Alert.Description>
A new version of the application is available. Please refresh to get the latest features
and bug fixes.
</Alert.Description>
<Button className="mt-2 sm:hidden" size="sm" variant="primary">
Refresh
</Button>
</Alert.Content>
<Button className="hidden sm:block" size="sm" variant="primary">
Refresh
</Button>
</Alert>
{/* Danger - Error with detailed steps */}
<Alert status="danger">
<Alert.Indicator />
<Alert.Content>
<Alert.Title>Unable to connect to server</Alert.Title>
<Alert.Description>
We're experiencing connection issues. Please try the following:
<ul className="mt-2 list-inside list-disc space-y-1 text-sm">
<li>Check your internet connection</li>
<li>Refresh the page</li>
<li>Clear your browser cache</li>
</ul>
</Alert.Description>
<Button className="mt-2 sm:hidden" size="sm" variant="danger">
Retry
</Button>
</Alert.Content>
<Button className="hidden sm:block" size="sm" variant="danger">
Retry
</Button>
</Alert>
{/* Without description */}
<Alert status="success">
<Alert.Indicator />
<Alert.Content>
<Alert.Title>Profile updated successfully</Alert.Title>
</Alert.Content>
<CloseButton />
</Alert>
{/* Custom indicator - Loading state */}
<Alert status="accent">
<Alert.Indicator>
<Spinner size="sm" />
</Alert.Indicator>
<Alert.Content>
<Alert.Title>Processing your request</Alert.Title>
<Alert.Description>
Please wait while we sync your data. This may take a few moments.
</Alert.Description>
</Alert.Content>
</Alert>
{/* Without close button */}
<Alert status="warning">
<Alert.Indicator />
<Alert.Content>
<Alert.Title>Scheduled maintenance</Alert.Title>
<Alert.Description>
Our services will be unavailable on Sunday, March 15th from 2:00 AM to 6:00 AM UTC for
scheduled maintenance.
</Alert.Description>
</Alert.Content>
</Alert>
</div>
);
}Checkbox & CheckboxGroup
import {Checkbox, Label} from "@heroui/react";
export function Basic() {
return (
<div className="flex items-center gap-3">
<Checkbox id="basic-terms">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
</Checkbox>
<Label htmlFor="basic-terms">Accept terms and conditions</Label>
</div>
);
}import {Checkbox, CheckboxGroup, Description, Label} from "@heroui/react";
export function Basic() {
return (
<CheckboxGroup name="interests">
<Label>Select your interests</Label>
<Description>Choose all that apply</Description>
<Checkbox value="coding">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Coding</Label>
<Description>Love building software</Description>
</Checkbox.Content>
</Checkbox>
<Checkbox value="design">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Design</Label>
<Description>Enjoy creating beautiful interfaces</Description>
</Checkbox.Content>
</Checkbox>
<Checkbox value="writing">
<Checkbox.Control>
<Checkbox.Indicator />
</Checkbox.Control>
<Checkbox.Content>
<Label>Writing</Label>
<Description>Passionate about content creation</Description>
</Checkbox.Content>
</Checkbox>
</CheckboxGroup>
);
}InputOTP
We've sent a code to a****@gmail.com
Didn't receive a code?
Resendimport {InputOTP, Label, Link} from "@heroui/react";
export function Basic() {
return (
<div className="flex w-[280px] flex-col gap-2">
<div className="flex flex-col gap-1">
<Label>Verify account</Label>
<p className="text-muted text-sm">We've sent a code to a****@gmail.com</p>
</div>
<InputOTP maxLength={6}>
<InputOTP.Group>
<InputOTP.Slot index={0} />
<InputOTP.Slot index={1} />
<InputOTP.Slot index={2} />
</InputOTP.Group>
<InputOTP.Separator />
<InputOTP.Group>
<InputOTP.Slot index={3} />
<InputOTP.Slot index={4} />
<InputOTP.Slot index={5} />
</InputOTP.Group>
</InputOTP>
<div className="flex items-center gap-[5px] px-1 pt-1">
<p className="text-muted text-sm">Didn't receive a code?</p>
<Link className="text-foreground" underline="always">
Resend
</Link>
</div>
</div>
);
}Listbox
import {Avatar, Description, Label, ListBox} from "@heroui/react";
export function Default() {
return (
<ListBox aria-label="Users" className="w-[220px]" selectionMode="single">
<ListBox.Item id="1" textValue="Bob">
<Avatar size="sm">
<Avatar.Image
alt="Bob"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg"
/>
<Avatar.Fallback>B</Avatar.Fallback>
</Avatar>
<div className="flex flex-col">
<Label>Bob</Label>
<Description>bob@heroui.com</Description>
</div>
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="2" textValue="Fred">
<Avatar size="sm">
<Avatar.Image
alt="Fred"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg"
/>
<Avatar.Fallback>F</Avatar.Fallback>
</Avatar>
<div className="flex flex-col">
<Label>Fred</Label>
<Description>fred@heroui.com</Description>
</div>
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="3" textValue="Martha">
<Avatar size="sm">
<Avatar.Image
alt="Martha"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg"
/>
<Avatar.Fallback>M</Avatar.Fallback>
</Avatar>
<div className="flex flex-col">
<Label>Martha</Label>
<Description>martha@heroui.com</Description>
</div>
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
);
}Select
import {Label, ListBox, Select} from "@heroui/react";
export function Default() {
return (
<Select className="w-[256px]" placeholder="Select one">
<Label>State</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Popover>
<ListBox>
<ListBox.Item id="florida" textValue="Florida">
Florida
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="delaware" textValue="Delaware">
Delaware
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="california" textValue="California">
California
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="texas" textValue="Texas">
Texas
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="new-york" textValue="New York">
New York
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="washington" textValue="Washington">
Washington
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Select.Popover>
</Select>
);
}Slider
import {Label, Slider} from "@heroui/react";
export function Default() {
return (
<Slider className="w-full max-w-xs" defaultValue={30}>
<Label>Volume</Label>
<Slider.Output />
<Slider.Track>
<Slider.Fill />
<Slider.Thumb />
</Slider.Track>
</Slider>
);
}Surface
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.
Quaternary
Surface Content
This is a quaternary surface variant. It uses bg-surface-quaternary styling.
import {Surface} from "@heroui/react";
export function Variants() {
return (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<p className="text-muted text-sm font-medium">Default</p>
<Surface className="flex min-w-[320px] flex-col gap-3 rounded-3xl p-6" variant="default">
<h3 className="text-foreground text-base font-semibold">Surface Content</h3>
<p className="text-muted text-sm">
This is a default surface variant. It uses bg-surface styling.
</p>
</Surface>
</div>
<div className="flex flex-col gap-2">
<p className="text-muted text-sm font-medium">Secondary</p>
<Surface className="flex min-w-[320px] flex-col gap-3 rounded-3xl p-6" variant="secondary">
<h3 className="text-foreground text-base font-semibold">Surface Content</h3>
<p className="text-muted text-sm">
This is a secondary surface variant. It uses bg-surface-secondary styling.
</p>
</Surface>
</div>
<div className="flex flex-col gap-2">
<p className="text-muted text-sm font-medium">Tertiary</p>
<Surface className="flex min-w-[320px] flex-col gap-3 rounded-3xl p-6" variant="tertiary">
<h3 className="text-foreground text-base font-semibold">Surface Content</h3>
<p className="text-muted text-sm">
This is a tertiary surface variant. It uses bg-surface-tertiary styling.
</p>
</Surface>
</div>
<div className="flex flex-col gap-2">
<p className="text-muted text-sm font-medium">Quaternary</p>
<Surface className="flex min-w-[320px] flex-col gap-3 rounded-3xl p-6" variant="quaternary">
<h3 className="text-foreground text-base font-semibold">Surface Content</h3>
<p className="text-muted text-sm">
This is a quaternary surface variant. It uses bg-surface-quaternary styling.
</p>
</Surface>
</div>
</div>
);
}Improved Component APIs
Several components have been refined with better APIs:
- Link: Added
underlineandunderlineOffsetprops for better customization
import {Link} from "@heroui/react";
export function LinkBasic() {
return (
<Link href="#">
Call to action
<Link.Icon />
</Link>
);
}- Card: Improved variants and styling system

Become an ACME Creator!
Lorem ipsum dolor sit amet consectetur. Sed arcu donec id aliquam dolor sed amet faucibus etiam.
You can now withdraw on crypto
Add your wallet in settings to withdraw
Indie Hackers
148 members
AI Builders
362 members
NEO
Home Robot

Bridging the Future
Today, 6:30 PM

Avocado Hackathon
Wed, 4:30 PM

Sound Electro | Beyond art
Fri, 8:00 PM
import {Avatar, Button, Card, CloseButton, Link} from "@heroui/react";
import {Icon} from "@iconify/react";
export function WithImages() {
return (
<div className="flex w-full items-center justify-center">
<div className="grid w-full max-w-2xl grid-cols-12 gap-4 p-4">
{/* Row 1: Large Product Card - Available Soon */}
<Card className="col-span-12 flex h-auto min-h-[152px] flex-col sm:flex-row">
<div className="relative h-[140px] w-full flex-shrink-0 overflow-hidden rounded-2xl sm:h-[120px] sm:w-[120px]">
<img
alt="Cherries"
className="pointer-events-none absolute inset-0 h-full w-full scale-125 select-none object-cover"
loading="lazy"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/cherries.jpeg"
/>
</div>
<div className="flex flex-1 flex-col gap-3">
<Card.Header className="gap-1">
<Card.Title className="pr-8">Become an ACME Creator!</Card.Title>
<Card.Description>
Lorem ipsum dolor sit amet consectetur. Sed arcu donec id aliquam dolor sed amet
faucibus etiam.
</Card.Description>
<CloseButton aria-label="Close banner" className="absolute right-3 top-3" />
</Card.Header>
<Card.Footer className="mt-auto flex w-full flex-col items-start gap-3 sm:flex-row sm:items-center sm:justify-between">
<div className="flex flex-col">
<span className="text-foreground text-sm font-medium">Only 10 spots</span>
<span className="text-muted text-xs">Submission ends Oct 10.</span>
</div>
<Button className="w-full sm:w-auto">Apply Now</Button>
</Card.Footer>
</div>
</Card>
{/* Row 2 */}
<div className="col-span-12 grid grid-cols-12 gap-4">
{/* Left Column */}
<div className="col-span-12 grid grid-cols-12 gap-4 lg:col-span-6">
{/* Top Card */}
<Card className="col-span-12">
<div className="absolute right-3 top-3 z-10">
<CloseButton aria-label="Close notification" />
</div>
<Card.Header className="gap-3">
<Icon
aria-label="Dollar sign icon"
className="text-primary size-8 flex-shrink-0"
icon="gravity-ui:circle-dollar"
role="img"
/>
<div className="flex flex-col gap-1">
<span className="text-muted text-xs font-medium uppercase">PAYMENT</span>
<Card.Title className="pr-8 text-sm sm:text-base">
You can now withdraw on crypto
</Card.Title>
<Card.Description className="text-xs sm:text-sm">
Add your wallet in settings to withdraw
</Card.Description>
</div>
</Card.Header>
<Card.Footer>
<Link aria-label="Go to settings" href="#" rel="noopener noreferrer">
Go to settings
<Link.Icon aria-hidden="true" />
</Link>
</Card.Footer>
</Card>
{/* Bottom cards */}
<div className="col-span-12 grid grid-cols-12 gap-4">
{/* Left Card */}
<Card className="col-span-12 gap-2 sm:col-span-6">
<Card.Header>
<Avatar className="size-[56px] rounded-xl">
<Avatar.Image
alt="Demo 1"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/demo1.jpg"
/>
<Avatar.Fallback>JK</Avatar.Fallback>
</Avatar>
</Card.Header>
<Card.Content className="mt-1">
<p className="text-sm font-medium leading-4">Indie Hackers</p>
<p className="text-muted text-xs">148 members</p>
</Card.Content>
<Card.Footer className="flex items-center gap-2">
<Avatar className="size-4">
<Avatar.Image
alt="John"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg"
/>
<Avatar.Fallback>JK</Avatar.Fallback>
</Avatar>
<p className="text-muted text-xs">By John</p>
</Card.Footer>
</Card>
{/* Right Card */}
<Card className="col-span-12 gap-2 sm:col-span-6">
<Card.Header>
<Avatar className="size-[56px] rounded-xl">
<Avatar.Image
alt="Demo 2"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/demo2.jpg"
/>
<Avatar.Fallback>AB</Avatar.Fallback>
</Avatar>
</Card.Header>
<Card.Content className="mt-1">
<p className="text-sm font-medium leading-4">AI Builders</p>
<p className="text-muted text-xs">362 members</p>
</Card.Content>
<Card.Footer className="flex items-center gap-2">
<Avatar className="size-4">
<Avatar.Image
alt="John"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg"
/>
<Avatar.Fallback>M</Avatar.Fallback>
</Avatar>
<p className="text-muted text-xs">By Martha</p>
</Card.Footer>
</Card>
</div>
</div>
{/* Right Column */}
<Card className="col-span-12 min-h-[200px] overflow-hidden rounded-3xl lg:col-span-6">
{/* Background image */}
<img
alt="NEO Home Robot"
aria-hidden="true"
className="absolute inset-0 h-full w-full object-cover"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/neo2.jpeg"
/>
{/* Header */}
<Card.Header className="z-10 text-white">
<Card.Title className="text-xs font-semibold tracking-wide text-black/70">
NEO
</Card.Title>
<Card.Description className="text-sm font-medium leading-5 text-black/50">
Home Robot
</Card.Description>
</Card.Header>
{/* Footer */}
<Card.Footer className="z-10 mt-auto flex items-center justify-between">
<div>
<div className="text-sm font-medium text-black">Available soon</div>
<div className="text-xs text-black/60">Get notified</div>
</div>
<Button className="bg-white text-black" size="sm" variant="tertiary">
Notify me
</Button>
</Card.Footer>
</Card>
</div>
{/* Row 3 */}
<div className="col-span-12 grid grid-cols-12 gap-4">
{/* Left Column: Card */}
<Card className="relative col-span-12 h-[250px] sm:h-[300px] md:col-span-8 md:h-[350px]">
<img
alt="NEO Home Robot"
aria-hidden="true"
className="absolute inset-0 h-full w-full object-cover"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/neo1.jpeg"
/>
<Card.Footer className="z-10 mt-auto flex items-end justify-between">
<div>
<div className="text-base font-medium text-black sm:text-lg">NEO</div>
<div className="text-xs font-medium text-black/50 sm:text-sm">$499/m</div>
</div>
<Button className="bg-white text-black" size="sm" variant="tertiary">
Get now
</Button>
</Card.Footer>
</Card>
{/* Right Column: Cards Stack */}
<div className="col-span-12 flex flex-col gap-2 md:col-span-4 md:justify-between md:gap-0 md:py-2">
{/* 1 */}
<Card className="flex flex-row gap-3 p-1" variant="transparent">
<img
alt="Futuristic Robot"
className="aspect-square h-16 w-16 shrink-0 select-none rounded-xl object-cover sm:h-20 sm:w-20"
loading="lazy"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/robot1.jpeg"
/>
<div className="flex flex-1 flex-col justify-center gap-1">
<Card.Title className="text-sm">Bridging the Future</Card.Title>
<Card.Description className="text-xs">Today, 6:30 PM</Card.Description>
</div>
</Card>
{/* 2 */}
<Card className="flex flex-row gap-3 p-1" variant="transparent">
<img
alt="Avocado"
className="aspect-square h-16 w-16 shrink-0 select-none rounded-xl object-cover sm:h-20 sm:w-20"
loading="lazy"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/avocado.jpeg"
/>
<div className="flex flex-1 flex-col justify-center gap-1">
<Card.Title className="text-sm">Avocado Hackathon</Card.Title>
<Card.Description className="text-xs">Wed, 4:30 PM</Card.Description>
</div>
</Card>
{/* 3 */}
<Card className="flex flex-row gap-3 p-1" variant="transparent">
<img
alt="Sound Electro event"
className="aspect-square h-16 w-16 shrink-0 select-none rounded-xl object-cover sm:h-20 sm:w-20"
loading="lazy"
src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/oranges.jpeg"
/>
<div className="flex flex-1 flex-col justify-center gap-1">
<Card.Title className="text-sm">Sound Electro | Beyond art</Card.Title>
<Card.Description className="text-xs">Fri, 8:00 PM</Card.Description>
</div>
</Card>
</div>
</div>
</div>
</div>
);
}- Chip: Enhanced with size variants and improved color system
import {Chip} from "@heroui/react";
export function ChipBasic() {
return (
<div className="flex flex-wrap items-center gap-3">
<Chip>Default</Chip>
<Chip color="accent">Accent</Chip>
<Chip color="success">Success</Chip>
<Chip color="warning">Warning</Chip>
<Chip color="danger">Danger</Chip>
</div>
);
}- Switch: Redesigned from the ground up with improved visual design and animations
import {Label, Switch} from "@heroui/react";
export function Basic() {
return (
<Switch>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label className="text-sm">Enable notifications</Label>
</Switch>
);
}- RadioGroup: Redesigned from the ground up with better API and styling
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Basic() {
return (
<RadioGroup defaultValue="premium" name="plan">
<Label>Plan selection</Label>
<Description>Choose the plan that suits you best</Description>
<Radio value="basic">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Basic Plan</Label>
<Description>Includes 100 messages per month</Description>
</Radio.Content>
</Radio>
<Radio value="premium">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Premium Plan</Label>
<Description>Includes 200 messages per month</Description>
</Radio.Content>
</Radio>
<Radio value="business">
<Radio.Control>
<Radio.Indicator />
</Radio.Control>
<Radio.Content>
<Label>Business Plan</Label>
<Description>Unlimited messages</Description>
</Radio.Content>
</Radio>
</RadioGroup>
);
}Flexible Component Patterns
HeroUI now supports flexible component syntax. Use compound patterns with or without .Root, or named exports - all three patterns work identically.
Available patterns:
import { Avatar } from "@heroui/react"
// 1. Compound pattern (no .Root needed) - recommended
<Avatar>
<Avatar.Image src="/avatar.jpg" alt="User" />
<Avatar.Fallback>JD</Avatar.Fallback>
</Avatar>
// 2. Compound pattern with .Root - still supported
<Avatar.Root>
<Avatar.Image src="/avatar.jpg" alt="User" />
<Avatar.Fallback>JD</Avatar.Fallback>
</Avatar.Root>
// 3. Named exports
import { AvatarRoot, AvatarImage, AvatarFallback } from "@heroui/react"
<AvatarRoot>
<AvatarImage src="/avatar.jpg" alt="User" />
<AvatarFallback>JD</AvatarFallback>
</AvatarRoot>Simple components like Button work the same way:
import { Button } from "@heroui/react"
// No .Root needed
<Button>Label</Button>
// Or with .Root
<Button.Root>Label</Button.Root>
// Or named export
import { ButtonRoot } from "@heroui/react"
<ButtonRoot>Label</ButtonRoot>You can mix compound and named exports in the same component:
import { Avatar, AvatarFallback } from "@heroui/react"
<Avatar>
<Avatar.Image src="/avatar.jpg" alt="User" />
<AvatarFallback>JD</AvatarFallback>
</Avatar>This provides:
- Simpler API: Main components no longer require
.Rootsuffix - Flexibility: Choose between compound pattern, compound with
.Root, or named exports - Backward Compatibility: The
.Rootpattern still works - Naming Consistency: Standardized naming (e.g., "Container" instead of "Wrapper")
Global Animation Control
HeroUI now supports easy global animation control through the data-reduce-motion attribute. Simply add data-reduce-motion="true" to your <html> or <body> tag to disable all animations across your application.
<!DOCTYPE html>
<html data-reduce-motion="true">
<!-- All HeroUI animations will be disabled -->
</html>HeroUI automatically respects user motion preferences using the prefers-reduced-motion media query and extends Tailwind's motion-reduce: variant to support both system preferences and manual control via the data attribute. This provides flexible control over animations while maintaining accessibility best practices.
Learn more about animations and motion preferences in the Animation documentation.
⚠️ Breaking Changes
Design System Variables
Panel → Surface & Overlay
The --panel variable has been replaced with --surface and --overlay to better distinguish between non-overlay components (cards, accordions) and floating components (tooltips, popovers, modals).
Before:
--panel: var(--white);
--panel-foreground: var(--foreground);
--shadow-panel: 0 0 1px 0 rgba(0, 0, 0, 0.3) inset, 0 2px 8px 0 rgba(0, 0, 0, 0.08);After:
--surface: var(--white);
--surface-foreground: var(--foreground);
--overlay: var(--white);
--overlay-foreground: var(--foreground);
--shadow-surface: 0 2px 4px 0 rgba(0, 0, 0, 0.04), 0 1px 2px 0 rgba(0, 0, 0, 0.06), 0 0 1px 0 rgba(0, 0, 0, 0.06);
--shadow-overlay: 0 4px 16px 0 rgba(24, 24, 27, 0.08), 0 8px 24px 0 rgba(24, 24, 27, 0.09);Migration:
- Replace
bg-panelwithbg-surfacefor non-overlay components - Replace
bg-panelwithbg-overlayfor floating components - Replace
shadow-panelwithshadow-surfaceorshadow-overlay - Replace
--color-panelwith--color-surfaceor--color-overlay
Surface Levels Simplified
The --surface-1, --surface-2, and --surface-3 variables have been removed. Surface levels are now automatically calculated from --surface using color-mix, so you only need to declare the base surface color.
Before (manual declaration):
--surface-1: var(--background);
--surface-2: var(--color-neutral-100);
--surface-3: var(--color-neutral-200);After (auto-calculated):
/* You only declare the base surface */
--surface: var(--white);
--surface-foreground: var(--foreground);
/* HeroUI automatically calculates these using color-mix */
--color-surface-secondary: color-mix(in oklab, var(--surface) 94%, var(--surface-foreground) 6%);
--color-surface-tertiary: color-mix(in oklab, var(--surface) 92%, var(--surface-foreground) 8%);
--color-surface-quaternary: color-mix(in oklab, var(--surface) 86%, var(--surface-foreground) 14%);Customization:
You can override the default calculations using Tailwind's @theme directive:
@theme inline {
--color-surface-secondary: color-mix(in oklab, var(--surface) 96%, var(--surface-foreground) 4%);
--color-surface-tertiary: color-mix(in oklab, var(--surface) 94%, var(--surface-foreground) 6%);
--color-surface-quaternary: color-mix(in oklab, var(--surface) 90%, var(--surface-foreground) 10%);
}Migration:
- Replace
bg-surface-1withbg-surface(base surface) - Replace
bg-surface-2withbg-surface-secondary(auto-calculated) - Replace
bg-surface-3withbg-surface-tertiary(auto-calculated)
The same auto-calculation pattern applies to:
- Background shades: Calculated from
--background→background-secondary,background-tertiary,background-quaternary - Soft colors: Calculated from status colors →
accent-soft,danger-soft,warning-soft,success-soft
Border Width Default Changed
The default border width has changed from 1px to 0px. Borders are now opt-in rather than default.
Before:
--border-width: 1px;After:
--border-width: 0px; /* no border by default */Migration:
- If you rely on default borders, explicitly set
border-widthin your custom styles - Form fields now use
transparentborders by default
Border Color Default Changed
The default border color opacity has changed from 15% to 0% (transparent).
Before:
--border: oklch(0 0 0 / 15%);After:
--border: oklch(0 0 0 / 0%);Field Border Default:
--field-border: transparent; /* no border by default on form fields */Shadow System Updates
The shadow system has been completely redesigned with separate shadows for surfaces and overlays.
Before:
--panel-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.3) inset, 0 2px 8px 0 rgba(0, 0, 0, 0.08);
--field-shadow: 0 0 0 0 rgba(255, 255, 255, 0.1) inset, 0 1px 2px 0 rgba(0, 0, 0, 0.05);After (Light):
--surface-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.04), 0 1px 2px 0 rgba(0, 0, 0, 0.06), 0 0 1px 0 rgba(0, 0, 0, 0.06);
--overlay-shadow: 0 4px 16px 0 rgba(24, 24, 27, 0.08), 0 8px 24px 0 rgba(24, 24, 27, 0.09);
--field-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.04), 0 1px 2px 0 rgba(0, 0, 0, 0.06), 0 0 1px 0 rgba(0, 0, 0, 0.06);After (Dark):
--surface-shadow: 0 0 0 0 transparent inset; /* No shadow on dark mode */
--overlay-shadow: 0 0 0 0 transparent inset; /* No shadow on dark mode */
--field-shadow: 0 0 0 0 transparent inset; /* Transparent shadow to allow ring utilities to work */Accent Color Updates
The accent color has been updated for better contrast and visual appeal.
Before:
--accent: var(--color-neutral-950);
--accent-foreground: var(--snow);After:
--accent: oklch(0.6204 0.195 253.83);
--accent-foreground: var(--snow);Status Color Refinements
Success, warning, and danger colors have been refined for better consistency and contrast.
Success:
- Before:
oklch(0.5503 0.1244 153.56) - After:
oklch(0.7329 0.1935 150.81) - Foreground changed from
var(--snow)tovar(--eclipse)in light mode
Warning:
- Before:
oklch(0.7186 0.1521 64.85) - After:
oklch(0.7819 0.1585 72.33)(light),oklch(0.8203 0.1388 76.34)(dark)
Danger:
- Before:
oklch(0.6259 0.1908 29.19) - After:
oklch(0.6532 0.2328 25.74)(light),oklch(0.594 0.1967 24.63)(dark)
Component API Changes
Chip Component
The Chip component's type prop has been renamed to color, and a new size prop has been added. A new soft variant has been introduced.
Before:
import { Chip } from "@heroui/react";
<Chip type="danger" variant="secondary">Label</Chip>After:
import { Chip } from "@heroui/react";
<Chip color="danger" variant="soft" size="md">Label</Chip>Migration:
- Replace
typeprop withcolorprop - Use
sizeprop (sm,md,lg) to control chip size - The
softvariant provides a subtle appearance for less prominent chips
Link Component
The Link component now supports underline and underlineOffset props, and includes asChild support.
Before:
import { Link } from "@heroui/react";
<Link href="#">Link text</Link>After:
import { Link } from "@heroui/react";
<Link href="#" underline="hover" underlineOffset={4}>Link text</Link>New Props:
underline:"none" | "hover" | "always"- Controls underline visibilityunderlineOffset:number- Controls underline offset from text
Type Reference Syntax
Due to the dual pattern implementation, type references through the namespace syntax are no longer supported. Use object-style syntax or named type imports instead.
Before (no longer works):
type AvatarProps = Avatar.RootPropsAfter (Option 1 - Object-style syntax):
type AvatarProps = Avatar["RootProps"]After (Option 2 - Named type imports, recommended):
import type { AvatarRootProps } from "@heroui/react"
type AvatarProps = AvatarRootPropsThis change affects all compound components when accessing prop types.
Tabs Component Renaming
The Tabs component's wrapper element has been renamed for consistency:
- Compound property:
Tabs.ListWrapper→Tabs.ListContainer - Named export:
TabListWrapper→TabListContainer - CSS class:
.tabs__list-wrapper→.tabs__list-container - Data attribute:
data-slot="tabs-list-wrapper"→data-slot="tabs-list-container"
Migration:
Find and replace all instances of TabListWrapper with TabListContainer:
# Component usage
TabListWrapper → TabListContainer
Tabs.ListWrapper → Tabs.ListContainer
# CSS selectors (if using custom styles)
.tabs__list-wrapper → .tabs__list-container
[data-slot="tabs-list-wrapper"] → [data-slot="tabs-list-container"]Removed Variables
The following variables have been removed:
--panel→ Use--surfaceor--overlay--panel-foreground→ Use--surface-foregroundor--overlay-foreground--surface-1,--surface-2,--surface-3→ Use background shades or surface levels--accent-soft→ Use--color-accent-soft(now calculated)--radius-paneland--radius-panel-inner→ Use standard radius values
Design System Updates
New Color System
Surface vs Overlay Concept
The design system now distinguishes between two types of elevated components:
- Surface: Used for non-overlay components like cards, accordions, and disclosure groups that sit on the page
- Overlay: Used for floating components like tooltips, popovers, modals, and menus that appear above the page
This distinction provides:
- Better visual hierarchy
- Appropriate shadow depths
- Improved dark mode contrast
- Clearer component semantics
Auto-Calculated Color System
HeroUI now automatically calculates shade levels and soft color variants using CSS color-mix. You only need to declare the base colors, and HeroUI handles the rest.
Background Shade Levels
Background shades are automatically calculated from --background:
/* You only declare the base */
--background: oklch(0.9702 0 0);
--foreground: var(--eclipse);
/* HeroUI automatically calculates these */
--color-background-secondary: color-mix(in oklab, var(--color-background) 96%, var(--color-foreground) 4%);
--color-background-tertiary: color-mix(in oklab, var(--color-background) 92%, var(--color-foreground) 8%);
--color-background-quaternary: color-mix(in oklab, var(--color-background) 86%, var(--color-foreground) 14%);Surface Levels
Surface levels are automatically calculated from --surface:
/* You only declare the base */
--surface: var(--white);
--surface-foreground: var(--foreground);
/* HeroUI automatically calculates these */
--color-surface-secondary: color-mix(in oklab, var(--surface) 94%, var(--surface-foreground) 6%);
--color-surface-tertiary: color-mix(in oklab, var(--surface) 92%, var(--surface-foreground) 8%);
--color-surface-quaternary: color-mix(in oklab, var(--surface) 86%, var(--surface-foreground) 14%);Soft Color Variants
Soft color variants are automatically calculated from status colors:
/* You declare the base status colors */
--accent: oklch(0.6204 0.195 253.83);
--danger: oklch(0.6532 0.2328 25.74);
--warning: oklch(0.7819 0.1585 72.33);
--success: oklch(0.7329 0.1935 150.81);
/* HeroUI automatically calculates these at 15% opacity */
--color-accent-soft: color-mix(in oklab, var(--color-accent) 15%, transparent);
--color-danger-soft: color-mix(in oklab, var(--color-danger) 15%, transparent);
--color-warning-soft: color-mix(in oklab, var(--color-warning) 15%, transparent);
--color-success-soft: color-mix(in oklab, var(--color-success) 15%, transparent);Each soft variant includes hover states (20% opacity) and foreground colors for proper contrast.
Customization:
You can override any auto-calculated values using Tailwind's @theme directive:
@theme inline {
/* Adjust surface levels */
--color-surface-secondary: color-mix(in oklab, var(--surface) 96%, var(--surface-foreground) 4%);
/* Adjust soft colors */
--color-accent-soft: color-mix(in oklab, var(--color-accent) 20%, transparent);
}This auto-calculation system reduces the number of variables you need to manage while providing full customization when needed.
Shadow System
The shadow system has been redesigned to provide:
- Separate shadows for surfaces and overlays
- Better depth perception
- Dark mode support (transparent shadows)
- Consistent field shadows
Shadows automatically adapt to light and dark modes, providing appropriate depth cues for each theme.
Focus System
The focus color now uses the accent color for consistency:
--focus: var(--accent);This ensures focus indicators align with your brand colors while maintaining accessibility.
Typography Tokens
Several typography-related variables have been removed in favor of using Tailwind's typography utilities directly. The design system now focuses on color and spacing tokens, letting Tailwind handle typography.
Migration Guide
Step 1: Update Design System Variables
Replace old panel variables with surface/overlay:
/* Before */
.my-card {
background: var(--panel);
box-shadow: var(--shadow-panel);
}
/* After */
.my-card {
background: var(--surface);
box-shadow: var(--shadow-surface);
}
.my-tooltip {
background: var(--overlay);
box-shadow: var(--shadow-overlay);
}Step 2: Update Surface Levels
Surface levels are now automatically calculated from --surface, so you don't need to manually declare them. Simply use the new utility classes:
/* Before */
.bg-surface-1 → .bg-surface (base surface)
.bg-surface-2 → .bg-surface-secondary (auto-calculated)
.bg-surface-3 → .bg-surface-tertiary (auto-calculated)
/* You can also use background shades */
.bg-surface-2 → .bg-background-secondary (auto-calculated from --background)
.bg-surface-3 → .bg-background-tertiary (auto-calculated from --background)Note: Surface levels (surface-secondary, surface-tertiary, etc.) are automatically calculated based on your --surface color. No manual CSS variables needed unless you want to customize the calculations.
Step 3: Update Component Props
Update Chip and Link components:
// Chip: type → color, add size if needed
<Chip type="danger" /> → <Chip color="danger" size="md" />
// Link: Add underline props if customizing underlines
<Link href="#">Text</Link> // Still works, underline props are optionalStep 4: Simplify Component Patterns (Optional)
If you adopted the .Root suffix from v3.0.0-alpha.35, you can now simplify your code by removing it:
Before (v3.0.0-alpha.35):
<Avatar.Root>
<Avatar.Image src="..." alt="..." />
<Avatar.Fallback>JD</Avatar.Fallback>
</Avatar.Root>After (simpler):
<Avatar>
<Avatar.Image src="..." alt="..." />
<Avatar.Fallback>JD</Avatar.Fallback>
</Avatar>Note: The .Root syntax still works if you prefer it.
Step 5: Update Type References
If you're using namespace syntax for types, switch to object-style syntax or named imports:
Before:
type ButtonProps = Button.RootPropsAfter (Option 1 - Object-style):
type ButtonProps = Button["RootProps"]After (Option 2 - Named imports, recommended):
import type { ButtonRootProps } from "@heroui/react"
type ButtonProps = ButtonRootPropsStep 6: Update Tabs Component
Replace TabListWrapper with TabListContainer:
Before:
import { Tabs } from "@heroui/react"
<Tabs.Root>
<Tabs.ListWrapper>
<Tabs.List>
<Tabs.Tab id="home">Home<Tabs.Indicator /></Tabs.Tab>
</Tabs.List>
</Tabs.ListWrapper>
<Tabs.Panel id="home">Content</Tabs.Panel>
</Tabs.Root>After:
import { Tabs } from "@heroui/react"
<Tabs>
<Tabs.ListContainer>
<Tabs.List>
<Tabs.Tab id="home">Home<Tabs.Indicator /></Tabs.Tab>
</Tabs.List>
</Tabs.ListContainer>
<Tabs.Panel id="home">Content</Tabs.Panel>
</Tabs>Step 7: Handle Border Changes
If your custom styles rely on default borders:
/* Add explicit borders where needed */
.my-component {
border-width: 1px;
border-color: var(--color-border);
}Step 8: Update Status Colors
If you've customized status colors, review the new values and adjust your custom theme if needed:
/* Check if your custom status colors need updates */
--success: oklch(0.7329 0.1935 150.81); /* New value */
--warning: oklch(0.7819 0.1585 72.33); /* New value */
--danger: oklch(0.6532 0.2328 25.74); /* New value */Automated Migration
For large codebases, you can use find-and-replace:
# Panel → Surface
--panel → --surface
bg-panel → bg-surface
shadow-panel → shadow-surface
# Panel → Overlay (for floating components)
--panel → --overlay (where appropriate)
bg-panel → bg-overlay (for tooltips, popovers, etc.)
shadow-panel → shadow-overlay (for floating components)
# Chip type prop
type=" → color="
# Surface levels
bg-surface-1 → bg-surface
bg-surface-2 → bg-surface-secondary
bg-surface-3 → bg-surface-tertiary
# Tabs component
TabListWrapper → TabListContainer
Tabs.ListWrapper → Tabs.ListContainer
# Type references
Component.RootProps → Component["RootProps"] or use named importsComponent Updates
Card Component
Card component has been refined with improved variants and better semantic structure. The component now uses the new surface system for consistent styling.
Accordion Component
Accordion now uses the surface system for better visual consistency with other components.
Form Components
Form components (Input, TextField, TextArea) have been updated to use the new field border system (transparent by default) for a cleaner look while maintaining accessibility.
Component Pattern Updates
All components now support flexible patterns. Components that support the dual pattern include:
- Simple components: Button, Link, Spinner, Chip, Kbd
- Compound components: Accordion, Avatar, Card, Disclosure, Fieldset, Popover, RadioGroup, Switch, Tabs, Tooltip
You can use any of the three patterns (compound without .Root, compound with .Root, or named exports) with all these components.
HeroUI Pro
HeroUI Pro is being reshaped from the ground up on top of the new design system. The new Pro version will feature:
- New components built on top of HeroUI v3
- Tailwind CSS v4 native support
- CSS native animations
- Enhanced customization options
We'll share more updates soon.
Roadmap
We're working towards a stable release in Q4 this year (2025). This beta release brings us significantly closer to that goal with:
- Comprehensive component set
- Refined design system
- Improved developer experience
- Better performance
Community
The reception on the native side has been phenomenal. Thank you for supporting us as we build HeroUI v3! Your feedback helps us improve every day.
See what the community is saying: HeroUI Native Reception
Links
- Component Documentation
- Design System - Figma Kit V3
- HeroUI Native
- GitHub Repository
- GitHub PR #5872
Contributors
Thanks to everyone who contributed to this release, helping us create a design system that's both beautiful and practical!

