--- name: state-management description: useState composable and SSR-friendly state management in Nuxt --- # State Management Nuxt provides `useState` for SSR-friendly reactive state that persists across components. ## useState SSR-safe replacement for `ref` that shares state across components: ```vue ``` ## Creating Shared State Define reusable state composables: ```ts // composables/useUser.ts export function useUser() { return useState('user', () => null) } export function useLocale() { return useState('locale', () => 'en') } ``` ```vue ``` ## Initializing State Use `callOnce` to initialize state with async data: ```vue ``` ## Best Practices ### ❌ Don't Define State Outside Setup ```ts // ❌ Wrong - causes memory leaks and shared state across requests export const globalState = ref({ user: null }) ``` ### ✅ Use useState Instead ```ts // ✅ Correct - SSR-safe export const useGlobalState = () => useState('global', () => ({ user: null })) ``` ## Clearing State ```ts // Clear specific state clearNuxtState('counter') // Clear multiple states clearNuxtState(['counter', 'user']) // Clear all state (use with caution) clearNuxtState() ``` ## With Pinia For complex state management, use Pinia: ```bash npx nuxi module add pinia ``` ```ts // stores/counter.ts export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, }), actions: { increment() { this.count++ }, }, }) ``` ```ts // stores/user.ts (Composition API style) export const useUserStore = defineStore('user', () => { const user = ref(null) const isLoggedIn = computed(() => !!user.value) async function login(credentials: Credentials) { user.value = await $fetch('/api/login', { method: 'POST', body: credentials, }) } return { user, isLoggedIn, login } }) ``` ```vue ``` ## Advanced: Locale Example ```ts // composables/useLocale.ts export function useLocale() { return useState('locale', () => useDefaultLocale().value) } export function useDefaultLocale(fallback = 'en-US') { const locale = ref(fallback) if (import.meta.server) { const reqLocale = useRequestHeaders()['accept-language']?.split(',')[0] if (reqLocale) locale.value = reqLocale } else if (import.meta.client) { const navLang = navigator.language if (navLang) locale.value = navLang } return locale } ``` ## State Serialization `useState` values are serialized to JSON. Avoid: - Functions - Classes - Symbols - Circular references ```ts // ❌ Won't work useState('fn', () => () => console.log('hi')) useState('instance', () => new MyClass()) // ✅ Works useState('data', () => ({ name: 'John', age: 30 })) useState('items', () => ['a', 'b', 'c']) ```