DEV Community

Cover image for Vue/Nuxt + ChakraUI: Tabs
James Foran
James Foran

Posted on

Vue/Nuxt + ChakraUI: Tabs

Chakra UI logo

I have been working with Chakra UI Vue for several months. I am using Chakra in a side project to learn more about JAMStack and Vue/Nuxt. I am drawn to Chakra UI because of its practical set of components, and its focus on accessibility. This is the first article in a series I will be publishing about Vue and Chakra. I hope you enjoy!

One of the components I have been working with a lot is the tabs component. Tabs works well when presenting content. I have also been using it to driver high-level option selection for specific processes and user flows. Let's jump right in...

This post assumes you already have a Nuxt project setup with Chakra installed, as well as auto-imports enabled.

The standard way of setting up tabs using Chakra is using a parent c-tabs element, containing a single c-tab-list and single c-tab-panels element.

<c-box>
    <c-tabs>
        <c-tab-list>
        <c-tab>One</c-tab>
        <c-tab>Two</c-tab>
        <c-tab>Three</c-tab>
        </c-tab-list>
        <c-tab-panels>
        <c-tab-panel>
            <p>one!</p>
        </c-tab-panel>
        <c-tab-panel>
            <p>two!</p>
        </c-tab-panel>
        <c-tab-panel>
            <p>three!</p>
        </c-tab-panel>
        </c-tab-panels>
    </c-tabs>
</c-box>
Enter fullscreen mode Exit fullscreen mode

It is important to ensure you have the same number of c-tab-panel elements as you do c-tab elements. Not doing so may mean your content is not accessible to the user. The order is also critical to this working, as the first c-tab button will correspond to the first c-tab-panel element. This is quick to get up and running. Note, each c-tab becomes a button when it renders.

In addition to the separation of content, I also want to know which tab a user has selected. To do this I recommend moving to a different pattern. This pattern involves moving the tab options to an array and then using Vues v-for directive look to generate the c-tab-list elements.

<c-box>
    <c-tabs>
        <c-tab-list>
        <c-tab v-for="tab in tabs" :key="tab">{{
            tab
          }}</c-tab>
        </c-tab-list>
...
    </c-tabs>
</c-box>
Enter fullscreen mode Exit fullscreen mode

We have now introduced a new data object here.. tabs. Lets set it up so the above code works:

data() {
    return {
      tabs: ['One', 'Two', 'Three'],
      tabIndex: 0,
    }
  },
Enter fullscreen mode Exit fullscreen mode

We then call a method on the @change event, to change the tabIndex value:

 <c-tabs @change="setTabIndex">
     <c-tab-list>...

...
methods: {
    setTabIndex(index) {
      this.tabIndex = index
    },
}
Enter fullscreen mode Exit fullscreen mode

As things are now, tabIndex will change as the user selects a different tab, but we don't yet know which option the user has selected, just its index value. To may the index value, back to the array, we then use a computed property to give us the selected tab text:

computed: {
    selectedTab() {
      return this.tabs[this.tabIndex]
    },
}
Enter fullscreen mode Exit fullscreen mode

We now have the selectedTab, and tabIndex values in the parent component, which we can used to drive other behaviour.

For one final feature, we can then drive the default index, property in the c-tabs element using the tabIndex field:

<c-tabs :default-index="tabIndex" @change="setTabIndex" >
Enter fullscreen mode Exit fullscreen mode

By doing this, you can set the initial tab selection.

Limitations of this approach

Your c-tab styling needs to be simple. Adding images or icons to your tab may over-complicate things. When using library components like this, keep it simple.

Another limitation may present itself if you want to enable/disable a tab. In this case, the following pattern works better.

Alternative

To enable a single tab to be disabled, the following approach can be applied.

 <c-tab-list>
    <c-tab>{{ tabs[0] }}</c-tab>
    <c-tab :is-disabled="tabTwoDisabled">{{ tabs[1] }}</c-tab>
    <c-tab>{{ tabs[2] }}</c-tab>
 </c-tab-list>

data() {
    return {
      tabs: ['One', 'Two', 'Three'],
      tabIndex: 0,
      tabTwoDisabled: false,
    }
  },
Enter fullscreen mode Exit fullscreen mode

This maintains the array of options, but gives the programmer the ability to enable/disable a single tab.

Summary

Chakra's tabs are a joy to work with, as long as you keep it simple. If you also want to know the current active tab, then the above pattern works well.

Links

Top comments (0)