Episode ini membahas pengelolaan state global dengan Pinia di Nuxt: mendefinisikan stores dengan syntax setup dan options, plugins Pinia, pola shared state dengan composables, serta cara sinkronisasi state saat SSR hydration.

Saat aplikasi tumbuh, banyak komponen butuh data yang sama — misalnya isi keranjang belanja yang ditampilkan di header, halaman keranjang, dan halaman checkout sekaligus. Melempar data lewat props berlapis membuat kode sulit dirawat. Jawabannya adalah state global dengan Pinia, store resmi Vue.
Episode 7 membahas Pinia di dalam Nuxt: cara mengaktifkan module-nya, dua cara mendefinisikan store, plugins untuk memperluas store, serta isu paling rumit dalam SSR — bagaimana memastikan state tidak bocor antar user dan tersinkronisasi dengan benar saat hydration.
Install module resmi Pinia untuk Nuxt:
npm install @pinia/nuxt piniaexport default defineNuxtConfig({
modules: ["@pinia/nuxt"],
})Setelah module terdaftar, kalian bisa membuat store di folder app/stores dan memakainya di mana pun tanpa import module — store yang dipanggil akan dibuat dan dibagikan secara otomatis.
Mari buat store keranjang belanja untuk project belajar-shop:
export const useKeranjangStore = defineStore("keranjang", {
state: () => ({
items: [] as { id: string; nama: string; harga: number }[],
}),
getters: {
total: (state) =>
state.items.reduce((jumlah, item) => jumlah + item.harga, 0),
},
actions: {
tambah(item: { id: string; nama: string; harga: number }) {
this.items.push(item)
},
kosongkan() {
this.items = []
},
},
})defineStore("keranjang", {...}) mendefinisikan store dengan state, getters, dan actions. Getters menghitung nilai turunan, actions memuat mutasi state — keduanya bisa dipakai dari komponen mana pun.
Selain syntax options di atas, Pinia mendukung syntax setup yang mirip komponen:
export const useKatalogStore = defineStore("katalog", () => {
const produk = ref([])
const total = computed(() => produk.value.length)
async function muat() {
produk.value = await $fetch("/api/produk")
}
return { produk, total, muat }
})Pilih berdasarkan kompleksitas: options store lebih deklaratif dan mudah dibaca, setup store lebih fleksibel untuk logika yang memakai computed dan fungsi pembantu.
const keranjang = useKeranjangStore()
function tambahKeKeranjang(produk) {
keranjang.tambah({ id: produk.id, nama: produk.nama, harga: produk.harga })
}useKeranjangStore() dipanggil di komponen untuk mengakses instance store. Seluruh komponen yang memakai store yang sama otomatis saling tersinkronasi.
Plugin Pinia adalah fungsi yang dijalankan untuk setiap store yang dibuat. Gunanya untuk menambahkan behavior global seperti logging atau persistence:
export default defineNuxtPlugin(() => {
const pinia = usePinia()
pinia.use((context) => {
context.store.$subscribe((mutation, state) => {
console.log("Store berubah", mutation.type)
})
})
})pinia.use((context) => {...}) mendaftarkan plugin yang menerima setiap store baru. Di sini $subscribe dipakai untuk memantau perubahan state — berguna untuk analytics atau logging.
Untuk state yang sangat kecil dan lokal, Nuxt menyediakan useState — composable untuk shared state yang aman untuk SSR:
export function usePromo() {
return useState("promo", () => "GRATIS-ONGKIR")
}useState("promo", () => "GRATIS-ONGKIR") membuat state reaktif yang dibagikan antar komponen dan aman dipakai di server maupun client. Gunakan ini untuk data ringan, dan Pinia untuk state bisnis yang kompleks dengan getters serta actions.
Saat SSR, satu request bisa menangani banyak user bersamaan. Jika state global diinisialisasi di luar komponen, data antar user bisa tercampur. Karena itu store Pinia di Nuxt dibuat per-request — setiap request mendapat instance store sendiri, sehingga tidak ada kebocoran data.
Pinia di Nuxt menyalurkan state dari server ke client lewat payload. Kunci suksesnya: state store harus diinisialisasi di server dan client dengan cara yang sama.
if (import.meta.server) {
const keranjang = useKeranjangStore()
await keranjang.muatDariServer()
}Pola if (import.meta.server) di atas memuat data awal hanya di server, lalu state otomatis tersinkronisasi ke client saat hydration. Hindari membaca data user secara asinkron langsung di state store tanpa inisialisasi yang konsisten.
Episode 7 menutup lapisan state Nuxt: Pinia sebagai state management global dengan options dan setup store, plugins untuk memperluas store, useState untuk shared state ringan, serta pemahaman penting bahwa store di Nuxt dibuat per-request agar tidak bocor antar user saat SSR.
Inti yang harus dibawa pulang:
@pinia/nuxt dan simpan store di app/stores.defineStore mendukung syntax options dan setup; pilih sesuai kebutuhan.pinia.use dan bisa menambah behavior global.useState adalah alternatif ringan untuk shared state yang aman SSR.Di episode 8 selanjutnya kita akan membahas configuration dan runtime config — mengelola nuxt.config.ts, runtime config untuk env variables public dan private, konfigurasi modules, serta build optimizations dan feature flags. Toko kalian akan mulai diatur dengan benar.