Implementing a global snackbar in Nuxt.js & Vuetify using Vuex
Software does things. And when it does them, we often want to tell users what has happened. For example, telling them that something has saved successfully, or that an error has occurred.
There are many ways to do this, but one of my favourites is the Material Design snackbar. This is a small message that appears temporarily at the bottom of the screen to tell users that something has happened.
This tutorial uses Nuxt.js and Vuetify, but it could work for any Vue.js application that uses Material Design, or could even be adapted to work with the snackbar equivalent in whatever UI toolkit you prefer.
If you want to look at the code for this, check out the open-all-hours repo on my GitHub.
What is Vuex?
The magick in this is Vuex. If you’re not familiar with Vuex, don’t worry: all will be revealed. Vuex is a way to manage state in a Vue.js application that transcends the state of individual components. A Vuex store allows you to query and modify global application state from within the code of any component.
Nuxt.js comes with built-in support for Vuex that is automatically enabled as soon as files are created in the store/ subdirectory of your project.
1. Bootstrapping your application
The quickest way to spin up a Nuxt.js application using Vuetify is to use the template. You should be able to get to the point where you have a Vuetify application running with just these steps:
vue init vuetifyjs/nuxt open-all-hours
yarn run dev
Visit http://localhost:3000/ and you should see a basic Material Design application running. Great! Now we can get started.
2. Creating a Snackbar component
Vuetify comes with a premade v-snackbar UI component, so let’s create our own component in components/Snackbar.vue that displays a message and has a boolean show that controls its visibility:
Now you can add it to your default.vue. I put it just inside the v-app. Don’t forget to import the component and add it to your list of components. See the finished file if you get stuck.
Try tweaking the values of message and show in the Vue devtools to test that it works as expected:
3. A Vuex store to hold the snackbar message
OK! So we’ve got our frontend working in just a few lines of code. Now we need to create an area in the Vuex store where we can put our globally accessible messages.
Create a file called store/snackbar.js and put the following in it:
This needs a little explaining, so I’ll break it down.
Nuxt allows you to write all the code for Vuex stores, but it also comes preconfigured for Vuex’s modules mode, which drastically cuts down the amount of code needed, and allows you to divide your store into semantic namespaces based on the filename. Calling the file ‘snackbar.js’ creates a Vuex module called snackbar.
When you want to change the state of the Vuex store, you don’t edit the existing state, you commit a new state on top. This means creating a new copy of the state object that then becomes the one in this.$store.state . There are many reasons for Vuex doing things this way (one particularly interesting one is time travel — you can easily “undo” changes to the Vuex store as many times as you like).
The way to commit a new state is to write a mutation. Mutations are methods that are passed the new copy of the existing state as their first parameter, and then other parameters as you like. All you have to do in your mutation method is change this new state object and then Vuex will commit the new state to the store when the method ends. Here we have a simple mutation setSnack that just sets the snack attribute to a passed parameter.
4. A page to test the store
OK. Now we’ve got a Vuex store for our snackbar, let’s check that it’s all working.
Create a new pages/snacktest.vue (and maybe put a link in the sidebar to it):
This is a really simple form that calls the snackTime method when its button is clicked.
There are two ways to call mutations on your store. You can call them directly, for example: this.$store.commit('snackbar/setSnack', 'some message'), or you can use Vuex’s mapMutations helper to convert them to methods in your current component, as we do in the example above.
After we set the snack, we ask the router to direct the user back to the homepage. That’s just so we can see the store is independent of page loads.
Open up Vue devtools, switch to the Vuex tab, and watch what happens to the store when you fill in the form and submit it:
You can see the snackbar/setSnack mutation appearing in the Vuex history and then on the right side you can see the new state of the Vuex store containing your new snack value.
5. Hooking the component and store together
OK! All that’s left is to hook these two pieces together.
What we’re going to do is have the Snackbar.vue component watch for changes to the snackbar.snack attribute in the store, using Vuex’s watch method. When the variable changes to something other than an empty string, we’ll copy it to the component’s own variable and show the snackbar. Finally, we’ll set the copy in the store back to an empty string: this is so that if we need to send the same message multiple times in a row, the attribute will still change and this code will still trigger.
Just add a created method that sets up the watcher. We use created here instead of mounted because it doesn’t need to be rerun if we leave this layout and come back to it.
And that should be all you need! Go back to your snacktest page and try clicking the button again. This time you should see your snackbar message pop up, persist across the page transition, and the Vuex store mutate back to an empty string ready for another message to be sent to it.
Now you’ve got your global snackbar, you can send messages to it from anywhere using the snackbar/setSnack mutation, as we did in the snacktest page.
This is a very simple introduction to managing global state in a Vue/Nuxt application using Vuex but obviously it can be much more complex than this! There are also features of Vuex that we haven’t gone into here, such as writing custom getters. The Vuex documentation is very good, so go have a read!
Feel free to clone the repo for this project and play around with it yourself.
If this article’s been helpful, please share it around, throw me a few claps, or let me know how you’re getting on in the comments.