Creating dropdown menus is always tough, especially if we need to apply custom styling to it — the select
element is very limited in what it can do. Therefore, if we’re using Vue to build our apps, we can use some components to help make our lives easier.
In this article, we’ll look at how the Vue-Multiselect library can help improve our dropdown menus.
To get started, we can install Vue-Multiselect by running:
npm install vue-multiselect --save
We can also add the library via script tags and add the CSS that’s associated with the package:
<script src="https://unpkg.com/[email protected]"></script> <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/vue-multiselect.min.css">
Then, in our component, we can write:
<template> <div> <multiselect v-model="value" :options="options"></multiselect> <p>{{value}}</p> </div> </template> <script> import Multiselect from "vue-multiselect"; export default { components: { Multiselect }, data() { return { value: null, options: ["foo", "baz", "baz"] }; } }; </script> <style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
The Multiselect
component is registered to the component. We have the multiselect
component with v-model
to bind to the value
state. The options
prop is set to options
, which has an array of strings.
Because of this, the value displayed to the user will be the same as the selected value; we can see that from the <p>
element below the dropdown. Also note that we added the styles from the package with the style
tag.
If we want to display items to the user and keep the values different from what’s displayed, then we need to have an array of objects for options.
For instance, we write:
<template> <div> <multiselect track-by="name" label="name" v-model="value" :options="options"></multiselect> <p>{{value}}</p> </div> </template> <script> import Multiselect from "vue-multiselect"; export default { components: { Multiselect }, data() { return { value: null, options: [ { name: "Orange", value: "orange" }, { name: "Apple", value: "apple" }, { name: "Grape", value: "grape" } ] }; } }; </script> <style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
We display the values in the name
properties because we set label
to name
. Now when we select a value, we select the whole object, and value
is set to the object selected when we pick an item.
Search is available by default since the searchable
prop is set to true
by default. We can display custom text for the dropdown entries with the custom-label
prop, which we set to a function.
For instance, we can write:
<template> <div> <multiselect track-by="name" label="name" :custom-label="nameFormatter" v-model="value" :options="options" ></multiselect> <p>{{value}}</p> </div> </template> <script> import Multiselect from "vue-multiselect"; export default { components: { Multiselect }, data() { return { value: null, options: [ { name: "Orange", color: "orange", value: "orange" }, { name: "Apple", color: "red", value: "apple" }, { name: "Grape", color: "purple", value: "grape" } ] }; }, methods: { nameFormatter({ name, color }) { return `${name} - ${color}`; } } }; </script> <style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
Now we get what’s returned in the nameFormatter
displayed for each entry.
Vue-Multiselect also supports multiple selections. For example, we can write:
<template> <div> <multiselect track-by="name" label="name" v-model="value" :options="options" multiple></multiselect> <p>{{value}}</p> </div> </template> <script> import Multiselect from "vue-multiselect"; export default { components: { Multiselect }, data() { return { value: null, options: [ { name: "Orange", value: "orange" }, { name: "Apple", value: "apple" }, { name: "Grape", value: "grape" } ] }; } }; </script> <style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
We added multiple
to multiselect
to enable multiple selection. We can add text to display when something is selected by filling the selection
slot, like so:
<template> <div> <multiselect track-by="name" label="name" v-model="value" :options="options" multiple> <template slot="selection" slot-scope="{ values, search, isOpen }"> <span v-if="values.length">{{ values.length }} options selected</span> </template> </multiselect> <p>{{value}}</p> </div> </template> <script> import Multiselect from "vue-multiselect"; export default { components: { Multiselect }, data() { return { value: null, options: [ { name: "Orange", value: "orange" }, { name: "Apple", value: "apple" }, { name: "Grape", value: "grape" } ] }; } }; </script> <style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
The selection
slot has the values
property with the selected values. isOpen
indicates whether the menu is open or not, and search
has the search term.
We can also let users add tags with Vue-Multiselect.
To let users add tags, we can write the following:
<template> <div> <multiselect v-model="values" taggable @tag="addTag" :options="options" multiple></multiselect> <p>{{values}}</p> </div> </template> <script> import Multiselect from "vue-multiselect"; export default { components: { Multiselect }, data() { return { values: [], options: ["orange", "apple", "grape"] }; }, methods: { addTag(newTag) { this.options.push(newTag); this.values.push(newTag); } } }; </script> <style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
We add the taggable
prop to let users enter their own tags, and we listen to the tag
event emitted by multiselect
by running the addTag
method. It takes the newTag
parameter, which has the tag name.
In the method, we add the this.values
and this.options
so that the new tag can be added to the list of options and to the list of the selected values.
One great feature of Vue-Multiselect is that the dropdown items can contain text and images.
We can write:
<template> <div> <multiselect v-model="values" :options="options"> <template slot="singleLabel" slot-scope="props"> <img class="option-image" :src="props.option.img"> <div> <span>{{ props.option.title }}</span> </div> </template> <template slot="option" slot-scope="props"> <img class="option-image" :src="props.option.img"> <div> <span>{{ props.option.title }}</span> </div> </template> </multiselect> <p>{{values}}</p> </div> </template> <script> import Multiselect from "vue-multiselect"; export default { components: { Multiselect }, data() { return { values: [], options: [ { title: "orange", img: "https://secure.webtoolhub.com/static/resources/icons/set114/5cfa0390.png" }, { title: "apple", img: "https://images.squarespace-cdn.com/content/v1/56ed6e3b1bbee05366b9f7a5/1464743651591-TJG1VO66UK1GI9LJ5WDO/ke17ZwdGBToddI8pDm48kHhlTY0to_qtyxq77jLiHTtZw-zPPgdn4jUwVcJE1ZvWhcwhEtWJXoshNdA9f1qD7T-j82ScS_xjTqFYGqFrT72qZ_E0ELtHpOZiWcSG1QwIMeEVreGuQ8F95X5MZTW1Jw/lodi-apple.png?format=300w" }, { title: "grape", img: "https://icons.iconarchive.com/icons/martin-berube/food/256/grapes-icon.png" } ] }; } }; </script> <style src="vue-multiselect/dist/vue-multiselect.min.css"></style> <style> .option-image { width: 100px; } </style>
We populate the singleLabel
slot with an image and text for the dropdown entry. The option
slot is populated the same way for populating the dropdown options.
Options can be grouped instead of placing options all at the top level, like so:
<template> <div> <multiselect group-values="items" group-label="type" group-select v-model="value" :options="options" label="name" ></multiselect> <p>{{value}}</p> </div> </template> <script> import Multiselect from "vue-multiselect"; export default { components: { Multiselect }, data() { return { value: undefined, options: [ { type: "fruit", items: [{ name: "apple" }, { name: "orange" }] }, { type: "drink", items: [{ name: "beer" }, { name: "wine" }] } ] }; } }; </script> <style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
options
is an array of objects with a property for the group label, which, in our example, is type
. items
has the items in the dropdown groups.
group-values
is set to the items
property to use them as group items, and group-label
is set to type
to display as group headings. label
is set to the name
property to display that to the user.
Vue-Multiselect integrates with Vuex to let us store the choices in a Vuex store instead of the component.
For instance, we write:
main.js
import Vue from "vue"; import App from "./App.vue"; import Vuex from "vuex"; Vue.use(Vuex); const store = new Vuex.Store({ state: { value: "apple", options: ["apple", "orange", "grape"] }, mutations: { updateValue(state, value) { state.value = value; } }, actions: { updateValueAction({ commit }, value) { commit("updateValue", value); } } }); Vue.config.productionTip = false; new Vue({ store, render: h => h(App) }).$mount("#app");
App.vue
<template> <div> <multiselect :value="value" @input="updateValueAction" :options="options"></multiselect> <p>{{value}}</p> </div> </template> <script> import Multiselect from "vue-multiselect"; import Vuex from "vuex"; const { mapActions, mapState } = Vuex; export default { components: { Multiselect }, computed: { ...mapState(["value", "options"]) }, methods: { ...mapActions(["updateValueAction"]) } }; </script> <style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
In main.js
, we used the Vuex.Store
constructor to create the store with the value
and options
states. We have a mutation to update the value, which is used by the updateValueAction
to update the value
state, then we put the store
in the object we passed into the Vue
constructor.
In App.vue
, instead of binding our dropdown’s selected value with v-model
, we map the states with mapState
to get the states from the store. And we have mapActions
to map the updateValueAction
from the store to update the value.
We listen to the input
event to get the item and call updateValueAction
to update the value
state with in the Vuex store via the mutation. Also, we set the value of the value
prop from the store. The input
event and value
prop replace v-model
.
The options
are also set from the options
state from the Vuex store via the mapState
method.
Vue-Multiselect is a very flexible dropdown component to let us create dropdowns that can have pictures and dropdown items with formatting.
We can also group dropdown options and enable multiple selections and tagging. It has integration with Vuex allows us to get and set options and values from the store.
Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps, including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error and what state the application was in when an issue occurred.
Modernize how you debug your Vue apps — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
3 Replies to "Exploring Vue-Multiselect"
I was able to implement show surnames in bold (in dropdown) after going through this Thanks a lot for writing this.
I’ve tried this in my Laravel project, but the dropdown is not being displayed
How do you know about selection slot? There is not documentation for this slot on their official website.