Implement Tabbed Content With React

07/09/2022

Intro

Tabbed content is a great way to lower the amount of scrollable content on a webpage. Each tab will have its own unique content, and will only be visible when it is clicked in the tab pane. My goal today is to show you just how easy something like this is to build with React. If you have any questions, feel free to message me.

Demo

Before reading the article, I thought it would be nice to give the finished version of the Tabs a spin. You can also mess around with a version of this on Code Sandbox

Home
About Me
Contact

"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

Planning Our Components

We will have two components:

  • Tabs
  • Tab

Tabs will be where most of the magic happens, where Tab is purely a presentational component.

Tabs

Our Tabs component is pretty simple, it contains a flex container to display children (Tab components), a horizontal line, and another div where we will display the content of the active tab.

import React from "react";

export default function Tabs({ children, activeTab, onSelect }) {
  const [activeChild] = children.filter(
    (child) => child.props.title === activeTab
  );

  return (
    <div>
      <div style={{ display: "flex" }}>
        {/* x amount of Tab components will be rendered here.  */}
      </div>

      <hr style={{ margin: 0, marginBottom: 10 }} />

      <div style={{ fontSize: 18 }}>{/* Active tab content will go here. */}</div>
    </div>
  );
}

Tab

Each Tab component is clickable, and will display the name of the tab. When a tab is active, it's title color will change to blue, and will be underlined blue as well.

import React from "react";

export default function Tab({ title, activeTab, onSelect }) {
  return (
    <div
      key={title}
      style={{
        marginRight: 10,
        cursor: "pointer",
        borderBottom:
          title === activeTab ? "2px solid rgb(25, 118, 210)" : "none",
        padding: 5,
        fontSize: 20
      }}
      onClick={() => onSelect(title)}
    >
      <div
        style={{
          color: title === activeTab ? " rgb(25, 118, 210)" : "#495057"
        }}
      >
        {title}
      </div>
    </div>
  );
}

Implementation

Let's implement the Tabs by adding the following code to an App.js file.

import { useState } from "react";
import Tabs from "./components/Tabs";
import Tab from "./components/Tab";

export default function App() {
  const [activeTab, setActiveTab] = useState("Home");

  return (
    <div className="App">
      <Tabs activeTab={activeTab} onSelect={setActiveTab}>
        <Tab title="Home">My home page.</Tab>
        <Tab title="About Me">About me.</Tab>
        <Tab title="Contact">Get in touch.</Tab>
      </Tabs>
    </div>
  );
}

Mapping Through Children

What we have is great so far, but our current code doesn't actually display anything. We need to update our Tabs component to handle Tab components, and also display the active tab's content.

import React from "react";

export default function Tabs({ children, activeTab, onSelect }) {
  const [activeChild] = children.filter(
    (child) => child.props.title === activeTab
  );

  return (
    <div>
      <div style={{ display: "flex" }}>
        {React.Children.map(children, (child) => {
          return React.cloneElement(child, { activeTab, onSelect });
        })}
      </div>

      <hr style={{ margin: 0, marginBottom: 10 }} />

      <div style={{ fontSize: 18 }}>{activeChild.props.children}</div>
    </div>
  );
}

If we use the Tabs component properly, then when we use React.Children.map we can be ensured that this will be an array of Tab components. In order to pass each Tab the activeTab and onSelect props, we use React.cloneElement to merge those props in and create a cloned version of each Tab.

Next, below the horizontal line, we display the active tabs contents.

Summary

That's about it for our React tabs folks. As always, if you have any problems understanding any of this, feel free to shoot me a message, and I'll make sure it's crystal clear.

P.S. Here is a link to the sandbox again. React Tabs

Want To Level Up Your JavaScript Game?

Book a private session with me, and you will be writing slick JavaScript code in no time.