How to register global components in Vue 3 dynamically?
Tags: vue, vue3, frontend, typescript
Published: 2021-07-10 17:52:39

*Header image above is a screenshot from the official website of [Vue 3](https://v3.vuejs.org/).*

**Vue 3** is now stable since June 8, 2021 with version 3.1.0 named Pluto. It is Typescript compatible and introduces Composition API where you can initialize most parts of the component inside the `setup` function. If you want more info about Vue 3, you can check the links below:

- Vue 3 Official Website: https://v3.vuejs.org/
- Vue 3 Github: https://github.com/vuejs/vue-next
    - Stable release note: https://github.com/vuejs/vue-next/releases/tag/v3.1.0
- Composition API: https://v3.vuejs.org/guide/composition-api-introduction.html#why-composition-api

Let's immediately proceed with the topic of this article. Vue 3 already has a documentation about registering components globally. **Global components** are Vue components that can be declared immediately inside the template of another component without import and declaration in components property. If you're not familiar with Vue 3, this is how we import other components inside a component:

```vue
<template>
    <div>
        <SomeComponent/>
    </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import SomeComponent from '@/components/SomeComponent.vue';

export default defineComponent({
    components: { SomeComponent },
});
</script>
```

Assuming the file above is inside the `views` directory and we're importing a reusable component from the `components` directory. As you can see that it's not different on how we import components in Vue 2 except the difference in creating a component and the usage of Typescript (by adding `lang="ts"` inside the script tag).

If we make the component globally accessible to other components, this will be very helpful when our project becomes big making it more reusable. It will also remove the import of multiple components inside a component unless the component is a local sub-component. See example below:

```vue
<template>
    <div>
        <SomeComponent/>
    </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
});
</script>
```

As you can see we just declared the `SomeComponent` component like a HTML element directly inside of another component without the `import` syntax and the `components` parameter inside the `defineComponent` function. 

So how do we make a global component in Vue 3? According to the [official documentation](https://v3.vuejs.org/guide/component-registration.html#global-registration), here's how we register a component globally in `main.ts` file if you're developing using Typescript else in `main.js` file:

```ts
import { createApp } from 'vue';
import ComponentC from './components/ComponentC.vue';

// Usually it is App.vue inside the createApp function.
// App.vue is your main or root Vue component.
const app = createApp({}); 

// Component named with kebab-case.
app.component('component-a', {
  /* ... */
});

// Component named with PascalCase.
app.component('ComponentB', {
  /* ... */
});

app.component('ComponentC', ComponentC);

// The second argument of the component function is the object that creates a component or it can be an imported component instance.

// It willl mount the Vue app inside the HTML element with ID of 
// #app in index.html which is located in public directory.
app.mount('#app');
```
Reference: [Vue 3 Global Registration](https://v3.vuejs.org/guide/component-registration.html#global-registration)

If we follow the procedure above, it will bloat our `main.js` or `main.ts` file especially if our application becomes bigger then our reusable components will increase. So how do we prevent that from happening?

I actually got the idea from Vue 2 documentation on registering global components dynamically which I always use when developing a frontend application using Vue 2. In Vue 3, they don't have this section in their documentation.

Reference: [Automatic Global Registration of Base Components](https://vuejs.org/v2/guide/components-registration.html#Automatic-Global-Registration-of-Base-Components)

Here's my implementation:

./components/base/index.ts
```ts
import { App } from 'vue';

const requireComponent = require.context(
  // The relative path of the components folder
  './',
  // Whether or not to look in subfolders
  false,
  // The regular expression used to match base component filenames
  /[A-Z]\w+Base\.vue$/,
);

const register = (app: App<Element>): void => {
  requireComponent.keys().forEach((fileName) => {
    // Get component config
    const componentConfig = requireComponent(fileName);
    // Get component name
    const componentName = fileName.split('/').pop()?.replace(/\.\w+$/, '') as string;

    app.component(componentName, componentConfig.default || componentConfig);
  });
};

export default {
  register,
};
```

If you checked the link I gave above for Vue 2 implementation, it is almost the same as my implementation above for Vue 3. Here are the differences in my implementation:
- I have an argument of `app` with type of `App<Element>`. `App` type is from `vue` package and the `Element` type is more of an interface. See more details [here](https://microsoft.github.io/PowerBI-JavaScript/interfaces/_node_modules_typedoc_node_modules_typescript_lib_lib_dom_d_.element.html).
- My relative path is `'./'` because I placed this code in `./components/base/index.ts`.
- My regex will get all the files that ends in `Base.vue` for them to be considered as a global component. I prefer to place all global components inside the `base` directory but you can name it anything you prefer. 
    - e.g. GlobalComponentBase.vue
- I didn't used the `lodash` package if you prefer minimum external packages. 

Here's how I implemented it in the `main.ts` file:

main.ts
```ts
import { createApp } from 'vue';

import BaseComponents from './components/base';

const app = createApp(App);

BaseComponents.register(app);

app.mount('#app');
```

That's it! Your main file will not bloat when you have hundreds of global components to register. I hope this article will help you a lot. 😉

I also created a Github Gist of the code implementation above:
https://gist.github.com/jirehnimes/2fcb31a2cbe7bb0c722a96f49e4cbf8f

Cheers!