frontend / vue / 22_component_libraries.md

Vue component libraries

5 interview angles 4 min read source

Vue component libraries

Verified 2026-08. The question is rarely “which is best” — it is “how did you choose, and what did it cost you”.

The landscape

Library Shape Pick it when
Vuetify full Material Design system, 80+ components you want batteries included and Material is acceptable
PrimeVue very large set, strong data table, themable + unstyled mode enterprise apps with heavy tables, filters, exports
Nuxt UI Tailwind-based, built by the Nuxt team the project is Nuxt — the integration is free
Reka UI (was Radix Vue) headless, accessible primitives, no styles the design is bespoke and you want to own every pixel
shadcn-vue copy-in components built on Reka UI + Tailwind you want a styled starting point you then own outright
Naive UI complete, TypeScript-first, SSR-friendly, theming via JS you want a full set without Material styling
Element Plus complete, widely used, especially in admin dashboards CRUD-heavy internal tools
Quasar framework, not just components — web, SSR, PWA, mobile, desktop one codebase must target several platforms

Radix Vue was renamed Reka UI in 2025. Referring to it by the old name dates you.

Styled versus headless

This is the actual decision, and the one worth articulating.

Styled (Vuetify, PrimeVue themed, Element Plus, Naive UI) gives you a working product in days. The cost arrives when the design diverges: you fight specificity, override internals that are not part of the public API, and every major version can break those overrides. Design systems drift toward the library’s opinions rather than yours.

Headless (Reka UI, and PrimeVue’s unstyled mode) gives you behaviour and accessibility — focus management, keyboard navigation, ARIA wiring, portalling, dismiss handling — with no visual opinion. You write all the CSS. It is more work up front and far less work when the design is genuinely custom, which is most product companies.

Copy-in (shadcn-vue) is the third position: the component source lands in your repo, so you edit it directly rather than overriding it. No upgrade path, which is the point — you own it.

The honest framing: for an internal tool, take the styled library and ship. For a customer-facing product with a design system, headless plus your own tokens ages much better.

What to evaluate

  • Accessibility. Do the dialog, combobox and menu trap focus, handle Escape, and wire ARIA correctly? This is the hardest part to retrofit and the main reason not to hand-roll.
  • Bundle cost. Tree-shakeable? Does the icon set import individually? A full Material set pulled in wholesale is a real first-load regression.
  • SSR. Does it hydrate cleanly under Nuxt, or does it touch window at module scope? Hydration mismatches from a component library are miserable to debug.
  • TypeScript. Are props, slots and emits typed, or is everything any?
  • Upgrade history. Look at the last two majors. A library that rewrote its theming twice will do it again.
  • Data table specifically. If you need virtualisation, column resizing, grouping and server-side pagination, that one component often decides the whole choice. TanStack Table is the headless alternative if the rest of the library does not fit.

Integration notes

Register globally only what is used almost everywhere; import the rest per component so tree-shaking works. Most of these libraries ship an unplugin-based auto-import resolver, which gives you both.

Theme by configuring the library’s tokens, not by overriding its CSS from the outside. An !important in your stylesheet against a library selector is a bug that will resurface on upgrade.

Wrap third-party components in your own thin components at the boundary (AppButton wrapping the library button). Swapping libraries then touches those wrappers rather than 400 call sites — the same argument as an anti-corruption layer in the backend. See ../../system_design/01_api_integrations/01_integration_design.md.

Interview angle

  • “How did you pick a component library?” - name the constraint that decided it: Material was acceptable, or the design was bespoke, or the data table requirements, or it was a Nuxt project. “It’s popular” is not a reason.
  • “Styled or headless?” - styled for speed on internal tools; headless when a design system exists, because behaviour and accessibility are the hard parts and styling is the part you actually want to own.
  • “What is the risk of a component library?” - lock-in through overrides. Every fix that reaches into a library’s internals is a hostage to its next major version. Wrapping components at your own boundary limits the blast radius.
  • “Why not build your own components?” - accessibility. A correct combobox or dialog with focus trapping, keyboard support and ARIA is a genuinely hard piece of work, and getting it wrong is invisible until an audit.
  • “What is Reka UI?” - the renamed Radix Vue: unstyled, accessible primitives. It is what shadcn-vue is built on.