Fix: Cookie Was Previously Set to Different Value in Nuxt

Error message:
Cookie `{cookie.name}` was previously set to `{cookie.value}` and will be overwritten.
cookies 2025-01-25

What Causes This Warning?

This warning appears when you’re setting a cookie to a new value that differs from what was previously set during the same request. This often indicates conflicting cookie writes in your application.

The Problem

<script setup>
// ❌ Multiple components/composables setting same cookie
const userPref = useCookie('theme')
userPref.value = 'dark'

// Somewhere else in the same request...
const themeCookie = useCookie('theme')
themeCookie.value = 'light'  // Warning: overwrites 'dark'
</script>

The Fix

// composables/useTheme.ts
export function useTheme() {
  // ✅ Single source of truth for theme cookie
  const theme = useCookie('theme', {
    default: () => 'light'
  })

  function setTheme(value: 'light' | 'dark') {
    theme.value = value
  }

  return {
    theme: readonly(theme),
    setTheme
  }
}
<script setup>
// ✅ Use centralized composable everywhere
const { theme, setTheme } = useTheme()

// All components use the same reference
setTheme('dark')
</script>

Use Shared State

// composables/useCookieState.ts
const themeState = ref<string | null>(null)

export function useThemeCookie() {
  const cookie = useCookie('theme')

  // Initialize from cookie if not set
  if (themeState.value === null) {
    themeState.value = cookie.value || 'light'
  }

  // Sync state to cookie
  watch(themeState, (newValue) => {
    cookie.value = newValue
  })

  return themeState
}

Common Scenarios

<!-- Header.vue -->
<script setup>
// ❌ Each component creates its own reference
const theme = useCookie('theme')
theme.value = 'dark'
</script>

<!-- Settings.vue -->
<script setup>
// ❌ Conflicts with Header.vue
const theme = useCookie('theme')
theme.value = 'light'
</script>

Solution:

// composables/useTheme.ts
export const useTheme = () => {
  const theme = useCookie('theme', { default: () => 'light' })
  return { theme }
}
<!-- Both components use same composable -->
<script setup>
const { theme } = useTheme()
// Both reference the same cookie instance
</script>

Server vs Client Mismatch

<script setup>
const cookie = useCookie('session')

// ❌ Setting different values on server vs client
if (process.server) {
  cookie.value = 'server-value'
}

if (process.client) {
  cookie.value = 'client-value'  // Warning on hydration
}
</script>

Solution:

<script setup>
const cookie = useCookie('session')

// ✅ Set once, consistently
onMounted(() => {
  // Only modify on client after hydration
  if (!cookie.value) {
    cookie.value = 'initial-value'
  }
})
</script>
// plugins/auth.ts
export default defineNuxtPlugin(() => {
  const token = useCookie('auth-token')

  // ❌ May conflict with component code
  if (!token.value) {
    token.value = 'default'
  }
})

Solution:

// plugins/auth.ts
export default defineNuxtPlugin(() => {
  // ✅ Only read in plugin, let components write
  const token = useCookie('auth-token')

  return {
    provide: {
      authToken: readonly(token)
    }
  }
})

Middleware Conflicts

// middleware/auth.ts
export default defineNuxtRouteMiddleware(() => {
  const session = useCookie('session')

  // ❌ May conflict with page-level cookie access
  session.value = generateSessionId()
})

Solution:

// middleware/auth.ts
export default defineNuxtRouteMiddleware(() => {
  const session = useCookie('session')

  // ✅ Only set if not already set
  if (!session.value) {
    session.value = generateSessionId()
  }
})

Best Practices

Single Writer Pattern

// composables/useAuth.ts
export function useAuth() {
  const tokenCookie = useCookie('auth-token', {
    maxAge: 60 * 60 * 24 * 7  // 7 days
  })

  const setToken = (token: string | null) => {
    tokenCookie.value = token
  }

  const clearToken = () => {
    tokenCookie.value = null
  }

  return {
    token: readonly(tokenCookie),
    setToken,
    clearToken
  }
}

// Usage everywhere
const { token, setToken } = useAuth()
setToken('new-token')  // Controlled access

Conditional Setting

<script setup>
const preferences = useCookie('preferences', {
  default: () => ({ theme: 'light', language: 'en' })
})

// ✅ Merge changes, don't overwrite entirely
function updatePreference(key: string, value: any) {
  preferences.value = {
    ...preferences.value,
    [key]: value
  }
}
</script>

Server-Side Only Writes

// server/api/login.post.ts
export default defineEventHandler(async (event) => {
  // ✅ Server-side cookie setting is safe
  setCookie(event, 'session', generateSessionId(), {
    httpOnly: true,
    secure: true
  })

  return { success: true }
})

Debugging

Find where cookies are being set:

// plugins/cookie-debug.ts
export default defineNuxtPlugin(() => {
  if (process.dev) {
    const originalUseCookie = useCookie

    // @ts-ignore - debugging only
    globalThis.useCookie = (name, options) => {
      console.log(`Cookie "${name}" accessed from:`, new Error().stack)
      return originalUseCookie(name, options)
    }
  }
})

Quick Checklist

  • Centralize cookie management in composables
  • Use single source of truth for each cookie
  • Avoid multiple writes to same cookie in one request
  • Use readonly() when exposing cookies
  • Check for server/client value mismatches
  • Conditional writes: only set if not already set