What we'll cover:
- What is react-router?
- Installation & setup
- Route component
- Inline rendering
- Multiple paths
- Link & NavLink components
- Dynamic routes
- Switch component
- Redirect component / Protected Routes
Feel free to click and go right to the part you want.
Let's jump right into it! 😄
What is react-router?
React-router is a routing solution designed specifically for React.js. The whole idea is to help you keep your user interface in sync with the URL.
In other words, react-router makes rendering different components when you hit different URLs relatively easy and also gives you control over the navigation of your app.
Installation & setup
Unlike other frameworks/libraries such as Angular which has a routing solution included out of the box, react-router is separate from the react.js library and you'll need to install it whenever needed.
Installation command:
npm install react-router-dom
Setup
As for the setup, it's quite easy. You'd wanna wrap your App component with the BrowserRouter component provided by react-router-dom.
If you're using the create-react-app starter configuration, then head to your index.js in the src folder.
Import:
import { BrowserRouter } from "react-router-dom"
Then, wrap your <App />
with the BrowserRouter like so:
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
)
This will enable routing across your app and will allow you to use the rest of the components provided by react-router-dom.
Now let's explore the first component on our list... 🧐
Route component
This component allows you to render a specific component only when you hit a specific URL. Also, it passes down an object as props containing useful information relevant to the component it's rendering.
We'll be taking a look at this object shortly.
To use the Route component, you'll need to import it:
import { Route } from "react-router-dom"
Route component takes 4 main props:
- path - On which route should the component render?
- component - Which component should be rendered on that path?
- exact - Should the component be rendered exactly on that path? or also on similar paths?
- render - Used instead of component prop to render inline components - we'll see an example in a moment
Example
// App.js
import React from "react"
import Nav from "./components/Nav"
import Home from "./components/Home"
import About from "./components/About"
import { Route } from "react-router-dom"
function App() {
return (
<div className="App">
<Nav />
<Route path="/" exact component={Home} />
<Route path="/about" exact component={About} />
</div>
)
}
export default App
Explanation
<Nav />
: Will always appear on every route since it's not rendered by a <Route />
component.
<Route path="/" exact component={Home} />
: Will only show the Home component on the "/" path which is localhost:3000 in this case.
<Route path="/about" exact component={About} />
: Will only show the About component on the "/about" path which is localhost:3000/about in this case.
If we don't use exact, Home will always appear, even on localhost:3000/about. The reason is "/about" also has a "/" included in it. So we add exact to prevent this behavior.
We mentioned that there are 4 main props and we only covered 3 so far. That brings us to the render prop...
Inline rendering
You might be asking, what if I want to pass down custom props to my components?
Since you can't specify any props to your components using the way we just explored using component prop. React-router has another prop called render.
Let's check out an example...
Example
// App.js
import Home from "./components/Home"
import About from "./components/About"
import { Route } from "react-router-dom"
function App() {
return (
<div className="App">
<Nav />
<Route
path="/"
exact
render={() => {
return (
<div>
<h1>Home</h1>
<p>welcome to my homepage</p>
</div>
)
}}
/>
<Route
path="/about"
exact
render={<About text="This is my text that I want to pass down" />}
/>
</div>
)
}
Explanation
Using render instead of component will allow you to either write components inline like in the Home Route and also write a component tag with any props this component is expecting.
Note: Using render will result in React unmounting and mounting the inline component again on every render, instead of just updating the existing component.
Multiple paths
If you wish to render the same component on the several routes, you can by specifying your path as a regular expression string:
<Route path="/(home|users|contact)/" component={Home} />
Link & NavLink components
You don't want your users to go type every route into the URL to go to a page. This is where the Link and NavLink components come in.
You can wrap some text or other components with a Link or a NavLink and specify where it should take the users when they click on it.
Import:
import { Link } from "react-router-dom"
Example
<Link to=”/contact>Contact</Link>
Explanation
Now when the users click on "Contact" they'll be taken to the specified route in the to="..." prop. This could be used in your Navbar component for example.
If you're using your localhost:3000, then, this link will take you to localhost:3000/contact.
What if you'd like to add a specific style only on the current link that you're currently on?
Use NavLink instead:
Import:
import { NavLink } from "react-router-dom"
Example
<NavLink to="/dashboard" activeClassName="selectedLink">
Dashboard
</NavLink>
Explanation
By adding " activeClassName="selectedLink" ", now you can add some styles to the class selectedLink and they will be applied only to that link when the user is on the route /dashboard.
Dynamic routes
When a component is routed by the react-router library, each route is provided with an object of information about that route and it's passed down to the component as props.
If you go into the file of a component routed by the <Route />
and add props to the component then console.log(props), you'll get the entire object logged.
Example
export default function Contact(props) {
console.log(props)
return (
<div>
<h1>Contact component</h1>
</div>
)
}
Output
// @ localhost:3000/contact
// Browser console output
Object
history:
action: "POP"
block: ƒ block(prompt)
createHref: ƒ createHref(location)
go: ƒ go(n)
goBack: ƒ goBack()
goForward: ƒ goForward()
length: 4
listen: ƒ listen(listener)
location: {pathname: "/contact", search: "", hash: "", state: undefined}
push: ƒ push(path, state)
replace: ƒ replace(path, state)
__proto__: Object
location:
hash: ""
pathname: "/contact"
search: ""
state: undefined
__proto__: Object
match:
isExact: true
params: {}
path: "/contact"
url: "/contact"
__proto__: Object
staticContext: undefined
__proto__: Object
You'll see all this information specific to the /contact route, which could be accessed in the component to make it more dynamic.
Unfortunately, we won't be exploring all of those in this blog but, you can experiment with them as much as you like. The idea is to let you know they exist and that you can use them. 😊
We'll be exploring the params object located in match.
// @ localhost:3000/contact
// Browser console output
match:
isExact: true
params: {} 👈
path: "/contact"
url: "/contact"
Let's set up a route with a param then access it from within the component.
Example
// App.js
function App() {
return (
<div className="App">
<Nav />
<Route path="/" exact component={Home} />
<Route path="/contact" exact component={Contact} />
<Route path="/user/:user" exact component={User} /> // New route
</div>
)
}
Explanation
You might be wondering what is that :user, this is called a parameter or param for short.
To put it in simple words, just think of it as a placeholder which it's value could be set only in the URL.
For example: if we visit localhost:3000/user/someone. The param will be set to the string "someone" and the same applies if you add anything in the place of :user.
You simply choose the value by visiting localhost:3000/user/ANYTHING_HERE
Now let's use that value in the user component:
Example
// User.js
export default function User(props) {
return (
<div>
<h1>Hello {props.match.params.user}</h1>
</div>
)
}
Explanation
{props.match.params.user}
: This is accessing the value of the created ( user param ) since we named it :user.
Now if you visit localhost:3000/user/your_name you'll see Hello your_name, the same applies for any other string.
Please note:
This object is passed down automatically ONLY if you render your component using the component={...} NOT render={...}.
If you wish to pass down the same props using the render={...} prop, you can do it like so:
// App.js
function App() {
return (
<div className="App">
<Nav />
<Route path="/" exact component={Home} />
// This way 👇
<Route path="/contact" exact render={props => <Contact {...props} />} />
</div>
)
}
This way you're creating a component which is taking props, then, using object destructuring to pass the props to the components by adding {...props}.
Switch component
The switch component could be used if you want nest routes and have the first matched route to be selected.
Let's see an example...
Import:
import { Switch } from "react-router-dom"
Example
// App.js
function App() {
return (
<Switch>
<Route path="/about" component={About} />
<Route path="/about/2" component={About} />
<Route path="/" component={Home} />
</Switch>
)
}
Explanation
This will simply go through each route from top to bottom and check if it matches the current route in the URL, then, choose the first match it finds and renders it.
Yes, you can do it by adding exact instead of relying on order.
There might be a special use case in your app where you might need this, so now you know that this option exists.
Your app, your choice.😉
Redirect component / Protected Routes
This component is useful if you have some routes that can't be accessed unless a specific condition is met.
Simply rendering the Redirect component will automatically send you to a specific route.
One of the common use cases of the Redirect component is when you have a login system and we don't want the user to access some routes unless they're logged in.
Import:
import { Redirect } from "react-router-dom"
Example
// App.js
import React, { useState } from "react"
import Nav from "./components/Nav"
import Home from "./components/Home"
import About from "./components/About"
import Dashboard from "./components/Dashboard"
import { Route, Redirect, Link } from "react-router-dom"
function App() {
// Simple boolean state
const [loggedin, setLoggedin] = useState(false) // initialized as false
const handleLogin = () => {
setLoggedin(true) // Toggle loggedin state to true
}
return (
<div className="App">
<Nav />
<Route path="/" exact component={Home} />
<Route path="/about" exact component={About} />
{// Simple protected route 👇}
<Route
path="/dashboard"
exact
render={() => (loggedin ? <Dashboard /> : <Redirect to="/" />)}
/>
{ // Changes the login state to true 👇}
<button onClick={handleLogin}>Login</button>
{ // Takes you to /dashboard route 👇}
<Link to="/dashboard">Dashboard</Link>
</div>
)
}
export default App
Explanation
Here we have a boolean state representing if the user is logged in or not, we also have a button which changes the state of loggedin to true and we have a link to take us to the /dashboard route.
We're using a ternary operator in the protected route saying:
if loggedin is true, then, render <Dashboard />
, otherwise, render a <Redirect to="/" />
component, which immediately redirects the user to the "/" route so that they can't access this route since they're not logged in.
Congratulations! 🎉
With this covered, you now know how to enable and use routing in your react app, as well as learned about the basic components that the react-router library provides.
Learned something new?
Share this post with someone who can benefit from too.😄
Have a great day! 😇
Top comments (0)