Skip to content

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Handling Tabs Using Page URLs and React Router Doms

Learn how to maintain your active tab by using URL parameters and leveraging React Router Dom instead of using React state.

Sep 29, 2020 • 13 Minute Read

Introduction

Tabs are critical elements for grouping similar data in web apps. However, most implementations of tabs in React use React state to maintain the active tab. This has the limitations of:

  • Inability to maintain the active tab on refreshing the page
  • A lot hassle to redirect to a specific tab

Learn how to maintain your active tab by using URL parameters and leveraging React Router Dom to overcoming these limitations.

This guide assumes that you are familiar with React, React Hooks, React Router Dom, Bootstrap, and Reactstrap.

Setup

Setup is split into two steps:

  • Setting Up the URL-Managed-Tabs React App
  • Installing Reactstrap

Setting Up the URL-Managed-Tabs React App

Use Create-React-App, a scaffold that lets you create React apps with no build configurations.

Ensure you have create-react-app installed on your machine. If not, you can install it by running the following:

      npm install -g create-react-app
    

Once it is installed, to create the app, run the following:

      npx create-react-app url-managed-tabs
    

The above command creates a React app with the name url-managed-tabs.

Navigate to your project's root directory.

      cd url-managed-tabs
    

Your folder structure should look like this:

      url-managed-tabs/    
    node_modules/
    public/
    src/   
    	App.css
        App.js
        App.test.js
        index.css
        index.js
        logo.svg
        serviceWorker.js
        setupTests.js
    package.json
    README.md
    yarn.lock
    

To start your app, run the following:

      yarn start
    

Install Reactstrap

First, install bootstrap, a pre-requisite for the Reactstrap package.

      npm install --save bootstrap
    

Then install the Reactstrap package.

      npm install --save reactstrap
    

Import Bootstrap CSS in the src/index.js file.

      import 'bootstrap/dist/css/bootstrap.min.css';
    

Development

Development will be split into the following steps:

  • Create the Tabs
  • Install React-Router-Dom
  • Create a Route with an Optional Parameter using React Router Dom
  • Refactor the Tabs to Use the URL active_page Parameter

Create the Tabs

Open the App.js file. Clear everything inside.

Then, create an object called tabs, defining the title and content of all the tabs.

      import React from 'react';
import {Row, Col} from 'reactstrap';

function App() {
    const tabs = {
            "draft": {
                title: "Draft",
                content: (
                    <Row className="p-2">
                        <Col sm="12" className="p-2">
                            <h4 className="text-info">Draft Tasks</h4>
                        </Col>
                    </Row>
                )
            },
            "in_progress": {
                title: "In Progress",
                content: (
                    <Row className="p-2">
                        <Col sm="12" className="p-2">
                            <h4 className="text-primary">In Progress Tasks</h4>
                        </Col>
                    </Row>
                )
            },
            "completed": {
                title: "Completed",
                content: (
                    <Row className="p-2">
                        <Col sm="12" className="p-2">
                            <h4 className="text-success">Completed Tasks</h4>
                        </Col>
                    </Row>
                )
            }
        }


    return (
        <React.Fragment/>
    );
}

export default App;
    

Create the tabs by iterating through the tabs object. Use useState React hook to add activeTab property to the React State of the App Component. Initialize the activeTab property with your preferred tab property name, in this case, in_progress. Define toggle function to handle the change of activeTab on clicking the NavLinks.

      import React from 'react';
import {Row, Col} from 'reactstrap';

function App() {
    const tabs = {
        "draft": {
            title: "Draft",
            content: (
                <Row className="p-2">
                    <Col sm="12" className="p-2">
                        <h4 className="text-info">Draft Tasks</h4>
                    </Col>
                </Row>
            )
        },
        "in_progress": {
            title: "In Progress",
            content: (
                <Row className="p-2">
                    <Col sm="12" className="p-2">
                        <h4 className="text-primary">In Progress Tasks</h4>
                    </Col>
                </Row>
            )
        },
        "completed": {
            title: "Completed",
            content: (
                <Row className="p-2">
                    <Col sm="12" className="p-2">
                        <h4 className="text-success">Completed Tasks</h4>
                    </Col>
                </Row>
            )
        }
    }

    const [activeTab, setActiveTab] = useState('in_progress');
    
    const toggle = tab => {
        if (activeTab !== tab) setActiveTab(tab);
    }

    return (
        <div className="row p-4">
            <div className="col-lg-12">
                <h2 className="mb-4">Tasks</h2>

                <Nav tabs>
                    {
                        Object.entries(tabs).map((tab) => (
                            <NavItem key={tab[0]}>
                                <NavLink
                                    className={activeTab === tab[0] ? "active" : ""}
                                    onClick={() => {
                                        toggle(tab[0]);
                                    }}
                                    role="button"
                                >
                                    {tab[1].title}
                                </NavLink>
                            </NavItem>
                        ))
                    }
                </Nav>

                <TabContent activeTab={activeTab}>
                    {
                        Object.entries(tabs).map((tab) => (
                            <TabPane key={tab[0]} tabId={tab[0]}>
                                {tab[1].content}
                            </TabPane>
                        ))
                    }
                </TabContent>
            </div>
        </div>
    );
}

export default App;
    

Install React Router Dom

      npm install --save react-router-dom
    

React Router is a routing library for React. React Router Dom is the dom binding package for React Router.

Create a Route with an Optional Parameter Using React Router Dom

Instead of just loading the App component directly in the index.js, define a route for the App component.

Change the index.js to this:

      import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {BrowserRouter, Switch, Route} from "react-router-dom";

ReactDOM.render(
  <React.StrictMode>
      <BrowserRouter>
          <Switch>
              <Route path="/:active_tab?" component={App}/>
          </Switch>
      </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
    

/:active_tab? means that whenever the route on the browser is / then the component to be loaded is App. The route can also take a parameter, though this is not mandatory. For example:

  • / - the active_tab parameter will be undefined
  • /in_progress - the active_tab parameter will be in_progress

So let's take advantage of this and ensure we can maintain our active tabs even on refreshing the page.

Refactor the Tabs to Use the URL active_page parameter

Start by creating a variable to hold the default active tab. jsx const DEFAULT_ACTIVE_TAB = "in_progress"; The URL Parameter can be accessed using React Router Dom's useParams hook: jsx const {active_tab} = useParams(); Do away with activeTab state variable. Initialize history variable with React Router Dom's useHistory hook, which is used for navigation. jsx const history = useHistory(); Default the active_tab parameter to the set DEFAULT_ACTIVE_TAB if not active_tab is specified on the URL. jsx useEffect(() => { if(!active_tab){ history.push(`/${DEFAULT_ACTIVE_TAB}`); } }, []); Refactor the toggle function to the route to the specified tab on click of a NavLink, thereby updating the active_tab URL parameter. jsx const toggle = tab => { if (active_tab !== tab) { history.push(`/${tab}`); } }

Refactor all initial instances of activeTab to active_tab. The final code should look like this: ```jsx import React, {useEffect} from 'react'; import {Col, Nav, NavItem, NavLink, Row, TabContent, TabPane} from 'reactstrap'; import {useParams, useHistory} from "react-router-dom"

      function App() {
    const DEFAULT_ACTIVE_TAB = "in_progress";
    const tabs = {
        "draft": {
            title: "Draft",
            content: (
                <Row className="p-2">
                    <Col sm="12" className="p-2">
                        <h4 className="text-info">Draft Tasks</h4>
                    </Col>
                </Row>
            )
        },
        "in_progress": {
            title: "In Progress",
            content: (
                <Row className="p-2">
                    <Col sm="12" className="p-2">
                        <h4 className="text-primary">In Progress Tasks</h4>
                    </Col>
                </Row>
            )
        },
        "completed": {
            title: "Completed",
            content: (
                <Row className="p-2">
                    <Col sm="12" className="p-2">
                        <h4 className="text-success">Completed Tasks</h4>
                    </Col>
                </Row>
            )
        }
    }

    const {active_tab} = useParams();
    const history = useHistory();
    
    useEffect(() => {
        if(!active_tab){
            history.push(`/${DEFAULT_ACTIVE_TAB}`);
        }
    }, []);

    const toggle = tab => {
       if (active_tab !== tab) {
            history.push(`/${tab}`);
       }
    }

    return (
        <div className="row p-4">
            <div className="col-lg-12">
                <h2 className="mb-4">Tasks</h2>

                <Nav tabs>
                    {
                        Object.entries(tabs).map((tab) => (
                            <NavItem key={tab[0]}>
                                <NavLink
                                    className={active_tab === tab[0] ? "active" : ""}
                                    onClick={() => {
                                        toggle(tab[0]);
                                    }}
                                    role="button"
                                >
                                    {tab[1].title}
                                </NavLink>
                            </NavItem>
                        ))
                    }
                </Nav>

                <TabContent activeTab={active_tab}>
                    {
                        Object.entries(tabs).map((tab) => (
                            <TabPane key={tab[0]} tabId={tab[0]}>
                                {tab[1].content}
                            </TabPane>
                        ))
                    }
                </TabContent>
            </div>
        </div>
    );
}

export default App;
```
    

Conclusion

There you have it. The active tab is now maintained using the active_tab URL Parameter. You can now share a link to access a specific tab, e.g., /completed, to redirect to the page with the completed tab active.

You are now familiar with some of the benefits of optional parameters on routes using React Router Dom and how to implement it. The next challenge is to explore other instances where this could be useful in your React apps.

Kimaru Thagana

Kimaru T.

Kimaru is a firm believer of education as a tool of self sufficiency. As software development consultant, living in Kenya, he mainly works to bring small and medium sized business to the internet with custom solutions ranging from data processing to business digitization. Away from the field of coding and computer science, he participates as a mentor for young university students. In his free time, he prefers peace and quiet, away from screens but close to nature.

More about this author