Should You Use Composition API as a Replacement for Vuex?


The Composition API has freed reactive data from Vue components and instances, allowing it to be passed around like any JavaScript object. This has an obvious application - global reactive state. If this can be achieved, what do you need Vuex for?

In this article, I'll show you how you can replace Vuex with features of the Composition API, and also make the case for whether this is a wise idea or not.

If you want to brush up on the concepts of Vuex, first read WTF is Vuex? A Beginner's Guide To Vue's Application Data Store. And if you aren't familiar with the Vue Composition API, you can learn about that here.

Global reactive state with Composition API

The main reason Vuex exists is to allow global reactive state in a Vue app. Without it, you could only share local component state via the component interface i.e. props/events. Passing props and events from a component four of five layers deep is a type of drudgery all too familiar to Vue users.

However, the Composition API (packaged with Vue 3) allows you to create independent reactive variables outside of a component (and, in fact, outside of a Vue app). You can, for example, create a module file exporting reactive variables and import this into any component where you want to use it.

DIY Vuex with Composition API

Let's see how we can roll our own Vuex using the Composition API. Firstly, we'll create a file to declare and export global state variables.

Let's use the reactive method to create a state object with a value count, assuming we want to use this count value throughout the app.

src/global.js

import { reactive } from "vue";

const state = reactive({
  count: 0
});

export default { state };

Vuex pattern

As the Vuex documentation says - Vuex is both a pattern and a library. To make state predictable, it's important that variables aren't directly mutated.

We can implement that easily enough in our DIY Vuex. We'll use the readonly method which creates a read-only copy of our state. We'll also provide a method increment which is what users will use to change the value of count (this is akin to a Vuex mutation).

src/global.js

import { reactive, readonly } from "vue";

const state = reactive({
  count: 0
});

const increment = function () {
  state.count++;
}

export default { state: readonly(state), increment };

Installing the store with provide/inject

The easiest way to use our DIY Vuex store is with the provide/inject feature. So we'll import this module in the app's root instance, then provide it so it's available to all child components.

src/main.js

import { createApp } from "vue";
import global from "@/global";

const app = createApp({
  provide: {
    global
  },
  ...
}

Now, we can access it by using inject:

src/components/MyComponent.vue

<template></template>
<script>
export default {
  inject: ["global"]
}
</script>

The states values and methods can be used now:

src/components/MyComponent.vue

<template>
  <div>{{ global.state.count }}
  <button @click="global.increment">Increment</button>
</template>
<script>
export default {
  inject: ["global"]
}
</script>

So should you ditch Vuex?

We've seen how we can roll our own Vuex using Composition API. In doing so, we've overcome a lot of the complaints about Vuex because we have:

  • Minimal boilerplate
  • No esoteric naming like "mutations", "commits", etc
  • No additional dependencies.

So why not ditch Vuex altogether?

Advantage of Vuex 1: debugging

Even though we've copied the global reactive state mechanism, one huge advantage of Vuex that we have not copied is it's debugging capabilities.

Firstly, all mutations are tracked with Vuex, allowing you to see in Vue Devtools what component changed the state and how.

Secondly, there's time-travel debugging. This is a feature of Vuex where you can select any previous state of the app.

Advantage of Vuex 2: plugins

Another advantage of Vuex is the plugin ecosystem. For example, the vuex-persisted state plugin allows you to persist app state in local storage, and the Vue Router plugin which syncs the current route data in store state.

All the existing plugins could indeed be replicated as Composition API utilities, but without the standardized structure of Vuex, they wouldn't be plug and play.

Conclusion

There's no harm in creating a simple module for global reactive state using Composition API in a small project, or if it's really important to avoid any overhead.

But the debugging capabilities of Vuex still make it an essential tool for large-scale Vue app development and I can't imagine I'd stop using it in this case.

Looking forward to what's next for Vuex in version 5.

Further reading


Anthony Gore

About Anthony Gore

I'm Anthony Gore and I'm a web developer with a crush on Vue.js. I'm a Vue Community Partner, curator of the weekly Vue.js Developers Newsletter, and the creator of Vue.js Developers.

If you enjoyed this article, show your support by buying me a coffee. You might also enjoy taking one of my online courses!

Click to load comments...