1. Quick Start
Element UI is a Vue 2.x component library providing 67+ production-ready UI components for building web applications. Developed by the Ele.me frontend team, it offers a complete design system with consistent styling, internationalization, and theme customization.
Key features:
- 67+ components covering forms, data display, navigation, feedback, and layout
- Built-in i18n with 50+ language packs
- Customizable theme system via SCSS variables
- TypeScript type definitions included
- Vue Router integration for navigation components
- Service-based components (Message, Notification, MessageBox, Loading)
- Responsive grid system with 5 breakpoints
- Form validation powered by async-validator
Prerequisites: Node.js 10+, Vue 2.5.17+
Install and verify:
npm install element-ui
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
new Vue({
el: '#app',
render: h => h(App)
});
What to try next: See Installation for all methods, Configuration for global options, or jump to Core Usage for component examples.
2. Installation
System Requirements
| Requirement | Version |
|---|---|
| Node.js | 10+ |
| Vue | ^2.5.17 |
| npm / yarn | Any recent |
Install Methods
npm
npm install element-ui
yarn
yarn add element-ui
CDN
<!-- CSS -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- JS -->
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
Tip
The CDN approach is the fastest way to prototype. Element UI auto-installs itself when it detects
window.Vue.
Full Import
Register all components globally:
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
Partial Import
Import only the components you need to reduce bundle size. Use babel-plugin-component for automatic style imports:
npm install babel-plugin-component --save-dev
Configure .babelrc:
{
"plugins": [
["component", {
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}]
]
}
Then import individual components:
import { Button, Select, Table } from 'element-ui';
Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
Vue.component(Table.name, Table);
Important
When using partial imports, you must still manually register service components if needed:
Vue.prototype.$message = Message;andVue.use(Loading.directive);.
Post-Install Verification
import ElementUI from 'element-ui';
console.log(ElementUI.version);
2.15.14
Install Troubleshooting
Error: node-sass build failure
Cause: node-sass is a devDependency of Element UI's theme package and can fail on newer Node versions.
Solution:
- Use
npm install --ignore-scriptsif you don't need to rebuild themes - Or install the pre-built CSS from
element-ui/lib/theme-chalk/index.css
Error: Module not found: element-ui
Cause: Package not installed or incorrect import path.
Solution:
npm install element-ui --save
Error: Components render without styles
Cause: Missing CSS import.
Solution:
import 'element-ui/lib/theme-chalk/index.css';
Error: Vue is not a constructor or Cannot read property 'use' of undefined
Cause: Vue 3 project trying to use Element UI (which requires Vue 2).
Solution: Element UI only supports Vue 2.x. For Vue 3, use Element Plus instead.
3. Configuration
Global Configuration
Pass options to Vue.use() to configure all components:
Vue.use(ElementUI, {
size: 'small', // Component size: 'medium' | 'small' | 'mini'
zIndex: 3000, // Starting z-index for popups (default: 2000)
locale: enLocale, // Language pack
i18n: fn // Custom i18n handler
});
| Option | Type | Default | Description |
|---|---|---|---|
size |
string |
'' |
Global component size: 'medium', 'small', 'mini' |
zIndex |
number |
2000 |
Starting z-index for popup-based components |
locale |
object |
zh-CN | Language pack object |
i18n |
function |
null |
Custom i18n translation function |
Note
The global
sizeandzIndexare stored onVue.prototype.$ELEMENTand can be accessed from any component instance.
Internationalization (i18n)
Element UI defaults to Chinese (zh-CN). Switch to English or other languages:
import lang from 'element-ui/lib/locale/lang/en';
import locale from 'element-ui/lib/locale';
locale.use(lang);
With vue-i18n:
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import ElementUI from 'element-ui';
import enLocale from 'element-ui/lib/locale/lang/en';
import zhLocale from 'element-ui/lib/locale/lang/zh-CN';
Vue.use(VueI18n);
const i18n = new VueI18n({
locale: 'en',
messages: {
en: { ...enLocale },
zh: { ...zhLocale }
}
});
Vue.use(ElementUI, { i18n: (key, value) => i18n.t(key, value) });
Tip
Element UI includes 50+ language packs in
element-ui/lib/locale/lang/. Check thesrc/locale/lang/directory for the full list.
For full configuration reference, see the Admin Guide.
4. Core Usage
Basic Components
Button
Buttons support types, sizes, icons, loading state, and grouping:
<el-button>Default</el-button>
<el-button type="primary">Primary</el-button>
<el-button type="success" icon="el-icon-check">Success</el-button>
<el-button type="danger" round>Rounded</el-button>
<el-button type="warning" circle icon="el-icon-star-off"></el-button>
<el-button :loading="true">Loading</el-button>
| Prop | Type | Default | Description |
|---|---|---|---|
type |
string |
— | 'primary', 'success', 'warning', 'danger', 'info', 'text' |
size |
string |
— | 'medium', 'small', 'mini' |
plain |
boolean |
false |
Plain style |
round |
boolean |
false |
Round corners |
circle |
boolean |
false |
Circle shape |
loading |
boolean |
false |
Show loading spinner |
disabled |
boolean |
false |
Disable the button |
icon |
string |
— | Icon class name |
nativeType |
string |
'button' |
Native type: 'button', 'submit', 'reset' |
Icon
Element UI provides a built-in icon set. Use icon names with the el-icon- prefix:
<i class="el-icon-edit"></i>
<i class="el-icon-share"></i>
<i class="el-icon-delete"></i>
<!-- In buttons -->
<el-button icon="el-icon-search">Search</el-button>
Link
Text hyperlink component:
<el-link href="https://example.com" target="_blank" type="primary">Link</el-link>
<el-link :underline="false">No Underline</el-link>
<el-link disabled>Disabled</el-link>
Layout
Container System
Build page layouts with semantic container components:
<el-container>
<el-header height="60px">Header</el-header>
<el-container>
<el-aside width="200px">Sidebar</el-aside>
<el-main>Content</el-main>
</el-container>
<el-footer height="60px">Footer</el-footer>
</el-container>
Note
When
<el-container>contains<el-header>or<el-footer>, its children arrange vertically. Otherwise they arrange horizontally. Override with thedirectionprop.
Grid System (Row / Col)
A responsive 24-column grid:
<el-row :gutter="20">
<el-col :span="6"><div>col-6</div></el-col>
<el-col :span="6"><div>col-6</div></el-col>
<el-col :span="6"><div>col-6</div></el-col>
<el-col :span="6"><div>col-6</div></el-col>
</el-row>
<!-- Responsive -->
<el-row>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4">
<div>Responsive column</div>
</el-col>
</el-row>
Row props:
| Prop | Type | Default | Description |
|---|---|---|---|
gutter |
number |
0 |
Grid spacing in pixels |
type |
string |
— | Set to 'flex' for flexbox mode |
justify |
string |
— | Flex horizontal alignment |
align |
string |
— | Flex vertical alignment |
Col responsive breakpoints:
| Prop | Breakpoint |
|---|---|
xs |
<768px |
sm |
≥768px |
md |
≥992px |
lg |
≥1200px |
xl |
≥1920px |
Form Components
Input
<el-input v-model="text" placeholder="Enter text"></el-input>
<!-- Textarea -->
<el-input type="textarea" :rows="4" v-model="content"></el-input>
<!-- With icon -->
<el-input prefix-icon="el-icon-search" v-model="search"></el-input>
<!-- Clearable -->
<el-input v-model="text" clearable></el-input>
<!-- Password toggle -->
<el-input v-model="password" show-password></el-input>
<!-- Word limit -->
<el-input v-model="text" maxlength="100" show-word-limit></el-input>
| Prop | Type | Default | Description |
|---|---|---|---|
type |
string |
'text' |
Input type: 'text', 'textarea', or native types |
v-model |
string/number |
— | Binding value |
placeholder |
string |
— | Placeholder text |
clearable |
boolean |
false |
Show clear button |
show-password |
boolean |
false |
Toggle password visibility |
show-word-limit |
boolean |
false |
Show word count (requires maxlength) |
disabled |
boolean |
false |
Disable input |
size |
string |
— | 'medium', 'small', 'mini' |
prefix-icon |
string |
— | Prefix icon class |
suffix-icon |
string |
— | Suffix icon class |
autosize |
boolean/object |
false |
Auto-resize textarea: true or { minRows: 2, maxRows: 6 } |
Slots: prefix, suffix, prepend, append
InputNumber
<el-input-number v-model="num" :min="1" :max="100" :step="2"></el-input-number>
Select
<el-select v-model="value" placeholder="Select">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<!-- Multiple selection -->
<el-select v-model="values" multiple placeholder="Select">
<el-option v-for="item in options" :key="item.value"
:label="item.label" :value="item.value">
</el-option>
</el-select>
<!-- Filterable -->
<el-select v-model="value" filterable placeholder="Search">
<el-option v-for="item in options" :key="item.value"
:label="item.label" :value="item.value">
</el-option>
</el-select>
<!-- Remote search -->
<el-select v-model="value" filterable remote :remote-method="search"
:loading="loading" placeholder="Search">
<el-option v-for="item in results" :key="item.value"
:label="item.label" :value="item.value">
</el-option>
</el-select>
| Prop | Type | Default | Description |
|---|---|---|---|
v-model |
any |
— | Binding value |
multiple |
boolean |
false |
Enable multi-select |
filterable |
boolean |
false |
Enable search filtering |
remote |
boolean |
false |
Enable remote search |
remote-method |
function |
— | Remote search function |
clearable |
boolean |
false |
Show clear button |
allow-create |
boolean |
false |
Allow creating new items (requires filterable) |
multiple-limit |
number |
0 |
Max selections (0 = unlimited) |
collapse-tags |
boolean |
false |
Collapse tags in multi-select |
default-first-option |
boolean |
false |
Select first match on Enter |
Tip
Group options with
<el-option-group>by wrapping related<el-option>elements and setting alabelprop on the group.
Radio and Checkbox
<!-- Radio -->
<el-radio-group v-model="radio">
<el-radio :label="1">Option A</el-radio>
<el-radio :label="2">Option B</el-radio>
</el-radio-group>
<!-- Radio buttons -->
<el-radio-group v-model="radio">
<el-radio-button label="Option A"></el-radio-button>
<el-radio-button label="Option B"></el-radio-button>
</el-radio-group>
<!-- Checkbox -->
<el-checkbox-group v-model="checked">
<el-checkbox label="Option A"></el-checkbox>
<el-checkbox label="Option B"></el-checkbox>
</el-checkbox-group>
Switch
<el-switch v-model="active"></el-switch>
<!-- Custom values and colors -->
<el-switch v-model="value"
active-color="#13ce66"
inactive-color="#ff4949"
active-value="on"
inactive-value="off">
</el-switch>
Slider
<el-slider v-model="value"></el-slider>
<!-- Range -->
<el-slider v-model="range" range :min="0" :max="100"></el-slider>
<!-- With input box -->
<el-slider v-model="value" show-input></el-slider>
DatePicker and TimePicker
<!-- Date -->
<el-date-picker v-model="date" type="date" placeholder="Pick a date">
</el-date-picker>
<!-- Date range -->
<el-date-picker v-model="dateRange" type="daterange"
start-placeholder="Start" end-placeholder="End">
</el-date-picker>
<!-- DateTime -->
<el-date-picker v-model="datetime" type="datetime"
placeholder="Pick date and time">
</el-date-picker>
<!-- Time picker -->
<el-time-picker v-model="time" placeholder="Pick time">
</el-time-picker>
<!-- Time select (fixed intervals) -->
<el-time-select v-model="time" placeholder="Pick time"
:picker-options="{ start: '08:00', step: '00:30', end: '18:00' }">
</el-time-select>
DatePicker type |
Description |
|---|---|
date |
Single date |
daterange |
Date range |
datetime |
Date with time |
datetimerange |
DateTime range |
week |
Week picker |
month |
Month picker |
monthrange |
Month range |
year |
Year picker |
Warning
The
firstDayOfWeekoption inpicker-optionshas known issues when used withtype="week"(see issue #23126). Test thoroughly if your application requires non-default week start days.
Upload
<el-upload action="https://api.example.com/upload"
:on-success="handleSuccess"
:on-error="handleError"
:limit="3"
:on-exceed="handleExceed">
<el-button type="primary">Click to Upload</el-button>
<div slot="tip" class="el-upload__tip">Only jpg/png files, max 500KB</div>
</el-upload>
<!-- Drag-and-drop -->
<el-upload action="https://api.example.com/upload" drag multiple>
<i class="el-icon-upload"></i>
<div class="el-upload__text">Drop files here or <em>click to upload</em></div>
</el-upload>
| Prop | Type | Default | Description |
|---|---|---|---|
action |
string |
required | Upload endpoint URL |
multiple |
boolean |
false |
Allow multiple files |
drag |
boolean |
false |
Enable drag-and-drop |
accept |
string |
— | Accepted file types |
limit |
number |
— | Max file count |
auto-upload |
boolean |
true |
Upload immediately on select |
list-type |
string |
'text' |
'text', 'picture', 'picture-card' |
headers |
object |
— | Request headers |
data |
object |
— | Additional form data |
with-credentials |
boolean |
false |
Send cookies |
http-request |
function |
— | Override default upload behavior |
Cascader
Hierarchical selection for data with parent-child relationships:
<el-cascader v-model="value" :options="options" :props="{ expandTrigger: 'hover' }">
</el-cascader>
<!-- Filterable -->
<el-cascader v-model="value" :options="options" filterable></el-cascader>
ColorPicker
<el-color-picker v-model="color"></el-color-picker>
<el-color-picker v-model="color" show-alpha></el-color-picker>
Transfer
Dual-list selection for moving items between two panels:
<el-transfer v-model="selected" :data="data"
:titles="['Available', 'Selected']" filterable>
</el-transfer>
Rate
<el-rate v-model="score"></el-rate>
<el-rate v-model="score" show-text :texts="['Bad','Poor','Normal','Good','Great']"></el-rate>
Form Validation
Combine <el-form> with validation rules powered by async-validator:
<el-form :model="form" :rules="rules" ref="form" label-width="120px">
<el-form-item label="Name" prop="name">
<el-input v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="Email" prop="email">
<el-input v-model="form.email"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm">Submit</el-button>
<el-button @click="resetForm">Reset</el-button>
</el-form-item>
</el-form>
export default {
data() {
return {
form: { name: '', email: '' },
rules: {
name: [
{ required: true, message: 'Name is required', trigger: 'blur' },
{ min: 3, max: 50, message: 'Length must be 3-50', trigger: 'blur' }
],
email: [
{ required: true, message: 'Email is required', trigger: 'blur' },
{ type: 'email', message: 'Invalid email format', trigger: ['blur', 'change'] }
]
}
};
},
methods: {
submitForm() {
this.$refs.form.validate(valid => {
if (valid) { /* submit */ }
});
},
resetForm() {
this.$refs.form.resetFields();
}
}
};
Form methods:
| Method | Description |
|---|---|
validate(callback) |
Validate entire form. Callback receives (valid, failedFields) |
validateField(props, callback) |
Validate specific field(s) |
resetFields() |
Reset all fields and clear validation |
clearValidate(props?) |
Clear validation messages |
Important
The
propattribute on<el-form-item>must match a key in the form's:modelobject. Nested paths use colon notation:prop="address:city".
Data Display
Table
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="name" label="Name" width="180"></el-table-column>
<el-table-column prop="email" label="Email"></el-table-column>
<el-table-column prop="date" label="Date" sortable></el-table-column>
<el-table-column label="Actions">
<template slot-scope="scope">
<el-button size="mini" @click="handleEdit(scope.row)">Edit</el-button>
<el-button size="mini" type="danger" @click="handleDelete(scope.row)">Delete</el-button>
</template>
</el-table-column>
</el-table>
Key Table features:
| Feature | How to enable |
|---|---|
| Striped rows | stripe prop on <el-table> |
| Border | border prop |
| Fixed height + scroll | height or max-height prop |
| Fixed columns | fixed prop on <el-table-column> (true, 'left', 'right') |
| Sorting | sortable prop on column (true or 'custom') |
| Filtering | filters + filter-method props on column |
| Selection | Column with type="selection" |
| Expand row | Column with type="expand" + scoped slot |
| Summary row | show-summary prop on table |
| Row index | Column with type="index" |
| Row highlight | highlight-current-row prop |
| Tree data | row-key + tree-structured data + lazy + load |
Warning
When using fixed columns with
show-summary, the scrollbar may not work correctly (known issue #23142, #20515). Test with your specific data layout.
Tag
<el-tag>Default</el-tag>
<el-tag type="success">Success</el-tag>
<el-tag type="warning">Warning</el-tag>
<el-tag type="danger">Danger</el-tag>
<el-tag closable @close="handleClose">Closable</el-tag>
Badge
<el-badge :value="12">
<el-button>Comments</el-button>
</el-badge>
<el-badge :value="200" :max="99">
<el-button>Replies</el-button>
</el-badge>
<el-badge is-dot>
<el-button>Notifications</el-button>
</el-badge>
Tree
<el-tree :data="data" :props="{ children: 'children', label: 'label' }"
show-checkbox node-key="id"
@check-change="handleCheckChange">
</el-tree>
Tree supports lazy loading, drag-and-drop (draggable), custom node content via scoped slots, filtering, and accordion mode.
Tip
Set
node-keyto a unique identifier in your data for reliable programmatic access to nodes via methods likegetCheckedKeys(),setCheckedKeys(), andgetNode().
Pagination
<el-pagination
@size-change="handleSizeChange"
@current-change="handlePageChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="20"
layout="total, sizes, prev, pager, next, jumper"
:total="400">
</el-pagination>
The layout prop controls which elements appear: sizes, prev, pager, next, jumper, -> (right-align separator), total, slot.
Progress
<el-progress :percentage="50"></el-progress>
<el-progress :percentage="75" status="success"></el-progress>
<el-progress :percentage="80" :stroke-width="18" text-inside></el-progress>
<el-progress type="circle" :percentage="60"></el-progress>
<el-progress type="dashboard" :percentage="80"></el-progress>
Descriptions
Display multiple fields in a list layout:
<el-descriptions title="User Info" :column="3" border>
<el-descriptions-item label="Name">John Doe</el-descriptions-item>
<el-descriptions-item label="Email">[email removed]</el-descriptions-item>
<el-descriptions-item label="Role">Admin</el-descriptions-item>
</el-descriptions>
Skeleton
Loading placeholder while data fetches:
<el-skeleton :loading="loading" animated :rows="5">
<template slot="template">
<el-skeleton-item variant="h1" style="width: 50%" />
<el-skeleton-item variant="text" />
<el-skeleton-item variant="text" />
<el-skeleton-item variant="text" style="width: 60%" />
</template>
<template>
<!-- Actual content here -->
</template>
</el-skeleton>
Statistic
Display a statistic with optional prefix/suffix:
<el-statistic :value="268500" title="Active Users"></el-statistic>
Empty and Result
<el-empty description="No data available"></el-empty>
<el-result icon="success" title="Success" sub-title="Your operation completed">
<template slot="extra">
<el-button type="primary">Back</el-button>
</template>
</el-result>
Avatar
<el-avatar src="https://example.com/avatar.jpg"></el-avatar>
<el-avatar icon="el-icon-user-solid"></el-avatar>
<el-avatar :size="50" shape="square">User</el-avatar>
Calendar
<el-calendar v-model="date"></el-calendar>
Navigation
Menu
<!-- Horizontal -->
<el-menu mode="horizontal" :default-active="activeIndex" @select="handleSelect">
<el-menu-item index="1">Home</el-menu-item>
<el-submenu index="2">
<template slot="title">Products</template>
<el-menu-item index="2-1">Product A</el-menu-item>
<el-menu-item index="2-2">Product B</el-menu-item>
</el-submenu>
<el-menu-item index="3">About</el-menu-item>
</el-menu>
<!-- Vertical with collapse -->
<el-menu :default-active="activeIndex" :collapse="isCollapsed">
<el-menu-item index="1"><i class="el-icon-house"></i><span>Home</span></el-menu-item>
<el-submenu index="2">
<template slot="title"><i class="el-icon-setting"></i><span>Settings</span></template>
<el-menu-item index="2-1">General</el-menu-item>
<el-menu-item index="2-2">Security</el-menu-item>
</el-submenu>
</el-menu>
Enable Vue Router integration by adding the router prop — menu items use their index as the route path.
Tabs
<el-tabs v-model="activeTab" type="card" @tab-click="handleClick">
<el-tab-pane label="Tab 1" name="first">Content 1</el-tab-pane>
<el-tab-pane label="Tab 2" name="second">Content 2</el-tab-pane>
<el-tab-pane label="Tab 3" name="third" lazy>Content 3 (lazy)</el-tab-pane>
</el-tabs>
Tab types: default (no type), card, border-card. Positions: top (default), right, bottom, left.
Breadcrumb
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{ path: '/' }">Home</el-breadcrumb-item>
<el-breadcrumb-item>Products</el-breadcrumb-item>
<el-breadcrumb-item>Detail</el-breadcrumb-item>
</el-breadcrumb>
Steps
<el-steps :active="active" finish-status="success">
<el-step title="Step 1" description="Description"></el-step>
<el-step title="Step 2"></el-step>
<el-step title="Step 3"></el-step>
</el-steps>
Supports direction="vertical", simple theme, and custom icons.
Dropdown
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
Actions<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="edit">Edit</el-dropdown-item>
<el-dropdown-item command="delete" divided>Delete</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
PageHeader
<el-page-header @back="goBack" content="Detail Page" title="Back">
</el-page-header>
Feedback Components
Dialog
<el-button @click="dialogVisible = true">Open Dialog</el-button>
<el-dialog title="Dialog Title" :visible.sync="dialogVisible" width="50%">
<p>Dialog content here.</p>
<span slot="footer">
<el-button @click="dialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="dialogVisible = false">Confirm</el-button>
</span>
</el-dialog>
| Prop | Type | Default | Description |
|---|---|---|---|
visible |
boolean |
false |
Use with .sync modifier |
title |
string |
— | Dialog title |
width |
string |
'50%' |
Dialog width |
fullscreen |
boolean |
false |
Fullscreen mode |
top |
string |
'15vh' |
Margin from top |
modal |
boolean |
true |
Show overlay mask |
close-on-click-modal |
boolean |
true |
Close on mask click |
close-on-press-escape |
boolean |
true |
Close on Escape key |
destroy-on-close |
boolean |
false |
Destroy inner content on close |
Drawer
Side panel alternative to Dialog:
<el-drawer title="Settings" :visible.sync="drawerVisible" direction="rtl" size="30%">
<p>Drawer content</p>
</el-drawer>
Direction options: rtl (right), ltr (left), ttb (top), btt (bottom).
Warning
When nesting Drawers or combining Drawers with Dialogs, set
append-to-bodyon the inner component. Improper nesting without this prop can cause z-index layering issues (see issue #23137).
Tooltip
<el-tooltip content="Helpful tip" placement="top">
<el-button>Hover me</el-button>
</el-tooltip>
Placement: top, top-start, top-end, bottom, left, right (and their -start/-end variants).
Popover
<el-popover placement="right" width="200" trigger="click" content="Popover content">
<el-button slot="reference">Click me</el-button>
</el-popover>
Trigger modes: click, focus, hover, manual.
Popconfirm
<el-popconfirm title="Are you sure?" @confirm="handleConfirm">
<el-button slot="reference">Delete</el-button>
</el-popconfirm>
Alert
<el-alert title="Info alert" type="info"></el-alert>
<el-alert title="Success" type="success" description="Operation completed" show-icon closable>
</el-alert>
Types: success, warning, info, error. Effects: light (default), dark.
Service Components
These components are invoked programmatically via this.$method() rather than as template tags.
Message
Global message displayed at the top of the page:
// Simple
this.$message('This is a message');
// Typed
this.$message.success('Success!');
this.$message.warning('Warning!');
this.$message.error('Error!');
// With options
this.$message({
message: 'Congratulations!',
type: 'success',
duration: 5000,
showClose: true,
center: true,
offset: 40
});
// Close all messages
this.$message.closeAll();
| Option | Type | Default | Description |
|---|---|---|---|
message |
string/VNode |
— | Message text |
type |
string |
'info' |
'success', 'warning', 'info', 'error' |
duration |
number |
3000 |
Display time in ms (0 = persistent) |
showClose |
boolean |
false |
Show close button |
center |
boolean |
false |
Center text |
offset |
number |
20 |
Vertical offset from top in pixels |
dangerouslyUseHTMLString |
boolean |
false |
Treat message as HTML |
onClose |
function |
null |
Callback when closed |
Notification
Corner notification with title and message:
this.$notify({
title: 'Title',
message: 'This is a notification',
type: 'success',
position: 'top-right',
duration: 4500
});
// Shorthand
this.$notify.error({ title: 'Error', message: 'Something failed' });
// Close all
this.$notify.closeAll();
| Option | Type | Default | Description |
|---|---|---|---|
title |
string |
— | Notification title |
message |
string/VNode |
— | Notification body |
type |
string |
— | 'success', 'warning', 'info', 'error' |
position |
string |
'top-right' |
'top-right', 'top-left', 'bottom-right', 'bottom-left' |
duration |
number |
4500 |
Display time in ms (0 = persistent) |
showClose |
boolean |
true |
Show close button |
offset |
number |
0 |
Vertical offset in pixels |
onClose |
function |
null |
Close callback |
onClick |
function |
null |
Click callback |
MessageBox
Modal dialog for confirmations and prompts:
// Alert
this.$alert('This is an alert', 'Title', { confirmButtonText: 'OK' });
// Confirm
this.$confirm('Are you sure?', 'Warning', {
confirmButtonText: 'Yes',
cancelButtonText: 'No',
type: 'warning'
}).then(() => {
this.$message.success('Confirmed');
}).catch(() => {
this.$message.info('Cancelled');
});
// Prompt
this.$prompt('Enter your email', 'Prompt', {
inputPattern: /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/,
inputErrorMessage: 'Invalid email format'
}).then(({ value }) => {
this.$message.success('Email: ' + value);
});
Note
MessageBox returns a Promise. Use
.then()for confirm and.catch()for cancel. Both$alertand$confirmuse the same underlying$msgboxservice.
Loading
Directive usage:
<div v-loading="isLoading">Content that's loading...</div>
<!-- Fullscreen -->
<div v-loading.fullscreen.lock="isLoading">Full page loading</div>
Customize via element attributes:
<div v-loading="true"
element-loading-text="Loading..."
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)">
</div>
Service usage:
const loading = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)',
target: document.querySelector('.target-element')
});
// Close when done
loading.close();
Other Components
Carousel
<el-carousel :interval="3000" height="200px">
<el-carousel-item v-for="item in 4" :key="item">
<div>Slide {{ item }}</div>
</el-carousel-item>
</el-carousel>
Supports type="card" for card-style layout, direction="vertical", and arrow="always".
Collapse
<el-collapse v-model="activeNames">
<el-collapse-item title="Section 1" name="1">Content 1</el-collapse-item>
<el-collapse-item title="Section 2" name="2">Content 2</el-collapse-item>
</el-collapse>
<!-- Accordion mode -->
<el-collapse accordion v-model="activeName">
<el-collapse-item title="Panel A" name="a">Only one open at a time</el-collapse-item>
<el-collapse-item title="Panel B" name="b">Content B</el-collapse-item>
</el-collapse>
Timeline
<el-timeline>
<el-timeline-item timestamp="2024-01-15" type="primary">Event 1</el-timeline-item>
<el-timeline-item timestamp="2024-02-01" type="success">Event 2</el-timeline-item>
</el-timeline>
Image
<el-image src="/img/photo.jpg" fit="cover" lazy>
<div slot="placeholder">Loading...</div>
<div slot="error"><i class="el-icon-picture-outline"></i></div>
</el-image>
<!-- With preview -->
<el-image src="/img/photo.jpg" :preview-src-list="['/img/photo.jpg', '/img/photo2.jpg']">
</el-image>
Supports lazy loading (lazy), object-fit modes (fill, contain, cover, none, scale-down), and full-screen image preview.
Backtop
<el-backtop :visibility-height="200" :right="40" :bottom="40"></el-backtop>
InfiniteScroll
<ul v-infinite-scroll="loadMore" infinite-scroll-disabled="disabled"
infinite-scroll-distance="10">
<li v-for="item in list" :key="item.id">{{ item.name }}</li>
</ul>
| Attribute | Type | Default | Description |
|---|---|---|---|
infinite-scroll-disabled |
boolean |
false |
Disable the scroll trigger |
infinite-scroll-delay |
number |
200 |
Throttle delay in ms |
infinite-scroll-distance |
number |
0 |
Distance threshold in px |
infinite-scroll-immediate |
boolean |
true |
Execute immediately if content doesn't fill the container |
Scrollbar
Custom scrollbar component (wraps content):
<el-scrollbar style="height: 300px;">
<div>Long content...</div>
</el-scrollbar>
Card and Divider
<el-card shadow="hover">
<div slot="header">Card Title</div>
<p>Card content</p>
</el-card>
<el-divider></el-divider>
<el-divider content-position="center">Section Title</el-divider>
<el-divider direction="vertical"></el-divider>
5. Common Tasks
Build a complete CRUD form
Goal: Create a form with validation, submit to server, show success/error feedback.
Steps:
- Wrap fields in
<el-form>with:modeland:rules - Each field in
<el-form-item>withpropmatching model key - Validate on submit with
this.$refs.form.validate() - Show feedback with
this.$message.success()orthis.$message.error() - Reset with
this.$refs.form.resetFields()
Verify: All required fields show validation errors when empty. Success message appears after valid submit.
Build a data table with sorting, filtering, and pagination
Goal: Display server data with column sort, filter dropdowns, and page navigation.
Steps:
- Use
<el-table>with:databound to current page data - Add
sortableto columns that should be sortable - Add
filtersand:filter-methodto filterable columns - Place
<el-pagination>below the table - Handle
@current-changeand@size-changeevents to fetch new data
Create a multi-level navigation sidebar
Goal: Vertical collapsible menu with router integration.
Steps:
- Use
<el-menu>withrouterprop and:collapsebound to a toggle - Add
<el-menu-item>for leaf pages (setindexto route path) - Wrap grouped items in
<el-submenu> - Toggle collapse state with a button
Implement file upload with preview
Goal: Upload images with thumbnail preview and drag support.
Steps:
- Use
<el-upload>withlist-type="picture-card"anddrag - Set
:actionto your upload endpoint - Use
:before-uploadto validate file type and size - Handle
:on-successto store server response - Use
:on-exceedto warn when file limit is reached
Build a date-ranged report filter
Goal: Let users pick a date range and filter a table.
Steps:
- Use
<el-date-picker type="daterange">withv-model - Watch the model value and re-fetch data on change
- Use
value-format="yyyy-MM-dd"for consistent string output
Customize the theme
Goal: Change primary color and component sizes globally.
Steps:
- Install
element-theme:npm install element-theme element-theme-chalk -g - Initialize:
et -i(generateselement-variables.scss) - Edit
$--color-primaryand other variables - Build:
et - Import the generated theme instead of the default CSS
Add internationalization
Goal: Support multiple languages in your app.
Steps:
- Import desired locale files from
element-ui/lib/locale/lang/ - Use
locale.use(lang)or integrate with vue-i18n - Switch languages at runtime by calling
locale.use()with a different pack
Create a dynamic form
Goal: Add/remove form items dynamically with validation.
Steps:
- Use a reactive array in your model (e.g.,
form.items: []) - Loop with
v-for, using index in theprop:prop="items." + index + ".value" - Set
ruleson each dynamic<el-form-item> - Add/remove items by pushing to or splicing the array
6. Troubleshooting
Error: Can not find table layout.
Cause: <el-table-column> used outside of <el-table>, or Table component not properly initialized.
Solution:
- Ensure every
<el-table-column>is a direct child of<el-table> - If columns are conditional, use
v-ifon the column, not on a wrapper div
Prevention: Always structure Table markup with columns as direct children.
Error: for nested data item, row-key is required.
Cause: Tree-structured table data provided without a row-key prop.
Solution:
<el-table :data="treeData" row-key="id">
Error: please transfer a valid prop path to form item!
Cause: The prop attribute on <el-form-item> doesn't match any key in the form model, or the model is undefined.
Solution:
- Verify
:modelis set on<el-form> - Verify
propvalue matches a key path in the model object - For nested paths, use
:separator:prop="address:city"
Error: Invalid Date in fecha.format
Cause: DatePicker received an unparseable date value.
Solution:
- Ensure
v-modelis bound to aDateobject, valid date string, ornull - If using
value-format, ensure the format string matches your data
Error: className should not contain space.
Cause: Passing a class string with spaces to a utility function that expects a single class name.
Solution: When programmatically adding classes, pass one class at a time or use an array.
Table header misalignment with fixed columns
Cause: Known rendering issue in Chrome when tables have fixed columns and the content doesn't fill the viewport (issues #21619, #23134).
Solution:
- Call
this.$refs.table.doLayout()after data changes or tab switches - Use
this.$nextTick()to ensure the DOM has updated before callingdoLayout()
Table memory leak when navigating away
Cause: Element Table can hold DOM references after component destruction (issue #22980).
Solution:
- Ensure tables are properly destroyed when leaving routes
- Use
v-ifrather thanv-showfor conditional tables - Clear table data before navigation:
this.tableData = []
el-select soft keyboard not appearing on Android
Cause: Known issue with filterable/remote selects on Android devices where the native keyboard doesn't appear (issue #12563).
Solution:
- Use
readonlyattribute on the inner input and trigger a custom input - Or use a native
<select>on mobile as a fallback
Cascader placeholder overlap in multi+filterable mode
Cause: When both multiple and filterable are enabled, the placeholder text can overlap with selected tags (issue #23129).
Solution: Apply custom CSS to hide the placeholder when tags are present:
.el-cascader .el-input .el-input__inner::placeholder {
opacity: 0;
}
Caution
Lodash v4.17.17 (bundled as a devDependency) has known security vulnerabilities. If your project uses Element UI's build system, consider upgrading lodash to 4.17.21 or later (issues #23127, #23132).
7. FAQ
Q: Does Element UI support Vue 3?
No. Element UI (this package) supports Vue 2.x only. For Vue 3, use Element Plus, the official successor.
Q: How do I import only specific components to reduce bundle size?
Use partial imports with babel-plugin-component. See the Installation section for setup details. Each component can be imported individually: import { Button, Table } from 'element-ui'.
Q: How do I change the default language from Chinese?
Import a language pack and set it before creating your Vue instance. See the Configuration section for examples with both direct locale setting and vue-i18n integration.
Q: Can I use Element UI with Nuxt.js?
Yes. Create a plugin file that imports Element UI, and register it in nuxt.config.js. Use ssr: false for the plugin since Element UI accesses browser APIs.
Q: How do I customize the theme colors?
Three approaches: (1) Override SCSS variables and rebuild with element-theme CLI, (2) Use the online theme editor at element.eleme.io, or (3) Override CSS custom properties at runtime. See Common Tasks for details.
Q: Why do my table columns misalign after a tab switch or data change?
Call this.$refs.table.doLayout() inside this.$nextTick() after the data changes or the table becomes visible again. This forces the table to recalculate column widths.
Q: How do I set a global component size?
Pass size in the options when calling Vue.use(ElementUI, { size: 'small' }). Valid sizes: 'medium', 'small', 'mini'.
Q: How do I access the Message/Notification service without this?
Import them directly:
import { Message, Notification } from 'element-ui';
Message.success('Done');
Notification({ title: 'Info', message: 'Hello' });
Q: How do I validate a form programmatically without user interaction?
Call this.$refs.formRef.validate() which returns a Promise (or accepts a callback). You can also validate individual fields with this.$refs.formRef.validateField('fieldName').
Q: Can I use Element UI components individually without Vue.use()?
Yes. Import each component and register it locally:
import { Button } from 'element-ui';
export default {
components: { 'el-button': Button }
};