Reusable Components With Composition API

Reduce and reuse your code.

Zainab Saify
Simform Engineering
6 min readJan 10, 2024

--

If you’re reading this, you likely have some knowledge about the amazing features of Vue and its component-driven architecture. If not, no worries — we’ll explore it together in this blog.

Vue is a component-based framework. With just a bit of code, we can create reusable components throughout our application.

Reusable components are an essential part of any developer’s toolkit. They help improve code organization, maintainability, and readability.

These components can communicate with each other via props and events, and through these connections, we create a dynamic application.

Reusable component
Reusable components

Table of content

  • Prerequisites
  • Understanding Components with Composition API
  • Why Use Reusable Components
  • Creating a Reusable Component With Composition API
  • Best Practices

Let’s get started!!

Prerequisites

You can refer to the tutorial below for an overview of Vue components.

Intro to vue-components.

Understanding Components with Composition API

Reusable components are one of the key building blocks of Vue applications. They are self-contained, modular units of code that can be easily reused and aid in building complex user interfaces.

These components can be defined and used throughout your application, promoting code reusability and maintainability.

The Composition API further elevates the reusability of these components by allowing the developers to structure their code in a more functional manner — i.e., segregated by logic.

Options API vs Composition API

Why Use Reusable Components?

Reusability — in general, leads to sustainability. The same rule applies to your code as well. Here are a few of the common advantages that reusable component provides:

  • Reusability — Reusable components promote DRY — ‘Don’t Repeat Yourself’ principle.
  • Modularity — As each component encapsulates a specific functionality, reusable components help in making the codebase highly modular.
  • Consistency — Reusable components help maintain a consistent look and feel across different parts of your application. Design elements, styles, and behaviors defined in a component can be consistently applied wherever that component is used.
  • Maintainability — When you have a quick update to be made to a feature used at various locations in your application, reusable components come to your rescue. Just change it in the component, and it will be reflected everywhere.
  • Readability — Reusable components can improve the code readability to a great extend, provided that the components are named according to their functionalities.
  • Separation of concerns — It is a good practice to maintain a component for just a single functionality. This separation makes the codebase more readable, maintainable, and easier to troubleshoot.

Creating a Reusable Component with Composition API

Let’s dive straight into coding and build a simple application, a Form app to be precise.

The purpose of this form is to book a doctor’s appointment, capturing general patient information such as name, age, contact info, and the appointment date and time.

UI for our form

For the initial form implementation, take a look at the following code snippet:

<template>
<h3>Please Fill in your details</h3>
<form @submit.prevent="submitForm">
<label for="name">Name:</label>
<input v-model="formData.name" id="name" required>
<span v-if="!formData.name.trim()">Name is required</span>

<label for="age">Age:</label>
<input v-model="formData.age" id="age" type="number" required>
<span v-if="!formData.age">Age is required</span>

<label for="email">Email:</label>
<input v-model="formData.email" id="email" type="email" required>
<span v-if="!isValidEmail(formData.email)">Invalid email address</span>

<label for="contactNumber">Contact Number:</label>
<input v-model="formData.contactNumber" id="contactNumber" type="tel" pattern="[0-9]{10}" required>
<span v-if="!isValidContactNumber(formData.contactNumber)">Invalid contact number</span>

<label for="date">Date:</label>
<input v-model="formData.date" id="date" type="date" required>
<span v-if="!formData.date">Date is required</span>

<label for="time">Time:</label>
<input v-model="formData.time" id="time" type="time" required>
<span v-if="!formData.time">Time is required</span>

<button type="submit" :disabled="isFormInvalid">Submit</button>
</form>

</template>
<script setup>
import { computed, reactive } from "vue";

// Entire Form Logic
const formData = reactive({
name: '',
age: 0,
email: '',
contactNumber: '',
date: '',
time: '',
});

function submitForm() {
if (!isFormInvalid.value) {
alert(`Form submitted!\n${JSON.stringify(formData)}`);
}}

const isFormInvalid = computed(() => {
return Object.values(formData).some(value => !value);
})


//Logic for checking validation for a perticular field
function isValidEmail(email) {
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailPattern.test(email.trim());
}

function isValidContactNumber(contactNumber) {
// Check if the contact number contains exactly 10 digits
return /^\d{10}$/.test(contactNumber.trim());
}

</script>Observe the repetition of the code here.

We can refactor this code to leverage the power of vue reusable components, which will lead to a more structured, readable and easy to maintain codebase.

We can create a custom-field component that has a label and validation included in it — that makes one complete functionality!

customInput.vue

<template>
<div>
<label :for="id" v-if="label">{{ label }}:</label>
<slot name="label" />
<input
v-bind="$attrs"
:value="value"
:id="id"
:type="type"
:class="propClass"
@input="$emit('valueUpdated', {fieldName: label , value: $event.target.value})">
<span
v-if="!isValid"
style="color: red;">
{{ errorMessage }}
</span>
<slot name="error" />
</div>
</template>
<script setup>
import { ref, computed, defineProps } from 'vue';

const props = defineProps(['id', 'label', 'type', 'value','propClass', 'isRequired']);

const isValid = computed(() => {
// Add validation logic based on the field type if needed
return props.value || !props.isRequired;
});

const errorMessage = computed(() => {
if (!isValid.value) {
return props.label + ' is required';
}
return '';
});
</script>

Form.vue

<template>
<form @submit.prevent="submitForm">
<CustomInput v-for="(field, key) in fields"
:key="key"
:id="key"
:type="field.type"
:label="field.label"
:isRequired="field.required"
@valueUpdated = "updateValueInFordData"/>
<button type="submit" :disabled="isFormInvalid">Submit</button>
</form>
</template>
<script setup>
import { ref, computed, reactive } from 'vue';
import CustomInput from '@/components/customInput.vue';

const formData = reactive({
name: '',
age: 0,
email: '',
contactNumber: '',
date: '',
time: '',
});

const fields = ref([
{label: "name", type: "text", required: true},
{label: "age", type: "number", required: true},
{label: "email", type: "text", required: true},
{label: "contactNumber", type: "tel", required: true},
{label: "date", type: "date", required: true},
{label: "time", type: "time", required: true}
])

const isFormInvalid = computed(() => {
return Object.values(formData).every((value) => !!value);
});

function submitForm() {
if (!isFormInvalid.value) {
alert(`Form submitted!\n${JSON.stringify(formData)}`);
}
}

function updateValueInFordData(fieldName, value) {
formData[fieldName] = value;
}
</script>

Reuse and reduce — a mantra for life, isn’t it?

Best Practices for Creating Reusable Components

Creating reusable components is an important aspect of developing modular and well-structured applications. By following a few best practices, we can ensure that these components can be reused efficiently and adapted to different needs.

  • Single responsibility principle — Make sure that each of your components has a single responsibility for doing one thing only but doing it well.
  • Props — Props provide the flexibility to the developer to customize the behavior, look and feel of that component accordingly.
  • Slots/Teleport — Slots and teleports are the key to actually being able to reuse our reusable components. We can leverage the use of default and named slots as well.
  • Dynamic classes — Dynamic classes provide a beautiful way to apply custom classes and alter the look and feel of the reusable component for various use cases.
  • Keep the component state local — Maintain the component state locally whenever possible. Avoid relying too much on the external state unless necessary.
  • Documentation — Creating a simple document about the component and its behaviors, along with the props that it accepts and events that it emits, can be beneficial.

Wrapping up

Reusable components are modular, self-contained block of code that encapsulates a specific functionality. These blocks of code can be easily integrated anywhere in your application. They provide a way to make your code more structured, reusable, and hassle-free to maintain.

The Composition API in Vue.js provides a powerful way to create reusable components by organizing logic into modular functions or modules. By following this approach, you can enhance code reusability, maintainability, and scalability in your Vue.js projects.

Start leveraging the Composition API today and build more efficient and reusable Vue.js components.

For more updates on the latest tools and technologies, follow the Simform Engineering blog.

Follow Us: Twitter | LinkedIn

--

--