N
Nuxt2mo ago
Jakob

Cookie Reactivity Issue

Hi, I have this cookie defined in my composable:
export function useSession() {
const currentPortfolioId = useCookie<number | undefined>('currentPortfolio', {
maxAge: 60 * 60 * 24 * 14
});

return {
currentPortfolioId
}
}
export function useSession() {
const currentPortfolioId = useCookie<number | undefined>('currentPortfolio', {
maxAge: 60 * 60 * 24 * 14
});

return {
currentPortfolioId
}
}
And this is where it use the cookie:
const {currentPortfolioId} = useSession();

const {data: portfolioData} = useAPI<PortfolioInfo>(`/api/portfolio/${currentPortfolioId.value}`, {
server: false,
watch: [currentPortfolioId]
});
const {currentPortfolioId} = useSession();

const {data: portfolioData} = useAPI<PortfolioInfo>(`/api/portfolio/${currentPortfolioId.value}`, {
server: false,
watch: [currentPortfolioId]
});
When I change the value of my cookie, useAPI still uses the old value when refetching. After reloading, the correct value is used. Can anyone help me?
11 Replies
kapa.ai
kapa.ai2mo ago
Hi @Jakob. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- The issue you're experiencing is a common one when working with cookies and reactivity in Nuxt. The value returned by useCookie is reactive, but when the cookie value changes outside of Vue's reactivity system (for example, by another tab, or by direct document.cookie manipulation), the ref may not automatically update. This means watchers or composables like useAPI (or useFetch) may not see the new value until a reload. Solution: You need to manually refresh the cookie's value in your app when you know it has changed. Nuxt provides a utility called refreshCookie for this purpose. After updating the cookie, call refreshCookie with the cookie's name to update the ref and trigger reactivity:
import { refreshCookie } from '#app'

const currentPortfolioId = useCookie<number | undefined>('currentPortfolio', {
maxAge: 60 * 60 * 24 * 14
})

// When you change the cookie value:
currentPortfolioId.value = newId
refreshCookie('currentPortfolio')
import { refreshCookie } from '#app'

const currentPortfolioId = useCookie<number | undefined>('currentPortfolio', {
maxAge: 60 * 60 * 24 * 14
})

// When you change the cookie value:
currentPortfolioId.value = newId
refreshCookie('currentPortfolio')
This will ensure that any watchers or composables depending on currentPortfolioId will react to the new value immediately, and your useAPI call will use the updated value without needing a page reload. Reference:
See the official Nuxt documentation for more details and examples: refreshCookie and useCookie reactivity. If you want automatic updates when the cookie changes in the browser, you can also enable the experimental cookieStore option as mentioned in the docs, but manual refresh with refreshCookie is the standard approach for now. --- Summary:
After changing the cookie value, call refreshCookie('currentPortfolio') to update the reactive ref and trigger reactivity in your composables. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@230675282959400961> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Jakob
JakobOP2mo ago
Didn't change anything @kapa.ai When I change the cookie like this:
useSession().currentPortfolioId.value = portfolio.id;
console.log('Selected portfolio:', portfolio.id);
console.log('Selected Cookie:', useSession().currentPortfolioId.value);
useSession().currentPortfolioId.value = portfolio.id;
console.log('Selected portfolio:', portfolio.id);
console.log('Selected Cookie:', useSession().currentPortfolioId.value);
The result looks like this:
Selected portfolio: 5
Selected Cookie: 1
Selected portfolio: 5
Selected Cookie: 1
So there is a mismatch between those two
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- The behavior you're seeing—where setting useSession().currentPortfolioId.value = portfolio.id does not immediately reflect the new value when you call useSession().currentPortfolioId.value again—is likely due to how your composable is structured. Each call to useSession() creates a new instance and, therefore, a new useCookie ref. This means you're not referencing the same reactive ref across calls. How to fix: You should ensure that your composable returns a singleton ref, not a new one each time. In Nuxt, you can use the useState composable to persist a value across the app, or you can define your composable so it only creates the ref once. Example fix:
let currentPortfolioId: Ref<number | undefined>;

export function useSession() {
if (!currentPortfolioId) {
currentPortfolioId = useCookie<number | undefined>('currentPortfolio', {
maxAge: 60 * 60 * 24 * 14
});
}
return {
currentPortfolioId
}
}
let currentPortfolioId: Ref<number | undefined>;

export function useSession() {
if (!currentPortfolioId) {
currentPortfolioId = useCookie<number | undefined>('currentPortfolio', {
maxAge: 60 * 60 * 24 * 14
});
}
return {
currentPortfolioId
}
}
Or, use Nuxt's useState to ensure a singleton:
export function useSession() {
const currentPortfolioId = useCookie<number | undefined>('currentPortfolio', {
maxAge: 60 * 60 * 24 * 14
});
return {
currentPortfolioId
}
}
export function useSession() {
const currentPortfolioId = useCookie<number | undefined>('currentPortfolio', {
maxAge: 60 * 60 * 24 * 14
});
return {
currentPortfolioId
}
}
But always call useSession() once and reuse the returned object, rather than calling it multiple times in the same scope. Key point:
If you call useSession() multiple times, you get different refs, so updating one does not update the other. Store the result of useSession() in a variable and reuse it. References:
This pattern is consistent with how reactivity and composables work in Nuxt and Vue, as shown in the Nuxt useCookie documentation. If you still see issues, ensure you are not calling useSession() multiple times in the same component or logic block. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@230675282959400961> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Jakob
JakobOP2mo ago
@kapa.ai Thanks, that worked but it's still wrong in my useapi call
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- If your useAPI call is still using the old cookie value after fixing the singleton issue, the likely cause is that the URL passed to useAPI is not reactive. In Nuxt, for reactivity to work with useFetch (and by extension, your custom useAPI), you need to pass a function or a computed ref as the URL, not just a string with the current value. This ensures that when currentPortfolioId.value changes, the fetch is re-triggered with the new value. How to fix: Change your useAPI usage from:
const { data: portfolioData } = useAPI<PortfolioInfo>(`/api/portfolio/${currentPortfolioId.value}`, {
server: false,
watch: [currentPortfolioId]
});
const { data: portfolioData } = useAPI<PortfolioInfo>(`/api/portfolio/${currentPortfolioId.value}`, {
server: false,
watch: [currentPortfolioId]
});
to:
const { data: portfolioData } = useAPI<PortfolioInfo>(
() => `/api/portfolio/${currentPortfolioId.value}`,
{ server: false }
);
const { data: portfolioData } = useAPI<PortfolioInfo>(
() => `/api/portfolio/${currentPortfolioId.value}`,
{ server: false }
);
This way, the URL is recomputed whenever currentPortfolioId.value changes, and useFetch (used inside useAPI) will refetch the data accordingly. You do not need the watch option in this case, as passing a computed or function as the URL already handles reactivity for you see docs. Reference:
You can use a computed ref or a plain ref as the URL, allowing for dynamic data fetching that automatically updates when the URL changes:
const { data: post } = await useFetch(() => `/api/posts/${id.value}`)
const { data: post } = await useFetch(() => `/api/posts/${id.value}`)
Docs: useFetch - Reactive Keys and Shared State
Summary:
- Pass a function or computed ref as the URL to useAPI/useFetch for reactivity. - Do not use the watch option for this use case; it's only needed if you want to refetch with the same URL when other values change. This should resolve your issue and ensure your API call always uses the latest cookie value. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@230675282959400961> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Jakob
JakobOP2mo ago
@kapa.ai This fix doesn't work: let currentPortfolioId: Ref<number | undefined>; export function useSession() { if (!currentPortfolioId) { currentPortfolioId = useCookie<number | undefined>('currentPortfolio', { maxAge: 60 * 60 * 24 * 14 }); } return { currentPortfolioId } } Because the variable is auto of scope
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- You're correct—declaring a variable like let currentPortfolioId at the top of your composable file will only persist within that module's scope, not globally across the app or between server/client contexts. In Nuxt, the recommended way to create a singleton, SSR-friendly, and reactive state is to use the useState composable or rely on the fact that useCookie itself is SSR-friendly and should be called once per component or composable usage, not multiple times in the same scope. Key points from the official docs: - Do not define refs outside of setup or composable functions. This can lead to state being shared across requests on the server, which is unsafe and can cause memory leaks. Instead, always define your state inside a composable or setup function, and use Nuxt's built-in composables for shared state see docs. - If you want to share state, use a composable that returns the result of useState or useCookie, and always call the composable once per consumer. Do not call the composable multiple times in the same component or logic block, as each call creates a new instance see docs. Example (recommended pattern):
// composables/useSession.ts
export function useSession() {
// This is safe and SSR-friendly
const currentPortfolioId = useCookie<number | undefined>('currentPortfolio', {
maxAge: 60 * 60 * 24 * 14
});
return {
currentPortfolioId
}
}
// composables/useSession.ts
export function useSession() {
// This is safe and SSR-friendly
const currentPortfolioId = useCookie<number | undefined>('currentPortfolio', {
maxAge: 60 * 60 * 24 * 14
});
return {
currentPortfolioId
}
}
Usage:
// In your component, call useSession() ONCE and reuse the returned object
const { currentPortfolioId } = useSession();
// In your component, call useSession() ONCE and reuse the returned object
const { currentPortfolioId } = useSession();
If you call useSession() multiple times in the same component, you will get different refs, which do not share state. Always store the result in a variable and reuse it. Summary:
- Do not use a module-scoped variable for state in Nuxt composables. - Always call your composable once per consumer and reuse the returned value. - This approach is SSR-safe and recommended by the Nuxt documentation see docs. If you need true global state across the app, consider using Pinia, which is the official state management solution for Vue 3 and Nuxt 3. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@230675282959400961> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?