# How to Migrate from Element UI to Element Plus for Vue 3 Compatibility

> Migrate from Element UI to Element Plus for Vue 3 compatibility. Learn to replace packages, update APIs, and adjust CSS imports for a seamless transition.

- Repository: [饿了么前端/element](https://github.com/ElemeFE/element)
- Tags: migration-guide
- Published: 2026-03-07

---

**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`](https://github.com/ElemeFE/element/blob/main/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`](https://github.com/ElemeFE/element/blob/main/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`](https://github.com/ElemeFE/element/blob/main/package.json) at line 73 explicitly depends on Vue 2 (`"vue": "^2.5.17"`). First upgrade your Vue version, then replace the UI library.

```bash

# 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`](https://github.com/ElemeFE/element/blob/main/main.js) or [`main.ts`](https://github.com/ElemeFE/element/blob/main/main.ts) file.

**Element UI (Vue 2) implementation:**

```javascript
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:**

```javascript
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:**

```html
<!-- 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.

```javascript
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.

```javascript
// 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`](https://github.com/ElemeFE/element/blob/main/types/element-ui.d.ts) as referenced in the source repository. Element Plus bundles TypeScript definitions automatically, eliminating the need for separate type installations.

```bash

# Types are included automatically

npm install element-plus

```

Update your imports to use the bundled types:

```typescript
import { ElButton } from 'element-plus'

```

## Complete Migration Example

**Original Vue 2 form with Element UI:**

```javascript
// 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) })

```

```html
<!-- 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:**

```javascript
// 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')

```

```html
<!-- 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`](https://github.com/ElemeFE/element/blob/main/README.md)** (line 43): Explicitly recommends Element Plus for Vue 3 projects instead of Element UI
- **[`package.json`](https://github.com/ElemeFE/element/blob/main/package.json)** (line 73): Confirms the Vue 2 peer dependency constraint that prevents Vue 3 usage
- **[`types/element-ui.d.ts`](https://github.com/ElemeFE/element/blob/main/types/element-ui.d.ts)**: Contains legacy TypeScript definitions that are replaced by Element Plus's bundled types
- **[`CHANGELOG.fr-FR.md`](https://github.com/ElemeFE/element/blob/main/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`](https://github.com/ElemeFE/element/blob/main/element-ui/lib/theme-chalk/index.css) to [`element-plus/dist/index.css`](https://github.com/ElemeFE/element/blob/main/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`](https://github.com/ElemeFE/element/blob/main/README.md) line 43 that Element UI only supports Vue 2 and recommends Element Plus for Vue 3 projects. The [`package.json`](https://github.com/ElemeFE/element/blob/main/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`](https://github.com/ElemeFE/element/blob/main/element-ui/lib/theme-chalk/index.css) to [`element-plus/dist/index.css`](https://github.com/ElemeFE/element/blob/main/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`](https://github.com/ElemeFE/element/blob/main/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.