How to implement routing in reactJs without using react router package ?

Well, Today in this article we are going to discuss about one of the famous react interview question "How to implement routing in reactJs application without using router package for a very small application like 2 to 3 pages ? ".

So first of all lets create a new react application using npx create-react-app routing-app This will create empty reactJs boiler plate code.

First thing that we are going to do is create a file name Routing.js where we will page mapper. This will contain the name of all the screens that we are going to have. Something like this.

// Routing.js
export const pagesMapping = {
    home:'home',
    about:'about',
    contact:'contact',
}

Now lets create a state which will keep our current selected page with the initial value of our first page which is home.

// Routing.js
const [page, setPage] = useState(pagesMapping.home);

When we are going to navigate from one page to other we will keep updating our page value using setPage for that this page and setPage need to be accessible in our complete application. To achieve that we are going to use React context api. Let's create it now.

// Routing.js
export const RoutingContext = createContext({ page: pagesMapping.home })

Now let's create context provider which will wrap our App.js

export default function AppRouter({children}) {
 const [page, setPage] = useState(pagesMapping.home)

    const value = useMemo(
        () => ({ page, setPage }),
        [page, setPage]
    )

     return (
        <RoutingContext.Provider value={value}>
            {children}
        </RoutingContext.Provider>
    )
}

In the above code we have passed the page state to the provider which will be accessible to our complete app once we wrap it in <App/> inside index.js file. See below code.

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import Router from "./Routing";

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
      <AppRouter>
        <App />
      </AppRouter>
  </React.StrictMode>
);

Now let's create our pages inside App.js and try to render it on the screen.

import logo from './logo.svg';
import './App.css';
import {pagesMapping, RoutingContext} from "./Routing";
import {useContext} from "react";

const Home = () => {
    const { setPage } = useContext(RoutingContext)
    const handleNavigation = () => {
        setPage(pagesMapping.about)
    }
  return (
      <div>
        <h1>Home</h1>
          <button onClick={handleNavigation}>GO TO ABOUT</button>
      </div>
  )
}
const About= () => {
  return (
      <div>
        <h1>About</h1>
      </div>
  )
}

const Contact = () => {
  return (
      <div>
        <h1>Contact</h1>
      </div>
  )
}


function App() {
  const { page } = useContext(RoutingContext)
  return (
      <>
        {(pagesMapping.home === page) && <Home />}
        {(pagesMapping.about === page) && <About />}
        {(pagesMapping.contact === page) && <Contact />}
      </>
  );
}

export default App;

Here in the above code i have created 3 pages which gets rendered on the screen. Once value of page state gets updated. And to navigation from Home page to About page I have added a handleNavigation function which update the page state using setPage function. If you try to run this code it will work fine but wait a minute we are not getting url updated. Let's try to add that as well.

In the Routing.js I have create an useEffect hook which will update the url as well on the page value update. Let's see how

 useEffect(() => {
        window.history.pushState({ page }, '', `/${page}`);

        const handlePopstate = (event) => {
            const { page } = event.state || { page: 'home' };
            setPage(page);
        };

        window.addEventListener('popstate', handlePopstate);

        // Clean up the event listener on component unmount
        return () => {
            window.removeEventListener('popstate', handlePopstate);
        };
    }, [page]);

So in the above code when page state changes useEffect will get triggered which will update the url with this line of code window.history.pushState({page},'','/${page}');

Similarly when we are going to navigate back we are having an eventListener popstate which will update the setPage with the previous page value on the url. This is how we will be able to update our page while moving back as well.

Here you can see complete code of Routing.js

import React, {useState, useMemo, createContext, useEffect} from 'react'


export const pagesMapping = {
    home:'home',
    about:'about',
    contact:'contact',
}


export const RoutingContext = createContext({ page: pagesMapping.home })

export default function Router({ children }) {
    /* Set the default page to Home if not specified otherwise in the URL */
    const [page, setPage] = useState( pagesMapping.home)

    const value = useMemo(
        () => ({ page, setPage }),
        [page, setPage]
    )

    useEffect(() => {
        window.history.pushState({ page }, '', `/${page}`);

        const handlePopstate = (event) => {
            const { page } = event.state || { page: 'home' };
            setPage(page);
        };

        window.addEventListener('popstate', handlePopstate);

        // Clean up the event listener on component unmount
        return () => {
            window.removeEventListener('popstate', handlePopstate);
        };
    }, [page]);

    return (
        <RoutingContext.Provider value={value}>
            {children}
        </RoutingContext.Provider>
    )
}
Caution: Don't use this approach of handling route in your application. This is just for understanding how you can achieve routing in reactJs application without using any library. But in production I will recommend to use react-router library which has way too many feature and edge case handling.

Did you find this article valuable?

Support aditya kumar by becoming a sponsor. Any amount is appreciated!