Creating a Tabs component with Next.js
Hey, It’s me again!
I’ve been asked by a couple of people new to Next.js how to implement tabs within a page. If you’re already familiar with Next.js, you’ll know that to create a page, you simply create a file in your pages
folder but not a lot of people know how to implement tabs within a particular page using Next.js.
Before we start this tutorial, Here are three reasons why I absolutely love Next.js:
- It’s so easy to setup
- It performs automatic code splitting
- Routing is extremely simple
You can read more about Next.js here
For this tutorial, I ’ll be using React to build out our component and Emotion for styling. If you’re familiar with styled-components, Emotion is very similar so you can still follow along. I’ll also be using Yarn for package management.
Step 1 — Install Packages
Install the packages we need with the command below in your terminal
yarn add react react-dom next @emotion/core @emotion/styled
Once you have these installed, your package.json
file should contain the following dependencies and scripts:
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@emotion/core": "^10.0.7",
"@emotion/styled": "^10.0.7",
"next": "^8.0.3",
"react": "^16.8.4",
"react-dom": "^16.8.4"
}
Step 2 — Folder Structure
Once your packages are installed, the next thing to do is create folders for your page layouts, pages, and components. Also, create a file for your styles. Your folder structure should look like this
.
├── components
├── layouts
├── node_modules
├── package.json
├── pages
├── styles.js
└── yarn.lock
Step 3 — Build Page Layout
The next step is to define how our pages will look. We want all our pages to have a navigation menu and the content of each page to be displayed below the navigation menu.
To do this, we’ll create index.js
file in the layouts
folder, then createNav
and PageBody
elements in our styles.js
file using emotion.
import styled from "@emotion/styled"export const Nav = styled("div")`
& > * {
margin-left: 1em;
color: white;
}
background: black;
padding: 1em;
height: 2em;
display: flex;
align-items: center;
`export const PageBody = styled("div")`
width: 100%;
height: 100%;
padding: 2em;
`
Import these into the layouts index file, and include our desired links within the Nav
tags using Next.js Link
component. Our layout component will also take children
as props so we can display our page content within the PageBody
tags.
Step 4 — Create pages
Now that we’ve created the layout, it’s time to create the home page and another page which you can name anything you like. I’ll call mine page-two.
The point of the layout is to have a common arrangement throughout the entire application without creating duplicate elements in each page.
All we have to do is import the layout component into each page and wrap it around our component and no matter the route we navigate to, we will always see the navigation bar.
Now, let’s create theindex.js
and page-two.js
files in the pages
folder. The home page is going to contain our Tabs Component which we’ll create later. For now, we’ll just have a placeholder text.
Run your application with the command below
yarn run dev
After running your application, if you navigate to http://localhost:3000
, you should see the navigation bar and the contents of each page.
You’ll notice that the navigation bar doesn’t extend fully to the sides of the page.
To solve this, we have to set the margin
and padding
of the HTML body to zero. We can also set the font and any other style we want to apply across the entire app.
There are two ways we can do this in our layout component. We can use Emotion’s Global
component in our layout component or Next.js Head
component. If you’re using styled-components, it has a createGlobalStyle function that can be used to achieve this.
The white space around the navigation bar should disappear after using any of these methods.
Step 5 — Create tabs component
Now, we’re going to create a basic tabs component with two tabs Tab 1
and Tab 2
Create a Tabs.js
file in the components
folder and create the following elements in your styled.js
file.
TabContainer — This will contain all the elements in the tabs component.
Tab — This is what we will click in order to switch tabs. The tab will take selected
prop. So we know which tab is currently selected and what background colour it should have.
TabHead — This will contain the tabs.
TabBody — This will contain the content for each tab.
export const TabHead = styled("div")`
border-bottom: 1px solid black;
display: flex;
background: black;
`export const TabContainer = styled("div")`
width: 30em;
height: 30em;
webkit-box-shadow: -1px 0px 5px 0px rgba(184, 184, 184, 1);
-moz-box-shadow: -1px 0px 5px 0px rgba(184, 184, 184, 1);
box-shadow: -1px 0px 5px 0px rgba(184, 184, 184, 1);
`export const TabBody = styled(PageBody)`
height: 100%;
`export const Tab = styled("div")`
padding: 1em;
background: ${({ selected }) => (selected ? "grey" : "black")};
* {
color: white;
}
`
Next, import these elements into Tabs.js
import React from "react"
import { TabHead, TabContainer, TabBody, Tab } from "../styles"const Tabs = () => {
return (
<TabContainer>
<TabHead>
<Tab />
<Tab />
</TabHead>
<TabBody />
</TabContainer>
)
}export default Tabs
Inside each Tab we need to add a link with the tab name, we’ll use the Next.js Link
component to do this. The Link
component takes a prop href
, that can either be a string or an object containing pathname
key. We’ll need to import the Link
component into the Tabs
component
import Link from “next/link”
then use it like this
<TabHead>
<Tab>
<Link href={{ pathname: "/" }}>
<a>Tab 1</a>
</Link>
</Tab>
<Tab>
<Link href={{ pathname: "/" }}>
<a>Tab 2</a>
</Link>
</Tab>
</TabHead>
The pathname
is the same because we still want to be on the same page when switching to different tabs. But how do we control which tab content to display when you click on any of them? By using queries.
The Link
component can also take aquery
key. Using the query
value, we can let our component know which content to display.
<TabHead>
<Tab>
<Link href={{ pathname: "/", query: { tab: "1" } }}>
<a>Tab 1</a>
</Link>
</Tab>
<Tab>
<Link href={{ pathname: "/", query: { tab: "2" } }}>
<a>Tab 2</a>
</Link>
</Tab>
</TabHead>
Remember our Tab element takes selected
prop, which should be a boolean. So for each Tab, we need to check that the current query is equal to 1
and 2
respectively. For scenarios where there’s no query then we’ll default to Tab 1.
To access the pathname and query, we need to wrap the Tabs component with Next.js withRouter
higher order component. After doing this, we can extract tab
from the route query and use it to check the selected tab.
We’ll create constants isTabOne
and isTabTwo
to hold this data.
When you run the application, you should see this.
Finally, we can render content for each tab using the constants we created.
<TabBody>
{isTabOne &&
<React.Fragment>This is tab one content</React.Fragment>
}
{isTabTwo &&
<React.Fragment>This is tab two content</React.Fragment>
}
</TabBody>
Our final product should look like this.
C’est Fini! Here’s a link to the source code on Github.
If you have any questions, leave a comment