Everyone talks about Vuex, but do you really need it? Here’s my take after using it in production for 6 months.

What is Vuex?

Vuex is a state management library for Vue. Think Redux for Vue, but simpler.

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++
    }
  },
  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => {
        commit('increment')
      }, 1000)
    }
  }
})

When You DON’T Need Vuex

Small Apps (< 10 Components)

For small apps, props and events are enough:

<!-- Parent.vue -->
<template>
  <Child :user="user" @update="handleUpdate" />
</template>

<script>
export default {
  data() {
    return { user: {} }
  },
  methods: {
    handleUpdate(newUser) {
      this.user = newUser
    }
  }
}
</script>

Simple Data Flow

If data flows in one direction (parent → child), you don’t need Vuex.

No Shared State

If components don’t share state, Vuex is overkill.

When You DO Need Vuex

Multiple Components Need Same Data

// Without Vuex - prop drilling hell
<GrandParent>
  <Parent :user="user">
    <Child :user="user">
      <GrandChild :user="user" />
    </Child>
  </Parent>
</GrandParent>

// With Vuex - access anywhere
computed: {
  user() {
    return this.$store.state.user
  }
}

Complex State Logic

When state updates involve multiple steps:

actions: {
  async login({ commit }, credentials) {
    commit('SET_LOADING', true)
    try {
      const user = await api.login(credentials)
      commit('SET_USER', user)
      commit('SET_AUTHENTICATED', true)
    } catch (error) {
      commit('SET_ERROR', error)
    } finally {
      commit('SET_LOADING', false)
    }
  }
}

Need Time-Travel Debugging

Vuex integrates with Vue DevTools for time-travel debugging. Super useful for tracking down bugs.

Our Vuex Structure

store/
├── index.js
├── modules/
│   ├── auth.js
│   ├── users.js
│   └── products.js
└── plugins/
    └── logger.js

Each module handles its own domain:

// store/modules/auth.js
export default {
  namespaced: true,
  state: {
    user: null,
    token: null
  },
  mutations: {
    SET_USER(state, user) {
      state.user = user
    },
    SET_TOKEN(state, token) {
      state.token = token
    }
  },
  actions: {
    async login({ commit }, credentials) {
      const { user, token } = await api.login(credentials)
      commit('SET_USER', user)
      commit('SET_TOKEN', token)
    }
  },
  getters: {
    isAuthenticated: state => !!state.token
  }
}

Best Practices

1. Use Modules

Don’t put everything in one store. Split by domain.

2. Use Getters for Computed State

getters: {
  activeUsers: state => {
    return state.users.filter(u => u.active)
  }
}

3. Actions for Async, Mutations for Sync

// Good
actions: {
  async fetchUsers({ commit }) {
    const users = await api.getUsers()
    commit('SET_USERS', users)
  }
}

// Bad - async in mutation
mutations: {
  async SET_USERS(state) {
    state.users = await api.getUsers() // Don't do this!
  }
}

4. Use Constants for Mutation Types

// mutation-types.js
export const SET_USER = 'SET_USER'
export const SET_LOADING = 'SET_LOADING'

// store.js
import * as types from './mutation-types'

mutations: {
  [types.SET_USER](state, user) {
    state.user = user
  }
}

Common Mistakes

1. Putting Everything in Vuex

Not everything needs to be in the store. Component-local state is fine for UI state (modals, tabs, etc.).

2. Mutating State Directly

// Bad
this.$store.state.user.name = 'John'

// Good
this.$store.commit('SET_USER_NAME', 'John')

3. Not Using Namespaces

Without namespaces, all mutations/actions are global. Use namespaced: true.

Performance

Vuex is fast. We have a store with 10,000+ items, and it’s still snappy. Vue’s reactivity system handles it well.

Testing

Vuex makes testing easier:

import { mutations } from './store'

describe('mutations', () => {
  it('SET_USER', () => {
    const state = { user: null }
    mutations.SET_USER(state, { name: 'John' })
    expect(state.user.name).toBe('John')
  })
})

The Verdict

Use Vuex if:

  • App has > 10 components
  • Multiple components share state
  • State logic is complex
  • You need time-travel debugging

Don’t use Vuex if:

  • App is small
  • Data flow is simple
  • No shared state

For us, Vuex was the right choice. Our app has 50+ components with lots of shared state. Vuex keeps it manageable.

Start without Vuex. Add it when you feel the pain of prop drilling and event bubbling.

Questions? Ask away!