Server-Side Rendering poses a unique challenge for state: a Pinia instance must be created per request to avoid a shared singleton state. This episode covers Pinia SSR fundamentals, the @pinia/nuxt module for auto-setup, and how to handle hydration state and use stores in definePageMeta.

In a typical SPA, Pinia is created once and used forever. In a Server-Side Rendering app, every request that hits the server must get clean state — otherwise user A's data could leak to user B. This is SSR's main challenge and the reason Pinia was designed with SSR awareness from the start.
Episode 11 covers three things: Pinia SSR fundamentals, Nuxt integration through @pinia/nuxt, and handling hydration state. If you're building with Nuxt, most of this episode will read like a production guide.
On the server, never create a single global Pinia. Every request must get a new instance:
export function createApp() {
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
return { app, pinia }
}
// server.js
server.get('/app', (req, res) => {
const { app, pinia } = createApp()
// render the app here, then send the state to the client
})createApp() is called for every request, so createPinia() produces a new instance. Without this pattern, two users arriving at the same time would share the same state — a bug that leaks data between requests.
After rendering on the server, Pinia's state must be sent to the client for rehydration. A common pattern: serialize pinia.state.value into the HTML, then use it as initialState on the client:
// Server: send the state
const state = JSON.stringify(pinia.state.value)
// Client: use it as the initial state
const pinia = createPinia()
pinia.state.value = JSON.parse(window.__PINIA_STATE__)pinia.state.value holds the state of all stores in a single object keyed by store id. This synchronization between server and client is what prevents hydration mismatch.
Nuxt automates all the steps above through the @pinia/nuxt module. Install it and register it in the config:
npm i pinia @pinia/nuxtmodules:
- '@pinia/nuxt'Once the module is active, defineStore and useStore() work directly inside setup components, pages, and plugins without boilerplate. The module handles creating the per-request instance, injecting it into the app, and hydration automatically.
In Nuxt, stores are used as usual inside setup:
<script setup lang="ts">
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>useCounterStore() in Nuxt automatically uses the active Pinia instance managed by the module. To call a store from outside setup — for example in definePageMeta or a plugin — use callWithNuxt so the store runs in the correct Nuxt context:
import { callWithNuxt, useNuxtApp } from '#app'
import { useUserStore } from '@/stores/user'
const nuxt = useNuxtApp()
callWithNuxt(nuxt, () => {
const userStore = useUserStore()
userStore.fetchProfile()
})callWithNuxt(nuxt, fn) runs the callback inside the currently active Nuxt app context, so useUserStore() finds the correct Pinia instance for that request. In plugins, Nuxt already provides nuxtApp as an argument — just wrap the store code with callWithNuxt inside it.
An alternative is reading the Pinia instance directly via useNuxtApp().$pinia. This route is suitable for accessing custom Pinia plugins registered with pinia.use() on the Nuxt side, because the @pinia/nuxt module exposes the same instance to the entire app.
Warning
Don't call stores at the module level (top-level imports) in Nuxt. Stores must be called in a setup context or in a function run by Nuxt, because a new Pinia instance is created per request.
Episode 11 opens Pinia up to the world of SSR. You now understand the reasoning behind per-request instances, the state serialization and hydration pattern, easy integration with @pinia/nuxt, and how to use stores inside Nuxt without boilerplate.
Key takeaways:
pinia.state.value.@pinia/nuxt handles setup, hydration, and per-request instances.callWithNuxt.In the next episode, episode 12, we'll discuss composing stores — using stores inside stores, splitting a domain into small stores like auth, cart, and ui, and coordinating between stores. This is the pattern that takes Pinia from small apps to large ones.