How to Migrate from Element UI to Element Plus for Vue 3 Compatibility
Replace the element-ui package with element-plus, upgrade to Vue 3's createApp API, and update your CSS import from theme-chalk to dist/index.css to complete the migration.
Element UI is a Vue 2 component library distributed under the ElemeFE/element repository that has been superseded by Element Plus, the official Vue 3 successor. According to the Element UI source code at README.md line 43, Vue 3 projects should use Element Plus instead of attempting to continue with Element UI. This guide walks through the complete migration path using the actual repository structure and API implementations.
Step 1: Upgrade Vue and Swap the Package
Element Plus requires Vue 3 as a peer dependency, whereas the original Element UI package.json at line 73 explicitly depends on Vue 2 ("vue": "^2.5.17"). First upgrade your Vue version, then replace the UI library.
# Upgrade to Vue 3 and Vue Router 4
npm install vue@^3 vue-router@^4
# Remove Element UI and install Element Plus
npm uninstall element-ui
npm install element-plus
Step 2: Update Your Application Entry File
The bootstrap API changes fundamentally between Vue 2 and Vue 3. Replace the legacy Vue.use() instantiation with the new createApp pattern in your main.js or main.ts file.
Element UI (Vue 2) implementation:
import Vue from 'vue'
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(Element)
new Vue({ render: h => h(App) }).$mount('#app')
Element Plus (Vue 3) implementation:
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
Step 3: Adapt Component APIs and Event Handling
Element Plus aligns with Vue 3's v-model conventions, changing the underlying prop from value to modelValue and the event from input to update:modelValue. While the template syntax v-model="text" remains syntactically identical, manual bindings require explicit updates.
| Feature | Element UI (Vue 2) | Element Plus (Vue 3) |
|---|---|---|
| v-model prop | value |
modelValue |
| v-model event | input |
update:modelValue |
| Icon system | <i class="el-icon-search"></i> |
<el-icon><Search /></el-icon> (imported component) |
Manual binding migration:
<!-- Vue 2 with Element UI -->
<el-input :value="text" @input="val => text = val"></el-input>
<!-- Vue 3 with Element Plus -->
<el-input :model-value="text" @update:modelValue="val => text = val"></el-input>
Step 4: Implement Tree-Shakable Imports
Element Plus supports tree-shaking to reduce bundle size, whereas Element UI typically required full library registration. Import individual components instead of the global package.
import { createApp } from 'vue'
import { ElButton, ElSelect } from 'element-plus'
import App from './App.vue'
const app = createApp(App)
app.component(ElButton.name, ElButton)
app.component(ElSelect.name, ElSelect)
app.mount('#app')
Step 5: Update CSS Imports and Theme Configuration
Element UI loads styles from lib/theme-chalk/, while Element Plus uses the dist/ directory. Update your CSS import path in the entry file.
// Element UI (old)
import 'element-ui/lib/theme-chalk/index.css'
// Element Plus (new)
import 'element-plus/dist/index.css'
For custom themes, Element Plus utilizes CSS variables and SCSS files rather than the old theme-chalk architecture. The migration requires updating any custom theme configurations to use the new CSS variable system documented in the Element Plus repository.
Step 6: Configure TypeScript Types
Element UI provided types via types/element-ui.d.ts as referenced in the source repository. Element Plus bundles TypeScript definitions automatically, eliminating the need for separate type installations.
# Types are included automatically
npm install element-plus
Update your imports to use the bundled types:
import { ElButton } from 'element-plus'
Complete Migration Example
Original Vue 2 form with Element UI:
// main.js
import Vue from 'vue'
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(Element)
new Vue({ el: '#app', render: h => h(App) })
<!-- App.vue -->
<template>
<el-form :model="form">
<el-form-item label="Name">
<el-input v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="Age">
<el-select v-model="form.age" placeholder="Select age">
<el-option label="18" :value="18"></el-option>
<el-option label="21" :value="21"></el-option>
</el-select>
</el-form-item>
<el-button type="primary" @click="submit">Submit</el-button>
</el-form>
</template>
<script>
export default {
data() {
return { form: { name: '', age: null } }
},
methods: {
submit() { console.log(this.form) }
}
}
</script>
Migrated Vue 3 form with Element Plus:
// main.js
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
<!-- App.vue -->
<template>
<el-form :model="form">
<el-form-item label="Name">
<el-input v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="Age">
<el-select v-model="form.age" placeholder="Select age">
<el-option label="18" :value="18"></el-option>
<el-option label="21" :value="21"></el-option>
</el-select>
</el-form-item>
<el-button type="primary" @click="submit">Submit</el-button>
</el-form>
</template>
<script setup>
import { reactive } from 'vue'
const form = reactive({ name: '', age: null })
function submit() {
console.log(form)
}
</script>
Key Repository Files for Reference
The following files in the ElemeFE/element repository provide authoritative context for this migration:
README.md(line 43): Explicitly recommends Element Plus for Vue 3 projects instead of Element UIpackage.json(line 73): Confirms the Vue 2 peer dependency constraint that prevents Vue 3 usagetypes/element-ui.d.ts: Contains legacy TypeScript definitions that are replaced by Element Plus's bundled typesCHANGELOG.fr-FR.md(line 898): Contains historical migration-related entries illustrating API evolution
Summary
- Uninstall
element-uiand installelement-plusalongside Vue 3 dependencies - Replace
Vue.use(Element)withapp.use(ElementPlus)using thecreateAppAPI - Update CSS imports from
element-ui/lib/theme-chalk/index.csstoelement-plus/dist/index.css - Adapt manual
v-modelbindings to usemodelValueandupdate:modelValueinstead ofvalueandinput - Switch icon usage from CSS classes to imported
el-iconcomponents - Remove legacy TypeScript definition files since Element Plus includes bundled type definitions
Frequently Asked Questions
Can Element UI be used with Vue 3 without migrating?
No. The ElemeFE/element repository explicitly states at README.md line 43 that Element UI only supports Vue 2 and recommends Element Plus for Vue 3 projects. The package.json file at line 73 confirms Vue 2 as a strict peer dependency, making Vue 3 compatibility technically impossible with Element UI.
Do existing v-model directives require syntax changes?
No. The template syntax v-model="value" works identically in both libraries because Vue 3's compiler automatically transforms the syntax to use modelValue and update:modelValue internally. You only need to update manual bindings where you explicitly use :value and @input, changing them to :model-value and @update:modelValue respectively.
How are custom themes migrated from Element UI to Element Plus?
Element Plus replaces the theme-chalk system with CSS variables and SCSS-based theming. Update your imports from element-ui/lib/theme-chalk/index.css to element-plus/dist/index.css, then configure custom themes using CSS variables or the SCSS variable maps provided by Element Plus instead of the legacy theme customization approach.
Are TypeScript definitions included in the Element Plus package?
Yes. Unlike Element UI which required separate type definitions in types/element-ui.d.ts, Element Plus bundles TypeScript definitions automatically with the package. After installing element-plus, you can import component types directly (for example, import type { ElButton } from 'element-plus') without additional @types packages or manual configuration files.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →