Building a Customizable Vue.js Tab Component

Murilo Livorato
5 min readDec 22, 2023

Vue.js is a popular JavaScript framework for building user interfaces, and it provides a flexible and efficient way to create interactive components.

In this tutorial, we’ll walk through the process of building two components.
The Tabs component and the Tab component , we gonna work with some functionalities like inject and slots . that allows we to pass values between them .

At the end we will be able to easily implement and customize tabs for dynamic content switching.

Step 1 — Start a Vue Project

npm create vue@latest

Install Bulma css

npm install bulma
npm install -D sass

Step 2 — Create the Tab.vue component

Add the CSS

<template>
<div className="tab-content" v-show="value === selectedTitle">
<slot></slot>
</div>
</template>
<script>
import {inject} from 'vue'

export default {
props: ['title', 'value', 'active'],
setup() {
const selectedTitle = inject('selectedTitle')
return {
selectedTitle
}
}
}
</script>

Step 3 — Create the Tabs.vue component

<template>
<div class="tabs_data">
<ul v-bind:id="nametab" v-bind:class="classname" >
<li v-for="(tab, index) in tabTitles" :class="verifyActive(tab)"
:key="index">
<a href="#" @click.prevent="selectedTitle = tab.value"> {{ tab.title }}</a>
</li>
</ul>
<slot/>
</div>
</template>

<script>
import { ref, provide, watchEffect } from 'vue'
export default {
props: {
nametab: { type: String },
classname: { type: String, default: null }
},
setup (props, { slots, emit }) {
const formIsSetted = ref(false)
const tabTitles = ref(slots.default().flatMap((tab) => tab.props !== null ? tab.props : []))
const isSelected = ref(Object.fromEntries(Object.entries(tabTitles.value).filter(([key, value]) => value.active === true )))
const selectedId = Object.keys(isSelected.value).length ? Object.keys(isSelected.value)[0] : null
//const selectedTitle = ref(tabTitles.value[0].value)
const selectedTitle = ref(tabTitles.value[selectedId].value)
provide('selectedTitle', selectedTitle)
watchEffect(() => {
if(selectedTitle.value && formIsSetted.value) {
emit('changestatustab', selectedTitle.value)
}
formIsSetted.value = true

})
return {
selectedTitle,
tabTitles,
formIsSetted,
isSelected
}
},
methods: {
verifyActive (tab) {
if (tab.value === this.selectedTitle) {
return 'is-active'
}
}
}
}
</script>

<style scoped>
ul.menu_type_slider_one {
margin:0px auto;
padding:10px 0 20px 0;
display: flex;
justify-content: center;

}
ul.menu_type_slider_one.is_left {
justify-content: left;
}
ul.menu_type_slider_one.is_center {
justify-content: center;
}
ul.menu_type_slider_one li {
display: inline-block;
padding:0 16px 0 0 !important;
}
ul.menu_type_slider_one li a {
border-radius:50px;

padding:12px 20px 12px 20px;
display:block;
font-size: 17px;
font-family:Myfont, Helvetica, Arial, Sans-Serif;
color: #202426;
-webkit-box-shadow: 2px 2px 2px 0 rgba(173,173,173,0.70) ;
box-shadow: 2px 2px 2px 0 rgba(173,173,173,0.70) ;
border-bottom:#eeeeee 1px solid;
background:#FFF;
}
ul.menu_type_slider_one li.is-active a {
background: #0e9129;
color: #FFF;
border-bottom:#818DA2FF 1px solid;
-webkit-box-shadow: 2px 2px 2px 0 rgba(62,128,165,0.50) ;
box-shadow: 2px 2px 2px 0 rgba(62,128,165,0.3) ;
}

ul.menu_type_slider_one li a:hover {
border-bottom: #818DA2FF 1px solid;
-webkit-box-shadow: 2px 2px 2px 0 rgba(62,128,165,0.50) ;
box-shadow: 2px 2px 2px 0 rgba(62,128,165,0.3) ;
}

ul.tabs li {
padding-bottom:25px !important;
}
</style>

Step 4— Call Those components together

<template>
<div class="container">
<div class="columns has-text-centered">
<div class="column">
<h1>Cars</h1>
</div>
</div>
<div class="columns">
<div class="column">

<tabs classname="menu_type_slider_one is_center"
:showstatus="true"
@changestatustab="changeStatusTab">
<!-- SPECIAL FEATURE -->
<tab title="Ford Mustang" value="ford_mustang" :active="true">
<h2>Ford Mustang </h2>
<figure class="image is-16by9">
<img src="/images/mustang.png" alt="Ford Mustang" />
</figure>
<p>The Ford Mustang, an emblem of American muscle and performance, first galloped onto the scene in 1964. Designed to appeal to the younger generation, the Mustang's sleek and sporty aesthetic captured the hearts of car enthusiasts worldwide. With its powerful V8 engine and unmistakable fastback design, the Mustang quickly became a symbol of freedom and the open road. Over the years, this classic pony car has undergone various transformations, adapting to modern tastes while retaining its timeless charm.</p>
</tab>
<tab title="Volkswagen Beetle" value="volkswagen_beetle" >
<h2>Volkswagen Beetle </h2>
<figure class="image is-16by9">
<img src="/images/volkswagen_beetle.png" alt="Ford Mustang" />
</figure>
<p>The Ford Mustang, an emblem of American muscle and performance, first galloped onto the scene in 1964. Designed to appeal to the younger generation, the Mustang's sleek and sporty aesthetic captured the hearts of car enthusiasts worldwide. With its powerful V8 engine and unmistakable fastback design, the Mustang quickly became a symbol of freedom and the open road. Over the years, this classic pony car has undergone various transformations, adapting to modern tastes while retaining its timeless charm.</p>
</tab>
<tab title="Ferrari 458 Italia" value="ferrari_458" >
<h2>Ferrari 458 Italia</h2>
<figure class="image is-16by9">
<img src="/images/ferrari_458_italia.png" alt="Ford Mustang" />
</figure>
<p>For enthusiasts with a penchant for Italian luxury and performance, the Ferrari 458 Italia stands as a testament to automotive excellence. Introduced in 2009, the 458 Italia quickly became a benchmark for supercars. Its mid-engine layout, breathtaking aerodynamics, and a high-revving V8 engine that produced an exhilarating symphony of power set it apart. The 458 Italia's precision handling and stunning design garnered acclaim from critics and enthusiasts alike, making it a modern classic in the Ferrari lineage.</p>
</tab>
</tabs>
</div>
</div>
</div>
</template>
<script setup>
import Tabs from '@/components/Tabs.vue'
import Tab from '@/components/Tab.vue'

const changeStatusTab = async (status) => {
console.log(status)
}
</script>
<style scoped>
h1 {
font-size: 20px;
text-transform: uppercase;
padding-bottom: 30px;
}
h2 {
padding-top:20px;
padding-bottom: 20px;
font-size: 21px;
text-align:center;

}
p {
padding-top:50px;
font-size: 17px;
line-height: 32px;
}
div.container {
padding-top: 400px;
width: 90%;
}
</style>

Now you can Run —

npm run dev

Final Result

Conclusion

That’s it! You’ve successfully created a custom Vue js Tabs Component. Feel free to enhance and customize the component further based on your project requirements.

The Git Hub Project -

Thanks a lot for reading till end. Follow or contact me via:
Github:https://github.com/murilolivorato
LinkedIn: https://www.linkedin.com/in/murilo-livorato-80985a4a/
Youtube : https://www.youtube.com/@murilolivorato1489

--

--