Skip to main content
ASoc
Comparison

shadcn/ui vs. Mantine: Copy-In Primitives vs. a Batteries-Included Library

120+ components vs. 92 hand-rolled ones, and the two real ARIA defects this codebase shipped with that a component library would have caught on day one.

The ASoc Team8 min read

Mantine is a batteries-included React component library — 120+ components, its own CSS-Modules-based styling engine, no Tailwind — installed as a dependency and themed through a provider. shadcn/ui is the opposite shape: 50-ish components copied into your repo via a CLI, styled with Tailwind utilities you already own. The real trade isn't "which is better," it's install-and-configure a finished system versus hand-own a smaller one and build the gaps yourself.

This storefront chose the second path all the way down: 92 components, zero component-library dependency, and a measured list of exactly what "build the gaps yourself" cost.

The two things being compared

shadcn/uiMantine
DistributionCLI copies .tsx source into your repoAn npm package (@mantine/core + friends)
Component count~50 primitives, source you edit120+ components, hooks, and a full theming system out of the box
StylingTailwind utility classesIts own CSS Modules engine — no Tailwind
OwnershipYou own every copied file from day oneYou own a config object; the component internals are the library's
UpgradesNo package to bump — re-copy to pick up fixesnpm update, same as any dependency
Dark modeWhatever your Tailwind @theme definesBuilt-in MantineProvider colour-scheme switch
Best fitA design system that doesn't look like the defaults, or a Tailwind codebase alreadyA data-heavy SaaS app (dashboard, forms, tables) wanting full coverage fast

Mantine's own selling point against shadcn is coverage: a project wiring up a dashboard with dates, tables, notifications and forms can reach for one package instead of assembling primitives. shadcn's counter-selling point is the same one Tailwind's is: nothing you didn't ask for ships, because there's no library — there's only the components you copied.

What zero-dependency actually costs, measured

Every component here is hand-written, not copied from either library. The full census:

LayerComponentsClient Components
Atoms70
Molecules4119
Organisms335
Templates110
Total9224

Against Mantine's 120+, this is fewer components — but the count is misleading on its own, because Mantine's 120+ includes primitives this site never needed (date pickers, rich-text editors, notification systems) and excludes the marketing-specific composites (a pricing tier card, a template gallery carousel, a related-products rail) it also never shipped. The comparison that matters isn't the count, it's what each approach makes you build by hand versus hand you finished.

What this repo built by hand, and had to get right without a library's help: a focus trap, a modal, a dropdown, tabs, an accordion, a tooltip, and an accessible mega menu. Mantine ships accessible versions of all seven already wired up.

The two real defects that cost of "build it yourself"

Two of those seven shipped broken before an audit caught them — both are the honest price of not having a library.

The pricing FAQ accordion had two of five WAI-ARIA accordion requirements. FaqItem (src/components/molecules/FaqItem.tsx) is the actual component — real code, not written for this article:

// src/components/molecules/FaqItem.tsx
const [open, setOpen] = useState(false);
const id = useId();
const buttonId = `${id}-button`;
const panelId = `${id}-panel`;
// ...
<button
  type="button"
  id={buttonId}
  onClick={() => setOpen((o) => !o)}
  aria-expanded={open}
  aria-controls={panelId}
>
  {question}
</button>
<div
  id={panelId}
  role={open ? "region" : undefined}
  aria-labelledby={open ? buttonId : undefined}
>

That's the fixed version. Before the audit behind React accordion, it was missing the aria-controls/aria-labelledby pair entirely — a screen reader had no way to announce which panel belonged to which question. A Mantine <Accordion> ships that wiring already correct; this codebase had to find the gap, then close it.

The dashboard's three-way tab switch had three of six. DashboardTabs (src/components/organisms/DashboardTabs.tsx) was missing the roving-tabindex behaviour the WAI-ARIA tabs pattern requires — the tablist wasn't one keyboard stop, so arrowing between tabs didn't work the way a screen-reader user would expect. It rendered correctly, was keyboard-reachable in the loosest sense, and no automated tool in this repo's own pipeline had ever flagged it — full audit in React tabs. Fixed, but the two together are the real cost line: a component library like Mantine buys you out of finding these at all.

Where each one actually wins

If you…Choose
Need 100+ components fast for a data-heavy SaaS appMantine — the coverage is the whole point
Have a design system that doesn't look like either library's defaultsshadcn/ui or hand-rolled — you're rewriting the visuals either way
Are already committed to Tailwindshadcn/ui — Mantine brings a second, competing styling system into the same app
Want zero JS shipped for anything you don't explicitly rendershadcn/ui — components are tree-shaken like your own code; Mantine ships as a package
Have one developer and a deadline, and the components are mostly forms/tablesMantine
Are shipping a template other people will customizeshadcn/ui or hand-rolled — a buyer inherits every dependency you chose for them

That last row is this repo's actual reason: every one of its 111 products is source another developer takes over, so a component-library dependency becomes their dependency too. gumroad-alternative covers the same "own it vs. depend on it" tradeoff on the commerce side of this same storefront.

The maintenance side nobody counts upfront

Mantine is a versioned dependency, which means it goes through the same lifecycle every dependency does: security patches, breaking changes at major versions, and a package.json line someone has to review before bumping. This repo's entire runtime dependencies block is 13 entries — no component library, no CSS-in-JS engine, no headless-primitives package — specifically so that a buyer who takes over one of its 111 templates inherits the smallest possible dependency surface. A Mantine-based template hands a buyer 120+ components' worth of transitive dependencies to keep current; a shadcn-based or hand-rolled one hands them source files with no upstream to track at all.

That distinction matters more here than in a typical app, because every product this storefront sells is exactly that: code someone else takes over, forks, and maintains going forward. The dependency choice made once, at build time, becomes every future buyer's maintenance burden — which is also why gumroad-alternative frames "own it vs. depend on it" as a commerce-architecture decision, not just a styling one.

Mistakes and troubleshooting

SymptomCauseFix
Mantine's MantineProvider styles don't applyIts CSS wasn't imported (@mantine/core/styles.css) or the provider doesn't wrap the treeImport the stylesheet once at the root and confirm <MantineProvider> is an ancestor
Tailwind and Mantine styles fight each otherBoth are global CSS systems targeting the same elementsScope one to specific routes, or don't run both — pick one styling system per app
A hand-rolled accordion/tabs component "looks done" but fails a screen-reader testVisual completeness and ARIA completeness are unrelated; nothing catches the second without a manual auditRun an audit against the WAI-ARIA pattern for that specific widget, as this repo did for both examples above
shadcn component's Tailwind classes don't compileThe file lives outside Tailwind's configured content pathsAdd the copied file's path to the scanned sources
Bundle grew after installing a component libraryMantine ships all its CSS and JS as a dependency regardless of how much you renderAudit with a bundle analyzer; import only the sub-packages you use
Dark mode looks different between two componentsOne uses Mantine's colour-scheme system, the other uses Tailwind dark: classesDon't mix; pick one dark-mode mechanism app-wide

Frequently asked questions

Does shadcn/ui have as many components as Mantine? No — shadcn ships around 50 primitives; Mantine ships 120+ including hooks, forms, dates, and notifications. shadcn assumes you'll write the composites yourself with Tailwind.

Can I use Mantine and Tailwind together? Technically yes, but they're two separate styling systems competing for the same elements. Most codebases pick one rather than maintain both.

Which one is more accessible out of the box? Mantine, because its components ship with the ARIA wiring and keyboard handling already implemented and tested. shadcn's components (via Radix) are also accessible, but anything you hand-roll instead of copying carries no such guarantee — this post's two defects are the proof.

Is shadcn/ui a dependency the way Mantine is? No. The CLI copies source files into your project once; there's no package to update afterward. Mantine stays a versioned dependency for the life of the project.

Templates in this post

ASoc Catalyst is an AI-automation agency marketing site with an automation-metrics hero, a four-service grid, and a three-tier monthly/yearly pricing table. ASoc Chain is a DeFi-protocol landing template built around a trustless-by-design pitch and an on-chain 3D hero visual. ASoc Cognition markets an AI/neuro consulting studio with a revenue-led hero, a services block, and an AI-capabilities section.

Browse the full sets: Next.js landing page templates, Tailwind landing page templates.

Keep reading

Comparison8 min read

shadcn vs. Tailwind Is a Category Error (One Runs on the Other)

92 components, 13 runtime dependencies, zero UI libraries — what hand-rolling actually cost this codebase, and the seven components shadcn would have handed over.

Read more
Comparison11 min read

Shopify vs a Next.js Storefront: What You Actually Inherit

Shopify's fee buys a checkout, tax handling and an ops backend — not hosting. The four jobs you take on by leaving, the headless hybrid, and when owning the frontend pays.

Read more
Comparison9 min read

SolidJS vs. Next.js: You're Comparing the Wrong Two Things

SolidStart is the real comparison. 24 of 92 component files here ship client JS — the number that decides fine-grained reactivity against RSC.

Read more