Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

normykinz's avatar

Custom Tabs Component Help Needed

This isn't really an Inertia question but I couldn'f find a better category. It's more of an slightly advanded Vue question.

I'm creating a reulable tabs component, which is going pretty well, but I've hit a snag and I could do with a hand to sort it out.

In my Tags component I have a slot for holding the content of any number of tabs. I generate TabLink components based on props passed in the TagContent component. The probkem is I can't change the show prop of any of the TagContent components programmatically.

Tags component

<template>
    <div class="mb-12 tabs tabs-boxed">
        <TabLink v-for="(tab, index) in tabs" @click="tabSelected(index)" :id="index"
            :active-tab="activeTab">{{
            tab.props.label }}
        </TabLink>
    </div>
    <slot></slot>
</template>

<script setup>
import { useSlots } from "vue";
import TabLink from "./TabLink.vue";

const slots = useSlots()
const tabs = slots.default()

const props = defineProps({
    active: { type: Number, default: 0}
})

let activeTab = ref(props.active)

function tabSelected(index) {
    const tab = tabs[index]
    activeTab.value = index
    if (tab.type.name === "TabContent") {
        tabs.forEach(tab => tab.props.show = false)
        tab.props.show = true
    }
}
</script>

TagLink component

<template>
    <a href="#" @click.prevent="tabSelected(id)" :class="{ 'tab-active': activeTab === id }"
        class="tab tab-lg">
        <slot></slot>
    </a>
</template>

<script setup>
let props = defineProps({
    activeTab: { required: true, type: Number },
    id: { required: true, type: Number },
});

let emit = defineEmits(["tabSelected"]);

function tabSelected(id) {
    emit("tabSelected", id);
}
</script>

TagContent component

<template>
    <section v-show="show" class="mx-6">
        <slot></slot>
    </section>
</template>

<script setup>
    let props = defineProps({
        label: { required: true, type: String },
        show: { type: Boolean, default: false }
    })
</script>

Implentation

<Tabs>
<TabContent label="details" :show=true>Details</TabContent>
<TabContent label="Address">Address</TabContent>
<TabContent label="Subscriptions">Subscriptions</TabContent>
<TabContent label="Roles">Roles</TabContent>
</Tabs>
0 likes
1 reply
Rutherfordium's avatar

In your TagLink component you use emit("tabSelected", id) in tabSelected function, but on your parent component (TagsComponent ) it might because you haven't specified the listener, e.g : @tab-selected=tabSelected(index)

You can check this vue docs for emitting and listening to events or Jeffrey's Learn Vue 3: Step by Step on episode 9 / episode 21

and from the above docs, instead of passing it into a function you can directly use

@click.prevent=$emit('tabSelected',  id)

Please or to participate in this conversation.