In traditional websites, the browser requests a document from a web server, downloads and evaluates CSS and JavaScript assets, and renders the HTML sent from the server. When the user clicks a link, it starts the process all over again for a new page.
In contrast, most modern applications use a process called Client side routing, which allows applications to update the URL from a link click and immediately render some new UI without making another request for another document from the server.
This enables faster user experiences because the browser doesn't need to request an entirely new document or re-evaluate CSS and JavaScript assets for the next page. It also facilitates more dynamic user experiences with things like animation.
In this chapter, we will learn how to implement Client side routing with the help of React Router, a popular routing library for React. React router enables us to implement client-side routing by specifying the components to be rendered for each URL declaratively. React router will render respective components based on the current URL.
To utilize React Router in a web application, we should install the package react-router-dom
. Even though the latest version of React Router is v6, we will be using v5 in this course. React Router v6 was released recently with significant architectural changes from v5. It takes a lot of work to migrate to v6. So, most of the products you will encounter today will be using v5 because, in most cases, there aren’t many significant advantages in migrating. So, we will also go with v5 because the goal of this book is to prepare you for the industry. If interested, you can utilize the official documentation to learn v6 architecture.
Now, let's get started by adding react-router-dom
to our project by executing the command below:
React Router offers a range of components and hooks for various purposes, such as tracking the current URL, managing browser history, updating URLs on user clicks, and rendering specific components based on URL matches.
BrowserRouter
is an important component among them. It serves as a wrapper around the entire application, enabling routing functionality by allowing other React Router components to interact with the browser's URL. It utilizes the HTML5 history API to synchronize the UI with the URL, stores the current location in the browser's address bar, and navigates through the built-in history stack.
Let's wrap the App
component in the src/index.jsx
with the BrowserRouter
to enable routing in our application.
Next, we will implement routing in our application by adding links to switch between two components: Home
and Product
. We will render the Home
component for the root URL ("/"), i.e., http://localhost:3000, and the Product
component for the "/product" URL (http://localhost:3000/product).
We already have the Product
component ready with us. Let's create a Home
component under the components directory. Add JSX to display a title, 'Smile Cart', and include placeholder text 'Home' as the content:
Now, let's head back to the App
component and add two links to the URLs: / and /product. React router provides the Link
component to create navigation links. Even though the Link
component uses the anchor tag <a>
under the hood, it updates the URL without full page reloads.
The Link
component accepts the prop to
to specify the URL:
Now you should be able to navigate between the root URL and the product URL:
The next step is to render the Home
component and the Product
component for the respective URLs. The Route
component from React Router enables this by accepting the path
and component
prop. If the current URL matches the path
, it renders the component
specified.
Let's add routes to Home
and Product
component in our app:
Now, try clicking the Home
and Product
links. You can see that the root URL renders the Home
component. But the product URL renders both the Home
and Product
components, which was not expected.
This happens because, by default, the Route
component doesn't perform an exact match of the current URL and path. Instead, if the specified path is a prefix of the current URL, it renders the associated component. In this case, the route "/" matches the "/product" URL because "/product" starts with "/". So both the routes "/" and "/product" will be rendered when the user visits "/product" URL.
However, if you want to ensure that the route only matches when the current URL and the specified path are the same, you can use the exact
prop:
Now, the links would work as expected:
While the above code works as intended, we will encapsulate the Route
components within the Switch
component from the React Router. The Switch
component renders the first child Route
that matches the location, rendering only one Route
component inside the Switch
at a time. Moreover, the Switch
component halts when it encounters the first route that matches the current location, thus enhancing the efficiency of the code.
Since the Switch
component renders the first matching route, the order in which you define the routes becomes significant when there are multiple matches.
For example, in the below route declaration, since the "/product" URL matches the first route with the path "/", the Switch
will render the Home
component.
To handle this, it's a good idea to place common or basic routes at the end of the Switch
component:
At present, navigating to any URL other than "/" or "/product" in our application by directly inputting the URL in the address bar leads to an empty page.
For a seamless user experience, it's essential to address such scenarios where users navigate to a location not defined within our application or isn't accessible to them. React Router provides a way to render fallback components to handle such unmatched routes.
Let's add a PageNotFound
component as a fallback component to display a helpful message when a user navigates to an undefined route.
First, let's create a PageNotFound
component. You can use the following JSX
to define the component:
Next, to render this component when the user navigates to a route other than the root URL or product URL, we only need to add a Route
component to the end of the Switch
block with the path as "*" and the component as PageNotFound
. It's that simple.
As we learned earlier, the Switch
component renders the first matched child Route
. Since the first two routes don't match, it checks the path of the last Route
. The wildcard character * matches any location, thus rendering the PageNotFound
component.
Now, when navigating to a URL other than "/" or "/product", you will be directed to the PageNotFound
component.
To enhance the user experience further, we can use the NavLink
component, a special version of the Link
that will add styling to the rendered element when it matches the current URL. NavLink
component accepts the activeClassName
prop for this:
After making the above change, the active link will be rendered with an underlined and bold text:
With this, we have covered the basics of client-side routing. In the next lesson, we will build upon these concepts to create a product listing page to display a set of product thumbnails and add navigation to the corresponding product details page upon clicking those thumbnails.
Let's commit the new changes:
You can verify the changes here.