Dark Mode Toggle for Vue.js Apps with Vuetify

Here's a simple way to set Vuetify's dark mode state and make it user-changeable

Featured image for Dark Mode Toggle for Vue.js Apps with Vuetify

Photo: Material.io

I use Vue.js and Vuetify for almost all of my websites, and I’m a huge supporter of dark mode. One of the many reasons I chose Vuetify is because it has dark mode support out-of-the-box. So, without further ado, let me guide you through easily changing the dark mode state.

Setting the Default Dark Mode State

In order to set the default dark mode state, we have to open the plugin file for Vuetify, which is available at src/plugins/vuetify.js. By default, the file should look like the following.

import Vue from "vue";
import Vuetify from "vuetify/lib";

Vue.use(Vuetify);

export default new Vuetify({});

To set the default state, we have to create a new object in the constructor called theme, and inside that, set a variable called dark to true.

export default new Vuetify({
    theme: {
        dark: true
    }
});

But if we want to change it from the user-facing interface, we have to use the variable provided by Vuetify.

Setting the Dark Mode State From the Interface

A copy of the final code is available at the bottom.

Before even adding the theme state-changing code, you have to decide where to put the code. You only have to put it in one location, preferably a location that is persistent, such as your App.vue or a component that is present on all pages, such as a navigation bar. With that decided, we can actually get to work.

In your file (I’m using a component that I’ve called NavigationBar), go to the script tag. There should be an export statement present. If not, go ahead and create it. The contents of the script tag should look similar to this:

export default {
    name: "NavigationBar"
}

First, we need to add the method that will be called when the user clicks on a button. Underneath the name parameter, add a new object called methods.

export default {
    name: "NavigationBar",
    methods: {}
}

I’m going to call my method toggleDarkMode, but feel free to call it whatever you’d like. This method is going to set the dark mode variable (this.$vuetify.theme.dark) to the inverse of what it is currently set to (if the theme is currently light, then this variable will be false), then set a local storage variable called darkTheme to the value of that variable.

methods: {
    toggleDarkMode: function() {
        this.$vuetify.theme.dark = !this.$vuetify.theme.dark;
        localStorage.setItem("darkTheme", this.$vuetify.theme.dark.toString());
    }
}

With the function implemented, we now have to make it so that site will automatically pick up the theme state from the browser with the prefers-color-scheme CSS media query and/or the local storage state. The prefers-color-scheme state is set by your system.

To accomplish our task, we will use a Vue lifecycle hook called mounted which is called, as you may have guessed, when the component is mounted. We’ll add mounted() {} underneath the methods object.

export default {
    name: "NavigationBar",
    methods: { ... },
    mounted() {}
}

We will first check what the value of our local storage variable is. If it exists, this.$vuetify.theme.dark is set to the value of the variable. If it doesn’t, we’ll check whether the user has dark mode enabled on their system, and set it to that.

mounted() {
    const theme = localStorage.getItem("darkTheme");

    // Check if the user has set the theme state before
    if (theme) {
        if (theme === "true") {
            this.$vuetify.theme.dark = true;
        } else {
            this.$vuetify.theme.dark = false;
        }
    } else if (
        window.matchMedia &&
        window.matchMedia("(prefers-color-scheme: dark)").matches
    ) {
        this.$vuetify.theme.dark = true;
        localStorage.setItem(
            "darkTheme",
            this.$vuetify.theme.dark.toString()
        );
    }
}

All that’s left is to add a button to toggle the state. In the template tag, add the following:

<v-btn icon @click="toggleDarkMode">
    <v-icon>mdi-theme-light-dark</v-icon>
</v-btn>

The code above is simple. It creates a Vuetify icon button, tells it to use the theme-light-dark icon from Material Design Icons and to add an event handler, which on click, calls the toggleDarkMode method.

That’s it. You’re finished! As I mentioned earlier, the final code is available on this GitHub Gist.

Thanks for reading!