Skip to content

State Management ​

Overview ​

State management in Vue.js refers to the practice of centrally handling shared data (state) across multiple components, ensuring consistent and predictable behavior throughout an application.

Instead of passing props and emitting events between deeply nested components, a state management library Pinia provides a single source of truth for your app’s data. With Pinia, developers can easily store, update, and access reactive state in a clear and organized way. This leads to cleaner code, simpler debugging, and improved scalability as the application grows.

Enabling Pinia ​

Current application setup comes with prebuilt state management library. This section will show how to enhance Pinia stores with configurable persistence and rehydration.

  1. Install a Pinia plugin for persisted state.
bash
pnpm install @erlihs/pinia-plugin-storage
  1. Modify @/main.ts to add persist plugin to pinia.
ts
// ...
import { createPiniaPluginStorage } from '@erlihs/pinia-plugin-storage'
// ...
app.use(createPinia().use(createPiniaLocalStoragePlugin()))
// ...
  1. Create app store @/stores/app/settings.ts
ts
import { defineStore, acceptHMRUpdate } from 'pinia'
import { ref, watch } from 'vue'
import { useTheme } from 'vuetify'

export const useSettingsStore = defineStore(
  'settings',
  () => {
    const themeManager = useTheme()
    const themes = computed(() => Object.keys(themeManager.themes.value))
    const theme = ref(themeManager.global.name.value)
    function toggleTheme() {
      theme.value = theme.value === 'dark' ? 'light' : 'dark'
    }
    watch(theme, (newTheme) => {
      if (!themes.value.includes(newTheme)) {
        console.warn(`[Settings Store] Invalid theme: ${newTheme}`)
        const firstTheme = themes.value[0]
        if (firstTheme) {
          theme.value = firstTheme
        }
        return
      }
      themeManager.change(newTheme)
    })

    return { theme, themes, toggleTheme }
  },
  {
    storage: {
      adapter: 'localStorage',
      include: ['theme'],
    },
  } as Record<string, unknown>,
)

if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(useSettingsStore, import.meta.hot))
}
  1. Modify @/pages/sandbox/index.vue to use store for persisting theme - after closing and reopening browser theme is as it was set before
vue
<template>
  <v-card :style="cardBackground">
    <v-card-title><v-icon icon="$mdiHome" />{{ t('sandbox.title') }}</v-card-title>
    <v-card-text>{{ t('sandbox.description') }}</v-card-text>
    <v-card-actions>
      <v-btn @click="console.log('Primary!')" color="primary">Primary</v-btn>
      <v-btn @click="console.log('Secondary!')" color="secondary">Secondary</v-btn>
      <v-btn @click="settings.toggleTheme()">Toggle theme</v-btn>
    </v-card-actions>
  </v-card>
</template>

<script setup lang="ts">
import { useCardBackground } from '@/composables/ui'
import { useI18n } from 'vue-i18n'
import { useSettingsStore } from '@/stores/app/settings'

const cardBackground = useCardBackground('#0000ff')
const { t } = useI18n()
const settings = useSettingsStore()
</script>
  1. Test

If toggling theme, it will change in localStorage and will persist after page reload.