Vue 3 Pinia vs Vuex: Modern State Management en Sollicitatievragen 2026

Pinia vs Vuex vergeleken: API-ontwerp, TypeScript-ondersteuning, performance, migratiestrategieën en veelgestelde Vue state management sollicitatievragen voor 2026.

Vue 3 Pinia vs Vuex state management vergelijkingsdiagram

Pinia vs Vuex vertegenwoordigt de meest significante verschuiving in Vue state management sinds de lancering van Vue 3. Nu Pinia 3 de officiële aanbeveling is en Vuex alleen nog in onderhoudsmodus wordt beheerd, is het begrijpen van de verschillen tussen beide bibliotheken essentieel voor Vue-ontwikkelaars — en een veelvoorkomend onderwerp tijdens technische sollicitatiegesprekken.

Kernpunt

Pinia is de officiële state management oplossing voor Vue 3. Vuex 5 is geannuleerd, en Evan You heeft Pinia aangeduid als het de facto Vuex 5. Alle nieuwe Vue 3-projecten dienen Pinia 3 te gebruiken.

Pinia vs Vuex: Fundamentele Architectuurverschillen

Het fundamentele architectuurverschil tussen Pinia en Vuex ligt in de manier waarop state-mutaties plaatsvinden. Vuex dwingt een strikte unidirectionele dataflow af: componenten dispatchen actions, actions committen mutations, en mutations wijzigen de state. Pinia verwijdert de mutation-laag volledig en staat directe state-wijzigingen toe vanuit actions of zelfs vanuit componenten.

Deze vereenvoudiging vermindert boilerplate-code met ongeveer 40% in de meeste codebases. Waar Vuex vier concepten vereist (state, getters, mutations, actions), werkt Pinia met drie (state, getters, actions).

| Kenmerk | Pinia 3 | Vuex 4 | |---------|---------|--------| | Mutations | Geen (directe state-wijzigingen) | Vereist voor state-wijzigingen | | TypeScript | Volledige inferentie, geen augmentation | Handmatige type-augmentation nodig | | Store-architectuur | Meerdere platte stores | Enkele store met geneste modules | | Composition API | Native ondersteuning | Options API gebaseerd | | Bundelgrootte | ~1 KB gzipped | ~6 KB gzipped | | Vue Devtools | Volledige ondersteuning (v7) | Volledige ondersteuning | | SSR | Ingebouwd | Configuratie vereist | | Hot Module Replacement | Ingebouwd | Handmatige setup |

Stores Definiëren: Options API vs Setup-Syntax

Pinia biedt twee syntaxvarianten voor het definiëren van stores. De Options-syntax weerspiegelt de structuur van Vuex, wat migratie vergemakkelijkt. De Setup-syntax benut de Composition API van Vue 3 voor maximale flexibiliteit.

stores/counter-options.tstypescript
import { defineStore } from 'pinia'

// Options Store syntax — vertrouwd voor Vuex-ontwikkelaars
export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    lastUpdated: null as Date | null,
  }),
  getters: {
    // Getters ontvangen state als eerste argument met volledige type-inferentie
    doubleCount: (state) => state.count * 2,
    isPositive(): boolean {
      // Toegang tot andere getters via `this`
      return this.count > 0
    },
  },
  actions: {
    increment() {
      // Directe state-mutatie — geen commit() nodig
      this.count++
      this.lastUpdated = new Date()
    },
    async fetchCount(id: string) {
      // Asynchrone actions werken zonder extra configuratie
      const response = await fetch(`/api/counters/${id}`)
      const data = await response.json()
      this.count = data.count
    },
  },
})

De Options-syntax vertaalt Vuex-concepten rechtstreeks. State vervangt Vuex state, getters blijven getters, en actions nemen zowel Vuex actions als mutations over.

stores/counter-setup.tstypescript
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

// Setup Store syntax — identieke patronen als Composition API
export const useCounterStore = defineStore('counter', () => {
  // ref() wordt state
  const count = ref(0)
  const lastUpdated = ref<Date | null>(null)

  // computed() wordt getters
  const doubleCount = computed(() => count.value * 2)
  const isPositive = computed(() => count.value > 0)

  // Functies worden 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
  }

  // Alle state, getters en actions moeten worden geretourneerd
  return { count, lastUpdated, doubleCount, isPositive, increment, fetchCount }
})

De Setup-syntax biedt volledige flexibiliteit: watchers, hergebruik van composables en conditionele logica werken op natuurlijke wijze binnen de store-definitie. De keerzijde is dat elke reactieve eigenschap en methode expliciet moet worden geretourneerd.

TypeScript-Integratie: Waar Pinia Vuex Overtreft

TypeScript-ondersteuning is het gebied waar Pinia overtuigend wint. Vuex 4 vereist handmatige type-augmentation via module-declaraties, en bij complexe geneste modules wordt type-inferentie broos. Pinia leidt types automatisch af uit de store-definitie.

typescript
// Vuex 4 — handmatige type-augmentation vereist
import { Store } from 'vuex'

declare module 'vuex' {
  interface State {
    counter: {
      count: number
      lastUpdated: Date | null
    }
  }
}

// Toegang tot state vereist type assertions of aangepaste helpers
const count = (store.state as { counter: { count: number } }).counter.count
typescript
// Pinia — volledige type-inferentie, geen configuratie
const counter = useCounterStore()

// TypeScript herkent counter.count als number
// TypeScript herkent counter.doubleCount als number
// TypeScript herkent counter.increment() als void
// TypeScript herkent counter.fetchCount() als Promise<void>
counter.increment()
console.log(counter.doubleCount)

Pinia ondersteunt bovendien generieke stores voor herbruikbare patronen — een mogelijkheid die aanzienlijke boilerplate vereist in Vuex.

Composition API en Composable-Integratie

Pinia Setup stores accepteren elke Vue composable, wat krachtige patronen mogelijk maakt zoals gedeelde VueUse-utilities of router-bewuste state.

stores/search.tstypescript
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 direct in de store gebruikt
  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 synchroniseert URL query parameter met 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 }
})

Dit patroon is onmogelijk in Vuex zonder workarounds. Vuex-modules kunnen geen composables, watchers of router-hooks gebruiken binnen hun definitie. De store-logica moet worden opgesplitst tussen de Vuex-module en externe hulpfuncties.

Klaar om je Vue.js / Nuxt.js gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

Store-Communicatie en Onderlinge Referenties

Vuex gebruikt een enkele root-store met namespaced modules. Toegang tot state over modules heen vereist de omslachtige rootState- en rootGetters-parameters. Pinia stores zijn bewust onafhankelijk ontworpen, en communicatie tussen stores verloopt via directe imports.

stores/cart.tstypescript
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(() => {
    // Toegang tot een andere store door het aanroepen van de 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('Authenticatie vereist')
    }
    // Checkout-logica
  }

  return { items, total, checkout }
})

Deze platte store-architectuur voorkomt de diep geneste module-bomen die grote Vuex-applicaties moeilijk navigeerbaar en refactorbaar maken.

Migratie van Vuex 4 naar Pinia 3

Migratie van Vuex naar Pinia kan module voor module plaatsvinden. Beide bibliotheken kunnen naast elkaar bestaan gedurende de overgangsperiode. De aanbevolen strategie: begin met blad-modules (stores waar geen andere modules van afhankelijk zijn) en werk naar binnen toe.

Migratiecompatibiliteit

Pinia en Vuex kunnen naast elkaar draaien in dezelfde applicatie. Installeer Pinia naast Vuex, migreer één module tegelijk en verwijder Vuex pas nadat alle modules zijn geconverteerd.

typescript
// Voorheen: 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',
  },
}
typescript
// Nadien: 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 }
})

Het migratiepatroon is consistent: mutations worden samengevoegd tot directe toewijzingen binnen actions, commit()-aanroepen verdwijnen, en mapState/mapGetters-helpers worden vervangen door destructurering van de store-composable.

SSR State Hydration met Pinia en Nuxt 4

Server-side rendering introduceert complexiteit voor state management. Pinia verzorgt SSR state-serialisatie en hydration automatisch in Nuxt 4, terwijl Vuex handmatige window.__INITIAL_STATE__-afhandeling vereist.

SSR-Valkuil

Setup stores die composables zoals useRoute() of useFetch() gebruiken, vereisen zorgvuldige afhandeling in SSR-contexten. Deze composables mogen alleen worden aangeroepen tijdens de setup-fase, nooit binnen asynchrone callbacks.

stores/products.ts — SSR-veilige Pinia store voor Nuxt 4typescript
import { defineStore } from 'pinia'

export const useProductStore = defineStore('products', {
  state: () => ({
    items: [] as Product[],
    selectedCategory: 'all',
  }),
  actions: {
    async fetchProducts() {
      // useFetch is Nuxt-specifiek — gebruik $fetch voor 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,
  },
})

Nuxt 4 serialiseert de Pinia-state automatisch op de server en hydrateert deze op de client. Geen handmatige replaceState()- of dehydratatielogica nodig.

Performancevergelijking en Bundelimpact

De footprint van Pinia van ~1 KB gzipped is ongeveer zes keer kleiner dan de ~6 KB van Vuex. In applicaties met code splitting zijn Pinia stores tree-shakable: ongebruikte stores en hun afhankelijkheden worden uitgesloten van productiebundels.

Runtime-performanceverschillen zijn verwaarloosbaar voor de meeste applicaties. Beide bibliotheken gebruiken onder de motorkap het reactiviteitssysteem van Vue. De praktische performancewinst komt voort uit verminderde boilerplate die leidt tot minder re-renders veroorzaakt door te brede state-subscriptions — het platte store-model van Pinia stimuleert granulaire stores, wat betekent dat componenten minder state abonneren.

Met de introductie van Vapor Mode in Vue 3.6 profiteren zowel Pinia als Vuex van de snellere rendering-pipeline. Pinia is echter door de nauwere integratie met de Composition API beter gepositioneerd voor Vapor Mode-optimalisaties.

Veelgestelde Vue State Management Sollicitatievragen

Technische sollicitatiegesprekken testen regelmatig kennis over state management. Hier volgen de vragen die in 2026 het vaakst worden gesteld in Vue-ontwikkelaarsgesprekken.

V: Waarom is de mutation-laag van Vuex verwijderd in Pinia?

Mutations bestonden in Vuex om time-travel debugging in DevTools mogelijk te maken. Elke state-wijziging moest via een synchrone mutation verlopen zodat DevTools snapshots konden vastleggen. Pinia bereikt dezelfde debugging-mogelijkheid door state-wijzigingen op reactief niveau te tracken, waardoor de expliciete mutation-laag overbodig wordt. Het resultaat: minder boilerplate, dezelfde debugging-kracht.

V: Wanneer verdient een Setup store de voorkeur boven een Options store?

Setup stores zijn de betere keuze wanneer de store composables nodig heeft (useRoute, useDebounceFn), complexe watchers, of gedeelde logica uit externe composable-functies. Options stores werken goed voor eenvoudige CRUD-state met basale getters. In de praktijk komen Setup stores vaker voor in productie-codebases omdat ze de Composition API-patronen in componenten weerspiegelen.

V: Hoe behandelt Pinia reactiviteit voor geneste objecten?

Pinia gebruikt Vue's reactive() voor het gehele state-object en ref() voor individuele eigenschappen in Setup stores. Diepe reactiviteit geldt standaard — mutaties van geneste objecten worden automatisch getrackt. Voor performance-gevoelige gevallen met grote datasets kan shallowRef() diepe reactiviteit uitschakelen.

V: Leg store-tot-store afhankelijkheden uit in Pinia vs Vuex.

In Vuex maakt cross-module toegang gebruik van rootState- en rootGetters-parameters in actions, wat impliciete koppeling creëert. In Pinia importeren stores elkaar direct via useOtherStore()-aanroepen. Dit maakt afhankelijkheden expliciet en stelt TypeScript in staat deze tijdens compilatie te verifiëren. Circulaire afhankelijkheden werken in Pinia zolang ze niet worden aangeroepen tijdens de initiële setup-fase.

V: Wat gebeurt er met Pinia-state tijdens SSR-hydration?

Tijdens SSR serialiseert Pinia alle actieve store-states naar JSON en sluit het resultaat in de HTML-payload in. Op de client hydrateert Pinia elke store voordat componenten worden gemount. Elke state die tijdens server-side rendering is ingesteld, is direct beschikbaar op de client zonder dubbele API-aanroepen. In Nuxt 4 is dit proces volledig automatisch.

Voor meer Vue- en Nuxt-sollicitatievoorbereiding zijn het state management interviewmodule en het Vue composables module beschikbaar.

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

Conclusie

  • Pinia 3 is de officiële Vue state management bibliotheek — Vuex bevindt zich in onderhoudsmodus zonder geplande v5-release
  • De mutation-laag is verdwenen: Pinia actions wijzigen state direct, wat boilerplate met ~40% vermindert
  • TypeScript-inferentie werkt direct uit de doos in Pinia zonder augmentation, in tegenstelling tot de handmatige type-declaraties van Vuex
  • Setup stores integreren naadloos met de Vue 3 Composition API, wat hergebruik van composables en watchers binnen stores mogelijk maakt
  • Platte store-architectuur vervangt de geneste module-boom van Vuex, waardoor communicatie tussen stores expliciet en type-safe wordt
  • Migratie kan incrementeel plaatsvinden: Pinia en Vuex bestaan naast elkaar, wat module-voor-module conversie mogelijk maakt
  • SSR-hydration is automatisch in Nuxt 4 met Pinia — geen handmatige serialisatie nodig
  • Voor alle nieuwe Vue 3-projecten in 2026 is Pinia de duidelijke keuze met een betere developer experience, kleinere bundels en sterkere ecosysteemondersteuning

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

Tags

#vue
#pinia
#vuex
#state-management
#interview

Delen

Gerelateerde artikelen