The icon system changed significantly between Element UI and Element Plus, with the latter adopting SVG icons. If you are accustomed to the Element UI aproach but prefer working with Vue 3, you can wrap Element Plus icons into a custom component that replicates the simpler interface of the older library.
Design Goals
The custom <e-icon> component should provide:
- A
typeproperty to select a specific SVG icon. - A
sizeproperty that accepts both numbers and strings. Element Plus only acceptsnumber, which forcesv-bind:size="20"or the shorthand:size="20". Supporting strings likesize="20"avoids the occasional mistake of forgetting thev-bind. - The
typeproperty should be case‑insensitive for the first character. Element Plus uses PascalCase for icon names, but short words are easy to mistype. - The
colorproperty works exactly as in Element Plus.
Example usage:
<e-icon type="Aim" :size="36" color="rgba(53, 131, 208, 0.5)" />
Note that the tag is not <el-icon>; the wrapper is named <e-icon> to emphasice its simplified nature.
Trade‑offs
The bundle size may increase slgihtly because all SVG icons must be imported.
Implementation
The development relies on:
- Component communication via props
- Dynamic components
- Full import of all Element Plus SVG icons
The component code is straightforward. Follow these steps:
- Import all SVG icons from
@element-plus/icons-vue. - Import the icon component CSS.
- Accept
size,color, andtypeprops. - Convert the
sizeprop to a number (Element Plus expects a number). - Ensure the first character of the
typestring is uppercase. - Dynamically resolve the icon component using bracket notation.
<template>
<ElIcon :size="normalizedSize" :color="color">
<component :is="resolvedIcon" />
</ElIcon>
</template>
<script>
export default {
name: 'EIcon'
}
</script>
<script setup>
import { computed } from 'vue'
import { ElIcon } from 'element-plus'
import * as Icons from '@element-plus/icons-vue'
import 'element-plus/es/components/icon/style/css'
const props = defineProps({
size: {
type: [Number, String],
default: 14,
validator(value) {
return !isNaN(value)
}
},
color: {
type: String,
default: 'inherit'
},
type: {
type: String,
default: ''
}
})
const normalizedSize = computed(() => {
if (isNaN(props.size)) {
return 20
}
return Number(props.size)
})
const resolvedIcon = computed(() => {
const rawType = props.type
if (rawType === '') return null
const corrected = rawType.charAt(0).toUpperCase() + rawType.slice(1)
return Icons[corrected] || null
})
</script>
Notice the separate <script> block that declares the component name – this is a common pattern even when using <script setup>.
Usage
Import and use the component in a Vue template:
<template>
<div>
<EIcon type="Aim" :size="36" color="rgba(53, 131, 208, 0.5)" />
</div>
</template>
<script setup>
import EIcon from '@/components/EIcon/index.vue'
</script>
Alternatively, register EIcon globally in main.js with app.component('EIcon', EIcon).