apps/docs/content/docs/en/react/releases/v3-0-0-beta-1.mdx
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.
Update to the latest version:
<Tabs items={["npm", "pnpm", "yarn", "bun"]}>
<Tab value="npm">
bash npm i @heroui/styles@beta @heroui/react@beta
</Tab>
<Tab value="pnpm">
bash pnpm add @heroui/styles@beta @heroui/react@beta
</Tab>
<Tab value="yarn">
bash yarn add @heroui/styles@beta @heroui/react@beta
</Tab>
<Tab value="bun">
bash bun add @heroui/styles@beta @heroui/react@beta
</Tab>
</Tabs>
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.
<VideoPlayer src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/videos/v3-beta-1.mp4" poster="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/videos/v3-beta-1-poster.png" height={430} muted />
The redesign brings:
isOnSurface support for form-based componentsThis release introduces 8 new essential components:
<span id="alert"></span>
<ComponentPreview name="alert-basic" />
<span id="checkbox-checkboxgroup"></span>
<ComponentPreview name="checkbox-basic" />
<ComponentPreview name="checkbox-group-basic" />
<span id="inputotp"></span>
<ComponentPreview name="input-otp-basic" />
<span id="listbox"></span>
<ComponentPreview name="list-box-default" />
<span id="select"></span>
<ComponentPreview name="select-default" />
<span id="slider"></span>
<ComponentPreview name="slider-default" />
<span id="surface"></span>
<ComponentPreview name="surface-variants" />
Several components have been refined with better APIs:
underline and underlineOffset props for better customization<ComponentPreview name="link-basic" />
<ComponentPreview name="card-with-images" />
<ComponentPreview name="chip-basic" />
<span id="switch"></span> <ComponentPreview name="switch-basic" />
<span id="radiogroup"></span> <ComponentPreview name="radio-group-basic" />
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:
.Root suffix.Root, or named exports.Root pattern still worksHeroUI 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.
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:
bg-panel with bg-surface for non-overlay componentsbg-panel with bg-overlay for floating componentsshadow-panel with shadow-surface or shadow-overlay--color-panel with --color-surface or --color-overlayThe --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:
bg-surface-1 with bg-surface (base surface)bg-surface-2 with bg-surface-secondary (auto-calculated)bg-surface-3 with bg-surface-tertiary (auto-calculated)The same auto-calculation pattern applies to:
--background → background-secondary, background-tertiary, background-quaternaryaccent-soft, danger-soft, warning-soft, success-softThe 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:
border-width in your custom stylestransparent borders by defaultThe 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 */
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 */
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);
Success, warning, and danger colors have been refined for better consistency and contrast.
Success:
oklch(0.5503 0.1244 153.56)oklch(0.7329 0.1935 150.81)var(--snow) to var(--eclipse) in light modeWarning:
oklch(0.7186 0.1521 64.85)oklch(0.7819 0.1585 72.33) (light), oklch(0.8203 0.1388 76.34) (dark)Danger:
oklch(0.6259 0.1908 29.19)oklch(0.6532 0.2328 25.74) (light), oklch(0.594 0.1967 24.63) (dark)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:
type prop with color propsize prop (sm, md, lg) to control chip sizesoft variant provides a subtle appearance for less prominent chipsThe 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 textDue 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.RootProps
After (Option 1 - Object-style syntax):
type AvatarProps = Avatar["RootProps"]
After (Option 2 - Named type imports, recommended):
import type { AvatarRootProps } from "@heroui/react"
type AvatarProps = AvatarRootProps
This change affects all compound components when accessing prop types.
The Tabs component's wrapper element has been renamed for consistency:
Tabs.ListWrapper → Tabs.ListContainerTabListWrapper → TabListContainer.tabs__list-wrapper → .tabs__list-containerdata-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"]
The following variables have been removed:
--panel → Use --surface or --overlay--panel-foreground → Use --surface-foreground or --overlay-foreground--surface-1, --surface-2, --surface-3 → Use background shades or surface levels--accent-soft → Use --color-accent-soft (now calculated)--radius-panel and --radius-panel-inner → Use standard radius valuesThe design system now distinguishes between two types of elevated components:
This distinction provides:
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.
The shadow system has been redesigned to provide:
Shadows automatically adapt to light and dark modes, providing appropriate depth cues for each theme.
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.
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.
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);
}
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.
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 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.
If you're using namespace syntax for types, switch to object-style syntax or named imports:
Before:
type ButtonProps = Button.RootProps
After (Option 1 - Object-style):
type ButtonProps = Button["RootProps"]
After (Option 2 - Named imports, recommended):
import type { ButtonRootProps } from "@heroui/react"
type ButtonProps = ButtonRootProps
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>
If your custom styles rely on default borders:
/* Add explicit borders where needed */
.my-component {
border-width: 1px;
border-color: var(--color-border);
}
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 */
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 imports
Card component has been refined with improved variants and better semantic structure. The component now uses the new surface system for consistent styling.
Accordion now uses the surface system for better visual consistency with other 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.
All components now support flexible patterns. Components that support the dual pattern include:
You can use any of the three patterns (compound without .Root, compound with .Root, or named exports) with all these components.
HeroUI Pro is being reshaped from the ground up on top of the new design system. The new Pro version will feature:
We'll share more updates soon.
We're working towards a stable release in Q4 this year (2025). This beta release brings us significantly closer to that goal with:
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
Thanks to everyone who contributed to this release, helping us create a design system that's both beautiful and practical!
<PRContributors />