How to make Radix UI Tabs URL based in NextJS
Tags: nextjs, react, frontend, a11y
Published: 2023-07-18 09:15:13

Tab is a common user interface pattern where contents are separated into different panes, each of which is viewable one at a time, allowing users to switch between different sections without changing the page.

[Radix UI](https://www.radix-ui.com) is a primitive, unstyled, customizable and accessibility focused React component library that provides a foundation for developers to build accessible components upon.

In this tutorial, you'll learn how to make Radix UI tabs URL based in NextJS without causing a full page reload. We'll start with setting up a new project, create a basic Radix UI tabs component, and finally make it URL based by integrating it with NextJS router.

### Requirements

- Basic understanding of JavaScript and React
- Node.js and NPM installed on your machine
- A text editor like VS Code


Making Tabs URL-based significantly makes your web application more accessible and user-friendly. It gives users and developers the ability to directly link to a specific content within the app, makes the URL bookmarkable and allows users to share specific content state by simply sharing a URL. It also maintains the user's navigation state if they accidentally reloaded the page, they would conveniently get back to their last active tab.

An example URL could look like this:

```js
https://app.com/page?tab=specificTab
```

### Step 1: Setting Up the Project

To bootstrap a Next.js project, we'll use the official `create-next-app` CLI by running the following commands:

```sh
npx create-next-app@latest
```

When you run the command, it initiates an interactive session where you have to provide specific details like project name. Once you answer all the prompts, a new Next.js application will be created for you.

Navigate to the newly created project folder and open it with a text editor like VS Code.

### Step 2: Creating a Basic Radix UI Tabs Component

The next step is to create a basic Radix UI tabs component. We'll use the `Tabs` component from `@radix-ui/react-tabs` package.
Install the package by running the following command:

```sh
# with npm
npm install @radix-ui/react-tabs

# with yarn
yarn add @radix-ui/react-tabs
```

The `Tabs` component is the parent component that wraps all the other components. It accepts a `defaultValue` prop that sets the default active tab. The `TabList` component is a wrapper for all the `TabTrigger` components. The `TabTrigger` component is a clickable element that triggers the tab change. The `TabContent` component is a wrapper for the tab content.

Next, create a Tabs component in `pages/index.tsx` file:

```jsx
import * as Tabs from "@radix-ui/react-tabs";

const TabsDemo = () => {
  return (
    <Tabs.Root>
      <Tabs.List>
        <Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
        <Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
        <Tabs.Trigger value="tab3">Tab 3</Tabs.Trigger>
      </Tabs.List>

      <Tabs.Content value="tab1">Tab 1 content</Tabs.Content>
      <Tabs.Content value="tab2">Tab 2 content</Tabs.Content>
      <Tabs.Content value="tab3">Tab 3 content</Tabs.Content>
    </Tabs.Root>
  );
};

export default function Home() {
  return <TabsDemo />;
}
```

### Step 3: Integrating with NextJS Router

The first step to make Radix UI tabs URL based is to make it's state controlled by using React's `useState` hook to create a state variable that holds the active tab value. We'll then pass the state variable to the `Tabs` component as a `value` prop then create a function that handles the tab change and pass it to the `Tabs` component as an `onValueChange` prop.

```jsx
import React from "react";
import * as Tabs from "@radix-ui/react-tabs";

const TabsDemo = () => {
  const [activeTab, setActiveTab] = React.useState("tab1");

  const handleTabChange = (value) => {
    setActiveTab(value);
  };

  return (
    <Tabs.Root
      value={activeTab}
      defaultValue="tab1"
      onValueChange={handleTabChange}
    >
      <Tabs.List>
        <Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
        <Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
        <Tabs.Trigger value="tab3">Tab 3</Tabs.Trigger>
      </Tabs.List>

      <Tabs.Content value="tab1">Tab 1 content</Tabs.Content>
      <Tabs.Content value="tab2">Tab 2 content</Tabs.Content>
      <Tabs.Content value="tab3">Tab 3 content</Tabs.Content>
    </Tabs.Root>
  );
};
```

Finally, we'll integrate the `Tabs` component with NextJS router by using the `useRouter` hook to get the current tab value from the URL query parameter and set it as the initial tab value. We'll also add a `useEffect` hook that listens to the router query parameter changes and updates the active tab value accordingly.

Here is the final code:

```jsx
import React from "react";
import { useRouter } from "next/router";
import * as Tabs from "@radix-ui/react-tabs";

const TabsDemo = () => {
  const router = useRouter();
  const initialTab = router.query.tab;
  const [activeTab, setActiveTab] = React.useState(initialTab || "tab1");

  const handleTabChange = (value) => {
    //update the state
    setActiveTab(value);
    // update the URL query parameter
    router.push({ query: { tab: value } });
  };

  // if the query parameter changes, update the state
  React.useEffect(() => {
    setActiveTab(router.query.tab);
  }, [router.query.tab]);

  return (
    <Tabs.Root
      value={activeTab}
      defaultValue="tab1"
      onValueChange={handleTabChange}
    >
      <Tabs.List>
        <Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
        <Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
        <Tabs.Trigger value="tab3">Tab 3</Tabs.Trigger>
      </Tabs.List>

      <Tabs.Content value="tab1">Tab 1 content</Tabs.Content>
      <Tabs.Content value="tab2">Tab 2 content</Tabs.Content>
      <Tabs.Content value="tab3">Tab 3 content</Tabs.Content>
    </Tabs.Root>
  );
};
```

### Conclusion

In this tutorial, you learned how to make Radix UI tabs URL based in NextJS without causing a full page reload. You can find the full source code on [GitHub](https://github.com/yinkakun/url-based-radix-tabs-nextjs) and the live demo [here](https://url-based-radix-tabs-nextjs.vercel.app).

### Resources

- [Radix UI Tabs](https://www.radix-ui.com/docs/primitives/components/tabs)

- [NextJS useRouter](https://nextjs.org/docs/pages/api-reference/functions/use-router)

- [Create Next App CLI](https://nextjs.org/docs/api-reference/create-next-app)