v3.0.0-alpha.35
React Server Components support for compound components, React 19 improvements, and critical bug fixes.
This release fixes a critical issue where compound components didn't work correctly in React Server Components (RSC). Additionally, this release adopts React 19 best practices by removing forwardRef and simplifying context usage. The Switch component has been refactored to match the Radio/RadioGroup pattern, providing a cleaner and more consistent API.
Installation
Update to the latest version:
npm i @heroui/styles@alpha @heroui/react@alphapnpm add @heroui/styles@alpha @heroui/react@alphayarn add @heroui/styles@alpha @heroui/react@alphabun add @heroui/styles@alpha @heroui/react@alphaUsing 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
React Server Components Support
Compound components now work correctly in React Server Components. The previous implementation had the compound pattern logic inside components, which conflicted with the "use client" directive. This has been fixed by moving the pattern logic to component index files.
React 19 Improvements
This release adopts React 19 best practices:
- Removed
forwardRef: No longer needed in React 19, whererefis now a prop (React 19 docs) - Simplified Context:
Context.Providerreplaced with justContext(React 19 docs)
Switch Component Architecture Improvement
The Switch component has been refactored to follow the same clean separation pattern as Radio/RadioGroup:
- Separate Components: Switch and SwitchGroup are now independent components (previously combined)
- Cleaner API:
<SwitchGroup>replaces the nested<Switch.Group>and<Switch.GroupItems>pattern - Better Organization: Separate styles, types, and implementations for each component
- Consistent Pattern: Matches the Radio/RadioGroup architecture for a more predictable API
Before:
<Switch.Group>
<Switch.GroupItems>
<Switch.Root>...</Switch.Root>
</Switch.GroupItems>
</Switch.Group>After:
<SwitchGroup>
<Switch.Root>...</Switch.Root>
<Switch.Root>...</Switch.Root>
</SwitchGroup>⚠️ Breaking Changes
Main Component Requires .Root Suffix
To support React Server Components, the compound component pattern has been restructured. The main component now requires the .Root suffix when using the compound pattern.
Before:
import { Avatar } from "@heroui/react"
<Avatar>
<Avatar.Image src="/images/avatar.jpeg" alt="Junior Garcia" />
<Avatar.Fallback>JR</Avatar.Fallback>
</Avatar>After:
import { Avatar } from "@heroui/react"
<Avatar.Root>
<Avatar.Image src="/images/avatar.jpeg" alt="Junior Garcia" />
<Avatar.Fallback>JR</Avatar.Fallback>
</Avatar.Root>Note: Named exports (e.g., <Avatar>, <AvatarImage>, <AvatarFallback>) remain unchanged and fully supported.
Switch Component API Changes
The Switch component grouping API has been restructured to match the Radio/RadioGroup pattern:
Before:
import { Switch } from "@heroui/react"
<Switch.Group orientation="horizontal">
<Switch.GroupItems>
<Switch.Root name="notifications">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Notifications</Label>
</Switch.Root>
<Switch.Root name="marketing">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Marketing</Label>
</Switch.Root>
</Switch.GroupItems>
</Switch.Group>After:
import { Switch, SwitchGroup } from "@heroui/react"
<SwitchGroup orientation="horizontal">
<Switch.Root name="notifications">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Notifications</Label>
</Switch.Root>
<Switch.Root name="marketing">
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Label>Marketing</Label>
</Switch.Root>
</SwitchGroup>This change helps to:
- Separate Components: Switch and SwitchGroup are now independent components (previously combined)
- Cleaner API:
<SwitchGroup>replaces the nested<Switch.Group>and<Switch.GroupItems>pattern - Better Organization: Separate styles, types, and implementations for each component
- Consistent Pattern: Matches the Radio/RadioGroup architecture for a more predictable API
Migration Steps:
- Import
SwitchGroupseparately:import { Switch, SwitchGroup } from "@heroui/react" - Replace
<Switch.Group>with<SwitchGroup> - Remove the nested
<Switch.GroupItems>wrapper - Individual
Switch.Rootcomponents remain unchanged
Affected Components
All compound components are affected:
Accordion→Accordion.RootAvatar→Avatar.RootCard→Card.RootDisclosure→Disclosure.RootFieldset→Fieldset.RootKbd→Kbd.RootLink→Link.RootPopover→Popover.RootRadioGroup→RadioGroup.RootSwitch→Switch.RootTabs→Tabs.RootTooltip→Tooltip.Root
Migration Guide
You have two options for using HeroUI compound components:
Option 1: Update to Use .Root (Compound Pattern)
If you're using the compound pattern (dot notation), update your code to use .Root for the main component:
Card Example:
import { Card } from "@heroui/react"
<Card.Root>
<Card.Header>
<Card.Title>Card Title</Card.Title>
<Card.Description>Card description</Card.Description>
</Card.Header>
<Card.Content>
Card content
</Card.Content>
<Card.Footer>
Card footer
</Card.Footer>
</Card.Root>Tabs Example:
import { Tabs } from "@heroui/react"
<Tabs.Root>
<Tabs.ListWrapper>
<Tabs.List>
<Tabs.Tab id="tab1">Tab 1<Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab id="tab2">Tab 2<Tabs.Indicator /></Tabs.Tab>
</Tabs.List>
</Tabs.ListWrapper>
<Tabs.Panel id="tab1">Panel 1</Tabs.Panel>
<Tabs.Panel id="tab2">Panel 2</Tabs.Panel>
</Tabs.Root>See more examples in the documentation
Avatar Example:
import { Avatar } from "@heroui/react"
<Avatar.Root>
<Avatar.Image alt="John Doe" src="..." />
<Avatar.Fallback>JD</Avatar.Fallback>
</Avatar.Root>See more examples in the documentation
Option 2: Use Named Exports
We added support for named exports for all compound components. You can use them like this:
Card Example:
import {
CardRoot,
CardHeader,
CardTitle,
CardDescription,
CardContent,
CardFooter,
} from "@heroui/react"
<CardRoot>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Card description</CardDescription>
</CardHeader>
<CardContent>
Card content
</CardContent>
<CardFooter>
Card footer
</CardFooter>
</CardRoot>Tabs Example:
import { TabsRoot, TabListWrapper, TabList, Tab, TabIndicator, TabPanel } from "@heroui/react"
<TabsRoot>
<TabListWrapper>
<TabList>
<Tab id="tab1">Tab 1<TabIndicator /></Tab>
<Tab id="tab2">Tab 2<TabIndicator /></Tab>
</TabList>
</TabListWrapper>
<TabPanel id="tab1">Panel 1</TabPanel>
<TabPanel id="tab2">Panel 2</TabPanel>
</TabsRoot>Avatar Example:
import { Avatar, AvatarImage, AvatarFallback } from "@heroui/react"
<Avatar>
<AvatarImage alt="John Doe" src="..." />
<AvatarFallback>JD</AvatarFallback>
</Avatar>Migration Steps
If you're using the compound pattern, you only need to update the main component to use .Root:
-
Find all instances of compound components (e.g.,
<Avatar>with<Avatar.Image>inside) -
Add
.Rootto the main component:// Before <Avatar> <Avatar.Image ... /> </Avatar> // After <Avatar.Root> <Avatar.Image ... /> </Avatar.Root> -
That's it! All child components (e.g.,
Avatar.Image,Avatar.Fallback) remain unchanged.
Complete Migration Reference
| Component | Named Export Pattern | Compound Pattern (with .Root) | Additional Changes |
|---|---|---|---|
| Accordion | <Accordion> | <Accordion.Root> | - |
| Avatar | <Avatar> | <Avatar.Root> | - |
| Card | <Card> | <Card.Root> | - |
| Disclosure | <Disclosure> | <Disclosure.Root> | - |
| Fieldset | <Fieldset> | <Fieldset.Root> | - |
| Kbd | <Kbd> | <Kbd.Root> | - |
| Link | <Link> | <Link.Root> | - |
| Popover | <Popover> | <Popover.Root> | - |
| Radio | <Radio> | <Radio.Root> | - |
| Switch | <Switch>, <SwitchControl> | <Switch.Root>, <Switch.Control> | <Switch.Group> → <SwitchGroup> (separate component) |
| Tabs | <Tabs>, <TabList> | <Tabs.Root>, <Tabs.List> | - |
| Tooltip | <Tooltip>, <TooltipTrigger> | <Tooltip.Root>, <Tooltip.Trigger> | - |
Automated Migration
For large codebases using the compound pattern, you can use find-and-replace:
# Example for Avatar component
# Update the main component to use .Root
sed -i 's/<Avatar>/<Avatar.Root>/g' **/*.tsx
sed -i 's/<\/Avatar>/<\/Avatar.Root>/g' **/*.tsx
# Switch component requires additional steps
# First, ensure SwitchGroup is imported
# Then replace Switch.Group with SwitchGroup
sed -i 's/<Switch\.Group/<SwitchGroup/g' **/*.tsx
sed -i 's/<\/Switch\.Group>/<\/SwitchGroup>/g' **/*.tsx
# Remove Switch.GroupItems wrapper
sed -i 's/<Switch\.GroupItems>//g' **/*.tsx
sed -i 's/<\/Switch\.GroupItems>//g' **/*.tsx
# Repeat for other compound components (Card, Tabs, etc.)
# Note: This only affects files using the compound patternImportant:
- Be careful with automated replacements. Make sure you're only replacing compound pattern usage, not named exports.
- For Switch migrations, verify that
SwitchGroupis imported:import { Switch, SwitchGroup } from "@heroui/react" - Test your code after running automated migrations to ensure all changes are correct.
Why This Change?
This change was necessary to fix React Server Components compatibility. The previous implementation had architectural limitations:
- RSC Compatibility: Compound pattern logic conflicted with
"use client"directives - React 19 Readiness: Removes deprecated patterns like
forwardRefandContext.Provider - Cleaner Architecture: Pattern logic is now in index files, not component files
- Better Separation: Server and client components can now work together seamlessly
Documentation Updates
Component documentation will be updated to reflect the new patterns:
- Examples will show the compound pattern with
.Root - Named export examples remain valid and supported
- Migration guides will help you transition smoothly
- Both patterns are fully supported and work identically
Need Help?
If you encounter any issues during migration:
- Compound pattern users: Update the main component to use
.Root(e.g.,<Avatar>→<Avatar.Root>) - Named export users: No changes needed - your code continues to work as-is
- Check the component documentation for examples
- Report issues at: GitHub Issues
Links
Contributors
Thanks to everyone who contributed to this release, improving React Server Components support and React 19 compatibility!