Responsive menu

for Vue.js

A renderless Vue component using Resize Observer to detect if menu items dont't fit its parent and moves them to a separate menu.

Key points

  • Nuxt/SSR support out of the box.
  • 🎨 No styling included, only exposes reactive state.
  • ️ Lightweight 1.14kb
  • 💪 Resize Observer for better performance.
  • 🔪 Set max amount of characters so extra items get cut off on initial render. Useful in SSR.

How to use (3 simple steps)

1). Install Responsive menu

yarn add vue-responsive-menu

2). Register as a Vue component

import VueResponsiveMenu from 'vue-responsive-menu'

export default {
  components: {
    VueResponsiveMenu
  }
}

3). Insert your menu inside the component

Pass your array of menu items in the component's :nav prop

The component will expose 2 new arrays in the default prop, 1 normal menu and 1 with the excess items.

<!-- Renderless component that exposes 2 arrays based on the array you pass in the nav prop. -->
<VueResponsiveMenu #default="{ menuItems, moreMenuItems}" :nav="mainMenu.items">

  <ul>
    <!-- Default menu -->
    <li v-for="item in menuItems" :key="item.id">
      <a :href="item.href">
        {{ item.name }}
      </a>
    </li>

    <!-- More menu with the items that didn't fit -->
    <li v-if="moreMenuItems.length">
      <button type="button">{{ menuItems.length === 0 ? '☰' : 'more ↓' }}</button>
      <ul>
        <li v-for="item in moreMenuItems" :key="item.id">
          <a :href="item.href">
            {{ item.name }}
          </a>
        </li>
      </ul>
    </li>
  </ul>

</VueResponsiveMenu>

Available options

Prop Type Default Description
:nav (required) Array of objects []
:maxCharacters Number | Boolean false
Label 'label' String

Key to read the menu item label.
Only needed if you enable maxCharacters.

offset 'number' 0

Adds x amount of pixels to the total width of menu items. This causes menu items to be moved more quickly to the more dropdown

Available events

Name Payload
@menu-resized number in pixels
@item-to-dropdown object From nav prop
@item-to-menu object From nav prop

Example with options

MainMenu.vue
<template>
  <!-- This will render max 35 characters counted from the name key in the nav array. In this case the first 5 menu items -->
  <VueResponsiveMenu
    #default="{ menuItems, moreMenuItems}" :maxCharacters="35" label="name" :nav="mainMenu.items">
      <!-- ... -->
  </VueResponsiveMenu>
</template>

<script>
  import VueResponsiveMenu from 'vue-responsive-menu'
  export default {
    components: {
      VueResponsiveMenu
    }
    data() {
      return {
        navigation: [
          { name: 'This', id: 1, link: '#1' },
          { name: 'is an', id: 2, link: '#2' },
          { name: 'example', id: 3, link: '#3' },
          { name: 'navigation', id: 4, link: '#4' },
          { name: 'with many', id: 5, link: '#5' },
          { name: 'many', id: 6, link: '#5' },
          { name: 'many', id: 7, link: '#5' },
          { name: 'many', id: 8, link: '#5' },
          { name: 'items', id: 9, link: '#6' }
        ]
      }
    }
  }
</script>