Dynamic and async components made easy with Vue.js
Tags: vue, javascript, tutorial, webdev
Published: 2018-02-12 14:12:19

---
title: Dynamic and async components made easy with Vue.js
published: true
tags: ["vue", "javascript", "tutorial", "webdev"]
canonical_url: https://lobotuerto.com/blog/dynamic-and-async-components-made-easy-with-vuejs/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/te7duuy5lrqug4laoxkg.jpg
---

***

You can check the latest updated version of this article at [lobotuerto's notes - Dynamic and async components made easy with Vue.js.](https://lobotuerto.com/blog/dynamic-and-async-components-made-easy-with-vuejs/)

***


I think this post will be most useful to advanced **Angular/AngularJS** developers that are still pondering what’s all the fuss about [Vue.js](https://vuejs.org/) —just like I was just a few weeks ago.

Nonetheless, I have included step-by-step instructions that will help beginners too.

So, in this —opinionated— tutorial, I’m hoping you’ll:

- Realize just how ludicrously easy is to setup **on-demand / lazy-load** for Vue.js components.
- Show you how to combine **dynamic components + async loading** for maximum effect!

# Use cases

These are the use cases we will review in this tutorial:

- The app router should only load code for sections of the app you actually visit.  
- You should be able to dynamically add/remove/swap components on a page.
- Inside a section, the app should only load code for components that are actually rendered.  
A component might be declared an available for rendering, but its code should only load if you display it.

# Prerequisites

These are the [Node.js](https://nodejs.org/en/) related packages and versions —and then some— I’ll be using for this tutorial:

```
git --version #git version 2.16.1
nvm --version #0.33.8
node --version #v.9.5.0
npm --version #5.6.0
yarn --version #1.3.2
vue --version #2.9.3
```

If you don’t have [Yarn](https://yarnpkg.com/) installed, you can do so with: `npm install -g yarn`.  
Or check out this setup guide on [how to install Node.js in Ubuntu](https://dev.to/lobo_tuerto/cmo-instalar-nodejs-en-ubuntu-3pi-temp-slug-8227842 "Cómo instalar Node.js en Ubuntu").

If you don’t have `vue-cli` installed yet, you can do so with: `yarn add global vue-cli`.  
Test it with `vue --version`.

# Creating a new Vue.js app

We’ll be using `vue-cli` with the `webpack` template.

Let’s create a new Vue.js app:

```
vue init webpack dynamic-async-components-demo
```

Accept all defaults, except when asked between NPM/Yarn; select Yarn.

After finishing the installation process, let’s initialize a Git repository for this app and make our first commit.

```
cd dynamic-async-components-demo/
git init
git add .
git commit -m "Initial commit"
yarn dev
```

You can see your brand new app working at: `http://localhost:8080`.

# Asynchronous route loading

Let’s create a new **Single File Component** (a **.vue** file) —don’t you just love this?— to showcase this feature.  
Create a `dynamic-async-components-demo/src/components/Playground.vue` file with this content:

```
<template>
  <section class="playground">
    <h1>Welcome to the component playground!</h1>
  </section>
</template>

<script>
export default {
  name: 'Playground'
}
</script>
```

Taking advantage of Webpack’s code chunking we can make our app’s initial rendering **very fast** by only loading the code we need at the beginning and loading everything else on-demand.

Let’s make our new `Playground.vue` component to load asynchronously.

Open the `dynamic-async-components-demo/src/router/index.js` file and modify it to look like this:

```
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/playground',
      name: 'Playground',
      component: () => import('@/components/Playground')
    }
  ]
})
```

* * *

Now would be a good time to save our progress:

```
git add .
git commit -m "Add Playground.vue to router and load it async"
```

* * *

To see the **async** load in action open the browser console (press F12) and go to the **Network** tab.

Now visit `http://localhost:8080/#/playground` and observe how it makes a request for a `.js` file when you change the URL.

The good thing? The app will only make that request once, then it’ll cache it afterwards!

The line of code making all this possible is this one:

```
component: () => import('@/components/Playground')
```

How hard is this for async loading of router components eh? :)

# Dynamic component rendering

This is so easy in Vue.js… just have a look for yourself and judge.

## Defining new components

Let’s create three more components for us to use on the `Playground`:

- A **Button** component.  
`dynamic-async-components-demo/src/components/dynamic/Button.vue`:

```
<template>
  <button>I'm a button</button>
</template>

<script>
export default {
  name: 'Button'
}
</script>
```

- A **Header** component.  
`dynamic-async-components-demo/src/components/dynamic/Header.vue`:

```
<template>
  <h1>I'm a header</h1>
</template>

<script>
export default {
  name: 'Header'
}
</script>
```

- A **TextInput** component.  
`dynamic-async-components-demo/src/components/dynamic/TextInput.vue`:

```
<template>
  <input type="text" placeholder="I'm a text input"/>
</template>

<script>
export default {
  name: 'TextInput'
}
</script>
```

## Static rendering

If you want to see your new components in action, modify the `Playground.vue` file to look like this:

```
<template>
  <section class="playground">
    <h1>Welcome to the component playground!</h1>

    <my-button/>
    <my-header/>
    <my-text-input/>
  </section>
</template>

<script>
import Button from '@/components/dynamic/Button'
import Header from '@/components/dynamic/Header'
import TextInput from '@/components/dynamic/TextInput'

export default {
  name: 'Playground',
  components: {
    'my-button': Button,
    'my-header': Header,
    'my-text-input': TextInput
  }
}
</script>
```

Then visit: `http://localhost:8080/#/playground`. You’ll see this:

![Static Rendering](https://lobotuerto.com/blog/dynamic-and-async-components-with-vuejs-made-easy/static-rendering.png)

Let’s save our progress:

```
git add .
git commit -m "Add Button, Header, TextInput components and display them"
```

* * *

## Dynamic rendering

What we’ll do in this section is present a `<select>` input as means to pick a component from a list and display it.

This can be accomplished using the powerful `<component></component>` element.

Modify the `Playground.vue` file to look like this:

```
<template>
  <section class="playground">
    <h1>Welcome to the component playground!</h1>

    <select v-model="selectedComponent">
      <option
        v-for="(component, index) in componentList"
        :key="index"
        :value="component"
      >
        {{ component.name }}
      </option>
    </select>

    <hr>
    <component :is="selectedComponent"></component>
  </section>
</template>

<script>
import Button from '@/components/dynamic/Button'
import Header from '@/components/dynamic/Header'
import TextInput from '@/components/dynamic/TextInput'

export default {
  name: 'Playground',
  data: function () {
    return {
      componentList: [Button, Header, TextInput],
      selectedComponent: null
    }
  }
}
</script>
```

Visit: `http://localhost:8080/#/playground`, pick **TextInput**. You’ll see this:

![Dynamic Rendering](https://lobotuerto.com/blog/dynamic-and-async-components-with-vuejs-made-easy/dynamic-rendering.png)

Let’s save our progress:

```
git add .
git ci -m "Dynamic component rendering"
```

* * *

# Async loading + dynamic rendering

What would it take to enable async loading for the **TextInput** component above?

Well, you’ll only need to change `Playground.vue` to this:

```
<template>
  <section class="playground">
    <h1>Welcome to the component playground!</h1>

    <select v-model="selectedComponent">
      <option
        v-for="(item, index) in componentList"
        :key="index"
        :value="item.component"
      >
        {{ item.label }}
      </option>
    </select>

    <hr>
    <component :is="selectedComponent"></component>
  </section>
</template>

<script>
import Button from '@/components/dynamic/Button'
import Header from '@/components/dynamic/Header'
// Comment out the line below, since we will be loading it asynchronously
// import TextInput from '@/components/dynamic/TextInput'

export default {
  name: 'Playground',
  data: function () {
    return {
      componentList: [
        { label: 'Button', component: Button },
        { label: 'Header', component: Header },
        {
          label: 'TextInput',
          // Async loading!
          component: () => import('@/components/dynamic/TextInput')
        }
      ],
      selectedComponent: null
    }
  }
}
</script>
```

Easy, isn’t it?

* * *

Save your progress:

```
git add .
git commit -m "Load TextInput asynchronously"
```

* * *

You can verify it’s loading asynchronously by watching the Network tab in your browser console and selecting the **TextInput** option. At that moment a request will be made to retrieve this component’s code!

## Staying alive

The astute reader might have noticed that whatever you type in the **TextInput** component is lost when you switch to another component. If you want to preserve on memory what’s on the dynamic components, all you have to do is surround the `<component></component>` element with `<keep-alive></keep-alive>` tags like this:

```
<keep-alive>
  <component :is="selectedComponent"></component>
</keep-alive>
```

Go try it out! Pick **TextInput,** type something in, then pick something else, then return to **TextInput** and you’ll see whatever you typed in before is still there. Awesome!

* * *

Let’s do our final commit for this tutorial:

```
git add .
git ci -m "Preserve dynamic components state in memory"
```

# Some observations

Do you know what other thing is a testament to Vue’s power and flexibility? that [Vuetify](https://vuetifyjs.com/) is a more advanced, featureful and complete implementation of the [Material Design Guidelines](https://material.io/guidelines/) than [Angular Material](https://material.angular.io/) itself.

I was going all over its components and API the other day, drooling and laughing at the sheer number of elements they already have. :)

* * *

If you are an **Angular** developer, compare the stuff we did above with how you’d do [lazy loading in Angular.](https://angularfirebase.com/lessons/how-to-lazy-load-components-in-angular-4-in-three-steps/)

# GitHub Repository

If you don’t want to type all the code, just clone the repo at:  
[https://github.com/lobo-tuerto/vuejs-dynamic-async-components-demo](https://github.com/lobo-tuerto/vuejs-dynamic-async-components-demo)