What is react-router-dom?
Ever wondered how websites have custom paths? How come you can type in https://dev.to/readinglist
and your browser knows to bring you directly to your dev.to
reading list? Well, the website you're navigating to, in our case dev.to
, has set up some custom routes. They either have client-side or server-side routes. Client-side routes are generally used in single-page applications. They allow for routing capabilities without needing multiple html pages. Server-side routes are used in multi-page applications, where each route returns a new html page with content.
If you're using React to build your webpage and you want to accomplish something like that, then you'll want to turn your attention to react-router-dom! React-router is a node module that makes use of the React component structure to add client-side routing functionality to a webpage.
Going forward on this blog, I'm going to assume that you have a basic understanding of Switches and Routes in react-router. If you'd like to read up on them before continuing, you can find the documentation here
What is nested routing?
Nested routing is the act of "burying" routes inside each other. Continuing our dev.to
example from before, here is an example of a nested route:
https://dev.to/readinglist/archive
First, you route to /readinglist
then you route to /archive
while inside /readinglist
, providing more specific information while still being inside that scope.
Nesting routes helps to create more organized, and clear routes for both the developers and clients to make use of. It also helps practice some separation of concerns. You could have a /readinglist
that brings you to your reading list and a /archive
that brings you to your reading list archive, but what happens when you want to implement a post archive? The route /archive
is already taken, so you'd have to name the new route /postarchive
or something, making the routes messy and hard to follow. By nesting them, you can have /readinglist/archive
and /post/archive
together.
Nested routing in react-router-dom
So, now we know that we know the advantages of nesting routes, how exactly do we do it in react-router-dom?
Lets set up a basic dev.to
route layout first:
//App.js
import {
BrowserRouter as Router,
Switch,
Route
} from 'react-router-dom'
function App() {
return (
<Router>
<Switch>
<Route path="/posts">
<p>Posts</p>
</Route>
<Route path="/readinglist">
<p>Reading List</p>
</Route>
<Route exact path="/">
<p>Homepage</p>
</Route>
</Switch>
</Router>
);
}
export default App;
This route layout has three paths currently:
Path | Web Display |
---|---|
/posts |
Posts |
/readinglist |
Reading List |
/ |
Homepage |
We can set up nested routing a few ways.
We could add /readinglist/archive
by adding a route directly like this:
//App.js
//Inside the Switch
<Route path="/readinglist/archive">
<p>Reading List Archive<p>
</Route>
Although this would work, what do we do if we want to have a lot of routes nested inside /readinglist
? We'd have to add each one to our App.js, quickly making it cluttered and hard to follow. This is why I suggest creating a custom component to store routing logic instead.
Routing logic component
Lets start by creating a custom component to store the routing logic for /readinglist
:
//ReadingListRoutes.js
import {
Switch,
Route
} from 'react-router-dom';
function ReadingListRoutes() {
return (
<Switch>
<Route exact path="/readinglist/archive">
<p>Reading List Archive</p>
</Route>
<Route path="/readinglist">
<p>Reading List</p>
</Route>
</Switch>
)
}
export default ReadingListRoutes;
We've now created our /readinglist
route logic component, but we still have to add it to our actual routes! Lets refactor our App.js and do just that:
//App.js
import {
BrowserRouter as Router,
Switch,
Route
} from 'react-router-dom'
import ReadingListRoutes from './ReadingListRoutes';
function App() {
return (
<Router>
<Switch>
<Route path="/posts">
<p>Posts</p>
</Route>
<Route path="/readinglist">
<ReadingListRoutes />
</Route>
<Route exact path="/">
<p>Homepage</p>
</Route>
</Switch>
</Router>
);
}
export default App;
As you can see, we now have it set up to where the path /readinglist/archive
first navigates to our ReadingListRoutes
component. Once in our ReadingListRoutes
component, it looks for the route /readinglist/archive
. If it finds it, it displays Reading List Archive
on the webpage.
Our routes table now looks like this:
Path | Web Display |
---|---|
/posts |
Posts |
/readinglist |
Reading List |
/readinglist/archive |
Reading List Archive |
/ |
Homepage |
Dynamic Routes
So now we've learned how to do nested routing, but how exactly can we define dynamic routes? Lets take a look at the dev.to
url to one of my blogs:
https://dev.to/christensenjoe/classes-in-javascript-f9g
This url first routes to my username, then my blog title (separated by hyphens instead of spaces). But how exactly is dev.to
doing this? It cant be hard coding every single possible username as a route.
Well, react-router-dom also provides a nice way to dynamically create routes.
Lets add a basic route to /(username)
that will take someone to their custom homepage. I'll go ahead and create a UserRoutes
logic component to handle some nested route functionality too:
//App.js
import {
BrowserRouter as Router,
Switch,
Route
} from 'react-router-dom'
import ReadingListRoutes from './ReadingListRoutes';
import UserRoutes from './UserRoutes';
function App() {
return (
<Router>
<Switch>
<Route path="/posts">
<p>Posts</p>
</Route>
<Route path="/readinglist">
<ReadingListRoutes />
</Route>
<Route path="/:username">
<UserRoutes />
</Route>
<Route exact path="/">
<p>Homepage</p>
</Route>
</Switch>
</Router>
);
}
export default App;
//UserRoutes.js
import {
Switch,
Route
} from 'react-router-dom';
function UserRoutes() {
return (
<Switch>
<Route exact path="/:username/:post_title">
<p>User Post</p>
</Route>
<Route path="/:username">
<p>User Homepage</p>
</Route>
</Switch>
)
}
export default UserRoutes;
By adding the /:username
and /:username/:post_title
routes, we can now dynamically create new routes. If you navigate to /joe
, you'll be met with the text User Homepage
, and if you navigate to /joe/new-post
, you'll find text User Post
.
Our routes now look like:
Path | Web Display |
---|---|
/:username |
User Homepage |
/:username/:post_title |
User Post |
/posts |
Posts |
/readinglist |
Reading List |
/readinglist/archive |
Reading List Archive |
/ |
Homepage |
useParams
We've now dynamically created new routes, but how can we take advantage of them to create custom pages?
First, lets start by creating two new components, UserProfilePage
and UserPostPage
, and connect them up to our UserRoutes
component:
//UserProfilePage.js
function UserProfilePage() {
return (
<h1>This is ___'s profile</h1>
)
}
export default UserProfilePage;
//UserPostPage.js
function UserPostPage() {
return (
<h1>This is ___'s post: ___</h1>
)
}
export default UserPostPage;
//UserRoutes.js
import {
Switch,
Route
} from 'react-router-dom';
import UserPostPage from './UserPostPage';
import UserProfilePage from './UserProfilePage';
function UserRoutes() {
return (
<Switch>
<Route exact path="/:username/:post_title">
<UserPostPage />
</Route>
<Route path="/:username">
<UserProfilePage />
</Route>
</Switch>
)
}
export default UserRoutes;
Now that we have separate pages, we can make use of a custom react-router-dom hook called useParams()
. useParams allows us to grab any params from the current route at the time of invocation. For example, if we went to /:username/:post-title
, we could grab the params username
and post-title
. These variables would store whatever we are currently routed to. So if we're routed to /joe/new-post
, then username
stores the string joe
and post-title
stores the string new-post
.
Let's update our UserProfilePage
and UserPostPage
to make use of the useParams()
hook to add custom messages:
//UserProfilePage.js
import {
useParams
} from 'react-router-dom';
function UserProfilePage() {
const { username } = useParams();
return (
<h1>{`This is ${username}'s profile`}</h1>
)
}
export default UserProfilePage;
//UserPostPage.js
import {
useParams
} from 'react-router-dom';
function UserPostPage() {
const { username, post_title } = useParams();
const parsedTitle = post_title.split("-").join(" ")
return (
<h1>{`This is ${username}'s post: ${parsedTitle}`}</h1>
)
}
export default UserPostPage;
As you can see, we're using useParams to get the username and post_title. We then use them to show some custom messages to our user based on what the put in. On the UserPostPage
, we're also mutating our post_title
to remove the dashes and replace them with spaces.
Now, if someone navigates to /joe/my-new-post
they'll be met with the message This is joe's post: my new post
.
Conclusion
React-router-dom is a great node package for React that allows for easy client-side routing. If you're creating a single-page web application, then definitely consider using it to implement static, nested, and dynamic routes. If you'd like to learn more, take a look at the docs here
Top comments (0)