Composition
Build flexible UI with component composition patterns
Overview
HeroUI uses composition patterns to create flexible, customizable components. This allows you to:
- Change the rendered element or component
- Compose components together
- Maintain full control over markup
The asChild Prop
The asChild
prop lets you change what element a component renders. When asChild
is true, HeroUI won't render its default element - instead, it clones the child element and merges props.
Basic Usage
import { Button } from '@heroui/react';
import Link from 'next/link';
// Renders as a Next.js Link
<Button asChild>
<Link href="/about">About</Link>
</Button>
// Renders as a regular anchor
<Button asChild>
<a href="https://example.com">External Link</a>
</Button>
Available Components
These components support asChild
:
Button
- Change button elementAlert
parts - Root, Icon, Content, Title, Description, Action, CloseAvatar
- Change root element- More components coming soon
Compound Components
HeroUI components are built as compound components - they export multiple parts that work together:
import { Alert } from '@heroui/react';
<Alert>
<Alert.Icon />
<Alert.Content>
<Alert.Title>Success</Alert.Title>
<Alert.Description>Your changes have been saved.</Alert.Description>
</Alert.Content>
<Alert.Close />
</Alert>
This pattern provides:
- Flexibility - Arrange parts as needed
- Customization - Style each part independently
- Control - Add or remove parts
Style Variants
HeroUI exports variant functions that can be applied to any component. This allows you to use HeroUI's design system with any element or component:
Using buttonVariants
Apply button styles to any component:
import { Link, buttonVariants } from '@heroui/react';
// Link styled as a tertiary button
<Link
className={buttonVariants({
size: "md",
variant: "tertiary",
className: "px-3"
})}
href="https://heroui.com"
target="_blank"
>
HeroUI
<Link.Icon className="h-2 w-2" />
</Link>
// Native anchor styled as primary button
<a
className={buttonVariants({ variant: "primary" })}
href="/dashboard"
>
Go to Dashboard
</a>
Available Variant Functions
Each component exports its variant function:
buttonVariants
- Button styleschipVariants
- Chip styleslinkVariants
- Link stylesspinnerVariants
- Spinner styles- More variant functions for each component
Custom Components
Create your own components by composing HeroUI primitives:
import { Button, Tooltip } from '@heroui/react';
// Link button component
function LinkButton({ href, children, ...props }) {
return (
<Button asChild {...props}>
<a href={href}>{children}</a>
</Button>
);
}
// Icon button with tooltip
function IconButton({ icon, label, ...props }) {
return (
<Tooltip content={label}>
<Button isIconOnly {...props}>
<Icon icon={icon} />
</Button>
</Tooltip>
);
}
Custom Variants
You can create your own custom variants by extending the component's variant function.
import type {ButtonProps} from "@heroui/react";
import type {VariantProps} from "tailwind-variants";
import {Button, buttonVariants} from "@heroui/react";
import {tv} from "tailwind-variants";
const myButtonVariants = tv({
extend: buttonVariants,
base: "text-md text-shadow-lg font-semibold shadow-md data-[pending=true]:opacity-40",
variants: {
radius: {
lg: "rounded-lg",
md: "rounded-md",
sm: "rounded-sm",
full: "rounded-full",
},
size: {
sm: "h-10 px-4",
md: "h-11 px-6",
lg: "h-12 px-8",
xl: "h-13 px-10",
},
variant: {
primary: "text-white dark:bg-white/10 dark:text-white dark:hover:bg-white/15",
},
},
defaultVariants: {
radius: "full",
variant: "primary",
},
});
type MyButtonVariants = VariantProps<typeof myButtonVariants>;
export type MyButtonProps = Omit<ButtonProps, "className"> &
MyButtonVariants & {className?: string};
function CustomButton({className, radius, variant, ...props}: MyButtonProps) {
return <Button className={myButtonVariants({className, radius, variant})} {...props} />;
}
export function CustomVariants() {
return <CustomButton>Custom Button</CustomButton>;
}
With Next.js
Use asChild
to integrate with Next.js:
import Link from 'next/link';
import { Button } from '@heroui/react';
<Button asChild variant="primary">
<Link href="/dashboard">Dashboard</Link>
</Button>
With React Router
Use asChild
to integrate with routing libraries:
import { Link } from 'react-router-dom';
import { Button } from '@heroui/react';
<Button asChild variant="primary">
<Link to="/dashboard">Dashboard</Link>
</Button>
Next Steps
- Learn about Styling components
- Explore Animation options
- Browse Components for more examples