# Composition
**Category**: react
**URL**: https://www.heroui.com/docs/react/getting-started/composition
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/react/getting-started/(handbook)/composition.mdx
> Build flexible UI with component composition patterns
***
HeroUI uses composition patterns to create flexible, customizable components. Change the rendered element, compose components together, and maintain full control over markup.
## Framework-Agnostic Styles
HeroUI's variant functions are available in the `@heroui/styles` package, which can be used independently of React. This enables Vue, Svelte, and other frameworks to use HeroUI's design system:
```tsx
// Import directly from @heroui/styles (framework-agnostic)
import { buttonVariants } from '@heroui/styles';
// Or import from @heroui/react (re-exports the same functions)
import { buttonVariants } from '@heroui/react';
```
Both imports work identically. Use `@heroui/styles` when building for non-React frameworks or when you want to avoid pulling in React dependencies.
## Polymorphic Styling
Apply HeroUI styles to any element using variant functions or BEM classes. Extend component styles to framework components, native HTML elements, or custom components with full type safety.
**Example: Styling a Link as a Button**
You can use `buttonVariants` to style a Link component with button styles:
```tsx
import { buttonVariants } from '@heroui/styles';
import Link from 'next/link';
// Style a Next.js Link as a primary button
About
// Style a native anchor as a secondary button with custom size
External Link
```
**Using BEM classes directly:**
```tsx
import Link from 'next/link';
// Apply button styles directly using BEM classes
About
```
**Working with Compound Components**
When using a custom root element instead of HeroUI's Root component, child components cannot access context slots. You can manually pass `className` to child components using variant functions or BEM classes:
```tsx
import { Link } from '@heroui/react';
import { linkVariants } from '@heroui/styles';
import NextLink from 'next/link';
// With custom root - pass className manually
const slots = linkVariants();
About Page
About Page
```
This approach works because HeroUI's variant functions and BEM classes can be applied to any element, giving you complete flexibility to style framework components, native elements, or custom components with HeroUI's design system.
## Direct Class Application
The simplest way to style links or other elements is to apply HeroUI's [BEM](https://getbem.com/) classes directly. This approach is straightforward and works with any framework or vanilla HTML.
**With Next.js Link:**
```tsx
import Link from 'next/link';
Return Home
```
**With native anchor:**
```tsx
Go to Dashboard
```
**Available button classes:**
- `.button` — Base button styles
- `.button--primary`, `.button--secondary`, `.button--tertiary`, `.button--danger`, `.button--ghost` — Variants
- `.button--sm`, `.button--md`, `.button--lg` — Sizes
- `.button--icon-only` — Icon-only button
This approach works because HeroUI uses [BEM](https://getbem.com/) classes that can be applied to any element. It's perfect when you don't need the component's interactive features (like `onPress` handlers) and just want the visual styling.
## Using Variant Functions
For more control and type safety, use variant functions to apply HeroUI styles to framework-specific components or custom elements. Variant functions are available from both `@heroui/styles` (framework-agnostic) and `@heroui/react` (re-exports).
**With Next.js Link:**
```tsx
import { Link } from '@heroui/react';
import { linkVariants } from '@heroui/styles';
import NextLink from 'next/link';
const slots = linkVariants();
About Page
```
**With Button styles:**
```tsx
import { buttonVariants } from '@heroui/styles';
import Link from 'next/link';
Dashboard
```
**Available variant functions:** Each component exports its variant function (`buttonVariants`, `chipVariants`, `linkVariants`, `spinnerVariants`, and more) from `@heroui/styles`. Use them to apply HeroUI's design system to any element while maintaining type safety.
## Compound Components
HeroUI components are built as compound components—they export multiple parts that work together. Use them in three flexible ways:
**Option 1: Compound pattern (recommended)** — Use the main component directly without `.Root` suffix:
```tsx
import { Alert } from '@heroui/react';
SuccessYour changes have been saved.
```
**Option 2: Compound pattern with .Root** — Use the `.Root` suffix if you prefer explicit naming:
```tsx
import { Alert } from '@heroui/react';
SuccessYour changes have been saved.
```
**Option 3: Named exports** — Import each part separately:
```tsx
import {
AlertRoot,
AlertIcon,
AlertContent,
AlertTitle,
AlertDescription,
AlertClose
} from '@heroui/react';
SuccessYour changes have been saved.
```
**Mixed syntax:** Mix compound and named exports in the same component:
```tsx
import { Alert, AlertTitle, AlertDescription } from '@heroui/react';
SuccessYour changes have been saved.
```
**Simple components:** Simple components like `Button` work the same way—no `.Root` needed:
```tsx
import { Button } from '@heroui/react';
// Recommended - no .Root needed
// Or with .Root
Click me
// Or named export
import { ButtonRoot } from '@heroui/react';
Click me
```
**Benefits:** All three patterns provide flexibility, customization, control, and consistency. Choose the pattern that fits your codebase.
## Mixing Variant Functions
You can combine variant functions from different components to create unique styles:
```tsx
import { Link } from '@heroui/react';
import { linkVariants, buttonVariants } from '@heroui/styles';
// Link styled with button variants
const buttonStyles = buttonVariants({ variant: "tertiary", size: "md" });
HeroUI
```
## Custom Components
Create your own components by composing HeroUI primitives:
```tsx
import { Button, Tooltip } from '@heroui/react';
import { buttonVariants } from '@heroui/styles';
// Link button component using variant functions
function LinkButton({ href, children, variant = "primary", ...props }) {
return (
{children}
);
}
// Icon button with tooltip
function IconButton({ icon, label, ...props }) {
return (
{label}
);
}
```
## Custom Variants
Create custom variants by extending the component's variant function:
```tsx
import type { ButtonRootProps } from "@heroui/react";
import type { VariantProps } from "tailwind-variants";
import { Button } from "@heroui/react";
import { buttonVariants, tv } from "@heroui/styles";
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;
export type MyButtonProps = Omit &
MyButtonVariants & { className?: string };
function CustomButton({ className, radius, variant, ...props }: MyButtonProps) {
return ;
}
export function CustomVariants() {
return Custom Button;
}
```
**Type references:** When working with component types, use named type imports or object-style syntax.
**Recommended — Named type imports:**
```tsx
import type { ButtonRootProps, AvatarRootProps } from "@heroui/react";
type MyButtonProps = ButtonRootProps;
type MyAvatarProps = AvatarRootProps;
```
**Alternative — Object-style syntax:**
```tsx
import { Button, Avatar } from "@heroui/react";
type MyButtonProps = Button["RootProps"];
type MyAvatarProps = Avatar["RootProps"];
```
**Note:** The namespace syntax `Button.RootProps` is no longer supported. Use `Button["RootProps"]` or named imports instead.
## Custom DOM Element
Use the `render` prop on the following components to render a custom component in place of the default DOM element.
For example, you can render a [Motion](https://motion.dev/) button and use the state to drive an animation.
```tsx
import {Button} from '@heroui/react';
import {motion} from 'motion/react';
```
The `render` prop is also useful for rendering link components from client-side routers, or reusing existing presentational components.
```tsx
import {Link} from '@heroui/react';
import NextLink from 'next/link';
(
} href="/privacy-policy" />
)}
>
Privacy Policy
```
Follow these rules to avoid breaking the behavior and accessibility of the component:
- Always render the expected element type (e.g. if `