963 markdown files reformatted with the repository's pinned prettier so pnpm format:check covers the folded tree like every other repo file. The formatter's embedded-language pass also normalized code fences (TS semicolons, closed HTML tags in examples, lowercased CSS hex colors, one renumbered list that skipped an index). Alphanumeric token deltas vs the fold commit were audited file-by-file; all are formatter-equivalent markup normalizations plus the four sanitized skills.
3.7 KiB
3.7 KiB
name, description
| name | description |
|---|---|
| state-management | 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:
<script setup lang="ts">
// State is shared by key 'counter' across all components
const counter = useState('counter', () => 0);
</script>
<template>
<div>
Counter: {{ counter }}
<button @click="counter++">+</button>
<button @click="counter--">-</button>
</div>
</template>
Creating Shared State
Define reusable state composables:
// composables/useUser.ts
export function useUser() {
return useState<User | null>('user', () => null);
}
export function useLocale() {
return useState('locale', () => 'en');
}
<script setup lang="ts">
// Same state instance everywhere
const user = useUser();
const locale = useLocale();
</script>
Initializing State
Use callOnce to initialize state with async data:
<script setup lang="ts">
const config = useState('site-config');
await callOnce(async () => {
config.value = await $fetch('/api/config');
});
</script>
Best Practices
❌ Don't Define State Outside Setup
// ❌ Wrong - causes memory leaks and shared state across requests
export const globalState = ref({ user: null });
✅ Use useState Instead
// ✅ Correct - SSR-safe
export const useGlobalState = () => useState('global', () => ({ user: null }));
Clearing State
// 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:
npx nuxi module add pinia
// stores/counter.ts
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
actions: {
increment() {
this.count++;
},
},
});
// stores/user.ts (Composition API style)
export const useUserStore = defineStore('user', () => {
const user = ref<User | null>(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 };
});
<script setup lang="ts">
const counterStore = useCounterStore();
const userStore = useUserStore();
// Initialize store data once
await callOnce(async () => {
await userStore.fetchUser();
});
</script>
Advanced: Locale Example
// 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
// ❌ Won't work
useState('fn', () => () => console.log('hi'));
useState('instance', () => new MyClass());
// ✅ Works
useState('data', () => ({ name: 'John', age: 30 }));
useState('items', () => ['a', 'b', 'c']);