Monday, 25 June, 2018 UTC


Summary

If you’ve ever built a web app, there’s a good chance you’ve built a tabbed document interface at some point or another. Tabs allow you to break up complex interfaces into (presumably) more manageable subsections that a user can quickly switch between. Love ‘em or hate ‘em, tabs aren’t going anywhere any time soon.
In this article you will learn how to create a simple and reusable tab container component that you can use by itself or with your existing components.
We will create three components:
  • Tabs that will display a list of clickable Tab components across the top, hold a state for which tab is active, and the active tab’s contents below the list of tabs.
  • Tab which displays the tab’s label, handles click events and let the Tabs component know which tab has been clicked.
  • App component so we can see our tabs in action!
Instead of going crazy with a bunch of tiny components, we will use boring old <div> tags for the tabs themselves. Each will have a label attribute that will be used for the list of clickable tabs.

Let’s get started with our App component that will hold our Tabs:
import React from 'react'; import { render } from "react-dom"; function App() { return ( <div> <h1>Tabs Demo</h1>  </div>  ); } const container = document.createElement('div'); document.body.appendChild(container); render(<App />, container); 
Nothing too exciting, yet. Just basic boilerplate stuff to get us up and running.

Next, we’ll build out our Tabs component:
Component: Tabs.js
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Tab from './Tab'; class Tabs extends Component { static propTypes = { children: PropTypes.instanceOf(Array).isRequired, } constructor(props) { super(props); this.state = { activeTab: this.props.children[0].props.label, }; } onClickTabItem = (tab) => { this.setState({ activeTab: tab }); } render() { const { onClickTabItem, props: { children, }, state: { activeTab, } } = this; return ( <div className="tabs"> <ol className="tab-list"> {children.map((child) => { const { label } = child.props; return ( <Tab activeTab={activeTab} key={label} label={label} onClick={onClickTabItem} />  ); })} </ol>  <div className="tab-content"> {children.map((child) => { if (child.props.label !== activeTab) return undefined; return child.props.children; })} </div>  </div>  ); } } export default Tabs; 
This component keeps track of which tab is active, displays a list of tabs and the content for the active tab.
The Tabs component utilizes our next component, Tab:
Component: Tab.js
import React, { Component } from 'react'; import PropTypes from 'prop-types'; class Tab extends Component { static propTypes = { activeTab: PropTypes.string.isRequired, label: PropTypes.string.isRequired, onClick: PropTypes.func.isRequired, }; onClick = () => { const { label, onClick } = this.props; onClick(label); } render() { const { onClick, props: { activeTab, label, }, } = this; let className = 'tab-list-item'; if (activeTab === label) { className += ' tab-list-active'; } return ( <li className={className} onClick={onClick} > {label} </li>  ); } } export default Tab; 
The Tab component displays the name of the tab and adds an additional class if the tab is active. When clicked, the component will fire a handler that will let Tabs know which tab should be active.

In addition to the components we just created, let’s add a small bit of CSS to make sure everything looks snazzy (and tab-like):
Styles: styles.css
.tab-list { border-bottom: 1px solid #ccc; padding-left: 0; } .tab-list-item { display: inline-block; list-style: none; margin-bottom: -1px; padding: 0.5rem 0.75rem; } .tab-list-active { background-color: white; border: solid #ccc; border-width: 1px 1px 0 1px; } 

Now that we have our components and associated styles, we will update our App component to utilize them:
import React from 'react'; import { render } from "react-dom"; import Tabs from './Tabs'; require('./styles.css'); function App() { return ( <div> <h1>Tabs Demo</h1>  <Tabs> <div label="Gator"> See ya later, <em>Alligator</em>! </div> <div label="Croc"> After 'while, <em>Crocodile</em>! </div> <div label="Sarcosuchus"> Nothing to see here, this tab is <em>extinct</em>! </div> </Tabs> </div> ); } const container = document.createElement('div'); document.body.appendChild(container); render(<App />, container); 
With Tabs added to our App component’s render method, we should have a working tabbed interface that allows us to toggle between sections!
It should look something like this:
You can find a working demo and code from this article over on CodeSandbox.
Enjoy! 💥