While developing with React, a framework for Single Page Applications (SPA), I had a question. React detects changes on a single page and updates the Virtual DOM, rendering only the parts that have changed. So I wondered, "Are all pages the same since there’s no URI distinction?"
However, when you look at popular portal sites, you’ll see that every page has its own unique URI, and these URLs are used to share and differentiate pages. So, does a React-based web application not support routing? The answer is: React does not provide routing out of the box.
In this post, I will explain how to implement routing and assign unique URIs to pages in a React app using the most widely used third-party library, React Router. We will also go through some sample components of the library.
Table of Contents
- Environment Setup
- Link
- Route
- Redirect
- Router
- Switch
Prerequisites
- Node.js
- npm (installed with Node.js)
- Yarn
- CRA (Create React App)
- Additional Libraries (React Router)
- VS Code (Visual Studio Code)
- Experience with React (see React Learning Part 1)
Installation
- Node.js: Download from the official website and install the LTS version suitable for your system.
- After Node.js installation, npm is installed automatically. You can use npm commands (see npm documentation).
- Install VSCode from Visual Studio Code.
To install Yarn, run:
npm -g install yarn
Install CRA (Create React App) globally via Yarn:
yarn global add create-react-app
Install React Router:
yarn global add react-router-dom
(Use PowerShell or Command Prompt on Windows, Terminal on macOS).
Project Setup
Create a sample React project using CRA. In your Command Prompt or Terminal, navigate to your desired project folder.
Go to project folder(In this case, my documents):
cd C:\Users\Nextree\Documents
On Windows:
npx create-react-app router-master
On macOS:
create-react-app router-master
After creating the project, open it in VSCode by selecting File > Open Folder and choosing the project directory.
Delete all files under the src
directory, except for index.js and index.css.
Now, update index.js as follows:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
ReactDOM.render(< />, document.getElementById('root'));
Now we’re ready to start creating the sample components!
Link
Before writing the sample components, create a new file SampleRouter.js in the same directory as index.js
:
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
class SampleRouter extends React.Component {
render() {
return (
<div>
<Link to='/link1'>Link1</Link>
<Link to='/link2'>Link2</Link>
</div>
);
}
}
export default SampleRouter;
Then, update index.js
to use SampleRouter:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import SampleRouter from './SampleRouter';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<SampleRouter />
</BrowserRouter>,
document.getElementById('root'));
Now, run the server to see the links. In VSCode, open the terminal by selecting Terminal > New Terminal. In the terminal, run:
yarn start
After a moment, the browser will show the two links. Clicking them will take you to http://localhost:3000/link1
and http://localhost:3000/link2
.
Route
First, you don’t need to stop the server. The server will automatically reload every time you save a change, so there's no need to manually stop it. If you do want to stop it, press Ctrl + C
.
Now, let's go back to SampleRouter.js
and update the code as follows:
import React from "react";
import { Router, Route, Link } from "react-router-dom";
class SampleRouter extends React.Component {
render(){
return(
<div>
<Link to='/link1'>Link1</Link>
<Link to='/link2'>Link2</Link>
<Route path='/link1' component={SampleComponent1} />
<Route path='/link2' component={SampleComponent2} />
</div>
);
}
}
const SampleComponent1 = () => {
return <div>Hello SampleComponent1!</div>;
};
const SampleComponent2 = () => {
return <div>Hello SampleComponent2!</div>;
};
export default SampleRouter;
In this code, we are using the Route
tag to associate each link with a specific path and component. The first Route
has a path='/link1'
and component={SampleComponent1}
. This means when the /link1
path is accessed, the SampleComponent1
will be rendered.
Save the changes and let's check the sample in the browser. When you click on the links, the corresponding components should be rendered where the <Route>
tags are defined.
Redirect
The <Redirect>
component works similarly to how redirects are used in general. Let’s consider a scenario where a user requests a certain URL. However, the requested service at that URL needs to redirect the user to another URL. The browser, after receiving the redirect command, will automatically make a request to the new URL.
Here’s how you use the <Redirect>
in React Router:
<Redirect to="/news" />
In this case, the user will be redirected to /news
. But this can be used in more complex situations. For example, let's say your web application originally provided an API at /news
, but later you change it to /good_news
. You want users who request /news
to be automatically redirected to /good_news
. You can do this like this:
<Redirect from="/news" to="/good_news" />
You can also use <Redirect>
in conditional rendering scenarios. For instance, let's say you want to redirect a user to the 'My Information' page if they are logged in, and to the 'Login' page if they are not. Here’s how you can do it:
<Route exact path="/my_info" render={() => (
isLoggedIn ? (
<MyInformation />
) : (
<Redirect to="/login" />
)
)} />
This is a route that checks the isLoggedIn
condition. If isLoggedIn
is true
, it renders the <MyInformation />
component. If false
, it redirects to /login
.
Router
Let’s take a closer look at the BrowserRouter
used in index.js
. There are six different types of routers in React Router:
Router
BrowserRouter
HashRouter
MemoryRouter
NativeRouter
StaticRouter
Among these, the one we use most often is BrowserRouter
. MemoryRouter
and NativeRouter
are used in non-web environments, while HashRouter
is used for older browser support. Router
is for low-level customization, and StaticRouter
is typically used in server-side rendering environments.
In most cases, we will work with BrowserRouter
, as it allows routing within the common web environment. All components inside BrowserRouter
can use common properties (like location
, history
, etc.), so it’s important to place the BrowserRouter
at the top of the component tree.
Switch
The <Switch>
component wraps around Route
and Redirect
. While using Switch
is not strictly necessary, it can significantly change how routing behaves in your application.
When using <Switch>
, only the first matching route or redirect will be rendered. Without it, React Router would render all routes that match the current path. Here's an example to illustrate the difference:
<Link to='/main'>Link1</Link>
<Link to='/sub'>Link2</Link>
<Route path='/' component={Home} />
<Route path='/main' component={Main} />
<Route path='/main/sub' component={Sub} />
<Route component={NoMatch} />
This code is ambiguous. For example, if you visit http://localhost:3000/main/sub
, it will match all four paths ('/'
, '/main'
, '/main/sub'
, and the route with no path). Without Switch
, all of these components will be rendered.
To resolve this, you can use the exact
prop on the Route
components, ensuring that a route will only be rendered if the path exactly matches:
<Link to='/main'>Link1</Link>
<Link to='/sub'>Link2</Link>
<Route exact path='/' component={Home} />
<Route exact path='/main' component={Main} />
<Route exact path='/main/sub' component={Sub} />
<Route component={NoMatch} />
Now, /main/sub
will only render the Sub
component, and /main
will only render the Main
component. However, there’s still a problem: any route with no path (like Route component={NoMatch}
) will match any path and render. To handle this case, we use <Switch>
.
<Link to='/main'>Link1</Link>
<Link to='/sub'>Link2</Link>
<Switch>
<Route path='/' component={Home} />
<Route path='/main' component={Main} />
<Route path='/main/sub' component={Sub} />
<Route component={NoMatch} />
</Switch>
The <Switch>
component ensures that only the first matching route is rendered. For instance, if you navigate to /main/sub
, it will render Sub
, and if you navigate to /main
, it will render Main
. If none of the paths match, it will render the NoMatch
component.
You can also combine the exact
prop with <Switch>
for even more precise matching:
<Link to='/main'>Link1</Link>
<Link to='/sub'>Link2</Link>
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/main' component={Main} />
<Route exact path='/main/sub' component={Sub} />
<Route component={NoMatch} />
</Switch>
Now, only the component for the exact matching path will be rendered, and NoMatch
will be rendered if no paths match.
Conclusion
In this post, we explored the basics of using the react-router
library, which provides routing functionality in React applications. By understanding the five core concepts—Route
, Redirect
, Router
, Switch
, and exact
—you can easily set up basic routing in your React app. In future posts, we will dive deeper into each component and explore more advanced routing techniques.
Thank you for reading!