Tutorial

Building a Tabs Component with React

React

While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.

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:

React Tabs Component

You can find a working demo and code from this article over on CodeSandbox.

Enjoy! đź’Ą

0 Comments

Creative Commons License