# State Management in Vue with Vuex

> Centralizing shared state across components using Vuex: state, mutations, actions, and getters explained.

- Published: 2017-04-02
- Category: Interface
- Tags: Vue.js
- Reading time: 3 min read
- Source: https://www.muhammetsafak.com.tr/en/blog/state-management-in-vue-with-vuex/
- Language: en-US
- Author: Muhammet Şafak

---
[When I first started writing component-based UIs with Vue](/en/blog/first-reactive-component-with-vue-js/), I got by with `$emit` and `props` for a while. That works fine for small apps. But once the number of components grows and unrelated sibling components need to share the same data, that approach hits a wall. Tracking who is sending what to whom becomes genuinely painful.

**Vuex** is the centralized state management library built for Vue. It is designed to consolidate shared data in a single place and ensure that all components read from the same source and update it according to the same rules.

## Core concepts

Vuex has four fundamental building blocks:

- **State:** The single source of truth for the application. All components read from here.
- **Mutation:** The only way to change state. Must be synchronous.
- **Action:** Handles asynchronous operations; commits a mutation when done.
- **Getter:** Derived, computed values calculated from state.

A simple shopping cart example:

```javascript
const store = new Vuex.Store({
  state: {
    cartItems: []
  },
  mutations: {
    ADD_ITEM(state, item) {
      state.cartItems.push(item);
    },
    REMOVE_ITEM(state, itemId) {
      state.cartItems = state.cartItems.filter(i => i.id !== itemId);
    }
  },
  actions: {
    addToCart({ commit }, item) {
      // An async check could go here, e.g. a stock availability query
      commit('ADD_ITEM', item);
    }
  },
  getters: {
    cartTotal(state) {
      return state.cartItems.reduce((sum, item) => sum + item.price, 0);
    }
  }
});
```

## Accessing the store from a component

Once the store is injected into the Vue instance, every component can reach it via `this.$store`:

```javascript
// Product component
methods: {
  addProduct(product) {
    this.$store.dispatch('addToCart', product);
  }
},
computed: {
  total() {
    return this.$store.getters.cartTotal;
  }
}
```

The cart component and the product list component can live in completely different parts of the tree — because both read from the same store, synchronization happens automatically.

## mapState and mapGetters

Writing `this.$store.state.cartItems` every time gets tedious. Vuex's helper functions cut that down:

```javascript
import { mapState, mapGetters } from 'vuex';

computed: {
  ...mapState(['cartItems']),
  ...mapGetters(['cartTotal'])
}
```

This makes `this.cartItems` and `this.cartTotal` directly available as computed properties.

## The difference between mutations and actions

When I first saw this split I thought it was unnecessary: why have both mutations and actions? The answer is all about asynchronous operations.

A mutation must always be **synchronous**. This rule is what allows Vuex DevTools to record state changes in the correct order. A non-synchronous mutation makes it nearly impossible for the tooling to track what changed and when.

Actions take on the async work. Make the API call, receive the result, then commit the mutation:

```javascript
actions: {
  async fetchCart({ commit }, userId) {
    const response = await fetch(`/api/cart/${userId}`);
    const items = await response.json();
    commit('SET_CART', items);
  }
}
```

This separation can feel overly strict at first, but you come to appreciate it over time. Because every state change goes through a mutation, you can see exactly why and when each change happened, right there in DevTools.

## When does Vuex actually make sense?

Not every Vue project needs Vuex. For a short-lived form UI with just a few components, `props` and `$emit` are perfectly sufficient. Consider Vuex when:

- Multiple unrelated components need to read the same data.
- Data fetched from the server is used in many parts of the application.
- A single user action affects more than one component.

If two or three of those apply, setting up a store requires a bit of upfront work but pays off quickly.

One warning though: jumping in too early and pushing everything into the store is its own trap. Moving state that genuinely belongs to a single component — like a form's `isLoading` flag — into the store adds unnecessary complexity. The rule is simple: if data concerns only one component, keep it local; if it concerns more than one, move it to the store.

## Splitting the store with modules

As an application grows, a single store file starts to balloon. Vuex modules let you break it into pieces:

```javascript
const cartModule = {
  namespaced: true,
  state: { items: [] },
  mutations: { /* ... */ },
  actions: { /* ... */ }
};

const store = new Vuex.Store({
  modules: {
    cart: cartModule,
    user: userModule
  }
});
```

With `namespaced: true`, access becomes `this.$store.dispatch('cart/addToCart', item)`. Without that option, modules sharing a mutation name all register under the global namespace, so a single commit fires every one of them — a rare but deeply annoying bug.

## Inspecting with Vuex DevTools

The Vue DevTools browser extension becomes significantly more useful when paired with Vuex. You can see every mutation — what data it carried, exactly when it fired — and step through state history with time-travel debugging. Tracking down the source of odd UI behavior gets genuinely easier with this tool.

When I first started using Vuex it felt like over-engineering. Then I recalled the code where I was trying to pass a single `cartItems` array to five different components through a chain of `$emit` calls — and suddenly understood why the store exists.
