diff --git a/examples/vue-pinia/components/PersistedCounter.vue b/examples/vue-pinia/components/PersistedCounter.vue
new file mode 100644
index 00000000..c24e0d85
--- /dev/null
+++ b/examples/vue-pinia/components/PersistedCounter.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/examples/vue-pinia/package.json b/examples/vue-pinia/package.json
index 1269ebaa..73b66c24 100644
--- a/examples/vue-pinia/package.json
+++ b/examples/vue-pinia/package.json
@@ -8,6 +8,7 @@
"dependencies": {
"@vitejs/plugin-vue": "^6.0.1",
"pinia": "^3.0.1",
+ "pinia-plugin-persistedstate": "^4.7.1",
"vike": "^0.4.247",
"vike-vue": "^0.9.6",
"vike-vue-pinia": "^0.2.3",
diff --git a/examples/vue-pinia/pages/+onCreatePinia.client.ts b/examples/vue-pinia/pages/+onCreatePinia.client.ts
new file mode 100644
index 00000000..9622c5c1
--- /dev/null
+++ b/examples/vue-pinia/pages/+onCreatePinia.client.ts
@@ -0,0 +1,6 @@
+import type { PageContext } from 'vike/types'
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
+
+export function onCreatePinia(pageContext: PageContext) {
+ pageContext.pinia!.use(piniaPluginPersistedstate)
+}
diff --git a/examples/vue-pinia/pages/about/+Page.vue b/examples/vue-pinia/pages/about/+Page.vue
index 3e7c3e28..c21eb11b 100644
--- a/examples/vue-pinia/pages/about/+Page.vue
+++ b/examples/vue-pinia/pages/about/+Page.vue
@@ -2,8 +2,19 @@
About
Example of using Pinia.
+ Example of using Pinia with state persistence.
+ This counter persists across page reloads (stored in localStorage)
+
+ Loading...
diff --git a/examples/vue-pinia/stores/usePersistedCounterStore.ts b/examples/vue-pinia/stores/usePersistedCounterStore.ts
new file mode 100644
index 00000000..99ac700b
--- /dev/null
+++ b/examples/vue-pinia/stores/usePersistedCounterStore.ts
@@ -0,0 +1,17 @@
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+
+export const usePersistedCounterStore = defineStore(
+ 'persistedCounter',
+ () => {
+ const count = ref(0)
+
+ const increment = () => count.value++
+
+ return { count, increment }
+ },
+ {
+ // Persist state to localStorage - counter persists across page reloads
+ persist: true,
+ },
+)
diff --git a/packages/vike-vue-pinia/integration/+config.ts b/packages/vike-vue-pinia/integration/+config.ts
index 26d90a44..70249e43 100644
--- a/packages/vike-vue-pinia/integration/+config.ts
+++ b/packages/vike-vue-pinia/integration/+config.ts
@@ -13,6 +13,12 @@ const config = {
onCreateApp: 'import:vike-vue-pinia/__internal/integration/onCreateApp:onCreateApp',
onAfterRenderHtml: 'import:vike-vue-pinia/__internal/integration/onAfterRenderHtml:onAfterRenderHtml',
onCreatePageContext: 'import:vike-vue-pinia/__internal/integration/onCreatePageContext:onCreatePageContext',
+ meta: {
+ onCreatePinia: {
+ env: { client: true, server: true },
+ cumulative: true,
+ },
+ },
} satisfies Config
declare global {
@@ -24,5 +30,16 @@ declare global {
interface GlobalContext {
pinia?: Pinia
}
+ interface Config {
+ /**
+ * Hook called after creating the Pinia instance.
+ *
+ * Use this to register Pinia plugins.
+ */
+ onCreatePinia?: (pageContext: PageContext) => void | Promise
+ }
+ interface ConfigResolved {
+ onCreatePinia?: Array<(pageContext: PageContext) => void | Promise>
+ }
}
}
diff --git a/packages/vike-vue-pinia/integration/createPiniaPlus.ts b/packages/vike-vue-pinia/integration/createPiniaPlus.ts
new file mode 100644
index 00000000..eda43e36
--- /dev/null
+++ b/packages/vike-vue-pinia/integration/createPiniaPlus.ts
@@ -0,0 +1,24 @@
+export { createPiniaPlus }
+
+import { createPinia } from 'pinia'
+import type { PageContext } from 'vike/types'
+
+// Call createPinia() and +onCreatePinia hooks
+async function createPiniaPlus(pageContext: PageContext, useGloablContext?: boolean) {
+ const pinia = createPinia()
+
+ if (useGloablContext) {
+ // Implicitly sets pageContext.pinia (because pageContext inherits all globalContext properties as fallback)
+ pageContext.globalContext.pinia = pinia
+ } else {
+ pageContext.pinia = pinia
+ }
+
+ // Call +onCreatePinia hooks
+ const { onCreatePinia } = pageContext.config
+ if (onCreatePinia) {
+ await Promise.all(onCreatePinia.map((hook) => hook(pageContext)))
+ }
+
+ return pinia
+}
diff --git a/packages/vike-vue-pinia/integration/onCreateApp.ts b/packages/vike-vue-pinia/integration/onCreateApp.ts
index 36f4b801..4bed0e45 100644
--- a/packages/vike-vue-pinia/integration/onCreateApp.ts
+++ b/packages/vike-vue-pinia/integration/onCreateApp.ts
@@ -1,14 +1,14 @@
export { onCreateApp }
import type { PageContext } from 'vike/types'
-import { createPinia } from 'pinia'
+import { createPiniaPlus } from './createPiniaPlus'
-function onCreateApp(pageContext: PageContext) {
+async function onCreateApp(pageContext: PageContext) {
const { app } = pageContext
if (!app) return
if (pageContext.isClientSide) {
- const pinia = createPinia()
+ const pinia = await createPiniaPlus(pageContext, true)
const { _piniaInitialState } = pageContext
if (_piniaInitialState) {
pinia.state.value = {
@@ -18,7 +18,6 @@ function onCreateApp(pageContext: PageContext) {
...pinia.state.value,
}
}
- pageContext.globalContext.pinia = pinia
}
app.use(pageContext.globalContext.pinia ?? pageContext.pinia!)
diff --git a/packages/vike-vue-pinia/integration/onCreatePageContext.server.ts b/packages/vike-vue-pinia/integration/onCreatePageContext.server.ts
index 9a0eb5b4..2cad2b2b 100644
--- a/packages/vike-vue-pinia/integration/onCreatePageContext.server.ts
+++ b/packages/vike-vue-pinia/integration/onCreatePageContext.server.ts
@@ -1,8 +1,8 @@
export { onCreatePageContext }
import type { PageContextServer } from 'vike/types'
-import { createPinia } from 'pinia'
+import { createPiniaPlus } from './createPiniaPlus'
-function onCreatePageContext(pageContext: PageContextServer) {
- pageContext.pinia = createPinia()
+async function onCreatePageContext(pageContext: PageContextServer) {
+ await createPiniaPlus(pageContext)
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8ee42f54..71d9fd58 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -97,6 +97,9 @@ importers:
pinia:
specifier: ^3.0.1
version: 3.0.1(typescript@5.9.2)(vue@3.5.13(typescript@5.9.2))
+ pinia-plugin-persistedstate:
+ specifier: ^4.7.1
+ version: 4.7.1(pinia@3.0.1(typescript@5.9.2)(vue@3.5.13(typescript@5.9.2)))
vike:
specifier: ^0.4.247
version: 0.4.247(vite@7.1.5)
@@ -1135,6 +1138,9 @@ packages:
resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==}
engines: {node: '>=6'}
+ defu@6.1.4:
+ resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
@@ -1510,6 +1516,20 @@ packages:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
+ pinia-plugin-persistedstate@4.7.1:
+ resolution: {integrity: sha512-WHOqh2esDlR3eAaknPbqXrkkj0D24h8shrDPqysgCFR6ghqP/fpFfJmMPJp0gETHsvrh9YNNg6dQfo2OEtDnIQ==}
+ peerDependencies:
+ '@nuxt/kit': '>=3.0.0'
+ '@pinia/nuxt': '>=0.10.0'
+ pinia: '>=3.0.0'
+ peerDependenciesMeta:
+ '@nuxt/kit':
+ optional: true
+ '@pinia/nuxt':
+ optional: true
+ pinia:
+ optional: true
+
pinia@3.0.1:
resolution: {integrity: sha512-WXglsDzztOTH6IfcJ99ltYZin2mY8XZCXujkYWVIJlBjqsP6ST7zw+Aarh63E1cDVYeyUcPCxPHzJpEOmzB6Wg==}
peerDependencies:
@@ -2666,6 +2686,8 @@ snapshots:
dependencies:
type-detect: 4.1.0
+ defu@6.1.4: {}
+
delayed-stream@1.0.0: {}
dot-prop@5.3.0:
@@ -3030,6 +3052,12 @@ snapshots:
picomatch@4.0.3: {}
+ pinia-plugin-persistedstate@4.7.1(pinia@3.0.1(typescript@5.9.2)(vue@3.5.13(typescript@5.9.2))):
+ dependencies:
+ defu: 6.1.4
+ optionalDependencies:
+ pinia: 3.0.1(typescript@5.9.2)(vue@3.5.13(typescript@5.9.2))
+
pinia@3.0.1(typescript@5.9.2)(vue@3.5.13(typescript@5.9.2)):
dependencies:
'@vue/devtools-api': 7.7.2