Next.js is a powerful React framework that makes it easy to build and deploy server-side rendered React applications. One of the many features that Next.js provides is the ability to easily create linkable tabs.
In this article, we'll explore how to create linkable tabs in Next.js using the built-in Link component and the styled-jsx library. We'll also look at how to handle the active state of the tabs and make the tabs look like they're part of a traditional tabbed interface.
Creating the Tabs
The first thing we need to do is create the tabs themselves. We'll do this using the Link component from Next.js.
Each Link component will take an href prop which will be the URL that the tab will link to. We'll also give each Link a className of "tab" so that we can style them later.
const Tab1 = () => (
<Link href="/tab1" className="tab">
Tab 1
</Link>
);
const Tab2 = () => (
<Link href="/tab2" className="tab">
Tab 2
</Link>
);
Now that we have our tabs, we need to style them. We'll do this using the styled-jsx library.
First, we'll create a global style sheet that will style our tabs. We'll give the tabs a width of 100% and some basic styles for the unselected state.
// Global stylesheet
const styles = (
<style jsx global>{`
.tab {
display: inline-block;
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-bottom: none;
text-align: center;
cursor: pointer;
}
.tab:hover {
background: #f0f0f0;
}
`}</style>
);
Next, we'll create a style sheet for the selected state of the tabs. We'll give the tabs a different color and make the bottom border thicker.
// Selected state stylesheet
const selectedStyles = (
<style jsx>{`
.tab.selected {
border-color: #aaa;
border-width: 2px;
background: #fff;
}
`}</style>
);
Now that we have our stylesheets, we need to apply them to our tabs. We'll do this using the jsx prop on the Link component.
const Tab1 = () => (
<Link
href="/tab1"
className="tab"
jsx={[styles, selectedStyles]}
>
Tab 1
</Link>
);
const Tab2 = () => (
<Link
href="/tab2"
className="tab"
jsx={styles}
>
Tab 2
</Link>
);
Now our tabs are styled and we can move on to handling the active state.
Handling the Active State
There are a few different ways that we can handle the active state of our tabs. In this section, we'll explore two of them.
The first method is to use the built-in router from Next.js. This method is simple and easy to use, but it has a few drawbacks. The biggest drawback is that it will only work with browser history API-enabled browsers.
The second method is to use a custom solution that uses the URL query string. This method is a bit more complex, but it will work with all browsers.
Using the built-in router
If we want to use the built-in router from Next.js, we need to wrap our tabs in a Router component. We also need to give each tab a unique key so that the router can keep track of them.
const Tab1 = () => (
<Router>
<Link
key="tab1"
href="/tab1"
className="tab"
jsx={[styles, selectedStyles]}
>
Tab 1
</Link>
</Router>
);
const Tab2 = () => (
<Router>
<Link
key="tab2"
href="/tab2"
className="tab"
jsx={styles}
>
Tab 2
</Link>
</Router>
);
Now that our tabs are wrapped in a Router component, the built-in router will take care of the active state for us.
Using the URL query string
If we want to use the URL query string to handle the active state, we need to write a custom React hook that parses the query string and returns the active tab.
// useActiveTab.js
import { useRouter } from "next/router";
export default function useActiveTab() {
const { query } = useRouter();
const activeTab = query.activeTab || "tab1";
return activeTab;
}
Now that we have our custom hook, we can use it in our tabs to add the active class to the correct tab.
import useActiveTab from "../hooks/useActiveTab";
const Tab1 = () => {
const activeTab = useActiveTab();
return (
<Link
href="/tab1"
className={`tab ${activeTab === "tab1" && "selected"}`}
jsx={[styles, selectedStyles]}
>
Tab 1
</Link>
);
};
const Tab2 = () => {
const activeTab = useActiveTab();
return (
<Link
href="/tab2"
className={`tab ${activeTab === "tab2" && "selected"}`}
jsx={styles}
>
Tab 2
</Link>
);
};
Now our tabs will correctly update the active state when the query string is updated.
Making the Tabs Look Like They're Part of a Tabbed Interface
If we want our tabs to look like they're part of a traditional tabbed interface, we need to add some additional styles.
First, we'll add a style sheet that styles the tab content. We'll give the content a width of 100% and make it hidden by default.
// Tab content stylesheet
const contentStyles = (
<style jsx>{`
.tab-content {
display: none;
width: 100%;
padding: 20px;
border: 1px solid #ccc;
background: #fff;
}
`}</style>
);
Next, we'll add a style sheet for the selected state of the tab content. We'll make the content visible when it's selected.
// Selected state stylesheet
const selectedContentStyles = (
<style jsx>{`
.tab-content.selected {
display: block;
}
`}</style>
);
Now we need to apply our stylesheets to our tab content. We'll do this using the jsx prop on the Link component.
const Tab1Content = () => (
<div
className="tab-content"
jsx={[contentStyles, selectedContentStyles]}
>
Tab 1 content
</div>
);
const Tab2Content = () => (
<div
className="tab-content"
jsx={contentStyles}
>
Tab 2 content
</div>
);
Finally, we need to update our tabs to render the correct tab content when they're clicked. We'll do this using the built-in router from Next.js.
const Tab1 = () => (
<Router>
<Link
key="tab1"
href="/tab1"
className="tab"
jsx={[styles, selectedStyles]}
>
Tab 1
</Link>
<Tab1Content />
</Router>
);
const Tab2 = () => (
<Router>
<Link
key="tab2"
href="/tab2"
className="tab"
jsx={styles}
>
Tab 2
</Link>
<Tab2Content />
</Router>
);
Now our tabs look like they're part of a traditional tabbed interface and we can easily switch between them.
Conclusion
In this article, we've explored how to create linkable tabs in Next.js using the built-in Link component and the styled-jsx library. We've also looked at how to handle the active state of the tabs and make the tabs look like they're part of a traditional tabbed interface.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.