Implementing Dark Mode with VueUse

The inclusion of dark color modes/themes has become quite common in modern web applications. Not only do dark modes look attractive on screen, but creating a dark mode/theme for your application can help improve readability, reduce eye strain, and even improve user experience.

In this article, we’ll be taking a look at how to add dark mode to a Vue application using the VueUse composable: useDark

The useDark Composable

useDark is a VueUse composable that provides a reactive dark mode that automatically persists inside session storage or local storage.

useDark first checks if there’s a value between session storage and local storage, and if there is none, it uses the system preference of the user.

Let’s go ahead and create an app that toggles between dark mode and light mode using useDark.


Project Setup

To get started, let’s create a simple Vue project with Vite.

Run the following command in your terminal:

yarn create vite vue-darkmode-app
# or with npm
npm create vite@latest vue-darkmode-app

Select Vue as your preferred framework, and install project dependencies in the project directory.

Install VueUse Library

Next, let’s make sure we have @vueuse/core installed in our project. We need this in order to use composables such as useDark and useToggle in our project.

Run the following command in your project’s root directory, in the terminal:

yarn add @vueuse/core
# or with npm
npm i @vueuse/core

Implementing Dark Mode

Now that we’re done with setting up, let’s go ahead and implement dark mode into our app.

Open up your App.vue file, delete the default starter code and import useDark

As earlier mentioned, useDark returns a reactive variable that checks the user’s session and local storage before resorting to the user’s system preferences for its value.

📁 src/App.vue

<script setup>
import { useDark } from "@vueuse/core";
const isDark = useDark();
</script>

<template>
  <p>Dark theme: {{isDark}}</p>
</template>

Run the server to view the app:

Demo of the browser displaying the system preferences

Whatever value is displayed on your screen at the moment is a result of your system preferences. The reason we’re seeing “false” in the screenshot above is because this system does not have a dark theme preference.

If you update your system preference to use a dark theme, the value of isDark would also update and “true” should display instead.

Now, let’s add a simple button that can toggle between light and dark mode. For this function, we’ll be using the VueUse composable useToggle, which takes in a boolean and switches its value.

Edit your App.vue file to look like this:

<script setup>
import { useDark, useToggle } from "@vueuse/core";

const isDark = useDark();
const toggleDark = useToggle(isDark);
</script>

<template>
  <p>Dark theme: {{isDark}}</p>

  <button @click="toggleDark()">
    Toggle Color Mode
  </button>

</template>

In this code, our useToggle function takes in isDark and changes its value every time our “Toggle Color Mode” button is clicked.

Now if you refresh your app, the value of isDark persists because useDark stores its value in the local storage.

useDark composable value in local storage


Styling the app

You may have noticed that adding useDark to our app and toggling between color modes didn’t change our application’s appearance. That is because useDark only handles the DOM attribute changes and does not affect the actual style, theme or CSS for you.

However, useDark makes it easy for us to update our styles depending on what color mode is selected by adding a “dark” class to our HTML element every time isDark is true.

To confirm this, we can open up the DOM tree in dev tools and watch as the class is being applied or removed when you click the button.

Now let’s improve our app’s appearance by adding a different style for our dark mode.

Open up your App.vue file and edit the style:

<script setup>
import { useDark, useToggle } from "@vueuse/core";

const isDark = useDark();
const toggleDark = useToggle(isDark);
</script>

<template>
  <p>Dark theme: {{isDark}}</p>

  <button @click="toggleDark()">
    Toggle Color Mode
  </button>
</template>

<style>
.dark {
  background: #16171d; // it's better to use a dark grey color instead of pure black
  color: #fff;
}
</style>

Now that we’ve done that, let’s go back to our app and take a look.


Customizing useDark

Another great thing about useDark is that it is completely customizable. You can choose to specify the selector, attribute, and value of dark and light modes in your app using four different properties.

Update useDark to look like this:

const isDark = useDark({
  selector: "body", //element to add attribute to
  attribute: "theme", // attribute name
  valueDark: "custom-dark", // attribute value for dark mode
  valueLight: "custom-light", // attribute value for light mode
});

This means you’ll also need to update your styles to look like this:

<style>
[theme="custom-dark"] {
  background: #16171d;
  color: #fff;
}
</style>

Everything should still work the same as before.

If this configuration isn’t enough for you, and you need even more customization, you can hook the onChanged event directly to take full control over how you handle the updates.

Example:

const isDark = useDark({
  onChanged(dark) {
    // update the dom or store user's preferences in a DB by making an API call
  },
})

Where to go from here

As you can see, useDark makes it really easy to include a dark mode for your app. If you would like the option to select different color modes aside from just “light” and “dark”, check out useColorMode from VueUse as well.

VueUse has a great collection of composables that make things like switching color modes an effortless task. Our article by Michael Thiessen goes through a list of some of the best VueUse composables being used in the development of modern web applications today.

If you want to learn how to craft your own composables from scratch, check out our Coding Better Composables course!

Download the cheatsheets

Save time and energy with our cheat sheets.