Vue.js: UI Component Libraries
Last updated: 2026-10-20
A UI component library lets you avoid reinventing the wheel — common components such as Button / Table / Form / Modal take just a few lines of code. The Vue 3 ecosystem has 5+ mainstream UI libraries (Element Plus / Naive UI / Vuetify / PrimeVue / Ant Design Vue); picking the right one can save 50% of your development time.
Choosing a UI library comes down to 5 dimensions: component coverage / TypeScript support / theme customization / bundle size / community activity. This lesson helps you make the right call.
1. What You'll Learn
- The 5 major Vue 3 UI libraries compared (Element Plus / Naive UI / Vuetify / PrimeVue / Ant Design Vue)
- On-demand imports (unplugin-vue-components)
- Theme customization (CSS variables / SCSS variables)
- Wrapping components for your business needs
- Icon library integration (@iconify / unplugin-icons)
- 5 common mistakes
2. The Nightmare of Writing the Same "Table" Component 5 Times
(1) Pain point: every table written from scratch
Alice's admin has 5 different table views, each written from scratch:
5 tables × 200 lines = 1,000 lines of duplicated code. Every style change means editing 5 places.
(2) The UI library solution: one el-table, reused 5 times
<!-- ✅ The correct version: use Element Plus el-table -->
<template>
<el-table :data="orders" stripe>
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="user" label="User" />
<el-table-column prop="total" label="Total" :formatter="formatTotal" />
</el-table>
</template>
<script setup>
import { ElTable, ElTableColumn } from 'element-plus'
const orders = ref([...])
function formatTotal(row) { return `$${row.total}` }
</script>
One el-table, reused in 5 places. Every table now shares the same styling and the same interactions.
(3) The payoff
After adopting Element Plus:
- Lines of code: 1,000 → 200 (-80%)
- Style consistency: 100% unified
- Adding a new table: a 1-line template
- TypeScript: full type inference
- Accessibility: a11y built in
3. Comparing the 5 Major Vue 3 UI Libraries
(1) Quick reference for the 5 libraries
| Library | Style | Components | Bundle size | TypeScript | Best for |
|---|---|---|---|---|---|
| Element Plus | Desktop | 80+ | ~250KB | ✅ | Most popular in China, admin backends |
| Naive UI | Desktop | 80+ | ~150KB | ✅ | Modern style, Vue 3 first |
| Vuetify | Material | 80+ | ~300KB | ✅ | Material Design apps |
| PrimeVue | Multi-style | 90+ | ~200KB | ✅ | Many themes, complete components |
| Ant Design Vue | Ant Design | 70+ | ~180KB | ✅ | Ant style, enterprise-grade |
(2) Detailed comparison across the 5 dimensions
| Dimension | Element Plus | Naive UI | Vuetify | PrimeVue | Ant Design Vue |
|---|---|---|---|---|---|
| GitHub ⭐ | 24k+ | 16k+ | 40k+ | 11k+ | 20k+ |
| Vue 3 first-class | ✅ | ✅ | ✅ | ✅ | ✅ |
| TypeScript | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| Theme customization | CSS variables | CSS variables | SCSS | SCSS + theme | less + theme |
| Bundle size | Medium | Small | Large | Medium | Medium |
| Chinese docs | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| Community activity | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| Learning curve | Gentle | Gentle | Medium | Medium | Medium |
(3) 5 selection recommendations
| Scenario | Recommendation |
|---|---|
| Chinese admin backends (e-commerce/CRM/ERP) | Element Plus ⭐⭐⭐⭐⭐ |
| Modern-style SaaS | Naive UI ⭐⭐⭐⭐ |
| Material Design apps | Vuetify ⭐⭐⭐⭐ |
| Multiple themes (dark/light/high contrast) | PrimeVue ⭐⭐⭐⭐ |
| The Ant Design ecosystem | Ant Design Vue ⭐⭐⭐⭐ |
4. Element Plus in Practice
(1) Installation
npm install element-plus @element-plus/icons-vue
(2) Global registration (simple)
(3) On-demand imports (recommended, saves 50% of the bundle)
npm install -D unplugin-vue-components unplugin-auto-import
// vite.config.ts
import AutoImport from 'unplugin-auto-import'
import Components from 'unplugin-vue-components'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()],
dts: 'src/auto-imports.d.ts'
}),
Components({
resolvers: [ElementPlusResolver()],
dts: 'src/components.d.ts'
})
]
})
(4) The 5 core components
▶ Example: Hand-Building a Library-Style Button + Card with Plain CSS (browser-runnable)
▶ Expand runnable example (78 lines)
Output:
First render: an outlined "Default" button, a large blue "Primary" button and a red "Delete" button; below them a card titled "Order #1024" with the total and a small blue button, and the hint "Waiting for input".
Click "Delete": the button greys out and shows "⏳ Delete" (disabled while loading) and the hint becomes "Deleting…"; about 800ms later it is clickable again and the hint reads "Deleted (request simulated)".
The same type / size / loading / disabled props plus slots produce 4 different looks — exactly the essence of libraries like Element Plus: components with one visual language and one API.
5. Theme Customization
(1) Overriding CSS variables (recommended)
// src/styles/element-plus.scss
:root {
--el-color-primary: #42b883; // change the primary color to Vue green
--el-color-success: #67c23a;
--el-color-warning: #e6a23c;
--el-color-danger: #f56c6c;
--el-color-info: #909399;
--el-border-radius-base: 8px; // larger corner radius
--el-font-size-base: 14px;
}
// main.js
import './styles/element-plus.scss'
import 'element-plus/dist/index.css'
(2) SCSS variables (deep customization)
// src/styles/element-plus-vars.scss
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
$colors: (
'primary': ('base': #42b883),
'success': ('base': #67c23a),
'warning': ('base': #e6a23c),
'danger': ('base': #f56c6c),
'info': ('base': #909399)
)
);
// main.js
import './styles/element-plus-vars.scss'
import 'element-plus/dist/index.css'
(3) Dark mode
// Dark theme
:root {
--el-color-primary: #42b883;
--el-bg-color: #1a1a1a;
--el-text-color-primary: #ffffff;
}
html.dark {
--el-bg-color: #1a1a1a;
--el-text-color-primary: #ffffff;
}
▶ Example: The 5 Theme Customizations
/* 1. CSS variables (recommended) */
:root { --el-color-primary: #42b883; }
/* 2. SCSS variables (deep customization) */
@forward 'var.scss' with ($colors: ...);
/* 3. Dark mode */
:root.dark { --el-bg-color: #1a1a1a; }
/* 4. Custom theme */
@import 'element-plus/theme-chalk/dark/css-vars.css';
/* 5. Runtime switching */
document.documentElement.classList.toggle('dark')
Output:
Compiled as-is, this stylesheet repaints every Element Plus component — the primary color turns Vue green, and toggling the dark class on <html> flips the background to #1a1a1a:
Before: primary #409eff, background #ffffff
After: primary #42b883, background #1a1a1a (when <html class="dark">)
6. Wrapping Components
(1) A business component example
<!-- Usage: one table in 3 lines -->
<BusinessTable :data="orders" :columns="orderColumns" :total="100" />
(2) The 5 benefits of wrapping
- Less duplicated code: shared by 50+ tables
- Consistent styling: every table looks the same
- Business customization: automatic loading + pagination + empty state
- TypeScript: strongly typed props
- Maintainable: change one place, fix N places
7. Icon Library Integration
(1) Installation
npm install -D unplugin-icons @iconify-json/carbon
(2) Configuration
// vite.config.ts
import Icons from 'unplugin-icons/vite'
import { FileSystemIconLoader } from 'unplugin-icons/dist/loader'
export default defineConfig({
plugins: [
Icons({
compiler: 'vue3',
autoInstall: true, // install icon sets automatically
collections: {
carbon: () => import('@iconify-json/carbon/icons.json').then(i => i.default)
}
})
]
})
(3) Usage
(4) Comparing the 5 major icon libraries
| Library | Icons | Loading | Bundle size |
|---|---|---|---|
| Iconify | 200,000+ | On demand (recommended) | 0 |
| Element Icons | 300+ | All / on demand | 100KB |
| Material Icons | 2,000+ | All / on demand | 300KB |
| Font Awesome | 7,000+ | All | 1MB+ |
| Heroicons | 300+ | On demand | 50KB |
8. Putting It Together: Five Components for an Admin UI
(1) 📊 Comparing the 5 UI Libraries (⚠️ Requires Vite + npm install)
| Library | ⭐ | Bundle size | Style |
|---|---|---|---|
| Element Plus | 24k | Medium | Desktop |
| Naive UI | 16k | Small | Modern |
| Vuetify | 40k | Large | Material |
| PrimeVue | 11k | Medium | Multi-style |
| Ant Design Vue | 20k | Medium | Ant Design |
Output:
The block renders as a four-column comparison table covering all five libraries:
Library ⭐ Bundle size Style
---------------- ---- ----------- --------------
Element Plus 24k Medium Desktop
Naive UI 16k Small Modern
Vuetify 40k Large Material
PrimeVue 11k Medium Multi-style
Ant Design Vue 20k Medium Ant Design
▶ Example: The 5 Core Components (Element Plus)
<!-- Requires Vite + npm install element-plus -->
<el-button>Button</el-button>
<el-form>Form</el-form>
<el-table>Table</el-table>
<el-dialog>Dialog</el-dialog>
<el-message>Message</el-message>
Output:
With Element Plus installed, each tag renders as its styled Element Plus widget (a primary button, a form container, a striped table, a modal dialog, and a top toast):
[ Button ] ← <el-button>
< el-form > ← form container, label width applied
┌──────────────┐
│ Table header │ ← <el-table>
└──────────────┘
┌ Dialog ──────┐ ← <el-dialog> (hidden until v-model="visible")
└──────────────┘
✓ Saved! ← <el-message> (auto-dismiss toast)
(2) 📊 The 5 Icon Libraries
| Library | Icons |
|---|---|
| Iconify | 200,000+ |
| Element Icons | 300+ |
| Material | 2,000+ |
| Font Awesome | 7,000+ |
| Heroicons | 300+ |
Output:
The block renders as a two-column table ordered from the largest collection to the smallest:
Library Icons
-------------- ---------
Iconify 200,000+
Element Icons 300+
Material 2,000+
Font Awesome 7,000+
Heroicons 300+
(3) 📊 Quick Reference for 5 Common Mistakes
| Error | Symptom | Solution |
|---|---|---|
| Importing everything bloats the bundle | bundle 500KB | Use unplugin-vue-components for on-demand imports |
| Theme color will not change | SCSS was used | Switch to CSS variables |
| Icons do not appear | Font failed to load | Use unplugin-icons SVG |
| TS type errors | import has no types | Install the @types package |
| i18n stops working | Wrong locale | Configure i18n |
Output:
The block renders as a five-row troubleshooting table covering bundle size, theming, icons, types and locales:
Error Symptom Solution
----------------------------- ----------------- ------------------------------
Full import bloats the bundle bundle 500KB unplugin-vue-components
Theme color will not change SCSS was used Switch to CSS variables
Icons do not appear Font load failed Use unplugin-icons SVG
TS type errors import has no types Install the @types package
i18n stops working Wrong locale Configure i18n
❓ FAQ
--el-color-primary. Customizing SCSS variables requires compiling the theme with unplugin-element-plus.📖 Summary
- The 5 major Vue 3 UI libraries: Element Plus / Naive UI / Vuetify / PrimeVue / Ant Design Vue
- Element Plus is the most popular in China, with rich components (80+) and TypeScript
- On-demand imports: unplugin-vue-components + ElementPlusResolver
- Theme customization: CSS variable overrides (recommended) + SCSS variables (deep)
- Wrapping: business components cut duplicated code
- Iconify 200,000+ icons + unplugin-icons on demand
- The 5 icon libraries: Iconify / Element / Material / Font Awesome / Heroicons
📝 Exercises
-
Basic (Difficulty: ⭐) Integrate Element Plus into a Vue 3 project:
- Install + on-demand imports
- 5 core components (Button / Form / Table / Dialog / Message)
- Change the theme color to the project's primary color
-
Intermediate (Difficulty: ⭐⭐) Implement a complete Element Plus integration:
- Use all 5 major components (Button / Form / Table / Dialog / Message)
- Dark mode toggle
- Iconify icons (10 different icons)
- Wrap a BusinessTable component
-
Challenge (Difficulty: ⭐⭐⭐) Build a complete "enterprise-grade UI library integration":
- Element Plus + Naive UI mixed (primary + secondary)
- 5 theme colors (primary / secondary / success / warning / danger)
- Dark mode + automatic system preference
- 10+ wrapped business components
- Iconify 200,000+ icons
- Strong TypeScript typing
- Performance optimization (on-demand + tree shaking + CDN)