Frameworks
Integrate HeroUI with your framework
Next.js
1. Create a Next.js project
npx heroui-cli@latest init -t appnpx heroui-cli@latest init -t pages-t skips the template picker. You are still prompted for a project name and package manager — pass them directly to skip all prompts: npx heroui-cli@latest init my-app -t app -p pnpm. Then open your new folder and install dependencies (for example pnpm install).
2. Use your first HeroUI component
Example: app/page.tsx
import {Button} from "@heroui/react";
export default function HomePage() {
return (
<main className="flex min-h-screen items-center justify-center">
<Button variant="tertiary">Hello HeroUI</Button>
</main>
);
}Example: pages/index.tsx
import {Button} from "@heroui/react";
export default function HomePage() {
return (
<main className="flex min-h-screen items-center justify-center">
<Button variant="tertiary">Hello HeroUI</Button>
</main>
);
}HeroUI v3 does not require a provider. Components work directly after installation and style import.
3. Locale Setup (Optional)
To integrate with Next.js, ensure the locale on the server matches the client.
In your root layout, determine the user's preferred language and set the lang and dir attributes on the <html> element.
// app/layout.tsx
import {headers} from 'next/headers';
import {isRTL} from '@heroui/react';
import {ClientProviders} from './provider';
export default async function RootLayout({children}) {
// Get the user's preferred language from the Accept-Language header.
// You could also get this from a database, URL param, etc.
const acceptLanguage = (await headers()).get('accept-language');
const lang = acceptLanguage?.split(/[,;]/)[0] || 'en-US';
return (
<html lang={lang} dir={isRTL(lang) ? 'rtl' : 'ltr'}>
<body>
<ClientProviders lang={lang}>
{children}
</ClientProviders>
</body>
</html>
);
}Create app/provider.tsx. This should render an I18nProvider to set the locale used by React Aria.
// app/provider.tsx
"use client";
import {I18nProvider} from '@heroui/react';
export function ClientProviders({lang, children}) {
return (
<I18nProvider locale={lang}>
{children}
</I18nProvider>
);
}If you are using a Content Security Policy (CSP) with a nonce, add a <meta property="csp-nonce"> tag to your document head, setting the content attribute to the generated nonce value. React Aria automatically reads the nonce from this tag.
Vite
1. Create a Vite project
npx heroui-cli@latest init -t vite-t skips the template picker. You are still prompted for a project name and package manager — pass them directly to skip all prompts: npx heroui-cli@latest init my-app -t vite -p pnpm. Then open your new folder and install dependencies (for example pnpm install).
2. Use your first HeroUI component
Example: src/App.tsx
import {Button} from "@heroui/react";
function App() {
return (
<main className="flex min-h-screen items-center justify-center">
<Button variant="tertiary">Hello HeroUI</Button>
</main>
);
}
export default App;HeroUI v3 does not require a provider. Components work directly after installation and style import.
React Router
1. Create a React Router project
npx heroui-cli@latest init -t react-router-t skips the template picker. You are still prompted for a project name and package manager — pass them directly to skip all prompts: npx heroui-cli@latest init my-app -t react-router -p pnpm. Then open your new folder and install dependencies (for example pnpm install).
2. Use your first HeroUI component
Example: app/routes/_index.tsx
import {Button} from "@heroui/react";
export default function Index() {
return (
<main className="flex min-h-screen items-center justify-center">
<Button variant="tertiary">Hello HeroUI</Button>
</main>
);
}The template loads styles with import "./tailwind.css"; in app/root.tsx. HeroUI v3 does not require a provider. Components work directly after installation and style import.
Other Frameworks
@heroui/react requires React because its behavior is built on React Aria. The design system itself lives in @heroui/styles — a package with no React dependency that ships HeroUI's BEM classes and framework-agnostic variant functions. You can use it from Vue, Svelte, Angular, or plain HTML.
This gives you HeroUI's visual design, not its behavior. Keyboard navigation, focus management, and ARIA attributes come from React Aria in @heroui/react, so you are responsible for them in your own components.
1. Install the styles package
npm i @heroui/stylespnpm add @heroui/stylesyarn add @heroui/stylesbun add @heroui/styles2. Import the styles
Add to your main CSS file:
@import "tailwindcss";
@import "@heroui/styles";This is the only option where Tailwind utility classes work alongside HeroUI classes.
Import the compiled stylesheet from your entry file. No Tailwind build required:
import "@heroui/styles/dist/heroui.min.css";No build step at all:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@heroui/styles@3/dist/heroui.min.css" />The prebuilt and CDN bundles contain HeroUI's component classes and theme tokens, but not Tailwind's utility classes. If you also want utilities like flex or gap-4, use the Tailwind CSS v4 option.
3. Use your first HeroUI component
Apply the BEM classes directly, or generate them with a variant function for type safety.
<script setup lang="ts">
import {buttonVariants} from "@heroui/styles";
const buttonClass = buttonVariants({variant: "primary"});
</script>
<template>
<button :class="buttonClass">Hello HeroUI</button>
<!-- Or with BEM classes directly -->
<button class="button button--primary">Hello HeroUI</button>
</template><script lang="ts">
import {buttonVariants} from "@heroui/styles";
const buttonClass = buttonVariants({variant: "primary"});
</script>
<button class={buttonClass}>Hello HeroUI</button>
<!-- Or with BEM classes directly -->
<button class="button button--primary">Hello HeroUI</button>import {Component} from "@angular/core";
import {buttonVariants} from "@heroui/styles";
@Component({
selector: "app-example",
standalone: true,
template: `
<button [class]="buttonClass">Hello HeroUI</button>
<!-- Or with BEM classes directly -->
<button class="button button--primary">Hello HeroUI</button>
`,
})
export class ExampleComponent {
buttonClass = buttonVariants({variant: "primary"});
}<button class="button button--primary">Hello HeroUI</button>
<button class="button button--secondary button--sm">Small</button>
<button class="button button--danger" disabled>Delete</button>Every component follows the same BEM convention: a block class, -- modifiers for variants and sizes, and __ elements for child parts. For button, the variants are .button--primary, --secondary, --tertiary, --ghost, --outline, --danger, and --danger-soft; the sizes are .button--sm, --md, and --lg; and .button--icon-only and .button--full-width are modifiers.
See Composition for more on variant functions and applying HeroUI styles to arbitrary elements.
4. Interactive states
HeroUI's CSS targets both native pseudo-classes and data-* attributes, so semantic HTML elements pick up hover, press, focus, and disabled styling with no JavaScript:
/* button.css */
.button {
&:active,
&[data-pressed="true"] { ... }
&:disabled,
&[aria-disabled="true"] { ... }
}States the browser cannot infer must be set by you. The most common are:
| Attribute | Purpose |
|---|---|
data-selected="true" | Selected items in menus, list boxes, and tabs |
data-entering="true" / data-exiting="true" | Enter and exit animations for overlays |
data-placement="top" | Arrow and offset direction for popovers and tooltips |
data-invalid="true" | Validation styling on form fields |
data-slot="..." | Marks child parts of compound components |
Stateless components such as button, chip, card, and skeleton work as drop-in markup. Overlays and collections (select, menu, popover, date picker) depend on state you would have to manage yourself — consider keeping those in React, or read the data-* selectors in the component's CSS file to see what is required.
5. Theming
Dark mode is driven by a .dark class or a data-theme="dark" attribute on any ancestor element:
<html class="dark">...</html>
<!-- or -->
<html data-theme="dark">...</html>Override the theme by redefining CSS variables:
:root {
--accent: oklch(0.62 0.19 253);
--radius: 0.5rem;
}Next steps
- Quick Start for the fastest setup path
- Themes to customize colors and tokens
- Composition for variant functions and polymorphic styling
- Components to explore all available components