Skip to content

Menu ​

Overview ​

The Menu component is used to create navigation menus. It supports vertical and horizontal layouts, multi-level nesting, and various interactive features.

Basic Usage ​

Vertical Menu ​

vue
<template>
  <el-menu
    default-active="2"
    class="el-menu-vertical-demo"
    @open="handleOpen"
    @close="handleClose"
  >
    <el-sub-menu index="1">
      <template #title>
        <el-icon><Location /></el-icon>
        <span>Navigator One</span>
      </template>
      <el-menu-item-group title="Group One">
        <el-menu-item index="1-1">item one</el-menu-item>
        <el-menu-item index="1-2">item two</el-menu-item>
      </el-menu-item-group>
      <el-menu-item-group title="Group Two">
        <el-menu-item index="1-3">item three</el-menu-item>
      </el-menu-item-group>
      <el-sub-menu index="1-4">
        <template #title>item four</template>
        <el-menu-item index="1-4-1">item one</el-menu-item>
      </el-sub-menu>
    </el-sub-menu>
    <el-menu-item index="2">
      <el-icon><Icon /></el-icon>
      <span>Navigator Two</span>
    </el-menu-item>
    <el-menu-item index="3" disabled>
      <el-icon><Document /></el-icon>
      <span>Navigator Three</span>
    </el-menu-item>
    <el-menu-item index="4">
      <el-icon><Setting /></el-icon>
      <span>Navigator Four</span>
    </el-menu-item>
  </el-menu>
</template>

<script setup>
import {
  Document,
  Menu as Icon,
  Location,
  Setting
} from '@element-plus/icons-vue'

const handleOpen = (key, keyPath) => {
  console.log(key, keyPath)
}

const handleClose = (key, keyPath) => {
  console.log(key, keyPath)
}
</script>

Horizontal Menu ​

vue
<template>
  <el-menu
    :default-active="activeIndex"
    class="el-menu-demo"
    mode="horizontal"
    @select="handleSelect"
  >
    <el-menu-item index="1">Processing Center</el-menu-item>
    <el-sub-menu index="2">
      <template #title>Workspace</template>
      <el-menu-item index="2-1">item one</el-menu-item>
      <el-menu-item index="2-2">item two</el-menu-item>
      <el-menu-item index="2-3">item three</el-menu-item>
      <el-sub-menu index="2-4">
        <template #title>item four</template>
        <el-menu-item index="2-4-1">item one</el-menu-item>
        <el-menu-item index="2-4-2">item two</el-menu-item>
        <el-menu-item index="2-4-3">item three</el-menu-item>
      </el-sub-menu>
    </el-sub-menu>
    <el-menu-item index="3" disabled>Info</el-menu-item>
    <el-menu-item index="4">
      <a href="https://www.ele.me" target="_blank">Orders</a>
    </el-menu-item>
  </el-menu>
  <div class="line"></div>
  <el-menu
    :default-active="activeIndex2"
    class="el-menu-demo"
    mode="horizontal"
    @select="handleSelect"
    background-color="#545c64"
    text-color="#fff"
    active-text-color="#ffd04b"
  >
    <el-menu-item index="1">Processing Center</el-menu-item>
    <el-sub-menu index="2">
      <template #title>Workspace</template>
      <el-menu-item index="2-1">item one</el-menu-item>
      <el-menu-item index="2-2">item two</el-menu-item>
      <el-menu-item index="2-3">item three</el-menu-item>
      <el-sub-menu index="2-4">
        <template #title>item four</template>
        <el-menu-item index="2-4-1">item one</el-menu-item>
        <el-menu-item index="2-4-2">item two</el-menu-item>
        <el-menu-item index="2-4-3">item three</el-menu-item>
      </el-sub-menu>
    </el-sub-menu>
    <el-menu-item index="3" disabled>Info</el-menu-item>
    <el-menu-item index="4">
      <a href="https://www.ele.me" target="_blank">Orders</a>
    </el-menu-item>
  </el-menu>
</template>

<script setup>
import { ref } from 'vue'

const activeIndex = ref('1')
const activeIndex2 = ref('1')

const handleSelect = (key, keyPath) => {
  console.log(key, keyPath)
}
</script>

<style>
.el-menu-demo {
  border-bottom: solid 1px var(--el-menu-border-color);
}
.line {
  height: 1px;
  background-color: var(--el-border-color);
  margin: 35px 0;
}
</style>

Collapsed Menu ​

vue
<template>
  <el-radio-group v-model="isCollapse" style="margin-bottom: 20px">
    <el-radio-button :label="false">expand</el-radio-button>
    <el-radio-button :label="true">collapse</el-radio-button>
  </el-radio-group>
  <el-menu
    default-active="2"
    class="el-menu-vertical-demo"
    :collapse="isCollapse"
    @open="handleOpen"
    @close="handleClose"
  >
    <el-menu-item index="1">
      <el-icon><House /></el-icon>
      <template #title>Navigator One</template>
    </el-menu-item>
    <el-sub-menu index="2">
      <template #title>
        <el-icon><Location /></el-icon>
        <span>Navigator Two</span>
      </template>
      <el-menu-item index="2-1">item one</el-menu-item>
      <el-menu-item index="2-2">item two</el-menu-item>
      <el-menu-item index="2-3">item three</el-menu-item>
      <el-sub-menu index="2-4">
        <template #title>item four</template>
        <el-menu-item index="2-4-1">item one</el-menu-item>
        <el-menu-item index="2-4-2">item two</el-menu-item>
        <el-menu-item index="2-4-3">item three</el-menu-item>
      </el-sub-menu>
    </el-sub-menu>
    <el-menu-item index="3">
      <el-icon><Document /></el-icon>
      <template #title>Navigator Three</template>
    </el-menu-item>
    <el-menu-item index="4">
      <el-icon><Setting /></el-icon>
      <template #title>Navigator Four</template>
    </el-menu-item>
  </el-menu>
</template>

<script setup>
import { ref } from 'vue'
import {
  Document,
  House,
  Location,
  Setting
} from '@element-plus/icons-vue'

const isCollapse = ref(true)

const handleOpen = (key, keyPath) => {
  console.log(key, keyPath)
}

const handleClose = (key, keyPath) => {
  console.log(key, keyPath)
}
</script>

<style>
.el-menu-vertical-demo:not(.el-menu--collapse) {
  width: 200px;
  min-height: 400px;
}
</style>

Router Integration ​

vue
<template>
  <el-menu
    :default-active="$route.path"
    class="el-menu-vertical-demo"
    router
  >
    <el-menu-item index="/home">
      <el-icon><House /></el-icon>
      <template #title>Home</template>
    </el-menu-item>
    <el-sub-menu index="/products">
      <template #title>
        <el-icon><Goods /></el-icon>
        <span>Products</span>
      </template>
      <el-menu-item index="/products/list">Product List</el-menu-item>
      <el-menu-item index="/products/categories">Categories</el-menu-item>
      <el-menu-item index="/products/inventory">Inventory</el-menu-item>
    </el-sub-menu>
    <el-menu-item index="/orders">
      <el-icon><Document /></el-icon>
      <template #title>Orders</template>
    </el-menu-item>
    <el-menu-item index="/settings">
      <el-icon><Setting /></el-icon>
      <template #title>Settings</template>
    </el-menu-item>
  </el-menu>
</template>

<script setup>
import {
  Document,
  Goods,
  House,
  Setting
} from '@element-plus/icons-vue'
</script>

Dynamic Menu ​

vue
<template>
  <el-menu
    :default-active="activeMenu"
    class="el-menu-vertical-demo"
    @select="handleMenuSelect"
  >
    <template v-for="item in menuItems" :key="item.index">
      <el-sub-menu v-if="item.children" :index="item.index">
        <template #title>
          <el-icon><component :is="item.icon" /></el-icon>
          <span>{{ item.title }}</span>
        </template>
        <el-menu-item
          v-for="child in item.children"
          :key="child.index"
          :index="child.index"
        >
          {{ child.title }}
        </el-menu-item>
      </el-sub-menu>
      <el-menu-item v-else :index="item.index" :disabled="item.disabled">
        <el-icon><component :is="item.icon" /></el-icon>
        <template #title>{{ item.title }}</template>
      </el-menu-item>
    </template>
  </el-menu>
</template>

<script setup>
import { ref } from 'vue'
import {
  Document,
  House,
  Location,
  Setting,
  User
} from '@element-plus/icons-vue'

const activeMenu = ref('1')

const menuItems = ref([
  {
    index: '1',
    title: 'Dashboard',
    icon: House
  },
  {
    index: '2',
    title: 'User Management',
    icon: User,
    children: [
      { index: '2-1', title: 'User List' },
      { index: '2-2', title: 'User Roles' },
      { index: '2-3', title: 'Permissions' }
    ]
  },
  {
    index: '3',
    title: 'Content Management',
    icon: Document,
    children: [
      { index: '3-1', title: 'Articles' },
      { index: '3-2', title: 'Categories' },
      { index: '3-3', title: 'Tags' }
    ]
  },
  {
    index: '4',
    title: 'System Settings',
    icon: Setting,
    disabled: false
  },
  {
    index: '5',
    title: 'Analytics',
    icon: Location,
    disabled: true
  }
])

const handleMenuSelect = (index, indexPath) => {
  console.log('Selected:', index, indexPath)
  activeMenu.value = index
}
</script>
vue
<template>
  <el-menu
    default-active="1"
    class="el-menu-vertical-demo"
  >
    <el-menu-item index="1">
      <el-icon><House /></el-icon>
      <template #title>
        <span>Dashboard</span>
      </template>
    </el-menu-item>
    
    <el-menu-item index="2">
      <el-icon><Message /></el-icon>
      <template #title>
        <span>Messages</span>
        <el-badge :value="12" class="menu-badge" />
      </template>
    </el-menu-item>
    
    <el-menu-item index="3">
      <el-icon><Bell /></el-icon>
      <template #title>
        <span>Notifications</span>
        <el-badge :value="3" type="danger" class="menu-badge" />
      </template>
    </el-menu-item>
    
    <el-sub-menu index="4">
      <template #title>
        <el-icon><Document /></el-icon>
        <span>Orders</span>
        <el-badge :value="newOrdersCount" type="warning" class="menu-badge" />
      </template>
      <el-menu-item index="4-1">Pending Orders</el-menu-item>
      <el-menu-item index="4-2">Completed Orders</el-menu-item>
      <el-menu-item index="4-3">Cancelled Orders</el-menu-item>
    </el-sub-menu>
  </el-menu>
</template>

<script setup>
import { ref } from 'vue'
import {
  Bell,
  Document,
  House,
  Message
} from '@element-plus/icons-vue'

const newOrdersCount = ref(5)
</script>

<style>
.menu-badge {
  margin-left: auto;
}
</style>
AttributeDescriptionTypeDefault
modeMenu display modestringvertical
collapseWhether the menu is collapsed (available only in vertical mode)booleanfalse
background-colorBackground color of Menustring#ffffff
text-colorText color of Menustring#303133
active-text-colorText color of currently active menu itemstring#409EFF
default-activeIndex of currently active menu itemstring—
default-openedsArray that contains indexes of currently opened sub-menusArray—
unique-openedWhether only one sub-menu can be activebooleanfalse
menu-triggerHow sub-menus are triggered, only works when mode is 'horizontal'stringhover
routerWhether vue-router mode is activatedbooleanfalse
collapse-transitionWhether to enable the collapse transitionbooleantrue
EventDescriptionParameters
selectCallback function when menu item is selectedindex: index of activated menu item, indexPath: index path of activated menu item, item: the selected menu item, routeResult: result returned by vue-router if router is enabled
openCallback function when sub-menu expandsindex: index of expanded sub-menu, indexPath: index path of expanded sub-menu
closeCallback function when sub-menu collapsesindex: index of collapsed sub-menu, indexPath: index path of collapsed sub-menu
MethodDescriptionParameters
openOpen a specific sub-menuindex: index of the sub-menu to open
closeClose a specific sub-menuindex: index of the sub-menu to close
AttributeDescriptionTypeDefault
indexUnique identificationstring—
popper-classCustom class name for the popup menustring—
show-timeoutTimeout before showing a sub-menunumber300
hide-timeoutTimeout before hiding a sub-menunumber300
disabledWhether the sub-menu is disabledbooleanfalse
popper-append-to-bodyWhether to append the popup menu to bodybooleanlevel one Submenu: true / other Submenus: false
AttributeDescriptionTypeDefault
indexUnique identificationstring/null—
routeVue Router objectobject—
disabledWhether disabledbooleanfalse
AttributeDescriptionTypeDefault
titleGroup titlestring—
NameDescription
—Content of SubMenu
titleContent of SubMenu title
NameDescription
—Content of Menu Item
titleContent of Menu Item title

Best Practices ​

  1. Navigation Structure: Keep menu hierarchy logical and not too deep
  2. Visual Feedback: Provide clear active states and hover effects
  3. Accessibility: Ensure keyboard navigation and screen reader support
  4. Performance: Use dynamic loading for large menu structures
  5. Responsive Design: Consider collapsible menus for mobile devices
  6. Consistency: Maintain consistent styling and behavior across the application

Common Patterns ​

Admin Sidebar Menu ​

vue
<template>
  <el-menu
    :default-active="currentRoute"
    class="admin-menu"
    :collapse="isCollapsed"
    router
  >
    <el-menu-item index="/dashboard">
      <el-icon><Odometer /></el-icon>
      <template #title>Dashboard</template>
    </el-menu-item>
    
    <el-sub-menu index="/users">
      <template #title>
        <el-icon><User /></el-icon>
        <span>User Management</span>
      </template>
      <el-menu-item index="/users/list">User List</el-menu-item>
      <el-menu-item index="/users/roles">Roles & Permissions</el-menu-item>
    </el-sub-menu>
    
    <el-sub-menu index="/content">
      <template #title>
        <el-icon><Document /></el-icon>
        <span>Content</span>
      </template>
      <el-menu-item index="/content/posts">Posts</el-menu-item>
      <el-menu-item index="/content/pages">Pages</el-menu-item>
      <el-menu-item index="/content/media">Media Library</el-menu-item>
    </el-sub-menu>
  </el-menu>
</template>

Top Navigation Menu ​

vue
<template>
  <el-menu
    :default-active="activeIndex"
    mode="horizontal"
    class="top-nav"
    router
  >
    <el-menu-item index="/">
      <img src="/logo.png" alt="Logo" class="logo" />
    </el-menu-item>
    
    <el-menu-item index="/products">Products</el-menu-item>
    <el-menu-item index="/solutions">Solutions</el-menu-item>
    <el-menu-item index="/pricing">Pricing</el-menu-item>
    <el-menu-item index="/support">Support</el-menu-item>
    
    <div class="flex-grow" />
    
    <el-sub-menu index="/account">
      <template #title>
        <el-avatar :size="32" :src="userAvatar" />
      </template>
      <el-menu-item index="/profile">Profile</el-menu-item>
      <el-menu-item index="/settings">Settings</el-menu-item>
      <el-menu-item index="/logout">Logout</el-menu-item>
    </el-sub-menu>
  </el-menu>
</template>

Element Plus Study Guide