This tutorial is the first of a three-part series on React by Brad Westfall. When Brad pitched me this, he pointed out there are a good amount of tutorials on getting started in React, but not as much about where to go from there. If you’re brand new to React, I recommend watching this intro video. This series picks up where the basics leave off.
Article Series:
- React Router (You are here!)
- Container Components
- Redux
Warning! This article was written pre-React Router 4, which has become a more standard choice for routing in React. There is a new article covering React Router 4 here you should definitely read.
When I was first learning, I found lots of beginner guides (i.e. 1, 2, 3, 4) that showed how to make single components and render them to the DOM. They did a fine job of teaching the basics like JSX and props, but I struggled with figuring out how React works in the bigger picture – like a real-world Single Page Application (SPA). Since this series covers a lot of material, it will not cover the absolute beginner concepts. Instead, it will start with the assumption that you already understand how to create and render at least one component.
For what it’s worth, here are some other great guides that aim at beginners:
- React.js and How Does It Fit In With Everything Else?
- Rethinking (Industry) Best Practices
- React.js Introduction For People Who Know Just Enough jQuery To Get By
Series Code
This series also comes with some code to play with at GitHub. Throughout the series, we’ll be building a basic SPA focused around users and widgets.
To keep things simple and brief, the examples in this series will start by assuming that React and React Router are retrieved from a CDN. So you won’t see require()
or import
in the immediate examples below. Towards the end of this tutorial though, we’ll introduce Webpack and Babel for the GitHub guides. At that point, it’s all ES6!
React-Router
React isn’t a framework, it’s a library. Therefore, it doesn’t solve all an application’s needs. It does a great job at creating components and providing a system for managing state, but creating a more complex SPA will require a supporting cast. The first that we’ll look at is React Router.
If you’ve used any front-end router before, many of these concepts will be familiar. But unlike any other router I’ve used before, React Router uses JSX, which might look a little strange at first.
As a primer, this is what it’s like to render a single component:
var Home = React.createClass({
render: function() {
return (<h1>Welcome to the Home Page</h1>);
}
});
ReactDOM.render((
<Home />
), document.getElementById('root'));
Here’s how the Home
component would be rendered with React Router:
...
ReactDOM.render((
<Router>
<Route path="/" component={Home} />
</Router>
), document.getElementById('root'));
Note that <Router>
and <Route>
are two different things. They are technically React components, but they don’t actually create DOM themselves. While it may look like the <Router>
itself is being rendered to the 'root'
, we’re actually just defining rules about how our application works. Moving forward, you’ll see this concept often: components sometimes exist not to create DOM themselves, but to coordinate other components that do.
In the example, the <Route>
defines a rule where visiting the home page /
will render the Home
component into the 'root'
.
Multiple Routes
In the previous example, the single route is very simple. It doesn’t give us much value since we already had the ability to render the Home
component without the router being involved.
React Router’s power comes in when we use multiple routes to define which component should render based on which path is currently active:
ReactDOM.render((
<Router>
<Route path="/" component={Home} />
<Route path="/users" component={Users} />
<Route path="/widgets" component={Widgets} />
</Router>
), document.getElementById('root'));
Each <Route>
will render its respective component when its path matches the URL. Only one of these three components will be rendered into the 'root'
at any given time. With this strategy, we mount the router to the DOM 'root'
once, then the router swap components in and out with route changes.
It’s also worth noting that the router will switch routes without making requests to the server, so imagine that each component could be a whole new page.
Re-usable Layout
We’re starting to see the humble beginnings of a Single Page Application. However, it still doesn’t solve real-world problems. Sure, we could build the three components to be full HTML pages, but what about code re-use? Chances are, these three components share common assets like a header and sidebar, so how do we prevent HTML repetition in each component?
Let’s imagine we were building a web app that resembled this mockup:
When you start to think about how this mockup can be broken down into re-usable sections, you might end up with this idea:
Thinking in terms of nestable components and layouts will allow us to create reusable parts.
Suddenly, the art department lets you know that the app needs a page for searching widgets which resembles the page for searching users. With User List and Widget List both requiring the same “look” for their search page, the idea to have Search Layout as a separate component makes even more sense now:
Search Layout can be a parent template for all kinds of search pages now. And while some pages might need Search Layout, others can directly use Main Layout without it:
This is a common strategy and if you’ve used any templating system, you’ve probably done something very similar. Now let’s work on the HTML. To start, we’ll do static HTML without considering JavaScript:
<div id="root">
<!-- Main Layout -->
<div class="app">
<header class="primary-header"><header>
<aside class="primary-aside"></aside>
<main>
<!-- Search Layout -->
<div class="search">
<header class="search-header"></header>
<div class="results">
<!-- User List -->
<ul class="user-list">
<li>Dan</li>
<li>Ryan</li>
<li>Michael</li>
</ul>
</div>
<div class="search-footer pagination"></div>
</div>
</main>
</div>
</div>
Remember, the 'root'
element will always be present since it’s the only element that the initial HTML Body has before JavaScript starts. The word “root” is appropriate because our entire React application will mount to it. But there’s no “right name” or convention to what you call it. I’ve chosen “root”, so we’ll continue to use it throughout the examples. Just beware that mounting directly to the <body>
element is highly discouraged.
After creating the static HTML, convert it into React components:
var MainLayout = React.createClass({
render: function() {
// Note the `className` rather than `class`
// `class` is a reserved word in JavaScript, so JSX uses `className`
// Ultimately, it will render with a `class` in the DOM
return (
<div className="app">
<header className="primary-header"><header>
<aside className="primary-aside"></aside>
<main>
{this.props.children}
</main>
</div>
);
}
});
var SearchLayout = React.createClass({
render: function() {
return (
<div className="search">
<header className="search-header"></header>
<div className="results">
{this.props.children}
</div>
<div className="search-footer pagination"></div>
</div>
);
}
});
var UserList = React.createClass({
render: function() {
return (
<ul className="user-list">
<li>Dan</li>
<li>Ryan</li>
<li>Michael</li>
</ul>
);
}
});
Don’t get too distracted between what I’m calling “Layout” vs “Component”. All three of these are React components. I just choose to call two of them “Layouts” since that’s the role they’re performing.
We will eventually use “nested routes” to place UserList
inside SearchLayout
, then inside MainLayout
. But first, notice that when UserList
is placed inside its parent SearchLayout
, the parent will use this.props.children
to determine its location. All components have this.props.children
as a prop, but it’s only when components are nested that the parent component gets this prop filled automatically by React. For components that aren’t parent components, this.props.children
will be null
.
Nested Routes
So how do we get these components to nest? The router does it for us when we nest routes:
ReactDOM.render((
<Router>
<Route component={MainLayout}>
<Route component={SearchLayout}>
<Route path="users" component={UserList} />
</Route>
</Route>
</Router>
), document.getElementById('root'));
Components will be nested in accordance with how the router nests its routes. When the user visits the /users
route, React Router will place the UserList
component inside SearchLayout
and then both inside MainLayout
. The end result of visiting /users
will be the three nested components placed inside 'root'
.
Notice that we don’t have a rule for when the user visits the home page path (/
) or wants to search widgets. Those were left out for simplicity, but let’s put them in with the new router:
ReactDOM.render((
<Router>
<Route component={MainLayout}>
<Route path="/" component={Home} />
<Route component={SearchLayout}>
<Route path="users" component={UserList} />
<Route path="widgets" component={WidgetList} />
</Route>
</Route>
</Router>
), document.getElementById('root'));
You’ve probably noticed by now that JSX follows XML rules in the sense that the Route
component can either be written as one tag: <Route />
or two: <Route>...</Route>
. This is true of all JSX including your custom components and normal DOM nodes. For instance, <div />
is valid JSX and will convert to <div></div>
when rendered.
For brevity, just imagine WidgetList
resembles the UserList
.
Since <Route component={SearchLayout}>
has two child routes now, the user can visit /users
or /widgets
and the corresponding <Route>
will load its respective components inside the SearchLayout
component.
Also, notice how the Home
component will be placed directly inside MainLayout
without SearchLayout
being involved — because of how the <Route>
s are nested. You can probably imagine it’s easy to rearrange how layouts and components are nested by rearranging the routes.
IndexRoutes
React Router is very expressive and often there’s more than one way to do the same thing. For example we could also have written the above router like this:
ReactDOM.render((
<Router>
<Route path="/" component={MainLayout}>
<IndexRoute component={Home} />
<Route component={SearchLayout}>
<Route path="users" component={UserList} />
<Route path="widgets" component={WidgetList} />
</Route>
</Route>
</Router>
), document.getElementById('root'));
Despite its different look, they both work the exact same way.
Optional Route Attributes
Sometimes, <Route>
will have a component
attribute with no path
, as in the SearchLayout
route from above. Other times, it might be necessary to have a <Route>
with a path
and no component
. To see why, let’s start with this example:
<Route path="product/settings" component={ProductSettings} />
<Route path="product/inventory" component={ProductInventory} />
<Route path="product/orders" component={ProductOrders} />
The /product
portion of the path
is repetitive. We can remove the repetition by wrapping all three routes in a new <Route>
:
<Route path="product">
<Route path="settings" component={ProductSettings} />
<Route path="inventory" component={ProductInventory} />
<Route path="orders" component={ProductOrders} />
</Route>
Again, React Router shows its expressiveness. Quiz: did you notice the issue with both solutions? At the moment we have no rules for when the user visits the /product
path.
To fix this, we can add an IndexRoute
:
<Route path="product">
<IndexRoute component={ProductProfile} />
<Route path="settings" component={ProductSettings} />
<Route path="inventory" component={ProductInventory} />
<Route path="orders" component={ProductOrders} />
</Route>
<Link>
not <a>
Use When creating anchors for your routes, you’ll need to use <Link to="">
instead of <a href="">
. Don’t worry though, when using the <Link>
component, React Router will ultimately give you an ordinary anchor in the DOM. Using <Link>
though is necessary for React Router to do some of its routing magic.
Let’s add some link (anchors) to our MainLayout
:
var MainLayout = React.createClass({
render: function() {
return (
<div className="app">
<header className="primary-header"></header>
<aside className="primary-aside">
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/users">Users</Link></li>
<li><Link to="/widgets">Widgets</Link></li>
</ul>
</aside>
<main>
{this.props.children}
</main>
</div>
);
}
});
Attributes on <Link>
components will be passed through to the anchor they create. So this JSX:
<Link to="/users" className="users">
Will become this in DOM:
<a href="/users" class="users">
If you need to create an anchor for non-router-paths, such as an outside website, then use normal anchor tags as usual. For more information, see the documentation for IndexRoute and Link.
Active Links
A cool feature of the <Link>
component is its ability to know when it’s active:
<Link to="/users" activeClassName="active">Users</Link>
If the user is on the /users
path, the router will seek out matching anchors that were made with <Link>
and it will toggle their active
class. See more on this feature.
Browser History
To prevent confusion, I’ve left out an important detail until now. The <Router>
needs to know which history tracking strategy to use. React Router docs recommend browserHistory which is implemented as follows:
var browserHistory = ReactRouter.browserHistory;
ReactDOM.render((
<Router history={browserHistory}>
...
</Router>
), document.getElementById('root'));
In previous versions of React Router, the history
attribute was not required and the default was to use hashHistory. As the name suggests, it used a #
hash sign in the URL to manage front-end SPA-style routing, similar to what you might expect from a Backbone.js router.
With hashHistory
, URLs will look like this:
- example.com
- example.com/#/users?_k=ckuvup
- example.com/#/widgets?_k=ckuvup
What’s up with those ugly query strings though?
When browserHistory
is implemented, the paths look more organic:
- example.com
- example.com/users
- example.com/widgets
There’s a caveat though on the server when browserHistory
is used on the front-end. If the user starts their visit at example.com
and then navigates to /users
and /widgets
, React Router handles this scenario as expected. However, if the user starts their visit by typing example.com/widgets
directly into the browser, or if they refresh on example.com/widgets
, then the browser must make at least one request to the server for /widgets
. If there isn’t a server-side router though, this will deliver a 404:

To solve the 404 problem from the server, React Router recommends a wildcard router on the server-side. With this strategy, no matter what server-side route is called, the server should always serve the same HTML file. Then if the user starts directly at example.com/widgets
, even though the same HTML file is returned, React Router is smart enough to load the correct component.
The user won’t notice anything weird, but you might have concerns about always serving the same HTML file. In code examples, this series will continue to use the “wildcard router” strategy, but it’s up to you to handle your server-side routing in ways that you see fit.
Can React Router be used on both server-side and client-side in an isomorphic way? Sure it can, but that’s way beyond the scope of this tutorial.
browserHistory
Redirect with The browserHistory
object is a singleton so you can include it in any of your files. If you need to manually redirect the user in any of your code, you can use it’s push
method to do so:
browserHistory.push('/some/path');
Route Matching
React router handles route matching similarly to other routers:
<Route path="users/:userId" component={UserProfile} />
This route will match when the user visits any path that starts with users/
and has any value afterwards. It will match /users/1
, /users/143
, or even /users/abc
(which you’ll need to validate on your own).
React Router will pass the value for :userId
as a prop to the UserProfile
. This props is accessed as this.props.params.userId
inside UserProfile
.
Router Demo
At this point, we have enough code to show a demo.
See the Pen React-Router Demo by Brad Westfall (@bradwestfall) on CodePen.
If you clicked on a few routes in the example, you might notice that the browser’s back and forward buttons work with the router. This is one of the main reasons these history
strategies exist. Also, keep in mind that with each route you visit, there are no requests being made to the server except the very first one to get the initial HTML. How cool is that?
ES6
In our CodePen example, React
, ReactDOM
, and ReactRouter
are global variables from a CDN. Inside the ReactRouter
object are all kinds of things we need like the Router
and Route
components. So we could use ReactRouter
like this:
ReactDOM.render((
<ReactRouter.Router>
<ReactRouter.Route ... />
</ReactRouter.Router>
), document.getElementById('root'));
Here, we have to prefix all of our router components with their parent object ReactRouter
. Or we could use ES6’s new destructuring syntax like this:
var { Router, Route, IndexRoute, Link } = ReactRouter
This “extracts” parts of ReactRouter
into normal variables so we can access them directly.
Starting now, the examples in this series will use a variety of ES6 syntaxes including destructuring, the spread operator, imports/exports, and perhaps others. There will be a brief explanation of each new syntax as they appear and the GitHub repo that comes with this series also has lots of ES6 explanations.
Bundling with webpack and Babel
As stated before, this series comes with a GitHub repo so you can experiment with code. Since it will resemble the makings of a real-world SPA, it will use tools like webpack and Babel.
- webpack bundles multiple JavaScript files into one file for the browser.
- Babel will convert ES6 (ES2015) code to ES5, since most browsers don’t yet understand all of ES6. As this article ages, browsers will support ES6 and Babel may not be needed.
If you’re not too comfortable using these tools yet, don’t worry, the example code has everything setup already so you can focus on React. But be sure to review the example code’s README.md file for additional workflow documentation.
Be Careful About Deprecated Syntax
Searching Google for info on React Router might land you on one of the many articles or StackOverflow pages that were written when React Router was in its pre-1.0 release. Many features from the pre-1.0 release are deprecated now. Here’s a short list:
<Route name="" />
is deprecated. Use<Route path="" />
instead.<Route handler="" />
is deprecated. Use<Route component="" />
instead.<NotFoundRoute />
is deprecated. See Alternative<RouteHandler />
is deprecated.willTransitionTo
is deprecated. See onEnterwillTransitionFrom
is deprecated. See onLeave- “Locations” are now called “histories”.
See the full list for 1.0.0 and 2.0.0
Summary
There are still more features in React Router that weren’t shown, so be sure to check out the API Docs. The creators of React Router have also created a step-by-step tutorial for React Router and also checkout this React.js Conf Video on how React Router was created.
Special thanks to Lynn Fisher for the artwork @lynnandtonic
Article Series:
- React Router (You are here!)
- Container Components
- Redux
I’m a bit sad that server-side rendering is “way beyond the scope of this tutorial”. I hope React tutorials will start seeing it as an integral part of any React setup; without it, the app isn’t fully functional, IMO.
I understand Šime. I could have made the article probably 3 times longer. There’s still so many things to learn in React Router beyond what this one article teaches. But to keep it within a certain scope, I couldn’t do everything.
It’s Monday! How about a little CAN DO attitude! Here’s an article on that exact subject I just web searched for.
Kinda looks like you can use one routes file for both server side and client side which I think is crazy holy grail magic stuff and I want it.
Yeah, that comment ended up sounding harsh, which was not my intention :) I feel the need to point out server rendering, because I think its absence limits the web app quite a lot, and people should be aware of that.
For the browserHistory issue, if you’re using Webpack – and running webpack-dev-server, then including ‘–history-api-fallback’ in your server startup command will fix the problem: “webpack-dev-server –inline –history-api-fallback”
Fantastic article – we are using react-router with AltContainer, webpack, babel – it has been fantastic. The explanations and walk-though written here is very well done (wish I would have this when we started!).
Hope to see you cover onEnter – we use this in a similar way as ‘resolve’ in Angular1. ie.
Thanks again – looking forward to the rest of the series!
Thanks Steve. What do you find are the tradeoffs of instigating XHR from
onEnter
vscomponentWillMount
?Why does this look like a javascript browser inside a browser? sigh
It seems like React.js may eventually go the route of jQuery/Flash/etc.., where eventually ES7/HTML6/CSS4/etc… will replace all of the usefullness of react/angular/latest-lib-framework-craze.
In the mean time, it’s good to see how these systems work without having to spend the time testing it myself. Just so it’s not misunderstood, I appreciate this article, and look forward to the next ones…
think you can mask your own content as curated – curate from your own properties!
Beylikdüzü evden eve nakliyat hizmetleri
http://www.beylikduzuevdenevenakliyati.com
think you can mask your own content as curated – curate from your own properties!
Bahçeşehir evden eve nakliyat hizmetleri
http://www.bahcesehirnakliyati.com
Great article — well paced, clearly written, smartly scoped. Thanks!
Great article! The graphics by Lynn make it very clear. Props on considering your article as it ages, such as how browsers might natively handle ES6 by the time some folks read your post.
Do you know how to do relative links? I’ve been following the discussion here, with no solution in sight yet. So I’m wondering how apps in production are handling this today?
Head’s up: the Active Links section’s “this feature” is not clickable. Same with previous section “documentation for IndexRoute”. I think it’s a CSS problem. Take off the ‘position: relative’ from the h4’s?
Thanks Tyler. And it’s a good idea you’re pointing out that discussion going on about relative links. I don’t know any of the latest updates. Seems pretty active still.
As for the broken links in the article, it works for me on Chrome 48. What browser are you running? I’ll also let Chris know since he just redid the design for this site
I’m on Chrome 49 on Linux. Surprise, surprise, but it works fine for me now.
Ok, I wonder why it wasn’t.
Relative Links can now be accomplished using this library by the react-router team:
https://github.com/ryanflorence/react-router-relative-links
Cool mockups. Did you use a mockup tool or is this your work entirely?
Lynn Fisher did them for me, twitter: @lynnandtonic. She’s an awesome illustrator and CSS expert. Checkout her website a.singlediv.com
Great article, Brad. And @Chris, great to see React articles here on CSS-Tricks. Hope to see more!
A new React-Router course was just published on Egghead. Haven’t watched it yet but from what I’ve seen, most things on that site are pretty great. https://egghead.io/series/getting-started-with-react-router
hi man!
Great article, thanks for taking the time to write it! I had just watched Egghead’s videos before reading this. They complement each other pretty well, since on Egghead he goes through the principles pretty quickly (but there is more code examples), while here you focus more on the concepts.
Again, great work on explaining the topic. Much appreciated!
I added a new li element to the nav in main-layout.js , but it does not appear when I refresh the page. What am I missing?
Have one tab open for
gulp
, in another dogulp watch
for re-building the Webpack (React stuff)Thanks!
Hi there,
I was wondering if you (or anyone) were able to get ReactRouter.browserHistory to work in JSX on the client side. What I mean is, if you write everything inside a
<script type="text/babel">
element and have the browser handle all the transpiling does it work for you? For me it doesn’t.It’s easiest to explain this by looking at my sample code here: http://codepen.io/etitcombe/pen/pyWXEG?editors=1000
The key lines are 66 and 67. If you uncomment 66 and comment out 67 the page will work – it’s using hashHistory. If you leave 66 commented out and 67 uncommented the page doesn’t render anything. In this case it’s using browserHistory.
I also built the same ‘site’ using webpack to transpile everything and serve up the site and in that case browserHistory works fine. Of course in that case the code is a little different. After importing browserHistory from react-router I can just use it directly in the history property of the Router component, no need for an interim variable or to prefix it with ReactRouter.
In both cases I’m using version 2.0.1 of react-router.
Can anyone explain the difference? If it’s not supposed to work when transpiled in the browser do you know why not? What am I missing?
Many thanks.
Hey there, nice article!
I’ve elaborated your examples using babel and browserHistory, here is the link to my pen: http://codepen.io/bluurn/pen/mPjgaV
Any comments will be much appreciated!