# Shadcn/ui Alternatives: When to Use Them and Why

> A straightforward comparison of shadcn/ui and its alternatives, helping you choose the right tool for your project needs.
> By Dave · 2026-08-27
> Source: https://otf-kit.dev/blog/shadcn-ui-alternatives

Shadcn/ui's actual innovation is small and easy to miss: a CLI drops a styled component into your codebase instead of installing a package. You own the source. You fork it when your design changes. For a web-only React app on utility CSS, that pattern is still the cleanest default we have.

The question isn't "is shadcn good." It's "is shadcn right for *this* project." Three cases break it: the same component has to render on iOS and Android without a webview, theming has to reach past utility-CSS variables, or `npm update` has to actually move your components. Each case has a different answer.

## 1. The decision tree in 30 seconds

Answer these in order:

1. **Native required (iOS or Android)?** Yes → cross-platform SDK or native toolchain. No → continue.
2. **`npm update` required?** Yes → maintained npm library (Mantine, MUI, Chakra). No → continue.
3. **Utility CSS + React + web-only?** Yes → shadcn. Done.

Everything else is a specific-answer problem: per-tenant themes, brand-strict design systems, large-org governance, accessibility audits — each has a different right answer, and you should pick based on the constraint that's actually breaking you.

The rest of this post expands each branch with the trade-offs.

## 2. Web-only React: why shadcn is still the right call

If you're building a SaaS dashboard, a marketing site, an internal tool — anything that ships to a browser — use shadcn. The pattern (CLI drops a styled component into your repo, you own the source, you can rewrite any part of it) is genuinely the best default we have for React + utility CSS. The alternatives below solve specific problems shadcn doesn't, not a "better shadcn."

What it gives you:

- Accessibility primitives underneath (focus traps, ARIA, keyboard nav — done)
- CSS variables for theming, swappable per brand
- A `cn()` helper and a variant API that codegen tools and Cursor understand
- ~40 components covering the dashboard essentials (`Button`, `Dialog`, `Sheet`, `Form`, `Table`, `Toast`, `Tooltip`, …)

What it costs you: every component is hand-vendored. When shadcn pushes a fix, you get it by re-running the CLI or merging a PR. There's no `npm update @shadcn/ui`. And the styling layer is utility classes — not because that's bad, but because every styling assumption that isn't utility classes (CSS-in-JS, CSS modules, Panda, vanilla CSS) means rewriting.

So: shadcn = right when you're web-only, utility-CSS-comfortable, and want a one-time copy-into-the-repo model.

## 3. Native required: where shadcn stops at the browser

The hard limitation: shadcn is React + DOM. A `<Dialog>` for iOS isn't a `<div role="dialog">` — it's a `UIViewController` modal with a swipe-down-to-dismiss gesture, a safe-area inset, and a `UIPresentationController` for the chrome. The web component won't render in a native shell without a webview, and a webview is exactly what most teams say they don't want.

The real options here:

| Option                                | How it gets to native                                      | Trade-off                                                                                    |
| ------------------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| shadcn + React Native Web             | Same React code, RN renders native primitives              | RNW hits visual parity limits; accessibility tree differs from DOM; two style systems        |
| Cross-platform JS primitives (NativeBase, etc.) | Native UI from JS                                  | Different API surface from shadcn; rewriting                                                 |
| Flutter / SwiftUI / Compose            | Truly native, separate codebases                           | Different language and toolchain per platform                                                |
| OTF (`@otfdashkit/ui` + `@otfdashkit/ui-native`) | Same component name + props, web and native share an API | Smaller ecosystem than shadcn; paid kits on top of the free SDK                              |

If "one component, web + iOS + Android, one API" is the actual requirement — not a wish — that's a small list. OTF's free MIT SDK is on it.



![a token source — design-tokens.json — fanning out to web CSS variables, iOS UIColor extens](https://cdn.otf-kit.dev/blog/shadcn-ui-alternatives/inline-1.png)



## 4. Themes that need to reach past the browser

Shadcn themes are CSS variables plus utility-class mappings. That covers 95% of brand work. Where it starts to break:

- Per-tenant theming at runtime (white-label SaaS where each customer gets their own palette from a DB column)
- Themes that aren't "light/dark" — a "high contrast accessibility" mode, a deuteranopia mode, a "brand event" mode
- Tokens that need to drive native (iOS/Android) at the same time as web — utility-CSS doesn't compile to a `UIColor` or a Compose `Color`

For per-tenant at runtime on web only: shadcn works fine, you just need a server-driven CSS variable override. For cross-platform theming where the iOS button color must match the web button color from the same JSON: shadcn doesn't have an answer, and you need a token system that reaches all three platforms from one source.

## 5. When you want `npm update` to actually update your components

This is the underrated one. Vendored code is great until:

- A security CVE lands in an accessibility primitive and you have to find every `<Dialog>` you copied six months ago
- shadcn ships a fix and you don't see it because no `node_modules` diff alerts you
- Your team grows from 3 to 30 and now you have 30 inconsistent copies of `Button.tsx`

If you want the package to actually move under you — like every other `node_modules` dependency — you don't want shadcn. You want a library you install.

Trade-offs of an npm-installed library:

- You give up the "fork anything" freedom. Bug fixes are upstream PRs.
- Bundle size is shared; code-splitting is the library's job.
- You get security patches, version upgrades, and one source of truth.

The honest list of maintained React component libraries with this model: Mantine, Chakra, MUI, Ant Design, Park UI, Radix Themes, Ark UI.

## 6. The alternatives, compared

| Need                                                | Pick                       | Why                                                |
| --------------------------------------------------- | -------------------------- | -------------------------------------------------- |
| Web-only React + utility CSS, one-time setup        | shadcn/ui                  | Best defaults, owned source                        |
| Web-only, npm-updateable, batteries included        | Mantine                    | ~120 components, MIT, hooks included              |
| Web-only, strict design system, large org           | MUI / Chakra               | Opinionated, accessible, mature                    |
| React + native (iOS/Android), one API               | OTF SDK                    | Free MIT, same `<Button>` everywhere               |
| Brand-strict cross-platform from one token source    | OTF SDK + tokens           | One JSON → web + iOS + Android                     |
| Just native (iOS or Android only)                   | SwiftUI / Compose / Flutter | Native toolchain is the right answer              |



![shadcn copy-paste vs npm-installed library vs cross-platform SDK](https://cdn.otf-kit.dev/blog/shadcn-ui-alternatives/inline-2.png)



## 7. What OTF actually gives you if you go that way

State the outcome, not the parts.

The free MIT SDK ships three packages: `@otfdashkit/ui` for web, `@otfdashkit/ui-native` for iOS and Android, and `@otfdashkit/tokens` for the shared design tokens. ~200 components. The same `<Button>`, the same `<Dialog>`, the same `<Card>` — same name, same props, same look — render on web, iOS, and Android from one codebase. Distribution is your choice: copy-paste CLI or `npm install`.

```bash
npm install @otfdashkit/ui @otfdashkit/ui-native @otfdashkit/tokens
```

Three things that aren't obvious from the landing page:

1. **One token source, three platforms.** Design tokens flip one theme across web and mobile. The iOS button and the web button are the same color because they read the same `color.primary.500` token from one JSON. Per-tenant theming becomes a token swap, not a rebuild.

2. **The kits ship with agent configs.** Every full-stack kit (SaaS Dashboard, Fitness, Booking — $99 each, Everything Bundle $149) comes with `CLAUDE.md`, `.cursorrules`, and 20+ tested prompts under `ai/prompts/`. A coding agent extends the kit instead of regenerating it. Plus 15 single-page landing templates at $9 each when you just need a marketing page.

3. **One script ships to production.** A single deploy script wires custom domain, DNS, TLS, and the mobile build pipeline. A 24-item design checklist runs before any kit ships, so the code you copy meets a documented bar — not "works on my machine."

Demos: `saas.otf-kit.dev` (web) and `fitness-preview.otf-kit.dev` (mobile).

---

Pick the library that matches the constraint you can't negotiate. For most web-only React projects, that's shadcn. For native-required work, that's a smaller list — OTF's free SDK is on it. The honest answer is that "the best component library" is the wrong question. The right question is which constraint you can't bend, and which library is built around that constraint.