Modern apps are data-driven to where routes can change dynamically based on back-end data.
React Router is a lightweight library that does dynamic routing. It works like any React component because URL routes can mutate when rendering components. In this take, we’ll explore React Router using stock data. We’ll put the data in a static JSON file, but this data can come from dynamic sources.
You can find the complete source code for this tutorial in this GitHub repo.
Feel free to follow along, we’ll assume no knowledge of React Router and little experience with React. To begin, fire up a new create-react-app
:
npx create-react-app programmers-guide-react-router --typescript
We’ll include TypeScript with a --typescript
parameter to add type safety. This helps to conceptualize stock data that goes in each route.
Go ahead and keep the existing App.tsx
as we’ll be reusing some of it. The initial Create React App template has enough to begin minus one dependency. To install React Router, do:
npm i react-router-dom --save
Because it needs type definitions, do:
npm i @types/react-router-dom --save-dev
Inside App.tsx
, import all components necessary:
import { BrowserRouter, NavLink, Switch, Route } from 'react-router-dom';
We’ll cover what each one does as we flesh out stock data with data-driven routes. Be sure to gut the App component down to the bare bones:
class App extends Component<{}, AppState> {
render() {
return (
<div className="App">
<header className="App-header">
// TODO: dynamic routes go here
<p style={{position: "absolute", bottom: 0}}>
Programmer’s Guide to React Router
</p>
</header>
</div>
);
}
Navigation
The data will be a list of stock information, for example, stock symbol, name, and description. To help visualize this in TypeScript, add a state type declaration:
interface AppState {
stocks: Array<StockState>
}
interface StockState {
name: string;
symbol: string;
description: string;
value: number;
}
These types can define state
in the App component:
state: AppState = {
stocks: []
};
Navigation data that generates the link collection comes from a JSON file on the server. We’ll use fetch
to put this stock data in place and mutate state:
async componentDidMount() {
const payload = await fetch('/stocks.json');
const data: Array<StockState> = await payload.json();
this.setState({stocks: data});
}
We’re using async/await by placing an async
prefix in componentDidMount
. This makes the code block look like synchronous code to improve readability. The await
keyword expects a Promise<any>
and only executes the next line when fulfilled.
Inside the render()
method, a map function is useful for getting a list. The NavLink
component creates the link collection:
const links = this.state.stocks.map(s =>
<NavLink to={"/" + s.symbol}
key={s.symbol}
className="App-link" style={{marginRight: "1em"}}
activeStyle={{color: "white", textDecorationLine: "none"}}>
{s.symbol}
</NavLink>);
Note the key
prop — React needs this anytime it has a list of components. The to
prop has the route where the link will go to. Be sure to prefix this with "/"
so it matches the current route and activates. The activeStyle
prop sets the CSS style when the route is active.
Place the link collection inside the return()
statement, right below the header
tag:
<BrowserRouter>
<p>
{links}
</p>
</BrowserRouter>
The NavLink
component must be wrapped around a BrowserRouter
component. This enables dynamic routes in React Router.
Dynamic Routers
The Route
component renders any React component when the route is active. Using stock data, we can create a list of components per route:
const routes = this.state.stocks.map(s =>
<Route key={s.symbol} path={"/" + s.symbol}
render={() => <StockComponent {...s} />} />);
Note the prefix "/"
— this is so it has an exact match to the route. With the render
prop, it is possible to set any number of props in the rendering component. The StockComponent
will use props that come from stock data when it renders. React Router uses rendering props to give you this flexibility. The render
prop takes a callback function with a single parameter. For example, (props) => <></>
, placing a props
argument in the callback gives you access to the parent’s props.
These routes can go below the link collection in the BrowserRouter
component, for example:
<Switch>
{routes}
</Switch>
A Switch
component works much like a switch
statement in JavaScript. It picks the first match, renders the route component, and exits. If you have a Route
that’s set to match a homepage like "/"
, be careful. It’ll need an exact
prop in the Route
component. Or set it as the fallback route by placing it last in the switch component.
Stock Component
For the StockComponent
, props come from StockState
. We can inherit StockProps
from state to make this distinction clear in code. For example:
interface StockProps extends StockState {
}
This is a nice clean way of reusing type declarations while making the code explicitly clear. The stock component can then use StockProps
as the type:
const StockComponent: React.FC<StockProps> =
({name, symbol, value, description}) =>
<>
<p style={{textAlign: "left"}}>
{name} <br />
Symbol: {symbol} <br />
Value: ${value}
</p>
<p style={{textAlign: "justify", margin: "1em 2em"}}>
{description}
</p>
</>;
This is a functional component, or often called a stateless component, because it does not mutate state. TypeScript makes it easy to conceptualize this component while adding type safety.
Clicking on each link now renders the stock component with route prop data. Because it is dynamic, changing the JSON file also changes the link collection and each route.
This is what all the components look like:

Conclusion
React Router is a nice simple way to put in place dynamic routing. The BrowserRoute
component encapsulates both navigation links and route components. The NavLink
component has props like activeStyle
to change style when the route activates. Dynamic routers can go in a Switch
component that acts like a switch statement. For each route, a React component can render with rendering props.
The library has a lot of flexibility so you can render all React components in a single page. It has features that work well in the browser, such as navigating between routes with the back button. All this, while embracing React components in a lightweight package.
Lastly, don't forget that if you're building React applications with sensitive logic, you should always protect them against abuse, tampering and reverse-engineering by following our guide.