DEV Community

loading...
Cover image for How to register global components in Vue 3 dynamically?

How to register global components in Vue 3 dynamically?

Jireh June Nimes
I love programming and coffee.
・4 min read

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!

Discussion (0)