# 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'; Success Your 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'; Success Your changes have been saved. ``` **Option 3: Named exports** — Import each part separately: ```tsx import { AlertRoot, AlertIcon, AlertContent, AlertTitle, AlertDescription, AlertClose } from '@heroui/react'; Success Your changes have been saved. ``` **Mixed syntax:** Mix compound and named exports in the same component: ```tsx import { Alert, AlertTitle, AlertDescription } from '@heroui/react'; Success Your 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 ``` 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 `