Getting Started

Set up Hareru UI in your React project

Installation

npm install @hareru/tokens @hareru/ui

CLI (optional)

The Hareru CLI generates CSS imports and auto-detects your CSS entry file:

# Add a component (defaults to per-component CSS mode)
npx @hareru/cli add Button --write

# Specify CSS mode explicitly
npx @hareru/cli add Button --write --mode tailwind

# List all available components
npx @hareru/cli list

CSS Setup

Add one of the following import patterns to your global stylesheet (e.g., globals.css):

A. Standalone (Hareru-only app — includes tokens, reset, and all component styles):

@import "@hareru/ui/styles.css";

B. Portable (alongside other frameworks — no global reset):

@import "@hareru/tokens/css";
@import "@hareru/ui/styles/components.css";
@import "@hareru/ui/styles/scope.css"; /* opt-in: .hui-root subtree typography */

C. Tailwind v4 coexistence (recommended for Tailwind projects):

@layer theme, base, hui, components, utilities;
@import "tailwindcss";
@import "@hareru/tokens/css";
@import "@hareru/ui/styles/components.layer.css";
@import "@hareru/ui/styles/scope.css"; /* opt-in */

D. Per-component (minimal bundle):

@import "@hareru/tokens/css";
@import "@hareru/ui/styles/components/Button.css";
@import "@hareru/ui/styles/components/Card.css";
/* Add @hareru/ui/styles/animations.css when using StreamingText, ReasoningPanel, or ToolCallCard */

styles.css already bundles tokens, so @hareru/tokens/css is not needed when using it.

If your host app has no CSS reset, optionally add @import "@hareru/ui/styles/baseline.css" for box-sizing: border-box and form element font inheritance.

Provider Setup

Create a providers component to wrap your application:

"use client"

import { ThemeProvider, Toaster } from "@hareru/ui"

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <ThemeProvider defaultTheme="system">
      {children}
      <Toaster />
    </ThemeProvider>
  )
}

Then integrate it in your root layout:

// app/layout.tsx
import { Providers } from "./providers"
import "./globals.css"

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  )
}

Package Exports

@hareru/tokens

Export pathContents
@hareru/tokensType definitions + TokenGenerator + defaultTheme
@hareru/tokens/presetsPreset themes
@hareru/tokens/cssPre-generated theme CSS variables

@hareru/ui

Export pathContents
@hareru/uiAll components, utilities, providers, Toast API
@hareru/ui/styles.cssConvenience bundle (tokens + reset + components)
@hareru/ui/styles.layer.cssConvenience bundle wrapped in @layer hui
@hareru/ui/styles/components.cssComponent styles only (portable)
@hareru/ui/styles/components.layer.cssComponent styles wrapped in @layer hui (Tailwind coexistence)
@hareru/ui/styles/baseline.cssMinimal reset (box-sizing, font: inherit)
@hareru/ui/styles/scope.css.hui-root typography scope helper
@hareru/ui/styles/animations.cssShared keyframes (for per-component usage)
@hareru/ui/styles/components/*.cssPer-component CSS (49 files)

All components are imported flat from @hareru/ui. Sub-path imports like @hareru/ui/components/Button are not available.

useTheme API

const { theme, setTheme, resolvedTheme } = useTheme()
PropertyTypeDescription
theme'light' | 'dark' | 'system'Current setting value
resolvedTheme'light' | 'dark'The actual resolved theme
setTheme(theme) => voidFunction to set the theme

Common Recipes

Form

import {
  FormField, FormFieldLabel, FormFieldControl,
  FormFieldDescription, FormFieldMessage, Input, Button
} from "@hareru/ui"

<FormField error={!!errors.email}>
  <FormFieldLabel>Email address</FormFieldLabel>
  <FormFieldControl>
    <Input type="email" placeholder="you@example.com" />
  </FormFieldControl>
  <FormFieldDescription>The email address used for login</FormFieldDescription>
  <FormFieldMessage>Please enter a valid email address</FormFieldMessage>
</FormField>

Confirmation Dialog

import {
  AlertDialog, AlertDialogTrigger, AlertDialogContent,
  AlertDialogHeader, AlertDialogFooter, AlertDialogTitle,
  AlertDialogDescription, AlertDialogAction, AlertDialogCancel,
  Button
} from "@hareru/ui"

<AlertDialog>
  <AlertDialogTrigger asChild>
    <Button variant="destructive">Delete</Button>
  </AlertDialogTrigger>
  <AlertDialogContent>
    <AlertDialogHeader>
      <AlertDialogTitle>Are you sure?</AlertDialogTitle>
      <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Cancel</AlertDialogCancel>
      <AlertDialogAction>Delete</AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>

Toast Notification

import { toast } from "@hareru/ui"

toast({ title: "Saved", description: "Your changes have been saved." })
toast({ title: "Error", description: "Failed to save changes.", variant: "destructive" })

Troubleshooting

SymptomCauseFix
CSS variables not working@import not configuredAdd @import "@hareru/ui/styles.css" (standalone) or @import "@hareru/tokens/css" + @import "@hareru/ui/styles/components.css" (portable) to globals.css
Styles not applied in Next.jstranspilePackages missingAdd transpilePackages: ["@hareru/ui", "@hareru/tokens"] to next.config
TypeScript type errordist not generatedRun pnpm build in hareru-ui directory
Theme switching not workingThemeProvider missingPlace ThemeProvider in root layout

On this page