Home / Blog / Initializing Vue 3 Applications: Benefits and Best Practices
Initializing Vue 3 Applications: Benefits and Best Practices

Initializing Vue 3 Applications: Benefits and Best Practices

Mikhail Kuznetsov
Mikhail Kuznetsov
Updated: January 1st 2025

When Vue 3 was released, it introduced a new way of initializing applications using the createApp function. This change brought several benefits, including improved isolation between applications and easier testing. In this article, we'll explore the differences between Vue 2 and Vue 3 application initialization, the benefits of the new vue createApp function, and provide best practices for initializing Vue 3 applications.

Vue 2 Application Initialization

In Vue 2, which has reached EOL now applications were initialized by creating a new instance of the Vue object.

import Vue from vue;
import App from ./App.vue;
import router from './router'
import SomeComponent from '@/components/SomeComponent.vue'
import SomePlugin from '@/plugins/SomePlugin'

const vue2AppCopy = new Vue({
  router,
  render: h => h(App)
});

vue2AppCopy.component('SomeComponent',SomeComponent);
vue2AppCopy.use(SomePlugin);

vue2AppCopy.$mount('#app')

This application instance will be serving all the logic throughout the lifetime of our SPA. That all works well, and for many years, we’ve been using this syntax to bootstrap our Vue 2 apps.

However, in Vue 3 the initialization code syntax has changed. Let’s have a look at the new syntax first and then take a look at the benefits of using it.

New Vue 3 createApp method

In Vue 3, we have a dedicated createApp function to do just that. The createApp function takes a root component (App.vue) as an argument and returns a Vue app instance. So the simplest app initialization will look like this:

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

The Vue app instance returned by the createApp is also called an application context object. This object can be used to further add more functionality to the app during the bootstrapping process. Here’s a more advanced initialization code example:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import SomeComponent from '@/components/SomeComponent.vue'
import SomePlugin from '@/plugins/SomePlugin'

const myV3App = createApp(App)
myV3App.component('SomeComponent', SomeComponent)
myV3App
.use(SomePlugin)
.use(router)
// add more functionality to myV3App

// now we're ready to mount
myV3App.mount('#app')

Compared to V2, not much has changed when it comes to adding extra logic, such as plugins and components, right?

You can find a full overview of the supported methods in the Vue 3 documentation.

That\'s good, but there’s one small but important change - we use a dedicated function, not the new Vue instance:

//v2
const vue2App = new Vue({}) // Working with the main Vue instace
//v3
const myV3App = createApp(App).mount('#app') // Create a copy of the Vue instance

So why is using that new dedicated createApp function better than using the new Vue constructor?

Benefits of the Vue 3 Initialization Code

In Vue 2, the initialization code for creating new app instances relied on the Vue object imported from the library. This approach had limitations, especially when trying to isolate functionality between multiple Vue instances, since all instances shared the same Vue object.

Vue 2 Example: Shared Functionality

To demonstrate this issue, consider the following example. Both vue2AppOne and vue2AppTwo share access to a directive called myDirective:

Vue.directive('myDirective', {
    /* ... */
})

Vue.component({
  /* ... */
})

const vue2AppOne = new Vue(/**/).mount('#app1')
const vue2AppTwo = new Vue(/**/).mount('#app1')

While it's not common to create multiple Vue apps within a single website or application, this becomes necessary when working on large projects, particularly those developed by separate teams or utilizing frontend microservices. In such cases, it's nearly impossible to isolate the Vue instances when using Vue 2.

Vue 3: Isolated App Instances

Vue 3 introduces a major improvement with the createApp function. This new syntax allows each app to be configured as an independent custom object, creating isolated Vue instances. Each instance does not share any features by default, even if they are created within the same file. This is a significant improvement over Vue 2's shared Vue object.

For instance, if you need to share functionality between multiple instances, Vue 3 makes it easy to do so. In the example below, vue3AppOne and vue3AppTwo share the LocalePlugin but not the SearchInputComponent:

const config = {/* some global config */}

const vue3AppOne = Vue.createApp(config)
vue3AppOne.component('SearchInput', SearchInputComponent)
vue3AppOne.use(LocalePlugin)

const vue3AppTwo = Vue.createApp(config)
vue3AppTwo.use(LocalePlugin)

Demo Code Repository

To explore this further, we've created a code repository that showcases two simple Vue 3 instances using the new createApp syntax. These instances do not share components and directives by default.

In this companion repository, we initialize two Vue 3 apps in different containers within a single page template. You can view the layout in public/index.html:

<div id="header-app"></div>
<div id="main-app"></div>

One app serves as a simple header, while the other uses the router and store.

Vue 3 Syntax in Action

With Vue 3, we can easily separate the initialization code for each app in the src/main.js file:

import { createApp } from 'vue'
import App from './App.vue'
import Header from './Header.vue'
import router from './router'
import store from './store'

createApp(App)
  .use(store)
  .use(router)
  .mount('#main-app')

createApp(Header)
  .mount('#header-app')

Running the App

If you run the app using vue serve, you will see both parts functioning on a single page. The Main app will have access to vue-router and the Vuex store, while the Header app will not.

In the console, you should see output like this from the Main app:

created () {
  console.log('Hello from Main app')
  console.log('Main app router', this.$route)
  console.log('Main app store:', this.$store)
}

A more straightforward testing setup

If you are using vue-test-utils (version < 2.0.0) for writing test for your Vue 2 components you may have come across the cases when you need to use the createLocalVue method to avoid polluting the global Vue instance.

We have the same potential issues in our test scenarios as we have in Vue 2 apps. We pollute the global Vue instance when we add components, plugins, etc., and they’re all shared with every available Vue instance.

To work around this issue, we have to use createLocalVue, which (you guessed it) creates a new local Vue instance, that is isolated.

import { createLocalVue, mount } from '@vue/test-utils'
import MyPlugin from '@/plugins/MyPlugin'

const localVueForTest = createLocalVue()
localVueForTest.use(MyPlugin)

mount(Component, {
  localVueForTest
})

This is no longer an issue in Vue 3, since all app extensions: plugins, mixins, and global components do not mutate the global Vue instance. So when using vue-test-utils (version >=2.0.0) with Vue 3, the app init code in the test file will look like this:

import { createStore } from 'vuex'
import { mount } from '@vue/test-utils'
import App from '@/App'
import MyPlugin from '@/plugins/MyPlugin'

const wrapper = mount(App, {
  global: {
    plugins: [MyPlugin]
  }
})

Summary

In this article, we looked at the differences between the initialization code for Vue 3 and Vue 2 apps, as well as the benefits of the new approach.

Related Courses

Start learning Vue.js for free

Mikhail Kuznetsov
Mikhail Kuznetsov
Mikhail is a passionate developer who loves to share his knowledge with the world. As a team leader, public speaker, workshop organizer, teacher of JavaScript and Vue.js, he has trained developers worldwide.

Comments

Latest Vue School Articles

Vue.js Testing with Vue Test Utils and Vitest

Vue.js Testing with Vue Test Utils and Vitest

For inexperienced testers, Vue.js testing can be intimidating. But Vitest and Vue Test Utils makes testing Vue components a breeze!
Daniel Kelly
Daniel Kelly
Building Advanced WYSIWYG Editors with Vue and TinyMCE &#8211; A Complete Guide

Building Advanced WYSIWYG Editors with Vue and TinyMCE – A Complete Guide

Learn to integrate and customize TinyMCE in Vue with the course 'Rich Text Editing and Beyond with TinyMCE and Vue.' From basic WYSIWYG setups to advanced CMS and CRM solutions, this hands-on course equips you with the tools to build powerful content editing applications.
Daniel Kelly
Daniel Kelly

Our goal is to be the number one source of Vue.js knowledge for all skill levels. We offer the knowledge of our industry leaders through awesome video courses for a ridiculously low price.

More than 200.000 users have already joined us. You are welcome too!

Follow us on Social

© All rights reserved. Made with ❤️ by BitterBrains, Inc.