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 elementAlertand its parts -Alert,AlertIcon,AlertContent,AlertTitle,AlertDescription,AlertAction,AlertCloseAvatarand its parts -Avatar,AvatarImage,AvatarFallback- More components coming soon
 
Compound Components
HeroUI components are built as compound components - they export multiple parts that work together. You can use them in two ways:
Option 1: Named Exports (Recommended)
Import each part separately:
import {
  Alert,
  AlertIcon,
  AlertContent,
  AlertTitle,
  AlertDescription,
  AlertClose
} from '@heroui/react';
<Alert>
  <AlertIcon />
  <AlertContent>
    <AlertTitle>Success</AlertTitle>
    <AlertDescription>Your changes have been saved.</AlertDescription>
  </AlertContent>
  <AlertClose />
</Alert>Option 2: Dot Notation (Compound Pattern)
Import the main component and access parts via dot notation:
import { Alert } from '@heroui/react';
<Alert.Root>
  <Alert.Icon />
  <Alert.Content>
    <Alert.Title>Success</Alert.Title>
    <Alert.Description>Your changes have been saved.</Alert.Description>
  </Alert.Content>
  <Alert.Close />
</Alert.Root>Note: When using dot notation, the main component requires .Root suffix (e.g., <Alert.Root> instead of <Alert>).
Benefits of Compound Components
Both patterns provide:
- 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, LinkIcon, buttonVariants } from '@heroui/react';
// Link styled as a tertiary button
<Link.Root
  className={buttonVariants({
    size: "md",
    variant: "tertiary",
    className: "px-3"
  })}
  href="https://heroui.com"
  target="_blank"
>
  HeroUI
  <LinkIcon className="h-2 w-2" />
</Link.Root>
// 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.Root>
      <Tooltip.Trigger>
        <Button isIconOnly {...props}>
          <Icon icon={icon} />
        </Button>
      </Tooltip.Trigger>
      <Tooltip.Content>{label}</Tooltip.Content>
    </Tooltip.Root>
  );
}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