Fix: useState Init Must Be a Function in Nuxt

Error message:
[nuxt] [useState] init must be a function: {init}
state 2025-01-25

What Causes This Error?

This error occurs when you pass a value directly to useState instead of a function that returns the initial value.

The Problem

// ❌ Wrong - passing value directly
const count = useState('count', 0)
const user = useState('user', { name: 'John' })
const items = useState('items', [])

// ❌ Also wrong - passing result of function call
const data = useState('data', fetchData())

The Fix

// ✅ Correct - pass a function that returns the value
const count = useState('count', () => 0)
const user = useState('user', () => ({ name: 'John' }))
const items = useState('items', () => [])

// ✅ Correct - async initialization
const data = useState('data', () => null)
// Then fetch data separately

Why a Function?

Nuxt uses the function (called a “factory function”) because:

  1. SSR Safety - The function is called once per request on the server
  2. Lazy Initialization - Value is only computed when needed
  3. Fresh State - Each request gets fresh initial state
  4. Avoids Shared State - Prevents state leaking between requests

Common Patterns

Basic Types

// Numbers
const count = useState('count', () => 0)

// Strings
const name = useState('name', () => '')

// Booleans
const isOpen = useState('isOpen', () => false)

// Arrays
const items = useState('items', () => [])

// Objects
const user = useState('user', () => ({
  name: '',
  email: ''
}))

With TypeScript

// Typed state
const count = useState<number>('count', () => 0)
const user = useState<User | null>('user', () => null)
const items = useState<string[]>('items', () => [])

interface User {
  id: number
  name: string
}
const currentUser = useState<User>('currentUser', () => ({
  id: 0,
  name: ''
}))

Complex Initial Values

// Computed initial value
const config = useState('config', () => {
  return {
    theme: 'dark',
    language: 'en',
    notifications: true
  }
})

// Based on runtime config
const apiUrl = useState('apiUrl', () => {
  const config = useRuntimeConfig()
  return config.public.apiBase
})

Nullable State

// State that starts as null
const user = useState<User | null>('user', () => null)

// Check before using
if (user.value) {
  console.log(user.value.name)
}

useState Without Initial Value

If state might be set elsewhere, you can omit the init function:

// Just reading state (might be undefined)
const count = useState<number>('count')

// Setting state
count.value = 42

But it’s safer to always provide an initial value:

// Always provide default
const count = useState('count', () => 0)

Async Data with useState

Don’t fetch data inside useState. Use separate composables:

// ❌ Wrong - async in useState
const user = useState('user', async () => {
  return await $fetch('/api/user')  // This won't work as expected!
})

// ✅ Correct - separate concerns
const user = useState<User | null>('user', () => null)

// Fetch data with useAsyncData
const { data } = await useAsyncData('user', () => $fetch('/api/user'))

// Or in onMounted
onMounted(async () => {
  user.value = await $fetch('/api/user')
})

Shared Composables with useState

// composables/useCounter.ts
export function useCounter() {
  const count = useState('counter', () => 0)

  function increment() {
    count.value++
  }

  function decrement() {
    count.value--
  }

  return {
    count: readonly(count),
    increment,
    decrement
  }
}

Common Mistakes

Mistake 1: Passing Variable

const initialValue = 0

// ❌ Wrong
const count = useState('count', initialValue)

// ✅ Correct
const count = useState('count', () => initialValue)

Mistake 2: Passing Function Result

function getDefault() {
  return { name: 'John' }
}

// ❌ Wrong - passes the result
const user = useState('user', getDefault())

// ✅ Correct - passes the function
const user = useState('user', getDefault)
// or
const user = useState('user', () => getDefault())

Mistake 3: Arrow Function Without Parentheses for Object

// ❌ Wrong - arrow function body, not return
const user = useState('user', () => { name: 'John' })

// ✅ Correct - wrap object in parentheses
const user = useState('user', () => ({ name: 'John' }))

Quick Reference

UsageValid?
useState('key', 0)❌ No
useState('key', () => 0)✅ Yes
useState('key', [])❌ No
useState('key', () => [])✅ Yes
useState('key', {})❌ No
useState('key', () => ({}))✅ Yes
useState('key', getValue())❌ No
useState('key', getValue)✅ Yes
useState('key', () => getValue())✅ Yes