Master Vue state management architectures. Compare Pinia and Vuex stores, TypeScript integration, module systems, and how the absence of mutations impacts store design.

Mark it as completed to track your progress, or bookmark it to review later.
Join senior engineers who receive practical, deep-dive frontend challenges, detailed concepts, and blueprints directly in their inbox.
We value your privacy. Unsubscribe at any time.
Expand your mastery. Deep dive into other frontend interview challenges in this category.
Learn why mutating props directly in Vue.js is an anti-pattern. Master correct patterns like using local data copies, computed properties, and emitting events to update parent state.
Understand the core differences between v-if and v-show in Vue.js. Learn about DOM lifecycle changes, mounting, rendering costs, and best practices for performance optimization.
Pinia is now the official state management library recommended for Vue. It replaces Vuex as the primary store module, offering a simpler API, native TypeScript typing, and a modular architecture.
Interviewers evaluate whether you can compare different state management strategies:
In Vuex, states can only be mutated inside Mutations, which must be synchronous. Actions perform asynchronous tasks and commit mutations.
// Traditional Vuex Store
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
// ONLY synchronous state changes allowed
INCREMENT(state) {
state.count++
}
},
actions: {
// Asynchronous work goes here
async incrementAsync({ commit }) {
await delay(1000)
commit('INCREMENT')
}
}
})In Vuex, actions commit mutations using string identifiers (e.g. commit('INCREMENT')). This makes it extremely difficult for TypeScript compilers to trace parameter types, resulting in weak compile-time type safety.
Pinia merges actions and mutations. Actions can update the state directly and perform both synchronous and asynchronous tasks.
// Modern Pinia Store
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
// Updates state directly and can run asynchronously
increment() {
this.count++
},
async incrementAsync() {
await delay(1000)
this.count++
}
}
})Pinia also supports defining stores like standard composables, mapping refs to state, computed to getters, and functions to actions:
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})In Vuex, separating mutations and actions made it easy to track state changes in DevTools. Modern devtools hook directly into Pinia's getter/setter proxy triggers, allowing full tracking and time travel without needing a separate mutations wrapper.
this.$store.dispatch('users/profiles/fetch').const userStore = useUserStore().| Metric | Vuex | Pinia |
|---|---|---|
| Structure | State, Getters, Mutations, Actions. | State, Getters, Actions (Mutations removed). |
| Type Safety | Weak (depends on magic string triggers). | Native (fully typed automatically). |
| Architecture | Single global store tree. | Flat modular imports. |
| Setup Options | Options API only. | Options API or Composition API (Setup Store). |
| Bundle Size | ~10kb. | ~1.5kb (highly tree-shakable). |