Pinia is not creating cookies when using SSR
Closed this issue · 3 comments
Are you using Nuxt?
- Check this box if you encountered the issue using Nuxt and the included module.
Describe the bug
When I create/set a state during a component's Setup phase (in Nuxt's SSR), it is expected that the state persists on every page refresh. I would expect the cookie with the new state to be hydrated to the client after the SSR completes.
Store:
import { defineStore } from "pinia"
export const useSessionIdStore = defineStore("sessionId", {
persist: true,
state: () => ({ sessionId: Math.random().toString(36).substring(2) as string }),
getters: {
getSessionId(state) {
return state.sessionId
},
},
actions: {},
})
nuxt.config:
(...)
ssr: true,
modules: ["@element-plus/nuxt", "@pinia/nuxt", "@pinia-plugin-persistedstate/nuxt"],
piniaPersistedstate: {
cookieOptions: {
sameSite: "lax",
},
storage: "cookies",
debug: true,
},
(...)
package.json:
{
"name": "Demo",
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "NODE_TLS_REJECT_UNAUTHORIZED=0 MODE=development nuxt dev --dotenv .env.development --host 0.0.0.0",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"lint-staged": {
"*.{js,ts,vue}": "eslint --cache"
},
"devDependencies": {
"@element-plus/nuxt": "^1.0.9",
"@iconify/vue": "^4.1.1",
"@nuxt/devtools": "^1.0.6",
"@nuxt/eslint-config": "^0.1.1",
"@nuxtjs/robots": "^4.1.7",
"@nuxtjs/seo": "^2.0.0-rc.21",
"@nuxtjs/sitemap": "^6.1.0",
"@pinia-plugin-persistedstate/nuxt": "^1.2.0",
"@types/aos": "^3.0.7",
"@types/markdown-it": "^13.0.7",
"@types/markdown-it-link-attributes": "^3.0.5",
"@types/node": "^18.17.1",
"autoprefixer": "^10.4.14",
"element-plus": "^2.7.2",
"eslint": "^8.56.0",
"eslint-config-prettier": "^8.9.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-vue": "^9.21.1",
"lint-staged": "^13.2.3",
"markdown-it-named-headers": "^0.0.4",
"nuxt": "^3.13.2",
"nuxt-link-checker": "^3.1.1",
"nuxt-schema-org": "^3.4.0",
"nuxt-site-config": "^2.2.18",
"nuxt-site-config-kit": "^2.2.18",
"prettier": "^3.0.0",
"pug": "^3.0.2",
"sass": "^1.64.2",
"tailwindcss": "^3.4.1",
"typescript": "^5.2.2",
"vue-gtag-next": "^1.14.0",
"vue-router": "^4.2.5"
},
"dependencies": {
"@chenfengyuan/vue-countdown": "^2.1.2",
"@pinia/nuxt": "^0.5.1",
"@sentry/vue": "^8.19.0",
"aos": "^3.0.0-beta.6",
"apexcharts": "^3.45.1",
"gauge-chart": "^1.0.0",
"highlight.js": "^11.9.0",
"markdown-it": "^14.0.0",
"markdown-it-link-attributes": "^4.0.1",
"pinia": "^2.2.2",
"vue": "^3.2.47",
"vue-country-flag-next": "^2.3.2",
"vue-tsc": "^2.1.6",
"vue3-apexcharts": "^1.4.4",
"vue3-lottie": "^3.2.0"
}
}
Then anywhere in the component's setup:
console.log("sessionId: " + useSessionIdStore().sessionId)
And you'll see that on every refresh this session Id is different, and no cookie gets created to persist it ever.
Wasn't this supposed to work in a SSR friendly way?
Reproduction
reproduction above
System Info
System:
OS: Linux 6.8 Linux Mint 21.3 (Virginia)
CPU: (16) x64 AMD Ryzen 7 5800H with Radeon Graphics
Memory: 11.72 GB / 27.26 GB
Container: Yes
Shell: 5.1.16 - /bin/bash
Binaries:
Node: 22.11.0 - ~/.nvm/versions/node/v22.11.0/bin/node
npm: 10.9.0 - ~/.nvm/versions/node/v22.11.0/bin/npm
Browsers:
Brave Browser: 129.1.70.126
Chromium: 129.0.6668.89
Used Package Manager
npm
Validations
- Follow our Code of Conduct
- Read the Contributing Guide.
- Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- The provided reproduction is a minimal reproducible of the bug.
Hi, while the reproduction is not what is expected (please read the article on what proper reproduction is), I think the issue is that persistence only happens on mutation (calling store.sessionId = 'something'
somewhere after store is initialized), and not when stores are created. You can still use $persist
if you need to force persistence at a given moment without mutating the state.
Sorry for the reproduction. But you nailed the issue directly.
I find it very strange that persist is set to true, but the very initial state (set when the store is created) does not get persisted, when it is a valid state like any other. Not a bug, but a design decision, I get it. But it seems incoherent, because the initial state should be no different than any other state IMO. I.e. setting the initial state should be seen as a mutation I guess.
And this hack was the best solution I found that:
- Keeps sessionId being
string
instead ofstring|null
- Persists the initial state on store creation
- Refrains me from polluting other parts of the project with a forced initial mutation
I added useCookie
, and removed the persist: true
, so I manually took over the store's persistence.
import { useCookie } from "#app" // Use Nuxt's useCookie helper
import { defineStore } from "pinia"
export const useSessionIdStore = defineStore("sessionId", {
state: () => {
const sessionIdCookie = useCookie("sessionId")
// Set sessionId from cookie if it exists; otherwise, generate a new one and store it in the cookie
const sessionId = sessionIdCookie.value || Math.random().toString(36).substring(2)
sessionIdCookie.value = sessionId
console.log("SessionId: ", sessionId)
return {
sessionId, // Initialized directly, never null
}
},
getters: {
getSessionId(state): string {
return state.sessionId
},
},
})
IMO this is the behavior I would expect by default when persist: true
.
Setting an initial state is different from not setting it at all, so rendering the same persisting behavior (none) for these two different scenarios makes no sense to me from a logical perspective. Any state change would be persisted, including the initial one.