UI Component Libraries
|
This section documents the current Vue 3.x release line as published at
the official Vue.js documentation, which is the reference these pages are written and
verified against. No specific patch version is pinned. The Composition API with This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, since Vue and its ecosystem iterate quickly. This section’s bibliography lists the reference material consulted while preparing these pages. |
This page surveys how to style a Vue app and which ready-made component libraries to reach for. It covers the most popular options only, free and open-source first, in rough order of adoption; commercial suites are listed last, without examples.
How to choose
-
Licence first. Prefer permissively licensed (MIT) libraries. Several otherwise-free libraries sell a paid tier — Nuxt UI Pro, PrimeVue’s UI templates, FormKit Pro, and AG Grid Enterprise — and Kendo UI for Vue is fully commercial. Check what sits behind the paywall before you depend on it.
-
Vue 3 and
<script setup>. Confirm first-class Vue 3 support, TypeScript types shipped in the package, and Composition API examples in the library’s own docs. -
Bundle size. Favour tree-shakeable libraries with on-demand auto-import (
unplugin-vue-componentsresolvers) over a globalapp.use()plugin that pulls in every component and all of its CSS. -
Styling model. Decide between an own-CSS design system (Vuetify, Element Plus, Naive UI), a Tailwind-based kit (Nuxt UI, shadcn-vue), or headless / unstyled primitives you style yourself (Reka UI, Headless UI).
-
Accessibility. Check for WAI-ARIA roles, keyboard navigation, and focus management — cross-link Security and Accessibility.
-
SSR / Nuxt. If you render on the server, confirm the library documents an SSR or Nuxt setup — cross-link Server-Side Rendering.
-
Maintenance and community. Check release cadence, open-issue trends, and ecosystem size before committing.
-
Theming, dark mode, i18n. Verify design tokens or a theme API, a dark-mode switch, and locale support if you need them.
Styling options
Scoped styles, CSS Modules, and v-bind() in CSS
An SFC <style scoped> block is rewritten so its rules only match that component’s own markup; <style
module> exposes hashed class names as a $style object; and v-bind() inside <style> pipes a reactive
value into CSS as a custom property. See Single-File Components for the full set. To reach
into a child (library) component’s internals from a scoped block, use the :deep() pseudo-class — see
SFC CSS features.
<script setup lang="ts">
import { ref } from 'vue'
const accent = ref('#1976d2')
</script>
<template>
<button class="btn"><span class="icon" />Save</button>
</template>
<style scoped>
.btn { background: v-bind(accent); }
.btn :deep(.icon) { margin-inline-end: 0.5rem; }
</style>
Tailwind CSS
Utility classes in <template> keep styling in the markup, and the build removes unused classes. Bind them
conditionally with an array or object on :class. See Tailwind Reference.
<script setup lang="ts">
const busy = false
</script>
<template>
<button :class="['rounded px-4 py-2 text-white', busy ? 'bg-gray-400' : 'bg-blue-600 hover:bg-blue-700']">
Save
</button>
</template>
Sass
Add lang="scss" to an SFC <style> block, or configure Sass once for the whole project in vite.config.ts;
component and global stylesheets then accept Sass syntax. See Sass Reference.
npm i -D sass-embedded
<style scoped lang="scss">
$brand: #1976d2;
.btn {
background: $brand;
&:hover { filter: brightness(1.1); }
}
</style>
Installing and registering a library
There are two ways to add a component library: register it once globally as a
plugin with app.use(), or auto-import only the components
each file actually uses. Element Plus is shown below; the same shape applies to Vuetify, Quasar, Ant Design
Vue, and the rest.
npm i element-plus
Global registration in main.ts — every component and the full stylesheet are always in the bundle:
// src/main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
createApp(App).use(ElementPlus).mount('#app')
On-demand auto-import with unplugin-vue-components and its
ElementPlusResolver — only the components you use (and their styles) are bundled, with no manual CSS import
and no per-file import:
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
Components({ resolvers: [ElementPlusResolver()] }),
],
})
See Tooling and Project Setup for the Vite config these plugins extend.
Component libraries — free and open-source
Each entry below is MIT-licensed unless noted, ships its own TypeScript types, works with Vue 3 and <script
setup>, and is listed roughly in order of adoption. Every entry has an install command and a short usage
snippet; the mobile, data-grid, and commercial options are grouped afterwards.
Vuetify 3 (MIT)
Vuetify is a comprehensive Material Design component set — the most widely used styled Vue library. It documents an SSR / Nuxt setup and provides a theme system with design tokens and dark mode.
npm i vuetify
// src/main.ts
import { createApp } from 'vue'
import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'
import 'vuetify/styles'
import App from './App.vue'
const vuetify = createVuetify({ components, directives })
createApp(App).use(vuetify).mount('#app')
<script setup lang="ts">
import { ref } from 'vue'
const dialog = ref(false)
</script>
<template>
<v-btn color="primary" @click="dialog = true">Save</v-btn>
<v-dialog v-model="dialog">
<v-card title="Confirm?" text="Save these changes?" />
</v-dialog>
</template>
PrimeVue (MIT core, paid templates)
PrimeVue is a large suite of ~90 components with design-token theming configured through the plugin options. Every component is MIT; only the optional UI templates (ready-made admin layouts) are paid. SSR and Nuxt are documented.
npm i primevue @primeuix/themes
// src/main.ts
import PrimeVue from 'primevue/config'
import Aura from '@primeuix/themes/aura'
app.use(PrimeVue, { theme: { preset: Aura } })
<script setup lang="ts">
import Button from 'primevue/button'
const save = (): void => {}
</script>
<template>
<Button label="Save" icon="pi pi-check" @click="save" />
</template>
Element Plus (MIT)
Element Plus is a desktop-oriented design system with a full component set and an SSR guide. Its install and auto-import setup is the worked example in Installing and registering a library above; a component looks like this:
npm i element-plus
<script setup lang="ts">
import { ref } from 'vue'
import { ElButton, ElDialog } from 'element-plus'
const visible = ref(false)
</script>
<template>
<ElButton type="primary" @click="visible = true">Save</ElButton>
<ElDialog v-model="visible" title="Confirm?" />
</template>
Naive UI (MIT)
Naive UI is a TypeScript-first suite with CSS-in-JS theming — there is no stylesheet to import, and the theme is a plain object. Components are individually importable and tree-shakeable.
npm i naive-ui
<script setup lang="ts">
import { NButton, NConfigProvider } from 'naive-ui'
const save = (): void => {}
</script>
<template>
<NConfigProvider>
<NButton type="primary" @click="save">Save</NButton>
</NConfigProvider>
</template>
Ant Design Vue (MIT)
Ant Design Vue is an enterprise-oriented Ant Design port with a rich a-table and
a-form. SSR is documented.
npm i ant-design-vue
// src/main.ts
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/reset.css'
app.use(Antd)
<template>
<a-button type="primary" @click="save">Save</a-button>
</template>
Quasar (MIT)
Quasar is a component set plus a build system: from one codebase it targets an SPA, SSR, a PWA, a desktop app (Electron), and mobile apps (Capacitor / Cordova). It is fully MIT with no paid tier.
npm i quasar @quasar/extras
npm i -D @quasar/vite-plugin sass-embedded
// src/main.ts
import { Quasar } from 'quasar'
import 'quasar/src/css/index.sass'
app.use(Quasar, { plugins: {} })
<template>
<q-btn color="primary" label="Save" @click="save" />
</template>
Nuxt UI v3 (MIT core, Nuxt UI Pro paid)
Nuxt UI v3 is built on Reka UI and Tailwind CSS v4. Despite the name it works in a plain Vue project (Vite, Inertia, SSR), not only in Nuxt. The core is MIT; Nuxt UI Pro (dashboards, page layouts, richer components) is a paid licence.
npm i @nuxt/ui
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [vue(), ui()],
})
<script setup lang="ts">
import { ref } from 'vue'
const open = ref(false)
</script>
<template>
<UButton @click="open = true">Save</UButton>
<UModal v-model:open="open" title="Confirm?" />
</template>
shadcn-vue (MIT)
shadcn-vue is not an npm dependency: a CLI copies component source (built on Reka UI and Tailwind) into your repo, so you own and edit it. It is the Vue analogue of shadcn/ui.
npx shadcn-vue@latest init
npx shadcn-vue@latest add button dialog
<script setup lang="ts">
import { Button } from '@/components/ui/button'
const save = (): void => {}
</script>
<template>
<Button @click="save">Save</Button>
</template>
Reka UI (MIT)
Reka UI is a set of unstyled, accessible behaviour primitives (dialog, menu, popover,
combobox, tabs) that ship the interaction and ARIA wiring and leave all styling to you. It is formerly Radix
Vue, renamed in 2024 — see the migration guide if you are moving
from radix-vue. It is what Nuxt UI and shadcn-vue are built on.
npm i reka-ui
<script setup lang="ts">
import {
DialogRoot, DialogTrigger, DialogPortal, DialogOverlay,
DialogContent, DialogTitle, DialogClose,
} from 'reka-ui'
</script>
<template>
<DialogRoot>
<DialogTrigger>Open</DialogTrigger>
<DialogPortal>
<DialogOverlay class="overlay" />
<DialogContent class="modal">
<DialogTitle>Confirm</DialogTitle>
<DialogClose>Cancel</DialogClose>
</DialogContent>
</DialogPortal>
</DialogRoot>
</template>
Headless UI (Vue) (MIT)
Headless UI is a small set of completely unstyled, fully accessible components (menu, listbox, combobox, dialog, disclosure, tabs) from the Tailwind Labs team, designed to pair with Tailwind utility classes.
npm i @headlessui/vue
<script setup lang="ts">
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
</script>
<template>
<Menu>
<MenuButton>Options</MenuButton>
<MenuItems>
<MenuItem v-slot="{ active }">
<button :class="{ 'bg-blue-500 text-white': active }">Edit</button>
</MenuItem>
</MenuItems>
</Menu>
</template>
A third headless option worth knowing is Ark UI — a framework-agnostic (React, Vue, Solid) library built on the Zag.js state machines, exposing each component as a set of composable parts.
Mobile-first components
For touch-oriented and hybrid-app UIs, three libraries dominate:
-
Vant (MIT) — the most-downloaded mobile Vue set, with a documented SSR setup. Install
vant, importvant/lib/index.css, and use components such as<van-button>and<van-cell>. -
Varlet (MIT) — a mobile Material Design 3 kit with a CLI scaffold and both global and on-demand import (
app.use(Varlet)). -
Ionic Vue (MIT) — components that adapt to iOS and Material styling; paired with Capacitor it builds native iOS and Android apps from the Vue codebase. Quasar’s Capacitor / Cordova mode covers the same ground.
<script setup lang="ts">
import { Button as VanButton } from 'vant'
import 'vant/lib/index.css'
const save = (): void => {}
</script>
<template>
<VanButton type="primary" @click="save">Save</VanButton>
</template>
Data grids and forms
The general suites above all ship a table and form, but two specialised needs have their own popular libraries:
-
Data grids. AG Grid is the standard for large, feature-heavy grids — the
ag-grid-communitypackage is MIT; row grouping, pivoting, the server-side row model, and integrated charts are AG Grid Enterprise (paid). TanStack Table is the headless alternative: it computes rows, sorting, filtering, and pagination and hands you the state while you render every element, and pairs with TanStack Virtual for windowing. -
Forms. FormKit is a schema-driven form framework with validation, submission handling, and accessibility built in; the core is MIT and FormKit Pro adds advanced inputs (autocomplete, repeater, datepicker) under a paid licence. For plain
v-modelbinding and VeeValidate, see Form Input Bindings.
Adjacent tools
-
VueUse (MIT) — a large collection of Composition API utilities (
useLocalStorage,useDark,useMediaQuery,useElementSize, and hundreds more). Not a component library, but almost always paired with one. -
unplugin-vue-components (MIT) — on-demand component auto-import with per-library resolvers, used in the Element Plus example above.
-
daisyUI (MIT) — a Tailwind CSS plugin that adds component classes (
btn,card,modal) with no JavaScript, so it adds nothing to the JS bundle; see Tailwind Reference.
A "which one?" decision aid
| Library / group | Style model | Best for | Licence |
|---|---|---|---|
Vuetify 3 |
Own design system (Material) |
A full styled component set with theming and SSR out of the box |
MIT |
PrimeVue |
Own design system (token themes) |
~90 components; enterprise apps; free unless you buy templates |
MIT core |
Element Plus |
Own design system (desktop) |
Data-dense desktop admin UIs; auto-import friendly |
MIT |
Naive UI |
Own design system (CSS-in-JS) |
TypeScript-first projects; no stylesheet to manage |
MIT |
Ant Design Vue |
Own design system (Ant Design) |
Enterprise forms and tables in the Ant Design language |
MIT |
Nuxt UI |
Tailwind + Reka UI |
Tailwind projects wanting accessible components; Nuxt and plain Vue |
MIT core |
shadcn-vue |
Tailwind + Reka UI (copy-in) |
Owning and editing the component source in your repo |
MIT |
Reka UI / Headless UI |
Headless (unstyled) |
Full markup and style control with accessible behaviour handled |
MIT |
Quasar |
Own design system + build modes |
One codebase for web, PWA, desktop, and mobile |
MIT |
Vant / Ionic Vue |
Own design system (mobile) |
Mobile-first web UIs and hybrid iOS / Android apps |
MIT |
AG Grid |
Own grid theme |
Large, feature-heavy data grids (Enterprise features are paid) |
MIT core |
In short: a full styled design system → Vuetify / PrimeVue / Element Plus / Naive UI / Ant Design Vue; Tailwind-first → Nuxt UI / shadcn-vue; full markup control with accessible behaviour only → Reka UI / Headless UI; mobile or hybrid → Vant / Ionic Vue / Quasar; one codebase for web, desktop, and mobile → Quasar; heavy data grids → AG Grid (Community) or TanStack Table.
Notes and caveats
-
SSR. Not every library hydrates cleanly on the server — cross-link Server-Side Rendering. Nuxt UI, Vuetify, PrimeVue, Element Plus, Ant Design Vue, and Quasar each document an SSR or Nuxt setup; confirm it before committing to server rendering.
-
Scoped styles vs. library CSS. A
<style scoped>block will not reach a library component’s inner markup; use the:deep()pseudo-class, or the library’s own theming API, rather than fighting specificity — cross-link Single-File Components. -
Types and editor support. Prefer libraries that ship their own TypeScript declarations and support Volar (the "Vue - Official" extension) template IntelliSense, so component props are checked in
<template>— cross-link Tooling and Project Setup.
Commercial suites — paid / licensed
These are licensed products; they are listed last and without examples.
-
Kendo UI for Vue — a broad commercial component and data-grid suite from Progress Telerik, with a large grid, scheduler, charts, and editors.
Beyond fully commercial suites, remember that Nuxt UI Pro, PrimeVue’s UI templates, FormKit Pro, and AG Grid Enterprise are the paid tiers of otherwise-free libraries covered above — budget for them if you rely on those features.
Accessibility
A component library gives you accessible primitives, but you still own the wiring: label every control, manage
focus in dialogs and menus, honour prefers-reduced-motion, and test with a keyboard and a screen reader.
Reka UI and Headless UI — and the accessible-by-default suites such as Vuetify and Nuxt UI — handle roving
focus, focus trapping, and ARIA roles for you, but the labels, error text, and reduced-motion choices are
still yours to add. Test with vue-axe during development and Lighthouse in CI.
Cross-link Security and Accessibility for Vue-specific guidance and Web Accessibility for conformance levels and validation tools.