Vue 3 Pinia vs Vuex: Panduan Lengkap State Management dan Pertanyaan Interview 2026
Perbandingan mendalam Pinia vs Vuex: arsitektur, TypeScript support, Composition API, performa, strategi migrasi, dan pertanyaan interview Vue state management 2026.

Ekosistem Vue 3 mengalami transformasi signifikan dalam pengelolaan state dengan hadirnya Pinia sebagai solusi resmi yang menggantikan Vuex. Pinia dirancang dari awal untuk memanfaatkan Composition API, memberikan type safety yang superior, dan menghilangkan boilerplate code yang sering ditemukan di Vuex. Artikel ini mengupas perbedaan arsitektur kedua library, pola migrasi praktis, dan pertanyaan interview yang relevan untuk tahun 2026.
Pinia menghilangkan konsep mutations sepenuhnya, mengurangi bundle size hingga 83%, dan memberikan full TypeScript inference tanpa konfigurasi tambahan. Untuk proyek Vue 3 baru di tahun 2026, Pinia adalah pilihan standar yang direkomendasikan official Vue team.
Perbedaan Arsitektur Inti
Vuex 4 dan Pinia 3 memiliki filosofi desain yang berbeda. Vuex menggunakan single store dengan nested modules, memerlukan mutations terpisah untuk state changes, dan dibangun berdasarkan Options API. Sebaliknya, Pinia mengadopsi pendekatan multiple flat stores, memungkinkan direct state mutations, dan native support untuk Composition API.
| Feature | Pinia 3 | Vuex 4 | |---------|---------|--------| | Mutations | None (direct state changes) | Required for state changes | | TypeScript | Full inference, no augmentation | Manual type augmentation needed | | Store architecture | Multiple flat stores | Single store with nested modules | | Composition API | Native support | Options API based | | Bundle size | ~1 KB gzipped | ~6 KB gzipped | | Vue Devtools | Full support (v7) | Full support | | SSR | Built-in | Requires configuration | | Hot Module Replacement | Built-in | Manual setup |
Arsitektur flat stores di Pinia memudahkan code splitting dan tree shaking, sementara eliminasi mutations mengurangi kompleksitas mental model. Developer tidak perlu lagi membedakan kapan menggunakan commit vs dispatch, karena semua state changes dilakukan langsung di dalam actions.
Mendefinisikan Store: Options API vs Setup Syntax
Pinia menawarkan dua sintaks untuk mendefinisikan store, keduanya dengan trade-off yang berbeda. Options Store API familiar bagi developer Vuex, sementara Setup Store memberikan fleksibilitas penuh Composition API.
import { defineStore } from 'pinia'
// Options Store syntax — familiar to Vuex developers
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
lastUpdated: null as Date | null,
}),
getters: {
// Getters receive state as first argument with full type inference
doubleCount: (state) => state.count * 2,
isPositive(): boolean {
// Access other getters via `this`
return this.count > 0
},
},
actions: {
increment() {
// Direct state mutation — no commit() needed
this.count++
this.lastUpdated = new Date()
},
async fetchCount(id: string) {
// Async actions work without extra configuration
const response = await fetch(`/api/counters/${id}`)
const data = await response.json()
this.count = data.count
},
},
})Setup Store memberikan kontrol penuh dengan menggunakan Composition API pattern yang sama seperti di component <script setup>:
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
// Setup Store syntax — identical patterns to Composition API
export const useCounterStore = defineStore('counter', () => {
// ref() becomes state
const count = ref(0)
const lastUpdated = ref<Date | null>(null)
// computed() becomes getters
const doubleCount = computed(() => count.value * 2)
const isPositive = computed(() => count.value > 0)
// Functions become actions
function increment() {
count.value++
lastUpdated.value = new Date()
}
async function fetchCount(id: string) {
const response = await fetch(`/api/counters/${id}`)
const data = await response.json()
count.value = data.count
}
// Must return all state, getters, and actions
return { count, lastUpdated, doubleCount, isPositive, increment, fetchCount }
})Setup syntax memungkinkan penggunaan composables Vue langsung di dalam store definition, memberikan fleksibilitas untuk mengintegrasikan third-party libraries seperti VueUse tanpa wrapper khusus.
Integrasi TypeScript
Salah satu keunggulan terbesar Pinia adalah type inference otomatis yang bekerja tanpa konfigurasi manual. Vuex 4 memerlukan module augmentation untuk mendapatkan type safety:
// Vuex 4 — manual type augmentation required
import { Store } from 'vuex'
declare module 'vuex' {
interface State {
counter: {
count: number
lastUpdated: Date | null
}
}
}
// Accessing state requires type assertions or custom helpers
const count = (store.state as { counter: { count: number } }).counter.countPinia menghilangkan kompleksitas ini sepenuhnya:
// Pinia — full type inference, zero configuration
const counter = useCounterStore()
// TypeScript knows counter.count is number
// TypeScript knows counter.doubleCount is number
// TypeScript knows counter.increment() returns void
// TypeScript knows counter.fetchCount() returns Promise<void>
counter.increment()
console.log(counter.doubleCount)Type inference bekerja end-to-end, dari state definition hingga component consumption. IDE autocomplete dan error checking berfungsi sempurna tanpa type assertions atau generic constraints manual.
Composition API dan Integrasi Composable
Pinia dirancang untuk bekerja seamless dengan Vue composables, memungkinkan reusable logic langsung di dalam store definition. Contoh berikut menggunakan VueUse untuk debouncing dan Vue Router untuk URL sync:
import { defineStore } from 'pinia'
import { ref, watch } from 'vue'
import { useDebounceFn } from '@vueuse/core'
import { useRoute } from 'vue-router'
export const useSearchStore = defineStore('search', () => {
const route = useRoute()
const query = ref('')
const results = ref<SearchResult[]>([])
const isLoading = ref(false)
// VueUse composable used directly inside the store
const debouncedSearch = useDebounceFn(async (term: string) => {
if (!term.trim()) {
results.value = []
return
}
isLoading.value = true
try {
const res = await fetch(`/api/search?q=${encodeURIComponent(term)}`)
results.value = await res.json()
} finally {
isLoading.value = false
}
}, 300)
// Watcher syncs URL query param to store state
watch(() => route.query.q, (q) => {
if (typeof q === 'string') {
query.value = q
debouncedSearch(q)
}
})
function setQuery(term: string) {
query.value = term
debouncedSearch(term)
}
return { query, results, isLoading, setQuery }
})Pattern ini memungkinkan business logic yang kompleks tetap berada di dalam store layer, sementara memanfaatkan ecosystem Vue composables yang sudah matang. Developer tidak perlu menulis custom plugins atau middleware untuk kebutuhan seperti debouncing, throttling, atau state persistence.
Siap menguasai wawancara Vue.js / Nuxt.js Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Komunikasi Antar Store dan Cross-Store References
Pinia memudahkan komunikasi antar store dengan memanggil store lain sebagai composable. Tidak ada namespace atau module paths yang perlu dikelola:
import { defineStore } from 'pinia'
import { useProductStore } from './products'
import { useAuthStore } from './auth'
import { ref, computed } from 'vue'
export const useCartStore = defineStore('cart', () => {
const items = ref<CartItem[]>([])
const total = computed(() => {
// Access another store by calling its composable
const productStore = useProductStore()
return items.value.reduce((sum, item) => {
const product = productStore.getById(item.productId)
return sum + (product?.price ?? 0) * item.quantity
}, 0)
})
async function checkout() {
const auth = useAuthStore()
if (!auth.isAuthenticated) {
throw new Error('Authentication required')
}
// Checkout logic
}
return { items, total, checkout }
})Pendekatan ini memberikan dependency graph yang eksplisit dan mudah di-trace. TypeScript akan warning jika terjadi circular dependency, dan hot module replacement tetap bekerja dengan benar karena setiap store adalah module terpisah.
Migrasi dari Vuex 4 ke Pinia 3
Proses migrasi dari Vuex ke Pinia mengikuti pola yang konsisten. Berikut contoh transformasi module Vuex menjadi Pinia store:
Pinia dan Vuex dapat berjalan bersamaan dalam satu aplikasi. Install Pinia bersamaan dengan Vuex, migrasikan satu module per waktu, dan hapus Vuex setelah semua modules dikonversi.
// Before: Vuex module
// store/modules/user.ts
const userModule = {
namespaced: true,
state: () => ({
profile: null as UserProfile | null,
preferences: {} as UserPreferences,
}),
mutations: {
SET_PROFILE(state, profile: UserProfile) {
state.profile = profile
},
UPDATE_PREFERENCES(state, prefs: Partial<UserPreferences>) {
state.preferences = { ...state.preferences, ...prefs }
},
},
actions: {
async fetchProfile({ commit }) {
const profile = await api.getProfile()
commit('SET_PROFILE', profile)
},
async updatePreferences({ commit }, prefs: Partial<UserPreferences>) {
await api.updatePreferences(prefs)
commit('UPDATE_PREFERENCES', prefs)
},
},
getters: {
isLoggedIn: (state) => state.profile !== null,
displayName: (state) => state.profile?.name ?? 'Guest',
},
}Setelah migrasi ke Pinia:
// After: Pinia store
// stores/user.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { api } from '@/lib/api'
export const useUserStore = defineStore('user', () => {
const profile = ref<UserProfile | null>(null)
const preferences = ref<UserPreferences>({})
const isLoggedIn = computed(() => profile.value !== null)
const displayName = computed(() => profile.value?.name ?? 'Guest')
async function fetchProfile() {
profile.value = await api.getProfile()
}
async function updatePreferences(prefs: Partial<UserPreferences>) {
await api.updatePreferences(prefs)
Object.assign(preferences.value, prefs)
}
return { profile, preferences, isLoggedIn, displayName, fetchProfile, updatePreferences }
})Perubahan kunci mencakup eliminasi mutations layer, penggunaan direct state assignment, dan transformasi getters menjadi computed properties. Component consumption berubah dari mapState, mapGetters, mapActions menjadi direct store instance usage.
SSR State Hydration dengan Pinia dan Nuxt 4
Pinia menyediakan built-in support untuk server-side rendering dengan state hydration otomatis. Pada Nuxt 4, Pinia terintegrasi natively tanpa konfigurasi tambahan:
Setup stores yang menggunakan composables seperti useRoute() atau useFetch() memerlukan penanganan khusus dalam konteks SSR. Composables ini hanya boleh dipanggil selama fase setup, tidak di dalam async callbacks.
import { defineStore } from 'pinia'
export const useProductStore = defineStore('products', {
state: () => ({
items: [] as Product[],
selectedCategory: 'all',
}),
actions: {
async fetchProducts() {
// useFetch is Nuxt-specific — use $fetch for store actions
this.items = await $fetch<Product[]>('/api/products', {
query: { category: this.selectedCategory },
})
},
},
getters: {
getById: (state) => (id: string) =>
state.items.find((item) => item.id === id),
filteredCount: (state) =>
state.items.filter((p) => p.inStock).length,
},
})State yang diisi di server otomatis di-serialize dan dikirim ke client, kemudian di-hydrate saat aplikasi boot di browser. Nuxt Pinia module menangani serialization, hydration, dan garbage collection secara otomatis. Developer hanya perlu memastikan state objects serializable dan tidak mengandung functions atau class instances.
Perbandingan Performa
Dari sisi bundle size, Pinia jauh lebih ringan. Runtime overhead Pinia sekitar 1 KB gzipped dibanding Vuex 6 KB gzipped, pengurangan sebesar 83%. Perbedaan ini signifikan untuk aplikasi dengan banyak stores atau environments dengan bandwidth terbatas.
Reactive updates di Pinia sedikit lebih cepat karena eliminasi mutations layer. Setiap state change di Vuex melewati mutation handler sebelum mencapai reactivity system, menambah overhead minimal namun measurable pada high-frequency updates. Pinia langsung memicu reactivity tanpa intermediary layer.
DevTools performance juga lebih baik di Pinia. Vue DevTools 7 dapat tree-shake stores yang tidak aktif, sementara Vuex single store architecture membuat seluruh state tree tetap observable bahkan saat tidak digunakan. Pada aplikasi besar dengan 20+ modules, perbedaan memory footprint bisa mencapai 15-20%.
Hot Module Replacement di Pinia bekerja per-store basis. Perubahan pada satu store tidak memicu reload stores lain, mempercepat development iteration. Vuex memerlukan manual HMR setup untuk behavior serupa.
Pertanyaan Interview yang Sering Muncul
Q: Mengapa Pinia menghilangkan mutations? Bukankah itu melanggar prinsip Flux architecture?
Mutations di Vuex diciptakan untuk strict mode debugging dan time-travel debugging. Namun, Vue DevTools modern dapat track state changes bahkan tanpa mutations layer. Pinia tetap mendukung DevTools timeline dan state inspection, sementara mengurangi boilerplate. Direct state mutations di Pinia tetap trackable karena Pinia wraps state dengan Proxy yang merecord semua changes. Developer mendapat debugging experience yang sama tanpa overhead menulis mutations terpisah.
Q: Bagaimana Pinia menangani code splitting untuk stores? Apakah semua stores di-bundle sekaligus?
Pinia stores adalah ES modules reguler, sehingga otomatis mendukung dynamic imports dan code splitting. Jika store dipanggil dalam async component atau lazy route, bundler akan memasukkan store ke chunk terpisah. Contoh: const useAdminStore = () => import('@/stores/admin') hanya di-load saat admin dashboard diakses. Vuex single store architecture membuat code splitting lebih sulit karena root store harus terdefinisi di entry point.
Q: Apakah Pinia kompatibel dengan Vuex plugins yang sudah ada? Bagaimana strategi migrasi plugin?
Pinia memiliki plugin system sendiri yang lebih simple namun powerful. Vuex plugins tidak compatible langsung, namun konsepnya mudah ditranslate. Vuex plugin menerima store instance dan mengakses subscribe(), sementara Pinia plugin menerima context dengan store, app, dan options. Untuk persistence plugin, gunakan pinia-plugin-persistedstate. Untuk DevTools integration, Pinia built-in support sudah mencakup mayoritas use cases.
Q: Bagaimana Pinia menangani optimistic updates dan rollback pada error?
Pinia tidak menyediakan built-in rollback mechanism seperti Redux atau Vuex strict mode, namun developer dapat implement manual snapshot. Pattern yang direkomendasikan adalah menyimpan previous state sebelum mutation, kemudian restore jika API call gagal. Untuk use cases kompleks, kombinasikan dengan TanStack Query atau VueUse useAsyncState yang memiliki rollback bawaan. Pattern ini lebih eksplisit dan testable dibanding implicit rollback.
Q: Apakah Pinia mendukung store composition dan reusable store logic?
Ya, melalui composables. Developer dapat membuat reusable functions yang menerima store instance atau mengembalikan partial store definition. Contoh: usePageable(store) composable dapat menambahkan pagination logic ke store manapun. Setup Store syntax memudahkan pattern ini karena store definition adalah function biasa. Options Store juga mendukung via action composition, namun Setup Store lebih natural untuk reusable logic. Lihat modul Vue composables untuk pattern detail.
Untuk persiapan interview lebih lanjut, pelajari juga modul interview state management yang membahas state management patterns secara mendalam.
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Kesimpulan
Migrasi dari Vuex ke Pinia membawa peningkatan signifikan dalam developer experience, type safety, dan performa runtime:
- Eliminasi mutations mengurangi boilerplate dan mental overhead tanpa mengorbankan debuggability
- Full TypeScript inference bekerja tanpa manual type augmentation atau generic constraints
- Bundle size 83% lebih kecil dengan runtime overhead minimal dan tree-shaking optimal
- Native Composition API support memungkinkan penggunaan ecosystem Vue composables langsung di stores
- SSR hydration otomatis pada Nuxt 4 tanpa konfigurasi tambahan atau manual serialization
- Code splitting per-store memudahkan lazy loading dan performance optimization
- Flat store architecture menghilangkan nested module complexity dan namespace conflicts
Untuk proyek Vue 3 baru di tahun 2026, Pinia adalah pilihan default yang direkomendasikan official Vue team. Migrasi dari Vuex dapat dilakukan secara incremental, memungkinkan kedua libraries coexist selama transisi period.
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Tag
Bagikan
Artikel terkait

Pertanyaan wawancara Vue.js: 25 pertanyaan untuk meraih pekerjaan
Persiapkan wawancara Vue.js dengan 25 pertanyaan penting ini. Dari reaktivitas hingga composables, kuasai konsep utama untuk wawancara berikutnya.

Nuxt 3: SSR dan generasi statis, panduan lengkap
Menguasai SSR dan generasi statis dengan Nuxt 3. Dari useFetch hingga route rules, pelajari cara mengoptimalkan performa aplikasi Vue.js.

Vue 3 Composition API: Panduan Lengkap Menguasai Reaktivitas
Kuasai Vue 3 Composition API melalui panduan praktis ini. Pelajari ref, reactive, computed, watch, dan composables untuk membangun aplikasi Vue yang berkinerja tinggi.