Join the Newsletter!

Exclusive content & updates. No spam.

Skip to content

How to Persist User Data with LocalStorage in Vue

Published: at 

Introduction

When developing apps, there’s often a need to store data. Consider a simple scenario where your application features a dark mode, and users want to save their preferred setting. Most users might prefer dark mode, but some will want light mode. This raises the question: where should we store this preference? We could use an API with a backend to store the setting. For configurations that affect the client’s experience, persisting this data locally makes more sense. LocalStorage offers a straightforward solution. In this blog post, I’ll guide you through using LocalStorage in Vue and show you how to handle this data in an elegant and type-safe manner.

Understanding LocalStorage

LocalStorage is a web storage API that lets JavaScript sites store and access data directly in the browser indefinitely. This data remains saved across browser sessions. LocalStorage is straightforward, using a key-value store model where both the key and the value are strings.

Here’s how you can use LocalStorage:

Diagram that explains LocalStorage

Using LocalStorage for Dark Mode Settings

In Vue, you can use LocalStorage to save a user’s preference for dark mode in a component.

Picture that shows a button where user can toggle dark mode

<template>
  <button class="dark-mode-toggle" @click="toggleDarkMode">
    {{ isDarkMode ? 'Switch to Light Mode' : 'Switch to Dark Mode' }}
    <span class="icon" v-html="isDarkMode ? moonIcon : sunIcon" />
  </button>
</template>

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'

const isDarkMode = ref(JSON.parse(localStorage.getItem('darkMode') ?? 'false'))

const styleProperties = computed(() => ({
  '--background-color': isDarkMode.value ? '#333' : '#FFF',
  '--text-color': isDarkMode.value ? '#FFF' : '#333'
}))

const sunIcon = `<svg some svg </svg>`

const moonIcon = `<svg some svg </svg>`

function applyStyles () {
  for (const [key, value] of Object.entries(styleProperties.value)) {
    document.documentElement.style.setProperty(key, value)
  }
}

function toggleDarkMode () {
  isDarkMode.value = !isDarkMode.value
  localStorage.setItem('darkMode', JSON.stringify(isDarkMode.value))
  applyStyles()
}

// On component mount, apply the stored or default styles
onMounted(applyStyles)
</script>

<style scoped>
.dark-mode-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 20px;
  font-size: 16px;
  color: var(--text-color);
  background-color: var(--background-color);
  border: 1px solid var(--text-color);
  border-radius: 5px;
  cursor: pointer;
}

.icon {
  display: inline-block;
  margin-left: 10px;
}

:root {
  --background-color: #FFF;
  --text-color: #333;
}

body {
  background-color: var(--background-color);
  color: var(--text-color);
  transition: background-color 0.3s, color 0.3s;
}
</style>

Addressing Issues with Initial Implementation

The basic approach works well for simple cases, but larger applications face these key challenges:

  1. Type Safety and Key Validation: Always check and handle data from LocalStorage to prevent errors.
  2. Decoupling from LocalStorage: Avoid direct LocalStorage interactions in your components. Instead, use a utility service or state management for better code maintenance and testing.
  3. Error Handling: Manage exceptions like browser restrictions or storage limits properly as LocalStorage operations can fail.
  4. Synchronization Across Components: Use event-driven communication or shared state to keep all components updated with changes.
  5. Serialization Constraints: LocalStorage stores data as strings, making serialization and deserialization challenging with complex data types.

Solutions and Best Practices for LocalStorage

To overcome these challenges, consider these solutions:

// types/localStorageTypes.ts
export type UserSettings = {name: string}

export type LocalStorageValues = {
    darkMode: boolean,
    userSettings: UserSettings,
    lastLogin: Date,
}

export type LocalStorageKeys = keyof LocalStorageValues
// utils/LocalStorageHandler.ts
// import { LocalStorageKeys, LocalStorageValues } from '@/types/localStorageTypes';

export class LocalStorageHandler {
  static getItem<K extends LocalStorageKeys>(
    key: K
  ): LocalStorageValues[K] | null {
    try {
      const item = localStorage.getItem(key);
      return item ? JSON.parse(item) as LocalStorageValues[K] : null;
    } catch (error) {
      console.error(`Error retrieving item from localStorage: ${error}`);
      return null;
    }
  }

  static setItem<K extends LocalStorageKeys>(
    key: K,
    value: LocalStorageValues[K]
  ): void {
    try {
      const item = JSON.stringify(value);
      localStorage.setItem(key, item);
    } catch (error) {
      console.error(`Error setting item in localStorage: ${error}`);
    }
  }

  static removeItem(key: LocalStorageKeys): void {
    localStorage.removeItem(key);
  }

  static clear(): void {
    localStorage.clear();
  }
}
// composables/useDarkMode.ts
import { ref, watch } from 'vue';
import { LocalStorageHandler } from './LocalStorageHandler';

export function useDarkMode() {
    const isDarkMode = ref(LocalStorageHandler.getItem('darkMode') ?? false);

    watch(isDarkMode, (newValue) => {
        LocalStorageHandler.setItem('darkMode', newValue);
    });

    return { isDarkMode };
}

Diagram that shows how component and localStorage work together

You can check the full refactored example out here

Play with Vue on Vue Playground

Conclusion

This post explained the effective use of LocalStorage in Vue to manage user settings such as dark mode. We covered its basic operations, addressed common issues, and provided solutions to ensure robust and efficient application development. With these strategies, developers can create more responsive applications that effectively meet user needs.

Stay Updated!

Subscribe to my newsletter for more TypeScript, Vue, and web dev insights directly in your inbox.

  • Background information about the articles
  • Weekly Summary of all the interesting blog posts that I read
  • Small tips and trick
Subscribe Now

Most Related Posts