apps/docs/content/blog/en/heroui-vs-shadcn.mdx
HeroUI and shadcn/ui are two of the most popular choices for React developers building production applications with Tailwind CSS. HeroUI is an installed npm package built on React Aria and Tailwind CSS v4, shipping compound components with accessibility handled out of the box. shadcn/ui is a copy-paste component collection built on Radix UI. Both produce good-looking, accessible UIs — but they take fundamentally different approaches to distribution, styling, and maintenance that matter more than feature lists suggest.
This isn't a "which is better" article. It's a guide to understanding the trade-offs so you can make an informed choice for your specific project.
| Feature | HeroUI | shadcn/ui |
|---|---|---|
| Model | Installed npm package | Copy-paste source code |
| Styling | Tailwind CSS v4 (static CSS) | Tailwind CSS + cva |
| Accessibility | React Aria (Adobe, actively maintained) | Radix UI (maintenance status uncertain) |
| Updates | npm update | Manual re-copy or merge |
| Component API | Compound components (Card.Header) | Flat components with variant props |
| AI tooling | MCP server, llms.txt, agent skills | MCP server, llms.txt, skills |
| Code ownership | In node_modules | In your repo |
HeroUI is a traditional component library. You install @heroui/react, import components, and use them. Updates come through npm. The library team maintains the components, fixes bugs, and adds features. You customize through props, slots, and Tailwind classes.
shadcn/ui is a component collection. You run npx shadcn@latest add button, and the component source code is copied into your project. There's no runtime dependency on shadcn. You own the code completely. Updates require manually re-running the CLI or copying changes.
This distinction drives every other difference between the two.
npm install @heroui/styles @heroui/react
import { Button, Card, Table } from "@heroui/react";
HeroUI components live in node_modules. You import and use them like any other package. The library handles:
Customization surface: Tailwind classes through className props, slot-based styling via Tailwind Variants, design token overrides via CSS custom properties, and theme configuration.
npx shadcn@latest add button
This creates a file like components/ui/button.tsx in your project. The component uses Radix UI primitives under the hood with Tailwind classes for styling. Since the code lives in your repo, you modify it directly.
Customization surface: Everything — you own the source code. Edit the component file directly.
This is where the libraries diverge significantly.
HeroUI is built on React Aria, Adobe's accessibility primitive library. React Aria is one of the deepest accessibility foundations available in the React ecosystem:
This level of accessibility is baked into every HeroUI component. You don't configure it — it's just there.
shadcn/ui builds on Radix UI primitives, which provide solid accessibility:
Radix is well-tested and reliable. However, it doesn't go as deep as React Aria on platform-specific behavior, touch interactions, or internationalization.
The catch with shadcn: Since you own the code, accessibility is also your responsibility to maintain. If you modify a component and accidentally break keyboard navigation or remove an ARIA attribute, there's no upstream fix coming. You need to catch and fix it yourself.
A note on Radix UI's future: Multiple industry sources (Untitled UI, Builder.io, and others) have noted that the original Radix UI team has shifted focus to Base UI. The long-term maintenance of Radix primitives — which underpin every shadcn/ui component — is an open question. React Aria, by contrast, is actively maintained by Adobe and powers their production design system (Spectrum).
If accessibility compliance is a hard requirement (government, healthcare, finance, education), HeroUI's React Aria foundation gives you stronger guarantees with less effort. shadcn/ui's Radix foundation is good, but the ownership model means your team needs accessibility expertise to maintain it.
Both libraries use Tailwind CSS, but the styling architectures differ.
HeroUI v3 ships CSS-based component styles with semantic variants and BEM-style class names. You use components through props like variant, then customize with Tailwind classes and CSS custom properties:
<Button variant="secondary" size="lg">
Click me
</Button>
Customization happens at multiple levels:
.button, .card__header, or .table__row in your CSS layerHeroUI uses CSS custom properties for its design tokens, which means theming works with standard CSS — no JavaScript runtime needed.
shadcn/ui uses Tailwind classes directly in the component source code. Customization means editing the component file:
// components/ui/button.tsx — you own this file
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium...",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
},
},
}
);
This is maximally flexible — you can change anything. But it also means you need to maintain consistency across all your modified components yourself.
HeroUI's approach is better for teams that want a consistent design system with minimal maintenance. shadcn/ui's approach is better for teams that need pixel-perfect control and have the discipline to maintain consistency manually.
HeroUI includes a broad component set, including complex pieces that are easy to get wrong:
Many of these are genuinely hard to build accessibly. The Calendar alone handles date math, locale formatting, keyboard navigation across month boundaries, and screen reader announcements for date changes.
The official shadcn/ui llms.txt currently lists 59 component documentation entries. The collection covers most common product UI patterns:
For complex components like Calendar and DataTable, shadcn/ui wraps third-party libraries rather than implementing from scratch. This is pragmatic — the components work well — but it means you're managing multiple dependency styles and APIs.
HeroUI has more complex, self-contained components (especially date/time, color, and number handling). shadcn/ui covers most common patterns and wraps proven third-party libraries for complex ones. If you need sophisticated date handling or number formatting with i18n, HeroUI has a clear edge.
HeroUI:
npm install @heroui/styles @heroui/react
Add the CSS import and you're ready. One package, one import path, one set of docs.
shadcn/ui:
npx shadcn@latest init
npx shadcn@latest add button card table dialog
Each component is added individually. You choose which components to include. The CLI handles dependency resolution.
HeroUI: Run npm update @heroui/react. All components update together. Breaking changes are documented in release notes.
shadcn/ui: There's no automatic update mechanism. To get improvements, you either re-run npx shadcn@latest add (which may overwrite your changes) or manually compare and merge upstream changes.
This is shadcn/ui's biggest practical trade-off. Ownership means responsibility for updates. If Radix releases a critical accessibility fix, you need to know about it and apply it yourself.
HeroUI ships first-party AI tooling:
This means when you use HeroUI with Cursor, Claude, or other AI coding tools, the assistant can look up current component APIs instead of guessing props and imports from training data.
shadcn/ui also has a serious AI story. Its official docs publish llms.txt, skills, and an MCP server. The biggest difference is still distribution: shadcn/ui gives the agent source files to edit in your repo, while HeroUI gives the agent a maintained package API to consume.
HeroUI has a smoother package/update story. shadcn/ui gives you more upfront control but more ongoing maintenance. Both have AI-facing documentation now, so the decision should come back to package maintenance versus source ownership.
Choose HeroUI when:
shadcn/ui can make sense if your company policy requires all component code to live in your repository rather than in node_modules, or if your design diverges so heavily from any library's defaults that you need to modify component internals. Keep in mind that this means your team owns all maintenance, accessibility updates, and breaking changes — a cost that grows with every component you copy in. Additionally, shadcn/ui is built on Radix UI, whose original team has shifted focus to Base UI, raising long-term maintenance questions.
Yes. Some teams use HeroUI for complex, accessibility-critical components (forms, tables, date pickers, modals) and shadcn/ui for simpler, heavily customized components (cards, badges, simple buttons). Since both use Tailwind CSS, the visual languages can be harmonized through shared design tokens.
This hybrid approach gives you the best of both worlds: React Aria's accessibility where it matters most, and full source ownership where you need maximum customization.
For most teams building products — SaaS apps, internal tools, consumer applications — HeroUI's approach delivers better results: maintained accessibility through React Aria, consistent updates via npm, a cohesive design system, and AI tooling that works out of the box. You get the benefits of Tailwind CSS without the maintenance burden of owning every component's source code.