heroui-components
About
This skill enables developers to add and customize HeroUI components in web applications using the HeroUI design system. It provides tools for discovering components, implementing new UI features, and updating component styling. Use it when building interfaces with HeroUI components or debugging styling issues.
Documentation
HeroUI Components Skill
This skill helps you work with HeroUI (Hero UI / NextUI v3) components in apps/web/.
When to Use This Skill
- Adding new HeroUI components to pages
- Customizing HeroUI component variants
- Implementing forms with HeroUI inputs
- Creating modal dialogs and drawers
- Building navigation with HeroUI components
- Debugging HeroUI styling issues
HeroUI Overview
HeroUI is a modern React UI library built on Tailwind CSS and React Aria, providing accessible components.
Discovery and Research
List Available Components
// Use MCP tool to discover components
mcp__heroui-react__list_components()
Get Component Information
// Get details about a specific component
mcp__heroui-react__get_component_info({ component: "Button" })
mcp__heroui-react__get_component_info({ component: "Input" })
Get Usage Examples
// Get examples for a component
mcp__heroui-react__get_component_examples({ component: "Modal" })
Installation and Setup
HeroUI is already installed in the web app. Import from @heroui/react:
import { Button, Input, Card } from "@heroui/react";
Common Components
1. Button Component
import { Button } from "@heroui/react";
export function ActionButton() {
return (
<>
{/* Basic button */}
<Button>Click me</Button>
{/* Color variants */}
<Button color="primary">Primary</Button>
<Button color="secondary">Secondary</Button>
<Button color="success">Success</Button>
<Button color="warning">Warning</Button>
<Button color="danger">Danger</Button>
{/* Size variants */}
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
{/* Variants */}
<Button variant="solid">Solid</Button>
<Button variant="bordered">Bordered</Button>
<Button variant="light">Light</Button>
<Button variant="flat">Flat</Button>
<Button variant="faded">Faded</Button>
<Button variant="shadow">Shadow</Button>
<Button variant="ghost">Ghost</Button>
{/* With icon */}
<Button startContent={<SearchIcon />}>Search</Button>
<Button endContent={<ArrowIcon />}>Next</Button>
{/* Loading state */}
<Button isLoading>Loading...</Button>
{/* Disabled */}
<Button isDisabled>Disabled</Button>
</>
);
}
2. Input Component
import { Input } from "@heroui/react";
export function SearchForm() {
return (
<>
{/* Basic input */}
<Input
type="text"
label="Search"
placeholder="Enter search term"
/>
{/* With validation */}
<Input
type="email"
label="Email"
placeholder="[email protected]"
isRequired
errorMessage="Please enter a valid email"
/>
{/* With start/end content */}
<Input
type="text"
label="Price"
placeholder="0.00"
startContent={<span>$</span>}
endContent={<span>SGD</span>}
/>
{/* Variants */}
<Input variant="flat" />
<Input variant="bordered" />
<Input variant="faded" />
<Input variant="underlined" />
{/* Colors */}
<Input color="primary" />
<Input color="success" />
<Input color="danger" />
</>
);
}
3. Card Component
import { Card, CardHeader, CardBody, CardFooter, Button } from "@heroui/react";
export function CarCard({ car }: { car: Car }) {
return (
<Card className="max-w-md">
<CardHeader className="flex gap-3">
<div className="flex flex-col">
<p className="text-md font-bold">{car.make}</p>
<p className="text-small text-default-500">{car.model}</p>
</div>
</CardHeader>
<CardBody>
<p>Year: {car.year}</p>
<p>Price: ${car.price?.toLocaleString()}</p>
</CardBody>
<CardFooter>
<Button color="primary" variant="flat">
View Details
</Button>
</CardFooter>
</Card>
);
}
4. Modal Component
"use client";
import {
Modal,
ModalContent,
ModalHeader,
ModalBody,
ModalFooter,
Button,
useDisclosure,
} from "@heroui/react";
export function CarDetailsModal({ car }: { car: Car }) {
const { isOpen, onOpen, onOpenChange } = useDisclosure();
return (
<>
<Button onPress={onOpen}>View Details</Button>
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent>
{(onClose) => (
<>
<ModalHeader className="flex flex-col gap-1">
{car.make} {car.model}
</ModalHeader>
<ModalBody>
<p>Year: {car.year}</p>
<p>Registration: {car.registrationDate}</p>
<p>Fuel Type: {car.fuelType}</p>
</ModalBody>
<ModalFooter>
<Button color="danger" variant="light" onPress={onClose}>
Close
</Button>
<Button color="primary" onPress={onClose}>
Save
</Button>
</ModalFooter>
</>
)}
</ModalContent>
</Modal>
</>
);
}
5. Table Component
import {
Table,
TableHeader,
TableColumn,
TableBody,
TableRow,
TableCell,
} from "@heroui/react";
export function CarTable({ cars }: { cars: Car[] }) {
return (
<Table aria-label="Car registrations table">
<TableHeader>
<TableColumn>MAKE</TableColumn>
<TableColumn>MODEL</TableColumn>
<TableColumn>YEAR</TableColumn>
<TableColumn>PRICE</TableColumn>
</TableHeader>
<TableBody>
{cars.map((car) => (
<TableRow key={car.id}>
<TableCell>{car.make}</TableCell>
<TableCell>{car.model}</TableCell>
<TableCell>{car.year}</TableCell>
<TableCell>${car.price?.toLocaleString()}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
}
6. Tabs Component
import { Tabs, Tab } from "@heroui/react";
export function DataTabs() {
return (
<Tabs aria-label="Data options">
<Tab key="cars" title="Car Registrations">
<CarRegistrationData />
</Tab>
<Tab key="coe" title="COE Results">
<COEBiddingData />
</Tab>
<Tab key="pqp" title="PQP Data">
<PQPData />
</Tab>
</Tabs>
);
}
7. Select Component
import { Select, SelectItem } from "@heroui/react";
export function CarMakeSelector() {
const makes = ["Toyota", "Honda", "BMW", "Mercedes"];
return (
<Select
label="Select car make"
placeholder="Choose a make"
className="max-w-xs"
>
{makes.map((make) => (
<SelectItem key={make} value={make}>
{make}
</SelectItem>
))}
</Select>
);
}
8. Dropdown Component
import {
Dropdown,
DropdownTrigger,
DropdownMenu,
DropdownItem,
Button,
} from "@heroui/react";
export function ActionsDropdown() {
return (
<Dropdown>
<DropdownTrigger>
<Button variant="bordered">Actions</Button>
</DropdownTrigger>
<DropdownMenu aria-label="Actions">
<DropdownItem key="view">View</DropdownItem>
<DropdownItem key="edit">Edit</DropdownItem>
<DropdownItem key="delete" className="text-danger" color="danger">
Delete
</DropdownItem>
</DropdownMenu>
</Dropdown>
);
}
Form Handling
Complete Form Example
"use client";
import { useState } from "react";
import { Input, Button, Textarea } from "@heroui/react";
import { createBlogPost } from "@/actions/blog";
export function CreatePostForm() {
const [isLoading, setIsLoading] = useState(false);
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setIsLoading(true);
const formData = new FormData(e.currentTarget);
const result = await createBlogPost(formData);
setIsLoading(false);
if (result.success) {
// Handle success
}
}
return (
<form onSubmit={handleSubmit} className="space-y-4">
<Input
name="title"
label="Post Title"
placeholder="Enter post title"
isRequired
/>
<Textarea
name="content"
label="Content"
placeholder="Enter post content"
minRows={5}
isRequired
/>
<Input
name="tags"
label="Tags"
placeholder="Enter tags separated by commas"
/>
<Button
type="submit"
color="primary"
isLoading={isLoading}
className="w-full"
>
Create Post
</Button>
</form>
);
}
Theming and Customization
Theme Provider Setup
// app/providers.tsx
"use client";
import { HeroUIProvider } from "@heroui/react";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<HeroUIProvider>
{children}
</HeroUIProvider>
);
}
// app/layout.tsx
import { Providers } from "./providers";
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
Custom Colors via Tailwind
// tailwind.config.ts
import { heroui } from "@heroui/react";
export default {
plugins: [
heroui({
themes: {
light: {
colors: {
primary: {
DEFAULT: "#0070F3",
foreground: "#FFFFFF",
},
focus: "#0070F3",
},
},
},
}),
],
};
Component-Level Customization
import { Button } from "@heroui/react";
export function CustomButton() {
return (
<Button
className="bg-gradient-to-r from-blue-500 to-purple-500"
variant="shadow"
>
Custom Styled Button
</Button>
);
}
Accessibility
HeroUI components are built on React Aria for accessibility:
import { Button } from "@heroui/react";
export function AccessibleButton() {
return (
<Button
aria-label="Close dialog"
aria-describedby="close-description"
>
Close
</Button>
);
}
Responsive Design
import { Button } from "@heroui/react";
export function ResponsiveButton() {
return (
<Button
size={{
initial: "sm", // Mobile
sm: "md", // Tablet
lg: "lg", // Desktop
}}
className="w-full sm:w-auto"
>
Responsive Button
</Button>
);
}
Common Patterns
Loading States
"use client";
import { useState } from "react";
import { Button } from "@heroui/react";
export function SubmitButton() {
const [isLoading, setIsLoading] = useState(false);
async function handleClick() {
setIsLoading(true);
await performAction();
setIsLoading(false);
}
return (
<Button
onPress={handleClick}
isLoading={isLoading}
color="primary"
>
{isLoading ? "Processing..." : "Submit"}
</Button>
);
}
Controlled Inputs
"use client";
import { useState } from "react";
import { Input } from "@heroui/react";
export function ControlledInput() {
const [value, setValue] = useState("");
return (
<Input
value={value}
onValueChange={setValue}
label="Search"
placeholder="Type to search..."
/>
);
}
Testing HeroUI Components
// __tests__/components/car-card.test.tsx
import { render, screen } from "@testing-library/react";
import { HeroUIProvider } from "@heroui/react";
import CarCard from "../car-card";
function renderWithProvider(component: React.ReactElement) {
return render(
<HeroUIProvider>{component}</HeroUIProvider>
);
}
describe("CarCard", () => {
it("renders car information", () => {
const car = { make: "Toyota", model: "Camry", year: 2024 };
renderWithProvider(<CarCard car={car} />);
expect(screen.getByText("Toyota")).toBeInTheDocument();
expect(screen.getByText("Camry")).toBeInTheDocument();
});
});
Debugging Tips
- Missing Provider: Ensure HeroUIProvider wraps your app
- Styling Issues: Check Tailwind CSS configuration includes HeroUI plugin
- Type Errors: Import types from
@heroui/react - SSR Issues: Mark client components with
"use client"
References
- HeroUI Documentation: Use MCP tools for component info
- Related files:
apps/web/src/components/- React componentsapps/web/src/app/- Pages using HeroUIapps/web/tailwind.config.ts- Tailwind configurationapps/web/CLAUDE.md- Web app documentation
Best Practices
- Use MCP Tools: Query component info before implementing
- Consistent Variants: Use same variant across similar components
- Accessibility: Always add aria labels for icon-only buttons
- Responsive: Test components on mobile, tablet, desktop
- Provider: Ensure HeroUIProvider at root level
- TypeScript: Leverage TypeScript types for props
- Testing: Wrap tests in HeroUIProvider
- Performance: Use client components only when needed
- Size Utility: Use
size-*instead ofh-* w-*for equal dimensions (Tailwind v3.4+)
Size Utility Convention
When styling HeroUI components with equal height and width, use the size-* utility:
// ✅ Good - Use size-* for equal dimensions
<Button isIconOnly className="size-10">
<Icon className="size-4" />
</Button>
// ❌ Avoid - Redundant h-* and w-*
<Button isIconOnly className="h-10 w-10">
<Icon className="h-4 w-4" />
</Button>
Quick Install
/plugin add https://github.com/sgcarstrends/sgcarstrends/tree/main/heroui-componentsCopy and paste this command in Claude Code to install this skill
GitHub 仓库
Related Skills
langchain
MetaLangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.
Algorithmic Art Generation
MetaThis skill helps developers create algorithmic art using p5.js, focusing on generative art, computational aesthetics, and interactive visualizations. It automatically activates for topics like "generative art" or "p5.js visualization" and guides you through creating unique algorithms with features like seeded randomness, flow fields, and particle systems. Use it when you need to build reproducible, code-driven artistic patterns.
webapp-testing
TestingThis Claude Skill provides a Playwright-based toolkit for testing local web applications through Python scripts. It enables frontend verification, UI debugging, screenshot capture, and log viewing while managing server lifecycles. Use it for browser automation tasks but run scripts directly rather than reading their source code to avoid context pollution.
finishing-a-development-branch
TestingThis skill helps developers complete finished work by verifying tests pass and then presenting structured integration options. It guides the workflow for merging, creating PRs, or cleaning up branches after implementation is done. Use it when your code is ready and tested to systematically finalize the development process.
