useState — Cross-Component State

A Ref Shared Across the App, SSR-Safe

useState — Cross-Component State

useState gives you a globally-named ref that's hydrated correctly between server and client, so you can share reactive state across pages and components without a separate store.

4 min read Level 2/5 #nuxt#state#useState
What you'll learn
  • Create a useState with a unique key and an initializer function
  • Share the same state across pages and components by reusing the key
  • Provide an SSR-safe initializer so server and client agree on the initial value

useState is Nuxt’s lightweight, SSR-safe answer to globally shared reactive state. Think of it as ref() with a string key — same key, same ref, anywhere in the app.

Basic Usage

<script setup lang="ts">
const counter = useState('counter', () => 0)
</script>

<template>
  <button @click="counter++">Count: {{ counter }}</button>
</template>

The first arg is a unique key. The second is a factory that returns the initial value. The factory runs once per request on the server, and the result is serialized into the page payload so the client picks up the same value.

Sharing Across Components

Two components calling useState('counter', ...) get the same underlying ref. The initializer is only used the first time; later calls ignore it.

<script setup lang="ts">
// components/CounterDisplay.vue
const counter = useState<number>('counter')
</script>

<template>
  <p>Current count: {{ counter }}</p>
</template>

Wrap It in a Composable

For typed access and a single source of truth, wrap useState in composables/:

// composables/useCounter.ts
export const useCounter = () => useState<number>('counter', () => 0)

Now any page just calls const counter = useCounter() — no need to remember the key string.

Pitfalls

  • Don’t initialize with non-serializable values (functions, class instances) — they won’t survive the server-to-client jump.
  • Keys must be unique across the app; collisions silently share state.
  • Prefer Pinia for complex state with actions and getters; useState shines for small, scattered values.
Pinia in Nuxt →