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 UI
  • package.json (line 73): Confirms the Vue 2 peer dependency constraint that prevents Vue 3 usage
  • types/element-ui.d.ts: Contains legacy TypeScript definitions that are replaced by Element Plus's bundled types
  • CHANGELOG.fr-FR.md (line 898): Contains historical migration-related entries illustrating API evolution

Summary

  • Uninstall element-ui and install element-plus alongside Vue 3 dependencies
  • Replace Vue.use(Element) with app.use(ElementPlus) using the createApp API
  • Update CSS imports from element-ui/lib/theme-chalk/index.css to element-plus/dist/index.css
  • Adapt manual v-model bindings to use modelValue and update:modelValue instead of value and input
  • Switch icon usage from CSS classes to imported el-icon components
  • 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →