DEV Community

Cover image for How to register global components in Vue 3 dynamically?
Jireh June Nimes
Jireh June Nimes

Posted on

How to register global components in Vue 3 dynamically?

Header image above is a screenshot from the official website of Vue 3.

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:

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:

<template>
    <div>
        <SomeComponent/>
    </div>
</template>

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

export default defineComponent({
    components: { SomeComponent },
});
</script>
Enter fullscreen mode Exit fullscreen mode

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:

<template>
    <div>
        <SomeComponent/>
    </div>
</template>

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

export default defineComponent({
});
</script>
Enter fullscreen mode Exit fullscreen mode

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, here's how we register a component globally in main.ts file if you're developing using Typescript else in main.js file:

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');
Enter fullscreen mode Exit fullscreen mode

Reference: Vue 3 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

Here's my implementation:

./components/base/index.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,
};
Enter fullscreen mode Exit fullscreen mode

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.
  • 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

import { createApp } from 'vue';

import BaseComponents from './components/base';

const app = createApp(App);

BaseComponents.register(app);

app.mount('#app');
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (4)

Collapse
 
oceangravity profile image
Juan Carlos Galindo Navarro

Hi Jireh, thanks for this interesting post.

Maybe you have an issue when import App from vue, I solve it using getCurrentInstance and using appContext.app.component to register my components.

I'm wondering, how I can unregister my components? Should I delete from components array?

Thanks!

Collapse
 
jirehnimes profile image
Jireh June Nimes

Hi, sorry for the very late response.

When I did this article, I never experienced any issue/s during that time though thanks very much for sharing your solution.

Are you planning to unregister the components but the files still remains in the project? I think the best way is to have an array that will exclude the components during the loop inside the register function.

Collapse
 
andythedishwasher profile image
andythedishwasher

Thanks a million for this! I've got a pretty hare-brained idea that would massively benefit from this workflow in terms of cleanliness and organization. I'm trying to generate Vue components on a server based on parameters received from my Vue frontend, then dynamically register those components after they are fetched from the server and saved in the base components directory. That last bit about saving the fetched component to a local directory is the part I'm still kinda scratching my head about...

Collapse
 
jirehnimes profile image
Jireh June Nimes

Hi, sorry for very late response on this. Are you talking about SSR?
Actually, this article will not work on the updated version of Vue 3 because of require.context. I just tried a modified version working in Vue 3 + Vite both for SPA and SSR that uses import.meta.glob instead. I'll create an another article for it.