This lesson is for PRO members.

Unlock this lesson NOW!
Already subscribed? sign in

Building a React.js App: Basic Routing with React Router

10:34 React lesson by

React Router is the most popular, and arguably the best solution for adding Routing to your React application. In this video, we’ll talk about the ideology around React Router as well as set up basic routing for our application.

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

React Router is the most popular, and arguably the best solution for adding Routing to your React application. In this video, we’ll talk about the ideology around React Router as well as set up basic routing for our application.

Avatar
Simon

I was wondering if anyone has an idea or a link on animating these route transitions, eg instead of showing the next page immediately , so how fade the current then fade in the new view? I have looked at adding removing classes on the main views, using componentWillMount etc. but this does not work, its not like activating the transition but showing imediatley etc, thanks

Avatar
Tyler

Simon. I believe React Router 1.0 is going to add route change animations. They're a beta branch right now that you can checkout.

In reply to Simon
Avatar
Ashwin

What text editor are you using here?

Avatar
Tyler

Hi Ashwin, I'm using Sublime. All my settings can be found in this tweet. https://twitter.com/tylermcginnis33/status/610920794189889536

In reply to Ashwin
Avatar
egghead.io

The lesson video has been updated!

Avatar
KKD

Trying to run webpack -w and I am getting:

"Module build failed: ReferenceError: [BABEL] /home/karan/projects/react-tutorial/app/App.js: Unknown option: direct.presets".

My webpack.config.js looks like the following:

module.exports = {
    entry: "./app/App.js",
    output: {
        filename: "public/bundle.js"
    },
    module:{
        loaders:[
            {
                test: /\.jsx?$/,
                exclude: /(node_modules|bower_components)/,
                loader: 'babel',
                query: {
                    presets: ['react', 'es2015']
                }
            }
        ]
    }
};

My package.json:

{
  "name": "react-tutorial",
  "version": "1.0.0",
  "description": "react tutorial",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "history": "^1.13.1",
    "react-dom": "^0.14.0",
    "react-router": "^1.0.1"
  },
  "devDependencies": {
    "babel-core": "^5.8.30",
    "babel-loader": "^5.3.2",
    "webpack": "^1.12.9"
  }
}

and I have no clue what I am doing wrong. I am not sure if I have the wrong version of any of the package described :(

Avatar
Joel

both the webpack config and package.json are provided in the lesson source. Have you compared with that?

In reply to KKD
Avatar
KKD

oh god, in routes.js, the problem was that I was using curly brace {} in module.exports instead of parenthesis (). noob alert!

In reply to Joel
Avatar
Maximilian Patan

This may be explained later on but I decided to name my component main instead of 'Main'. So...

var main = React.createClass...
...
ReactDOM.render(<main/>, ...

This did not work. Had to use the upper case M. Why is that?

Avatar
Maximilian Patan

Interesting - thanks Joel.

In reply to Joel
Avatar
Daniel

Just in-case anyone gets stuck like I did. I was receiving an error in the dev console stating "React.createElement: type should not be null, undefined, booolean, or number".

The issue is in the way we instantiate our react-router. Instead of var Route = Router.route, I needed to either use import { Route } from 'react-router' or var Route = require('react-router').Route. I was then able to view pages correctly.

Avatar
fannarx

(y)
Worked for me.

In reply to Daniel
Avatar
Sky

If you are following along and are having issues, make sure that you are exporting Home.js (it took me too long to realize that was my issue!)

Avatar
Marciano

Hi there,

I'm running against problems with React-router. Not sure why exactly. But this is the warning I get: Warning: [react-router]Routerno longer defaults the history prop to hash history. Please use thehashHistorysingleton instead. http://tiny.cc/router-defaulthistory

I'm not sure what to do about this. I checked out this url: https://github.com/rackt/react-router/blob/master/upgrade-guides/v2.0.0.md

But this doesn't help me to get along.
Does anyone know what I need to do?

Thanks.

Avatar
Tyler

Hi Marciano,

You probably downloaded the latest Router and not the one downloaded in the video. With React Router 2.0 they, as the error mentioned, no longer use hashHistory by default when they instantiate the Router. Basically that means
<Router>

becomes

{ hashHistory } from 'react-router'
<Router history={hashHistory} />

see more here https://github.com/rackt/react-router/blob/master/upgrade-guides/v2.0.0.md#no-default-history

In reply to Marciano
Avatar
Gee Jay Almestas

Hi, i'm kind of confuse when i tried to view the index.html i didn't see anything, it doesn't gives me an error in the console also even doing webpack -w everything is fine... can you guys look at my files if i did something wrong?

here's my git files https://jigsgfx@bitbucket.org/jigsgfx/egghead-react.git

do you think is it because i don't have a package.json? why because when i install the package it didn't gives a package.json

EDIT:
ok my bad i did a typo error on routes.js

what i did is instead of "component" i made it "componnent" with double "n" :D

Avatar
Jin

when I try to run the app, it renders OK, but in the console I am getting this warning: "Warning: [react-router] Router no longer defaults the history prop to hash history. Please use the hashHistory singleton instead. http://tiny.cc/router-defaulthistory"

Avatar
Jin

ok, it's because react-router wont create a default history since version 2, https://github.com/reactjs/react-router/blob/master/upgrade-guides/v2.0.0.md#no-default-history

you need to provide it by yourself

In reply to Jin
Avatar
Tyler

Correct. React Router is constantly changing. In the video we're on 1.0 but they're on 2.0 already (3 months later, sigh). They've committed to support 1.0 until 3.0 is out. So once 3.0 drops I'll update the videos again.

In reply to Jin
Avatar
Jin

the nested routing in this example could cause problems, instead of going to the child Route, it always render the {Main}, solved by doing something like this:

Avatar
Joe

Excellent! You just saved me hours most likely. :)

In reply to Sky
Avatar
Bharat Soni

In App.js file I was adding spaces around {routes} this line <Router history={hashHistory}>{routes}</Router>. Which caused me 2 errors in console:

Warning: Failed propType: Invalid prop `children` supplied to `Router`.
Warning: [react-router] Location "/" did not match any routes

Just in case if anybody else is getting the same error.

Avatar
Ashish

Thanks mate. It helped me.

In reply to Daniel
Avatar
Niccolo Perra

It's not shown in the video somehow, but in Home.js don't forget to do module.exports = Home; if not then the Home will never show!

Avatar
Glenn

I foolishly npm installed the latest versions of everything so ran into the issues other have mentioned regarding no default history. It didn't prevent my code from running but the error was annoying.

hashHistory needs to be called on

var React = require('react-router');

... as against on

var React = require('react-router').Router;

This confused me a little so I broke ReactRouter out as you can see below:

App.js:

var React = require('react');
var ReactDOM = require('react-dom');
var ReactRouter = require('react-router');
var Router = ReactRouter.Router;
var hashHistory = ReactRouter.hashHistory;
var routes = require('./config/routes');

ReactDOM.render(
  <Router history={hashHistory}>
    {routes}
  </Router>,
  document.getElementById('app')
)

I learnt more about React from using the latest version so I hope that helps someone else. It doesn't help that React's documentation is all in ES6.

Avatar
Rolando

If you are using react-router v2+ with ES6, in the route.js, you need to import the file like this

import {Router,Route} from 'react-router';

In the App.js do this:

import {Router,Route, browserHistory} from 'react-router';

and

<Router history={browserHistory}>{routes}</Router>,
document.getElementById('app')
Avatar
Reactor

There are actually curly braces in transcript ;)
This way it works:

routes = (
  <Route path="/" component={Main}>
  </Route>
);

module.exports = routes;
In reply to KKD
Avatar
Kirk Sefchik

How did you accomplish the component highlighting?

In reply to egghead.io
Avatar
Priyanka Malviya

You just saved me a lot of time! Thanks :)

In reply to Sky
Avatar
Rajaprabhakaran

I followed the lesson and when I opened index.html, I have an error in the output bundle.js

var history = this.props.history;

this.props.history is undefined.

I have installed the current version of react and its deps. May be the code and lesson are not compatible with current version. ?

Avatar
Rachel

I realize this error may be because I accidentally installed the current version of everything but I keep getting a weird error in the console (see below). Anyone have any ideas? Here's my github repository for the project if that helps https://github.com/noeladd/egghead-react

bundle.js:22102 Uncaught TypeError: Cannot read property 'getCurrentLocation' of undefined(…)createTransitionManager @ bundle.js:22102RoutercomponentWillMount @ bundle.js:22109(anonymous function) @ bundle.js:15390measureLifeCyclePerf @ bundle.js:15117performInitialMount @ bundle.js:15389mountComponent @ bundle.js:15300mountComponent @ bundle.js:7757performInitialMount @ bundle.js:15413mountComponent @ bundle.js:15300mountComponent @ bundle.js:7757mountComponentIntoNode @ bundle.js:20540perform @ bundle.js:8750batchedMountComponentIntoNode @ bundle.js:20562perform @ bundle.js:8750batchedUpdates @ bundle.js:17620batchedUpdates @ bundle.js:7412renderNewRootComponent @ bundle.js:20756renderSubtreeIntoContainer @ bundle.js:20837render @ bundle.js:20858(anonymous function) @ bundle.js:54webpackrequire__ @ bundle.js:20(anonymous function) @ bundle.js:40(anonymous function) @ bundle.js:43

Avatar
Kacper Myslinski

+1

In reply to Glenn
Avatar
Alvin

Working through this when the code is out of date caused me so much pain!

Avatar
Jen

if you are copying the code from the transcript be aware that:
module.exports = {
<Route path="/" component={Main}>
</Route>
};

should be:
module.exports = (
<Route path="/" component={Main}>
</Route>
);

Let's talk a little bit more about the importance of components. We talked about how to build a component in the last video, but we didn't really get too much into the parent-child relationships with components. You'll notice here that what I've done is I've highlighted every component in red. The deeper the red, that means the more nested the component is.

Red Components

The good thing about React, the nice thing about React is that you're able to have components which manage a state. Then you're able to pass that state down to child components. What we have here is this container right here is one component. Then I'm taking data from that component, and I'm passing it down to these children components.

So that makes it really easy to reason about your data because now I know that my data, the state of my application, is mostly living in this component. If that data changes or that state changes, I know exactly where it changed at.

What we're going to do first, just so we can see this idea, is we're going to build another component. Go ahead and go over to your components folder and make a new filed called Home.js. The component we're going to build is this main component that you see here when you first load up the app, so this one right here.

Main Component

Let's go ahead and in Home.js we're going to require React. Then we're going to make a variable called Home and set it equal to React.createClass as we normally do. Here we're going to have a render method, which is going to return us what our UI looks like. What it's going to have is a className of text-center. All it's going to say is, "Search by GitHub Username Above."

components/Home.js

var React = require('react');
    var Home = React.createClass({
        render: function(){
            return (
                <h2 classname="text-center">
                    Search by Github Username Above
                </h2>
            )
        }
    });

You'll notice we use className here instead of class. Because class is a reserved word in JavaScript and technically we're in a JavaScript file, in order to do styling or in order to do classes with React, CSS classes, we need to use className instead of just class.

Now that we've done that, if we head over to our page here you'll notice that we need some sort of way to have these components of order. You'll notice here I have this menu component here

Menu Component

and I have another component here,

Another Component

but when I go to this new route I want this component to stay here and I want to load in all these other components.

[Header and other components](../images/react-building-a-react-js-app-basic-routing-with-react-router-header-and-body-components.png)

You'll also notice we've introduced this new idea of route parameters. What we're describing is this idea of routing. What we're going to use is react-router for this. Because we're doing routing, we don't want our main controller to be in charge of rendering our component anymore, so what we're going to do is head over to Main.js and remove ReactDOM.render from there.

components/Main.js

// REMOVED 
ReactDOM.render(<Main />, document.getElementById('app'));

Instead you're going to do module.exports = Main;. Now whenever we require main we're going to get this component.

components/Main.js

module.exports = Main;

Last thing, because Main.js is no longer going to be handling the rendering of our app, we can go ahead and delete this line.

components/Main.js

// REMOVED
var ReactDOM = require('react-dom');

Now what we're going to do is make another component that is basically in charge of handing our routing. Go ahead and, in your app file, create a new file called App.js.

App.js file path

The very first thing, as always, is we are going to require React. Then, because App.js is going to be in charge of our rendering, we are going to require('react-dom'); as we did earlier.

Now let's go ahead and get our router in here by require('react-router');. react-router is going to return an object, obviously, and we want Router to be a property on that object called Router. Then the last thing we're going to require is this routes object we're going to make here in a little bit.

app/App.js

var React = require('react');
var ReactDOM = require('react-dom');
var Router = require('react-router').Router;
var routes = require('./config/routes');

Now let's go ahead and head over to our terminal. Let's $ npm install, save it, react-router at version number 1.0.1. react-router has a dependency of the history package. Let's go ahead and install version 1.13.1 of that.

Terminal

$ npm install --save react-router@1.0.1 history@1.13.1

Now let's head over and finish up our App.js file. Just like we did before, we're going to call ReactDOM.render, but instead of passing in a component here what we're going to do is actually pass in our Router, which makes sense because our Router is going to be what's handling the different routes in our application.

{routes}, which we will eventually build, is basically just an instruction sheet to Router to decide which component to render based on which route we're at, in very simple terms. The second property, the second argument to .render is going to be getElementById('app');.

app/App.js

ReactDOM.render(
        <Router>{routes}</Router>,
        document.getElementbyId('app')
    )

This is looking good. We need to change one thing. If you'll remember, in our webpack.config file we had our main entry point being component/Main.js, but this is no longer the case. It's actually App.js now.

webpack.config.js

entry: "./app/App.js",

Our App.js file looks good. Now we need to go ahead and build out our routes.

Let's go ahead and make our config folder. We have our folder called config, and inside of that let's make a routes.js file. As I mentioned earlier, this file is going to be the instruction sheet for our router so our router knows which React components to render based on which path we're at.

What we're going to do is go ahead and require all of our components that we've built so far. We have Main, and now let's go ahead and get our Home component, and, of course, we're going to require our Router with react-router. Then that router has a Route property under Router.Route.

config/routes.js

var React = require('react');
    var Main = require('../components/Main');
    var Home = require('../components/Home');
    var Router = require('react-router');
    var Route = Router.Route;

We've required everything we need. Let's go ahead and describe what we're going to export from this file. What I really like about react-router is your routes can be expressed as JSX just like you're used to. Here we're going to set a path.

What's going to happen is whenever anybody goes to the root path in our application the component they're going to get served is this Main component. You'll remember from earlier the Main component, all it's going to do is render "Hello world."

To recap again, all we're doing is we're exporting the instructions for our router which will then go to our App.js and our router now receives those routes and says, "OK, whenever someone is at the home index of our app, go ahead and render this main component."

config/routes.js

module.exports = {
        <Route path="/" component={Main}>

        </Route>
    };

Let's go ahead and see if this works. Run Webpack. We get some errors. That's because this is components, not component. Let's run it again. We're good, so if we go and refresh this view, we get "Hello world", but now this "Hello world" is being served to us by our router.

You'll notice earlier when we take a look at our application, we don't want just "Hello world." We want something a little bit more complex. We want this idea of routing. We want to even be able to have a home route that we can hit and it gives us this component. Then when we switch over, that component gets swapped out with this new component.

Also, too, you'll notice that this menu bar we want to stay up here the whole time.

Menu Component

We basically want this menu to always be an active route. Then it just renders some child components.

Child Components

Now I'm back in my routes.js file. We know what we want to do is we want to specify some children routes.

The very first thing I'm going to do is require IndexRoute from the router.

config/routes.js

var IndexRoute = Router.IndexRoute;

Eventually what we're going to have is a bunch of stuff like this. When I get to users, the component we're going to use is this user component,

config/routes.js

module.exports = {
        <Route path="/" component={Main}>
            <Route path="users" component={users}
        </Route>
    };

but we don't have that now.
What if we had a bunch of these but none of these matched the certain path that we're on?

config/routes.js

module.exports = {
        <Route path="/" component={Main}>
            <Route path="users" component={users}
            <Route path="users" component={users}
            <Route path="users" component={users}
            <Route path="users" component={users}
        </Route>
    };

We need some sort of default path. That's where IndexRoute comes into play. IndexRoute says, "Specify this component or activate this component if none of our other routes that we had earlier match."

config/routes.js

module.exports = {
        <Route path="/" component={Main}>
            <IndexRoute component={Home} />
        </Route>
    };

Because we don't have any other child routes, every time we go to our main url this Home component now is going to be activated and rendered. What we should see to the screen is Search by GitHub Username, but as we talked about earlier, we don't just want to render Search by GitHub Username. We also want to render the Header in the menu bar.

Let's head over to our Main.js file. Here I'm just going to paste in this code. You'll notice up here all we have is this <nav>, but down here we're rendering this.props.children. this.props.children is going to get replaced with whatever the active component is.

components/Main.js

var Main = React.createClass({
      render: function(){
        return (
          <div className="main-container">
            <nav className="navbar navbar-default" role="navigation">
              <div className="col-sm-7 col-sm-offset-2" style={{marginTop: 15}}>
                MENU
              </div>
            </nav>
            <div className="container">
              {this.props.children}
            </div>
          </div>
        )
      }
    });

If we go back to our routes file, when I go to / path we're going to render menu, which is going to come up and render this menu, but we're also going to render this IndexRoute, which is our Home component. Then this.props.children is going to get swapped out with our Home component, which will say, "Search by GitHub Username."

Let's go ahead and see if this works. Webpack is still running. I'll hit refresh. There we go. We have our Menu component because Main is active. The Home route is also active, so we get "Search by GitHub Username."

HEY, QUICK QUESTION!
Joel's Head
Why are we asking?