Before Starting: Embarking on a Journey of Custom Components

Welcome to the kickoff of a series where we delve into the art of building customized components. I am excited to share my journey with you, exploring the intricacies of frontend development and unveiling the mysteries behind each meticulously designed element.

Purpose

At the heart of this series lies a labor of love - my very own Custom UI library. And the purpose of the series is self reflection, seeking communication and sharing knowledge. I hope the way I've developed these components can provide you some help or inspirations and I would also learn a lot by writing my thoughts done, and improving my skills by your comments. It would be an opportunity for mutual growth.

What to expect

In each article, we'll dissect a specific component, explore its purpose&design, dive into the code, and discuss possible improvements and derived thoughts.

Techs I used

React, Tailwind and TypeScript

I also used `clsx` , a tiny utility for constructing className strings conditionally, and it's represented as `cx` in my codes. It's totally fine if you skip it.

Now, let the journey begin!

Tabs make it easy to switch between different views.

After this tutorial we're gonna make a Tabs component which looks like ⬇️

def Tabs

And it can be customized into

Card Tabs

In fact, both Normal and Card style Tabs are common. We can configure styles the two styles and add `type` abi to control which style we want to use(which we won't go there in this article). However, sometimes, we only need one style and the configuration for another style may seem redundant, especially if you want the total bundle size to be as small as possible. In fact, while we dealt with different situations, we keep expanding the features of a component to make it powerful and suit for various situations. The drawback is that it can highly increase the total bundle size. So the ability to build component and adapt according to different situation is more important than having a powerful component.

All codes can be found from my custom-ui repo

Essentials

What are essential elements for a Tabs component?

Let's start from the simplest part, no need to consider the whole Tabs in the beginning. When we first look at the Tabs component, what's our impression of its constitution? Navigations and content. And what's the element that constitute Tabs from the aspect of the words `Tabs` ? Of course, tab! Though we won't have the Tab component, but the concept of tab remains everywhere in the designing of Tabs.

So the essential elements from our first impression is:

* TabNavItem: which is the single item component that constitutes the navigation part of the Tabs component. It represents the navigation part of a specific Tab.

* TabContent: which is the body/content component for each tab. It represents the body/content part of a specific Tab.

But that's not enough, we also need to distinguish which tab is currently active, and switch the active status of tabs. To do that we need assign each tab a id to distinguish them and a controller component, which is to manage all the tabs, control the active status and is also the component that's exposed to developers(the controller component is the Tabs component).

TabNavItem

In this element, we not only need to conditionally render navigation item's styles according to whether or not the tab it represents is active, but also need to react to users' action to set the tab(it represents) as active. So TabNavItem requires the id of the tab, current active tab's id and a function to set the tab's id as active.

Besides, the TabNavItem normally isn't highly or complexly customized, so it would only requires the title or a string from the tab to represent the navigation item of the tab.

Apart of these functionality, it's better to also provide abis for customizing styles, which would make the Tabs component reusable.

So the codes of TabNavItem can be as following:

TabContent

This part is very easy, the Tabs component only render the content of the active tab, so it only requires the representing tab's id and current tab's id. Since content of each tab is alway highly customized and complex, we just allow users to pass the content element for each tab.

Tabs

This is the controller component as well as the component that's exposed to developers. We would assemble all TabNavItems and TabContents here and provide wrapper elements for navigation part(where all TabNavItems locate), body part(where all TabContents locate) and the wrapper of our whole Tabs component. We would also need to control activeId state here and provide a function for TabNavItems to set activeId state.

Besides of these functionality, for better customizability, the component also need to provide style apis for all wrapper elements as well as apis to pass styles for TabNavItem since TabNavItem isn't exposed to developers.

Congratulations ! We've just built a Tabs component. Pass some dumb items and it would look like:

This is just a very basic Tabs component which provides few necessary apis for customizing styles. We may want to set configurations for different style types or different positions of Tabs' navigation part, e.t. I would cover these topics my future articles.

See you next time <3~