Vue 3 Basic Tutorial  (Migration plan for Vue 2 users)
Published: 2020-11-25 12:27:11

---
title: Vue 3 Basic Tutorial  (Migration plan for Vue 2 users)
published: true
description: A simple guide to how we migrated to Vue.js 3 from Vue.js 2. Spoiler: it wasn't that hard. Follow along!
tags: 
canonical_url: https://blog.codersrank.io/vue-3-migration-plan-for-vue-2-users/
cover_image: ![migrating-to-vue3](https://dev-to-uploads.s3.amazonaws.com/i/ubwv7ybzxjuze4n134tv.jpg)
---

<!-- wp:heading -->
<h2 id="h-intro"><strong>Intro</strong></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>At <a href="https://codersrank.io/">CodersRank</a> we build our front-end with Vue.js. And with the recent release of <a href="https://v3.vuejs.org/">Vue.js version 3</a> we decided to upgrade. Here, we’ll cover what needs to be changed to migrate to the all new Vue.js based on our experience.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>We have 2 main projects. One project is the large <a href="https://profile.codersrank.io/">developers profile website</a> and the other one is a smaller admin section. To start, we decided to migrate the smaller admin section to see how hard it is to migrate (spoiler: not hard).</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>So, let's start.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="h-init-vue-app"><strong>Init Vue App</strong></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Let’s start at the main app entrypoint script. Vue.js version 3 uses new Global API and requires a different approach to initialize the app:</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In Vue 2 we had:</p>
<!-- /wp:paragraph -->
```javascript
// Import Vue 
import Vue from "vue";

// Import main App component
import App from "./App.vue";

// Init Vue app
const app = new Vue({
  // element where to mount the app
  el: "#app",
  // render main component
  render: (h) => h(App),
});
```
And in Vue 3 it is now:
```javascript
// Import createApp function
import { createApp } from 'vue';

// Import main App component
import App from './App.vue';

// Init Vue app and pass main app component
const app = createApp(App);

// Mount app
app.mount('#app');
```
<!-- wp:heading -->
<h2 id="h-vuex-store"><strong>Vuex Store</strong></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>If you use <a href="https://vuex.vuejs.org/">Vuex</a> state management library, then it also needs to be updated to the latest version for Vue 3. And Vuex v4 also has new global API changes.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Let's look at how we use that and the init Vuex store in Vue 2:</p>
<!-- /wp:paragraph -->
```javascript
import Vue from "vue";

// Import Vuex
import Vuex from "vuex";

import App from "./App.vue";

// Tell Vue.js to use Vuex plugin
Vue.use(Vuex);

// Create store instance
const store = new Vuex.Store({
  state: {
    /* ... */
  },
  getters: {
    /* ... */
  },
  mutations: {
    /* ... */
  },
  actions: {
    /* ... */
  },
});

const app = new Vue({
  el: "#app",
  render: (h) => h(App),
  // pass store instance
  store: store,
});

```
The same but in Vue 3 should be the following:
```javascript
import { createApp } from 'vue';

// Import createStore function
import { createStore } from 'vuex';

import App from './App.vue';

const app = createApp(App);

// Create store instance
const store = createStore({
  state: { /* ... */ },
  getters: { /* ... */ },
  mutations: { /* ... */ },
  actions: { /* ... */ },
})

// Tell app to use store
app.use(store);

// Mount app
app.mount('#app');
```
<!-- wp:heading -->
<h2 id="h-slots"><strong>Slots</strong></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>In our Vue 2 app we were still using legacy slots API:</p>
<!-- /wp:paragraph -->
```html
<some-component>
   <h1 slot="header">Title</h1>
 <p slot="content">Content</p>
</some-component>
```
It is required to be changed to a new one using `<template>` tags:
```html
<some-component>
  <template #header>
    <h1>Title</h1>
  </template>
  <template #content>
   <p>Content</p>
  </template>
</some-component>
```
<!-- wp:heading -->
<h2 id="h-v-model"><strong>v-model</strong></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p><em>v-model</em> also has new syntax in Vue 3. For example, if in Vue 2 we had the following component:</p>
<!-- /wp:paragraph -->
```html
<custom-input v-model="inputValue" />
<template>
  <input :value="value" @input="onInput" />
</template>
<script>
  export default {
    model: {
      // specify prop that will be modified by v-model
      props: 'value',
      // specify event that should be received by v-model with the new value
      event: 'input',
    },
    props: {
      value: String,
    },
    methods: {
      onInput(e) {
        this.$emit('input', e.target.value),
      },
    },
  }
</script>

```
In Vue 3, by default, it expects that v-model should be bound to modelValue prop of the component and emit `update:modelValue` event in order to update model value. So we have to change the component to the following:
```html
<script>
  export default {
    props: {
      // change "value" prop to "modelValue"
      modelValue: String,
    },
    methods: {
      onInput(e) {
        // emit "update:modelValue" prop with new value
        this.$emit('update:modelValue', e.target.value),
      },
    },
  }
</script>

```
But also Vue 3 provides more control over it and we still keep the prop named `value`. In this case we need to emit `update:value event`:
```html
<script>
  export default {
    props: {
      // keep name as value
      value: String,
    },
    methods: {
      onInput(e) {
        // emit "update:value" prop with new value
        this.$emit('update:value', e.target.value),
      },
    },
  }
</script>

```
And to let Vue know that we need model to be bound to the `value` prop instead of the default `modelValue`, we should use `v-model` like this:
```html
<custom-input v-model:value="inputValue" />
```
The best thing about it is that now components can have multiple `v-model`s:
```html
<some-component v-model:title="titleValue" v-model:content="contentValue" />
```
<!-- wp:heading -->
<h2 id="h-composition-api"><strong>Composition API</strong></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Vue 3 comes with a new <a href="https://v3.vuejs.org/guide/composition-api-introduction.html">Composition API</a>.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>It is not necessary to change all your components to the new Composition API as Vue 3 still works perfectly with the current Options API. That is why we decided to keep it at the moment.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="h-custom-elements-web-components"><strong>Custom Elements (Web Components)</strong></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>At CodersRank we have a nice set of web components for developers to integrate on their personal websites. We also use them on our website:</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li><a href="https://github.com/codersrank-org/activity-widget">Activity Chart</a></li><li><a href="https://github.com/codersrank-org/skills-chart-widget">Skills Chart</a></li></ul>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p>It was not so straightforward to make Vue 3 understand them properly and not to think these are not Vue components.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
In Vue 2 to specify custom elements, we used `Vue.config.ignoredElements`
<!-- /wp:paragraph -->

<!-- wp:html -->
```javascript
import Vue from "vue";

Vue.config.ignoredElements = ["codersrank-activity", "codersrank-skills-chart"];

```
<!-- /wp:html -->
```javascript
import Vue from "vue";
Vue.config.ignoredElements = ["codersrank-activity", "codersrank-skills-chart"];
```
In Vue 3 it is decided whether it is a custom element or not during template compilation phase, so it should be specified in webpack config Vue loader options:
```javascript
{
  test: /\.vue$/,
  use: {
    loader: 'vue-loader',
    options: {
      compilerOptions: {
        // ignore elements that starts with codersrank-
        isCustomElement: (tag) => tag.indexOf('codersrank-') === 0,
      },
    },
  },
},

```
<!-- wp:heading -->
<h2 id="h-typescript"><strong>TypeScript</strong></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Vue 3 has much better <a href="https://v3.vuejs.org/guide/typescript-support.html">TypeScript support</a>. And during the migration to Vue 3 all we needed to change was the component declaration in single-file components:</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In Vue 2 we used `Vue.extend` to define the Vue component:</p>
<!-- /wp:paragraph -->
```html
<template>
  <!-- ... -->
</template>
<script>
  import Vue from "vue";

  export default Vue.extend({
    props: {
      // ...
    },
    data() {
      // ...
    },
    // ...
  });
</script>

```
And in Vue 3 we need to use the new `defineComponent` function:
```html
<template>
  <!-- ... -->
</template>
<script>
  import { defineComponent } from "vue";

  export default defineComponent({
    props: {
      // ...
    },
    data() {
      // ...
    },
    // ...
  });
</script>

```
<!-- wp:heading -->
<h2 id="h-post-scriptum"><strong>Post Scriptum</strong></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>In this article we have covered just the basics that we faced ourselves in our own project during migration from Vue.js 2 to Vue.js 3. Of course there are many things to pay attention to if you use other APIs, Vue features and plugins. Worth to mention:</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>Official <a href="https://v3.vuejs.org/guide/migration/introduction.html#overview">Vue.js 3 migration guide</a></li><li>Official <a href="https://next.router.vuejs.org/guide/migration/index.html#breaking-changes">Vue Router migration guide</a> for Vue.js 3 (in case you use Vue Router)</li><li>New built-in <a href="https://v3.vuejs.org/guide/teleport.html">Teleport functionality</a> (if you used `portal-vue` plugin which is not yet supported for Vue.js 3)</li><li><a href="https://v3.vuejs.org/guide/composition-api-introduction.html">Composition API Reference</a></li><li><a href="https://v3.vuejs.org/guide/migration/render-function-api.html#_3-x-syntax-2">Render function API changes</a></li></ul>
<!-- /wp:list -->

<!-- wp:heading -->
<h2 id="h-about-codersrank">About CodersRank</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Interested in learning more about how we help developers? <a href="https://codersrank.io/">Check out our website here &amp; create your own profile</a>!</p>
<!-- /wp:paragraph -->