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.
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
We will have two components:
Tabs will be where most of the magic happens, where Tab is purely a presentational component.
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>
);
}
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>
);
}
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>
);
}
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.
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