Implementing Reusable Components in Vue.js

Vue.js relies heavily on a component-based architecture. Mastering components allows developers to build complex user interfaces from small, isolated, and reusable pieces of code. This approach aligns with the MVVM pattern, ensuring that applications remain decoupled and maintainable.

Defining a Global Component

A component is essentially a reusable Vue instance with a specified name. To create a global component accessible anywhere in your application, use Vue.component. The following example defines a navigation bar:

Vue.component('app-navbar', {
    data() {
        return {
            clicks: 0
        };
    },
    template: `
        <nav class="navigation">
            <div class="container">
                <div class="brand">
                    <img src="./assets/logo.png" alt="Logo" />
                </div>
                <div class="user-controls">
                    <button>Log In</button>
                    <button>Sign Up</button>
                </div>
            </div>
        </nav>
    `
});

Once registered, this component acts as a custom element and can be used within a root Vue instance's template.

<div id="root">
    <app-navbar></app-navbar>
</div>

<script>
    const vm = new Vue({
        el: '#root'
    });
</script>

Component Reusability and Data Scope

Since components are reusable, you can include them multiple times in the DOM. Each instance maintains its own independent state. For instance, if you bind a counter to a button inside the component, clicking the button in one instance will not affect the counter in another.

<div id="root">
    <app-navbar></app-navbar>
    <hr />
    <app-navbar></app-navbar>
</div>

There is a critical distinction between the root Vue instance and a component instance regarding the data option. While the root instance can accept an object, a component's data option must be a function that returns an object. This ensures that every time a component is used, a fresh data object is generated, preventing state contamination between instances.

Local Registration

Global registration is convenient for small projects, but in larger applications using build tools like Webpack, global registration can lead to bloated bundles. Unused components will still be included in the final build. To optimize performance, components should be registered locally within the parent component that uses them.

First, define the component as a plain JavaScript object:

const UserProfile = { /* ... options ... */ };
const UserSettings = { /* ... options ... */ };

Then, register them inside the parent component's components option:

new Vue({
    el: '#root',
    components: {
        'user-profile': UserProfile,
        'user-settings': UserSettings
    }
});

Passing Data via Props

Props allow a parent component to pass data down to a child component. This makes the component dynamic and reusable for different data contexts. To register a prop, add it to the props array or object in the component definition.

Vue.component('app-navbar', {
    props: ['brandName'],
    template: `
        <nav class="navigation">
            <div class="container">
                <div class="brand">
                    <img src="./assets/logo.png" />
                    <span>{{ brandName }}</span>
                </div>
                <div class="user-controls">
                    <button>Log In</button>
                </div>
            </div>
        </nav>
    `
});

You can pass a static string or bind a dynamic variable using v-bind:

<app-navbar brand-name="My Awesome Site"></app-navbar>
<!-- or dynamically -->
<app-navbar :brand-name="siteName"></app-navbar>

Single Root Element Requirement

Vue templates require exactly one root element. Attempting to define multiple top-level elements (like an <h1> and a <div> side-by-side) will cause a compilation error. All content must be wrapped in a single parent tag, such as a <div>. Note that while mustache syntax renders data as plain text, the v-html directive is necessary to render raw HTML strings within a component.

<!-- Valid -->
<template>
    <div class="card">
        <h3>{{ title }}</h3>
        <div v-html="bodyContent"></div>
    </div>
</template>

Emitting Events to Parent Components

Communication from child to parent is achieved via custom events. The child component uses this.$emit(eventName, payload) to signal that something happened, and the parent listens for that event using v-on.

Consider a scenario where a child button should increase the font size in the parent. The child component emits an event:

Vue.component('text-controls', {
    template: `
        <div>
            <button @click="increaseSize">Increase Text Size</button>
        </div>
    `,
    methods: {
        increaseSize() {
            this.$emit('enlarge-text', 2);
        }
    }
});

The parent component handles this event to update its state:

new Vue({
    el: '#root',
    data: {
        baseFontSize: 14
    },
    methods: {
        onEnlargeText(amount) {
            this.baseFontSize += amount;
        }
    }
});

In the parent template, bind the event listener and apply the style:

<div id="root" :style="{ fontSize: baseFontSize + 'px' }">
    <text-controls @enlarge-text="onEnlargeText"></text-controls>
    <p>This text will resize.</p>
</div>

Tags: vuejs components web-development javascript

Posted on Wed, 09 Sep 2026 16:13:38 +0000 by theinfamousmielie