Back to journal
·2 min readVue.jsVueVuexPiniaState ManagementComparison

State Management Showdown: Vuex vs Pinia

Vuex vs Pinia on a toy counter app. API shape, TypeScript, and which one I'd pick for a new project today.

ShareCopy failed

Vue apps eventually need shared state. Vuex was the default for years. Pinia is what most new Vue 3 projects reach for now. Here's how they differ on a stupid-simple counter.

Vuex

Central store, mutations for writes, actions for async, getters for derived values. Powerful, verbose. Feels like Redux with Vue seasoning.

Pinia

Lighter API, multiple stores, plays nicely with the Composition API. Types infer without fighting the compiler. DevTools still work.

Side by side

Vuex wants mutations for every state change. Pinia lets actions mutate directly (still keep them tidy). Vuex modules nest under one tree. Pinia stores stay independent, which helps code splitting.

TypeScript: Pinia wins for most teams. Vuex types are doable but annoying.

Performance differences are real but rarely your bottleneck unless the store is huge.

Counter example

Vuex

// store.js
const state = {
  count: 0,
};

const mutations = {
  increment(state, amount) {
    state.count += amount;
  },
};

const getters = {
  doubleCount(state) {
    return state.count * 2;
  },
};

export default new Vuex.Store({
  state,
  mutations,
  getters,
});

// component.vue
computed: {
  doubledCount() {
    return this.$store.getters.doubleCount;
  },
},
methods: {
  incrementCount() {
    this.$store.commit('increment', 1);
  },
},

Pinia

// store.js
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
  }),
  actions: {
    increment(amount) {
      this.count += amount;
    },
  },
  getters: {
    doubleCount() {
      return this.count * 2;
    },
  },
});

// component.vue
import { useCounterStore } from 'store.js';

const counterStore = useCounterStore();

computed: {
  doubledCount() {
    return counterStore.doubleCount;
  },
},
methods: {
  incrementCount() {
    counterStore.increment(1);
  },
},

Same idea: store number, bump it, read doubled value. Pinia just has less ceremony.

Choosing

Legacy Vue 2 app on Vuex? Don't migrate for fun. New Vue 3 app? Pinia unless you're copying an old Vuex tutorial.

Big enterprise app with strict mutation audit trails? Vuex still has fans. Small to medium SPA? Pinia's ergonomics usually win.

Docs: Vuex · Pinia

Try both on a branch. The one that feels boring to maintain is probably the right one.

ShareCopy failed