DEV Community

Cover image for Create a Simple Dark Mode Toggle With Svelte
Lena Schnedlitz
Lena Schnedlitz

Posted on • Edited on • Originally published at lenaschnedlitz.com

Create a Simple Dark Mode Toggle With Svelte

Note: While this tutorial is focused on Svelte, it should be possible to make it work with other UI frameworks as well. Feel free to give it a try!

You might have noticed that light/dark mode toggles are everywhere right now - pretty much every popular website allows users to choose which theme they prefer. So how do you add a theme toggle to your Svelte app?

Luckily, its pretty easy - here's what you need to do:

1. Add a Toggle Component

Create a new component, DarkModeToggle.svelte, and add a new checkbox inside it:

<input type="checkbox" on:click={toggleTheme} />
Enter fullscreen mode Exit fullscreen mode

Checkboxes are pretty good choices for toggles like this one because they are widely supported and represent Boolean states. In our case, false represents "light off" (= "dark"), while true represents "on".

Append the new component to your app.

2. Toggle Themes

Next, we need to implement the toggleTheme handler. Add a script tag to your component:

<script>
  const STORAGE_KEY = 'theme';
  const DARK_PREFERENCE = '(prefers-color-scheme: dark)';

  const THEMES = {
    DARK: 'dark',
    LIGHT: 'light',
  };

  const prefersDarkThemes = () => window.matchMedia(DARK_PREFERENCE).matches;

  const toggleTheme = () => {
    const stored = localStorage.getItem(STORAGE_KEY);

    if (stored) {
      // clear storage
      localStorage.removeItem(STORAGE_KEY);
    } else {
      // store opposite of preference
      localStorage.setItem(STORAGE_KEY, prefersDarkThemes() ? THEMES.LIGHT : THEMES.DARK);
    }

    // TODO: apply new theme
  };
</script>

...
Enter fullscreen mode Exit fullscreen mode

As you can see, there's quite a lot going on in here:

  • We use localStorage to store a user's theme preference.
  • We use a media query to figure out whether the user has a theme preference set in their OS.
  • If the user generally prefers dark themes and also picks the dark theme on our website, we do not need to store a preference. The same is true for users who do not use dark themes both generally and on our site. For all others, we store their preference in their localStorage.

3. Apply the Theme

Finally, we need to apply the new theme. Add another function to your file and call it at the end of toggleTheme:

  const applyTheme = () => {
    const preferredTheme = prefersDarkThemes() ? THEMES.DARK : THEMES.LIGHT;
    currentTheme = localStorage.getItem(STORAGE_KEY) ?? preferredTheme;

    if (currentTheme === THEMES.DARK) {
      document.body.classList.remove(THEMES.LIGHT);
      document.body.classList.add(THEMES.DARK);
    } else {
      document.body.classList.remove(THEMES.DARK);
      document.body.classList.add(THEMES.LIGHT);
    }
  };

  const toggleTheme = () => {
    ...

    applyTheme();
  };
Enter fullscreen mode Exit fullscreen mode

Looks good so far! However, we still don't handle some edge cases:

  • How do we apply the initial theme when a user first visits our website?
  • What happens when a user changes their system wide theme preference?

We can take care of both situations in onMount:

  import { onMount } from 'svelte';

  ...

  onMount(() => {
    applyTheme();
    window.matchMedia(DARK_PREFERENCE).addEventListener('change', applyTheme);
  });
Enter fullscreen mode Exit fullscreen mode

Last but not least, adapt the checkbox so it's state matches the theme:

  <input type="checkbox" checked={currentTheme !== THEMES.DARK} on:click={toggleTheme} />
Enter fullscreen mode Exit fullscreen mode

Now, all that's left to do is:

4. Write Custom CSS

We can now write custom styles for dark mode:

@media (prefers-color-scheme: dark) {
  body:not(.light) {
    /* Your stuff here... */
  }
}

body.dark {
  /* And also here... */
}
Enter fullscreen mode Exit fullscreen mode

Aaaaand we're done! ๐ŸŽ‰

If you are interested in a fully working example, check out my webapp Manicure (source code). It might also give you some ideas how to style the toggle.

Top comments (4)

Collapse
 
alexstaroselsky profile image
Alexander Staroselsky

Great article. Iโ€™d recommend to remove the event listener when the component is destroyed. This can be done in a returned function in the onMount.

Collapse
 
lenaschnedlitz profile image
Lena Schnedlitz

Thanks for the hint! I'll update it ๐Ÿ˜ƒ

Collapse
 
jonnyhoff profile image
Jonathan Hoffman • Edited

Great article. I recommend you add the file names that you put the code in.

Collapse
 
wesleymutwiri profile image
Wesley Mutwiri

Great article. I think in the apply theme function snippet you have repeated the declaration for the current theme variable.

Also, thank you very much, your article has helped me a lot