Server-Side Rendering punya tantangan unik untuk state: instance Pinia harus dibuat per-request agar tidak terjadi shared singleton state. Episode ini membahas fundamental SSR Pinia, module @pinia/nuxt untuk auto-setup, serta cara menangani hydration state dan memakai store di definePageMeta.

Di aplikasi SPA biasa, Pinia dibuat satu kali dan dipakai selamanya. Di aplikasi Server-Side Rendering, setiap request datang ke server harus mendapatkan state yang bersih — kalau tidak, data user A bisa bocor ke user B. Inilah tantangan utama SSR dan alasan Pinia dirancang dengan kesadaran SSR sejak awal.
Episode 11 membahas tiga hal: fundamental SSR Pinia, integrasi dengan Nuxt lewat @pinia/nuxt, dan penanganan hydration state. Jika kalian membangun Nuxt, sebagian besar episode ini akan terasa seperti membaca panduan produksi.
Di server, jangan pernah membuat satu Pinia global. Setiap request harus mendapat instance baru:
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 app di sini, lalu kirim state ke client
})createApp() dipanggil untuk setiap request, sehingga createPinia() menghasilkan instance baru. Tanpa pola ini, dua pengguna yang datang bersamaan akan berbagi state yang sama — bug bocornya data antar request.
Setelah render di server, state Pinia harus dikirim ke client agar dihidrasi ulang. Pola umum: serialisasi pinia.state.value ke HTML, lalu gunakan sebagai initialState di client:
// Server: kirim state
const state = JSON.stringify(pinia.state.value)
// Client: pakai sebagai state awal
const pinia = createPinia()
pinia.state.value = JSON.parse(window.__PINIA_STATE__)pinia.state.value berisi state semua store dalam satu object per id store. Sinkronisasi antara server dan client inilah yang mencegah hydration mismatch.
Nuxt mengotomatiskan semua langkah di atas lewat module @pinia/nuxt. Install lalu daftarkan di konfigurasi:
npm i pinia @pinia/nuxtmodules:
- '@pinia/nuxt'Setelah module aktif, defineStore dan useStore() langsung bekerja di dalam komponen setup, pages, dan plugins tanpa boilerplate. Module menangani pembuatan instance per request, injection ke aplikasi, dan hydration secara otomatis.
Di Nuxt, store dipakai seperti biasa di dalam setup:
<script setup lang="ts">
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>useCounterStore() di Nuxt otomatis memakai instance Pinia aktif yang dikelola module. Untuk memanggil store dari luar setup — misalnya di definePageMeta atau plugin — gunakan callWithNuxt agar store berjalan dalam konteks Nuxt yang benar:
import { callWithNuxt, useNuxtApp } from '#app'
import { useUserStore } from '@/stores/user'
const nuxt = useNuxtApp()
callWithNuxt(nuxt, () => {
const userStore = useUserStore()
userStore.fetchProfile()
})callWithNuxt(nuxt, fn) menjalankan callback di dalam konteks aplikasi Nuxt yang sedang aktif, sehingga useUserStore() menemukan instance Pinia yang benar untuk request tersebut. Di plugin, Nuxt sudah menyediakan nuxtApp sebagai argumen — tinggal bungkus kode store dengan callWithNuxt di dalamnya.
Alternatif lain adalah membaca instance Pinia langsung lewat useNuxtApp().$pinia. Jalur ini cocok untuk mengakses plugin Pinia kustom yang didaftarkan lewat pinia.use() pada sisi Nuxt, karena module @pinia/nuxt mengekspos instance yang sama ke seluruh aplikasi.
Warning
Jangan panggil store di level modul (top-level import) pada Nuxt. Store harus dipanggil dalam konteks setup atau fungsi yang dijalankan Nuxt, karena instance Pinia baru dibuat per request.
Episode 11 membuka Pinia untuk dunia SSR. Kalian sekarang memahami alasan instance per request, pola serialisasi dan hidrasi state, integrasi mudah dengan @pinia/nuxt, serta cara memakai store di dalam Nuxt tanpa boilerplate.
Inti yang harus dibawa pulang:
pinia.state.value.@pinia/nuxt menangani setup, hydration, dan instance per request.callWithNuxt.Di episode 12 selanjutnya kita akan membahas composing stores — memakai store di dalam store, memecah domain menjadi store kecil seperti auth, cart, dan ui, serta mengoordinasikan antar store. Ini pola yang membawa Pinia dari aplikasi kecil ke aplikasi besar.