DEV Community

Cover image for Vertical Tabs with React Router
Yuko
Yuko

Posted on

Vertical Tabs with React Router

This is an idea to implement tabs with React Router (v6) and styled-components. (demo)

tabs image

Overview

  • Use React Router to switch panels

  • Use React Router NavLink as a Tab

  • Each panel is a Route whose element is TabPanel

Tabs

Tabs.tsx

    import React from "react";
    import { TabContainer, TabPanelContainer } from "./Tabs.styles";
    import { TabListType } from "../../data/tab-lists";
    import TabList from "../TabList/TabList";

    type TabsProps = {
      children: JSX.Element;
      tabLists: TabListType[];
    };

    const Tabs = ({ children, tabLists }: TabsProps) => {
      return (
        <TabContainer>
          <TabList tabLists={tabLists} />
          <TabPanelContainer>{children}</TabPanelContainer>
        </TabContainer>
      );
    };

    export default Tabs;
Enter fullscreen mode Exit fullscreen mode

Tabs.styles.tsx

    import styled from "styled-components";

    type TabPanelProps = {
      height?: string;
    };

    export const TabContainer = styled.div`
      display: grid;
      grid-template-columns: 120px auto;
    `;
    export const TabPanelContainer = styled.div`
      overflow-y: scroll;
      height: ${({ height = "100vh" }: TabPanelProps) => height};
    `;
Enter fullscreen mode Exit fullscreen mode

TabList

TabList.tsx

    import React from "react";
    import { TabListType } from "../../data/tab-lists";
    import { TabListContainer, StyledNavLink } from "./TabList.styles";

    type TabListProps = {
      tabLists: TabListType[];
    };

    const TabList = ({ tabLists }: TabListProps) => {
      return (
        <TabListContainer>
          {tabLists.map((TabList) => (
            <StyledNavLink key={TabList.tabKey} to={TabList.tabKey}>
              {TabList.title}
            </StyledNavLink>
          ))}
        </TabListContainer>
      );
    };

    export default TabList;
Enter fullscreen mode Exit fullscreen mode

TabList.styles.tsx

I made use of the default active class to style the active navigation state. (ref: https://reactrouter.com/en/main/components/nav-link#default-active-class)

    import styled from "styled-components";
    import { NavLink } from "react-router-dom";

    type TabListContainerProps = {
      height?: string;
    };

    export const TabListContainer = styled.div`
      display: flex;
      flex-direction: column;
      overflow-y: scroll;
      height: ${({ height = "100vh" }: TabListContainerProps) => height};
    `;

    export const StyledNavLink = styled(NavLink)`
      display: block;
      color: black;
      text-transform: capitalize;
      text-align: center;
      text-decoration: none;
      padding: 5px;
      background-color: #efefef;
      &.active {
        border-left: 3px solid black;
        background-color: white;
      }
    `;
Enter fullscreen mode Exit fullscreen mode

TabPanel

TabPanel.tsx

    import React from "react";
    import { TabPanelContainer } from "./TabPanel.styles";

    type TabPanelProps = {
      title: string;
    };

    const TabPanel = ({ title }: TabPanelProps) => {
      return (
        <TabPanelContainer>
          <h1>{title}</h1>
        </TabPanelContainer>
      );
    };

    export default TabPanel;
Enter fullscreen mode Exit fullscreen mode

TabPanel.styles.tsx

    import styled from "styled-components";

    export const TabPanelContainer = styled.div`
      padding: 10px;
    `;
Enter fullscreen mode Exit fullscreen mode

App.tsx

    import "./styles.css";
    import { Routes, Route, Navigate } from "react-router-dom";
    import Tabs from "./components/Tabs/Tabs";
    import TabPanel from "./components/TabPanel/TabPanel";
    import { tabLists } from "./data/tab-lists";

    export default function App() {
      return (
        <Tabs tabLists={tabLists}>
          <Routes>
            {tabLists.map((tabList) => (
              <Route
                key={tabList.tabKey}
                path={tabList.tabKey}
                element={<TabPanel title={tabList.title} />}
              />
            ))}
            <Route path="*" element={<Navigate to="home" />} />
          </Routes>
        </Tabs>
      );
    }
Enter fullscreen mode Exit fullscreen mode

That’s it!

As a side note, I created the mock tabList with chatGPT.

The entire code is available here.
The original article is here

Top comments (0)