Fredrik Mäkilä

3 min read

Vue.js Do you really need Vuex?

I recently wanted to create a basic marketing website for my consulting firm. For this, I needed to create some shared state. I am using Nuxt and adding Vuex support to achieve this is really easy. It did feel like overkill, however, so I started looking for options.

I was going to implement dark mode through a toggle, and I wanted to make use of Tailwind's dark mode to do so. That meant adding a dark class somewhere at the root of the dom tree. I was using Typescript (perhaps also a bit overkill, but I like it), so to set up Vuex is a bit more involved to get it to play well with Nuxt. I wanted two-way data flow so that the toggle could be updated in case the media query for dark mode preference changes. This would have been really straight forward with Vuex, but I wanted something simpler.

Option 1: Event Bus

An event bus is a pretty neat way to pass data between components using events. The basic idea is that a component can emit an event on the event bus and any number of other components can then subscribe to that event.

eventBus.js
import Vue from "vue";
const eventBus = new Vue();
export default eventBus;
MySender.vue
<script>
import eventBus from './eventBus'
export default {
  methods: {
    send () {
      eventBus.$emit('message', 'hello')
    }
  }
}
</script>
MyReceiver.vue
<script>
import eventBus from './eventBus';
export default {
  mounted () {
    eventBus.$on('message', message => {
      console.log(message); // 'hello'
    })
  }
}
</script>

The biggest benefit from this technique can be had when two components do not have a direct parent and child relationship. The example above is only possible in Vue 2.x. For Vue 3 you can use something like Mitt to achieve the same thing.

The downside, and the reason why I started to look further than this technique, is that it is designed for communication one way. For my use case, that was a requirement so further research was needed.

Option 2: Vue.observable

Reactivity in Vue is a funny thing and can sometimes be tricky to get right. To turn any object reactive we can use Vue.observable (renamed reactive in Vue 3).

state.js
import Vue from "vue";

const state = {
  message: "",
  edits: 0
};

export default Vue.observable({
  get message() {
    return state.message;
  },

  set message(message) {
    state.message = message;
    state.edits++;
  }
});

By using the get and set syntax it is possible to introduce side effects whenever some property changes, which is quite neat. The example above can simply be used with v-model, making it really easy to use.

MyInput.vue
<template>
  <input v-model="state.message" />
</template>

<script>
import state from "../state";

export default {
  data() {
    return {
      state,
    };
  },
};
</script>

Summary

These are some alternatives to Vuex. For state that needs to be immediately reactive, I prefer the Vue.observable approach. In cases where an event triggers, for example, a backend request I think the event bus is useful. Better than either of these approaches is when components can communicate with each other as a parent and child. When that is not possible using Vuex or these techniques is a good solution.

Full examples can be found in the following Codesandbox.