This site is under construction Subscribe to updates

Tab with Next.js App Router

Using Next.js Parallel Routes to create accessible Tabs and tab panels that are rendered on the server and controlled by the URL.

Open in new tab
import "./style.css";
import type { ReactNode } from "react";
import Link from "next/link.js";
import { Tab, TabList, TabPanel, Tabs } from "./tabs.jsx";
export default function Layout(props: { tabs: ReactNode }) {
return (
<main className="main">
<h1 className="heading">Posts</h1>
Check out the{" "}
<Link href="/previews/tab-next-router" className="link">
trending posts
</Link>{" "}
or stay up to date with the{" "}
<Link href="/previews/tab-next-router/new" className="link">
latest posts
<div className="wrapper">
<Tab href="/previews/tab-next-router">Hot</Tab>
<Tab href="/previews/tab-next-router/new">New</Tab>

Abstracting the Tab components

In this example, we're abstracting the Ariakit Tab components into higher-level components with a simpler API integrated with the Next.js App Router. Check out the tabs.tsx file above to see the source code.

We're using React Context to provide the tab store to the TabList and TabPanel components. You can learn more about this pattern on the Component stores guide.

Controlling the Tab state

To control the selected tab state, you can pass the selectedId and setSelectedId props to useTabStore. These props allow you to synchronize the tab state with other state sources, such as the browser history.

const router = useRouter();
const pathname = usePathname();
const tab = Ariakit.useTabStore({
selectedId: pathname,
setSelectedId(id) {
router.push(id || pathname);

You can learn more about controlled state on the Component stores guide.

Rendering a single TabPanel

It's possible to render a single TabPanel component and use the tabId prop to point to the selected tab.

const tabId = tab.useState("selectedId");
<TabPanel tabId={tabId}>{props.children}</TabPanel>;