Introduction:

As web developers, we often rely on libraries and frameworks to handle routing for us. In the case of a client-side application built with React, the most popular choice is probably React Router. However, there may be situations where you want to build your own custom router for various reasons such as performance, custom features, or simply for learning purposes.

In this blog post, we will explore how to build a custom React router from scratch. We will start by discussing the basics of client-side routing and the role of a router in a React application. Then, we will implement a simple router using the window.history API and the useEffect hook from the React Hooks API. Finally, we will see how to add additional features such as route matching, URL parameters, and programmatic navigation to our router.

Basics of Client-Side Routing:

Client-side routing refers to the process of navigating between different pages or views of a client-side application without reloading the whole page. This is in contrast to server-side routing, where the server handles the navigation and sends a new HTML document to the client on each navigation.

In a client-side application, the router is responsible for mapping the current URL to a specific view or component to be displayed. It also updates the URL when the user navigates to a different view. This way, the application can have multiple views that can be accessed through different URLs, just like a traditional website.

Building a Simple Router:

To build our custom router, we will start by creating a Router component that will act as the root of our application. This component will have a routes prop that is an array of objects representing the routes in our application. Each route object should have a path and a component property. The path is a string representing the URL pattern of the route, and the component is a React component that will be displayed when the route is active.

Here is an example of how our Router component could look:

Copy to Clipboard

In the Router component, we are using the useEffect hook to listen for popstate events on the window object. The popstate event is fired every time the user navigates through the history, either by clicking on the back or forward buttons of the browser or by calling the history.back() or history.forward() methods.

In the handleLocationChange function, we will update the active route based on the current URL. To do this, we will loop through the routes prop and check if the path of each route matches the current URL. We can use the window.location.pathname property to get the current URL and the matchPath function from the react-router-dom library to check if a route’s path matches the current URL. Here is an example of how we can implement this in the handleLocationChange function:

Copy to Clipboard

Now that we have the active route, we need to render the corresponding component. To do this, we can add a state variable to the Router component to store the active route. Then, we can use this state variable to render the active route’s component in the Route component.

Here is an example of how this could look:

Copy to Clipboard

With this implementation, the Route component will render the corresponding component if the route is active, otherwise it will return null.

Adding URL Parameters and Programmatic Navigation:

Now that we have a basic router working, we can add some additional features to it. One common feature of routers is the ability to extract URL parameters and pass them to the route’s component as props. For example, if we have a route with the path /users/:id, we can extract the id parameter from the URL and pass it to the component as a prop.

To implement this feature, we can modify the Route component to extract the parameters from the URL and pass them as props to the component. We can use the matchPath function from react-router-dom again to extract the parameters from the URL.

Here is an example of how this could look:

Copy to Clipboard

Another useful feature of routers is the ability to navigate programmatically. This means that we can change the URL and the active route from within our application code, without the user clicking on a link or using the browser’s navigation buttons.

To implement this feature, we can add a navigate method to the Router component that updates the URL and triggers the popstate event. We can use the history.pushState method of the window.history API to update the URL and the dispatchEvent method to trigger the popstate event.

Here is an example of how this could look:

Copy to Clipboard

With this implementation, we can call the navigate method from anywhere in our application to update the URL and trigger a navigation.

Conclusion:

In this blog post, we have seen how to build a custom React router from scratch. We started by discussing the basics of client-side routing and the role of a router in a React application. Then, we implemented a simple router using the window.history API and the useEffect hook from the React Hooks API. Finally, we added additional features such as route matching, URL parameters, and programmatic navigation to our router.

While building a custom router can be a fun and educational exercise, in most cases it is probably easier and more practical to use an existing router library such as React Router. However, understanding how routers work and being able to build your own can give you a deeper understanding of how client-side routing works and give you more control over your application’s routing behavior.