Button
Migration guide for Button from HeroUI v2 to v3
Refer to the v3 Button documentation for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.
Overview
The Button component in HeroUI v3 has been simplified with variant-based styling replacing the separate color prop, and onClick has been renamed to onPress.
Structure Changes
v2: Single Component with Color and Variant Props
In v2, Button used a combination of color and variant props:
import { Button } from "@heroui/react";
<Button color="primary" variant="solid">
Button
</Button>v3: Single Component with Variant Only
In v3, Button uses only the variant prop (no separate color prop):
import { Button } from "@heroui/react";
<Button variant="primary">
Button
</Button>Key Changes
1. Event Handler: onClick → onPress
v2: Supported both onClick (deprecated) and onPress
v3: Only onPress is supported
<Button onClick={() => console.log("clicked")}>Click me</Button><Button onPress={() => console.log("pressed")}>Click me</Button>2. Variants and Colors
v2: Used color + variant combination
v3: Uses variant only (no separate color prop)
| v2 Color + Variant | v3 Variant | Notes |
|---|---|---|
color="primary" variant="solid" | variant="primary" | Default primary button |
color="default" variant="solid" | variant="primary" | Use primary variant |
color="secondary" variant="solid" | variant="secondary" | Same |
color="success" variant="solid" | variant="primary" | Use primary with custom styling if needed |
color="warning" variant="solid" | variant="primary" | Use primary with custom styling if needed |
color="danger" variant="solid" | variant="danger" | Danger variant available |
color="primary" variant="bordered" | variant="secondary" | Similar appearance |
color="primary" variant="light" | variant="tertiary" | Similar appearance |
color="primary" variant="flat" | variant="tertiary" | Similar appearance |
color="primary" variant="faded" | variant="secondary" | Similar appearance |
color="primary" variant="ghost" | variant="ghost" | Same |
color="danger" variant="flat" | variant="danger-soft" | New soft danger variant |
v2 Variants: solid, bordered, light, flat, faded, shadow, ghost
v3 Variants: primary, secondary, tertiary, ghost, danger, danger-soft
3. Loading State: isLoading → isPending
v2: Used isLoading prop
v3: Uses isPending prop
4. Removed Props
The following props are no longer available in v3:
color- Variants handle styling insteadradius- Use Tailwind CSS classes likerounded-lg,rounded-full, etc.startContent/endContent- Place icons directly as childrenspinner/spinnerPlacement- Handle loading state manually with render propsfullWidth- Use Tailwind CSS classw-fulldisableRipple- Ripple effect removed in v3disableAnimation- Animations are handled internallyclassNames- UseclassNameprop instead
5. ButtonGroup Removed
v2: Had a dedicated ButtonGroup component
v3: No ButtonGroup component - create groups manually with CSS classes
Migration Examples
Basic Usage
import { Button } from "@heroui/react";
export default function App() {
return <Button color="primary">Button</Button>;
}import { Button } from "@heroui/react";
export default function App() {
return <Button variant="primary">Button</Button>;
}Variants
<Button color="primary" variant="solid">Solid</Button>
<Button color="primary" variant="bordered">Bordered</Button>
<Button color="primary" variant="light">Light</Button>
<Button color="primary" variant="flat">Flat</Button>
<Button color="primary" variant="ghost">Ghost</Button><Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="tertiary">Tertiary</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="danger">Danger</Button>Colors
<Button color="default">Default</Button>
<Button color="primary">Primary</Button>
<Button color="secondary">Secondary</Button>
<Button color="success">Success</Button>
<Button color="warning">Warning</Button>
<Button color="danger">Danger</Button><Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="tertiary">Tertiary</Button>
<Button variant="danger">Danger</Button>
<Button variant="danger-soft">Danger Soft</Button>
{/* For success/warning, use primary with custom styling */}
<Button variant="primary" className="bg-success text-white">
Success
</Button>With Icons
import { Icon } from "@iconify/react";
<Button
color="success"
endContent={<Icon icon="gravity-ui:camera" />}
>
Take a photo
</Button>
<Button
color="danger"
startContent={<Icon icon="gravity-ui:trash-bin" />}
variant="bordered"
>
Delete user
</Button>import { Icon } from "@iconify/react";
<Button variant="primary">
<Icon icon="gravity-ui:camera" />
Take a photo
</Button>
<Button variant="secondary">
<Icon icon="gravity-ui:trash-bin" />
Delete user
</Button>Icon Only Button
<Button isIconOnly color="danger">
<HeartIcon />
</Button>import { Icon } from "@iconify/react";
<Button isIconOnly variant="danger">
<Icon icon="gravity-ui:heart" />
</Button>Loading State
<Button isLoading color="primary">
Loading
</Button>import { Spinner } from "@heroui/react";
<Button isPending>
{({isPending}) => (
<>
{isPending ? <Spinner color="current" size="sm" /> : null}
Loading
</>
)}
</Button>Loading State with Conditional Content
import { useState } from "react";
const [isLoading, setIsLoading] = useState(false);
<Button
isLoading={isLoading}
spinnerPlacement="start"
color="primary"
onPress={() => setIsLoading(true)}
>
Upload File
</Button>import { useState } from "react";
import { Spinner } from "@heroui/react";
import { Icon } from "@iconify/react";
const [isLoading, setIsLoading] = useState(false);
<Button
isPending={isLoading}
onPress={() => setIsLoading(true)}
>
{({isPending}) => (
<>
{isPending ? (
<Spinner color="current" size="sm" />
) : (
<Icon icon="gravity-ui:paperclip" />
)}
{isPending ? "Uploading..." : "Upload File"}
</>
)}
</Button>Full Width Button
<Button fullWidth color="primary">
Full Width
</Button><Button className="w-full" variant="primary">
Full Width
</Button>Radius
<Button radius="full" color="primary">
Rounded
</Button>
<Button radius="lg" color="primary">
Large Radius
</Button><Button className="rounded-full" variant="primary">
Rounded
</Button>
<Button className="rounded-lg" variant="primary">
Large Radius
</Button>Button Group
import { Button, ButtonGroup } from "@heroui/react";
<ButtonGroup>
<Button>One</Button>
<Button>Two</Button>
<Button>Three</Button>
</ButtonGroup><div className="inline-flex rounded-lg border border-divider" role="group">
<Button className="rounded-r-none border-r border-divider">
One
</Button>
<Button className="rounded-none border-r border-divider">
Two
</Button>
<Button className="rounded-l-none">
Three
</Button>
</div>Disabled State
<Button isDisabled color="primary">
Disabled
</Button><Button isDisabled variant="primary">
Disabled
</Button>Sizes
<Button size="sm" color="primary">Small</Button>
<Button size="md" color="primary">Medium</Button>
<Button size="lg" color="primary">Large</Button><Button size="sm" variant="primary">Small</Button>
<Button size="md" variant="primary">Medium</Button>
<Button size="lg" variant="primary">Large</Button>Render Props Pattern
v3 Button supports a render prop pattern that provides state information:
<Button isPending={isLoading}>
{({isPending, isPressed, isHovered, isFocused, isDisabled}) => (
<>
{isPending && <Spinner size="sm" />}
{isPressed ? "Pressed!" : "Click me"}
</>
)}
</Button>Available render props:
isPending- Whether button is in loading stateisPressed- Whether button is currently pressedisHovered- Whether button is hoveredisFocused- Whether button is focusedisFocusVisible- Whether button should show focus indicatorisDisabled- Whether button is disabled
Breaking Changes Summary
- onClick → onPress: Must use
onPressinstead ofonClick - Color Prop Removed: Use
variantprop instead ofcolor+variant - Variants Changed: New variant system (
primary,secondary,tertiary,ghost,danger,danger-soft) - isLoading → isPending: Loading prop renamed
- Icons:
startContent/endContentremoved - place icons as children - Loading Spinner: Must handle spinner manually with render props
- Radius Removed: Use Tailwind CSS classes
- FullWidth Removed: Use Tailwind
w-fullclass - Ripple Removed: No ripple effect in v3
- ButtonGroup Removed: Create groups manually with CSS
- ClassNames Removed: Use
classNameprop
Tips for Migration
- Replace onClick: Change all
onClickhandlers toonPress - Map variants: Use the variant mapping table to find equivalent v3 variants
- Handle loading: Use render props pattern for loading states with spinners
- Icons as children: Move icons from
startContent/endContentto direct children - Use Tailwind: Many removed props can be replaced with Tailwind utility classes
- Button groups: Use flexbox and border utilities to create button groups
- Custom colors: For success/warning colors, use
variant="primary"with custom Tailwind classes
Need Help?
For v3 Button features and API:
- See the API Reference
- Check interactive examples
For community support: