Awesome breaking changes in Vue 3, if you migrate from Vue 2

chenxeed profile image Albert Mulia Shintra Updated on ・5 min read


Thanks to the Vue Core team, we can try out the most anticipated Vue 3 which has a lot of great improvements and features to look forward.

Currently it's in the beta release, and if you want to get hands-on with the latest changes you can try to install it and followup the changes in their github release.

As a VueJS developer, I have experienced the current code implementation of Vue 2, and when I tried the Vue 3, I noticed there are some breaking changes. But don't get me wrong, I personally like the breaking changes as I believe it will help to improve the code quality and lesser chance of unexpected bugs. Also, these breaking changes are coming from the agreed RFC by the Vue Core team, so check them out for more detail.

Alas, here we go!

Vue app initialization

In the Vue 2, usually the plugin installation will be done in the global instance of the Vue object, and then we create a new instance of the Vue to initialize the Vue app. For example:

// Vue 2 example of initialization
import Vue from 'vue';
import Vuex from 'vuex';
import App from './App.vue';

// install vuex plugin
const store = new Vuex.store(/* store configuration object */);

// initiate the vue app
new Vue({
  render: (h) => h(App),

This implementation has a flaw if you need to create multiple Vue app in the same page. Since it uses the global Vue instance to install the app, you can't initiate multiple Vue app with different plugins to be installed. This can lead to the pollution of the Vue instance.

In the Vue 3, plugin installation and app initialization are immutable from the original Vue instance, since you must initiate the Vue app first before installing the plugins.

// Vue 3 example of initialization
import { createApp } from 'vue';
import { createStore } from 'vuex';
import App from './App.vue';

// initialize the store object
const store = createStore(/* store configuration object */);

// create the app and install the store

Notice that there's no global Vue defined and mutated here. With this, now you can be sure that every plugin used on each application is specific and won't pollute other Vue app. Profit! 😄

More details and the reason behind in the RFC: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0009-global-api-change.md

Allow multiple v-model, and deprecate model option

💓 Before I make anyone panic here, please note that this changes on v-model API is not affecting the usage of v-model in the native elements like <input>, <select>, <textarea>, etc.

The model option that I referred on the title above is the model option that we use for custom v-model on the custom component. So yes, this breaking change is only meant for the custom component that uses model option, as mentioned here that it will be removed in the Vue 3.

In the Vue 2, you can only define one v-model to have a two-way data binding. If you need multiple props to have two-way data binding, you can use .sync instead.

// Vue 2 example of v-model and sync

Having v-model and .sync directive takes more learning curve while they are doing similar things. Thus, in Vue 3 the .sync are deprecated, and then you can use multiple v-model instead! 😍

Vue 3 example of v-model

More consistent, so less bikeshedding with your teammates, profit! 😄

More details and the reason behind in the RFC: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0011-v-model-api-change.md

Event Bus is deprecated

Do you love the concept of Event Bus in Vue?

If yes, then this might disappoint you a bit. Vue has no official documentation for Event Bus, but the API built in the Vue 2 instance allows us to create a publish-subscribe object with the vm.$emit and vm.$on method.

// Vue 2 example of event bus

const eventBus = new Vue();

// subscribe
eventBus.$on('sandwich-made', () => console.log('sandwich made!'));

// publish

There is a good motivation behind this changes, because Vue encourages more state-driven data flow, which data are passed from parent components to its child, and events are emitted from the child to the parent component. Profit! 😄

But of course using Event Bus pattern is still allowed in the Vue 3. If you still need it, you can install any 3rd party library or write your own.

More details and the reason behind in the RFC: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0020-events-api-change.md

filter is deprecated

Filter is one of the early feature introduced by Vue to easily map your rendered value. It is usually used for price tag, currency, and capitalize.

The usage of filter is usually to beautify your vue template code:

// Vue 2 example of filter
<div class="name">{{ user.name | capitalize }}</div>

import capitalize from './filter/capitalize';
export default {
  /* vue configuration */,
  filter: {

Filter are encouraged for simplicity and re-usability. But, it is also replaceable with methods because there is no difference in terms of performance. Removing filter will encourage more explicit methods to be defined on each component, and if you need to reuse the filter function on multiple components, it can just be simply imported and registered as part of the method.

// Vue 3 example of filter
<div class="name">{{ capitalize(user.name) }}</div>

import capitalize from './filter/capitalize';
export default {
  /* vue configuration */,
  methods: {

This is just my personal opinion, but if you're looking forward for the Vue 3 Composition API, you might notice that filter can also be easily replaced as well by manually return the filter function in the setup.

// Vue 3 composition api setup example
import { useState, useRefs } from 'vue';
import capitalize from './filter/capitalize';

setup () {
  const data = {
    user: { name: 'Albert' }

  return {

Deprecating filter will help us to code more consistently and no more bikeshedding on deciding whether the function shall be registered in the filter or method. Profit! 😄

More details and the reason behind in the RFC: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0015-remove-filters.md

Summary ⭐

With the upcoming release of Vue 3, the changes are going to a better direction for the sake of the code quality. All of these breaking changes are considered carefully in the RFC, so feel free to check and contribute to the discussion!

Lastly, thank you for going through this article! I hope this help anyone that consider to migrate their existing Vue 2 application to the Vue 3, to be aware of the changes and be prepared!

What do you think of the breaking changes? Your comment and suggestion to help me improve this documentation and my writing skill is very much appreciated!

note: This documentation will be updated according to any changes to be made, before it finally reach the stable release.

Posted on May 1 by:

chenxeed profile

Albert Mulia Shintra


Web Developer with the passion of solving problem and clean solution.


markdown guide

I do still think that removal of the Event Bus is a big blow for developing complex frontend.

there will always be systems that will need that kind of architecture.

not all the time it is parent and child, there are systems that will need component to component data transfer and removing the Event Bus is a big blow


It's not like you now cannot use any event bus in your app. It's just that you can't use a shallow Vue instance to create one. Npm is full of event bus packages, a lot of them weighing in at ~300 bytes.

Removing bloat and keeping the core of Vue lean is essential to move the framework forward and keep it maintainable.


The new changes is absolutely necessary and right for the betterment of VueJS.
Great article 👍👌.


The new composition syntax is miscalculated move from the Vue team, and with a change like this the past in most cases tell a story of the future. Just like React and Angular, the community will be split or forced to run with this new trendy syntax. The core values of Vue are now depreciated.