If you are anything like me, after playing around with React and rendering components here and there you probably
started
wondering how someone would go about loading a component conditionally.
Soon you realized that you can conditionally render your components using your everyday if, switch, ternary operator
and what
not.
All is good for a while but then it falls into you oh man, wouldn't it be great to load components base on the
URL
🤔
After googling for a few seconds you found about BrowserRouter. You go through the tutorials and it seems simple enough, you end up with something similar to this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React from 'react';
import './App.css';
import {BrowserRouter as Router, Switch, Route} from 'react-router-dom';
function App() {
return (
<Router basename="/tutorial">
<NavbarComponent></NavbarComponent>
<Switch>
<Route path={`/users`}>
<UsersComponent>
</UserComponent>
</Route>
<Route path={`/posts`}>
<PostsComponent></PostsComponent>
</Route>
</Switch>
</Router>
);
}
export default App;
Somewhere else in your app:
1
<a href="/posts">View Posts</a>
You have your Router that can switch between routes and you have links with relative paths to navigate around.
You run npm start
(or yarn start
🐱), the dev server fires up, you try your links and they work like a charm.
You have been coding your React app for while and you already have a bunch of features working, so you start looking into the deployment process.
It seems simple enough, for instance, if you used Create-react-app you just run npm run build
, which
generates a /build folder with your compiled site.
Then you copy the content to your server (apache, nginx, IIS), you browse to your newly deployed app, which loads
without any issues, as you are about to celebrate, you try one of your links and you are met by a
beautiful:
What they didn't tell you on those BrowserRouter tutorials (though to be fair is rather obvious when you think about
it) is that each time you click on one of your links the web browser will go to the server and ask for the
corresponding route, for example http://example.com/posts
, since there is nothing there on the server,
it will of course reply with a 404. Why did it work on the React dev server you ask? Simple, the dev server is defaulting any
route to /index.html, thus it doesn't matter where your links point to, it will always return /index.html,
then locally on the web browser the BrowserRouter component kicks in, checks on the URL and does its trick.
If you look around on google for a solution you will probably find suggestions about configuring your HTTP Server to do what the dev server does, that is to redirect all the requests to /index.html. That works, but why do you need to go to the server to load a component when your SPA is already fully loaded on the browser? The answer is that you don't, what you really need is HashRouter.
The HashRouter component is included on the React Router library ready to be used and you barely need to change your code to use it. Basically you need to import HashRouter instead of BrowserRouter and prepend your links with # instead of /
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React from 'react';
import './App.css';
import {HashRouter as Router, Switch, Route} from 'react-router-dom';
function App() {
return (
<Router basename="/tutorial">
<NavbarComponent></NavbarComponent>
<Switch>
<Route path={`/users`}>
<UsersComponent>
</UserComponent>
</Route>
<Route path={`/posts`}>
<PostsComponent></PostsComponent>
</Route>
</Switch>
</Router>
);
}
export default App;
Somewhere else in your app:
1
<a href="#posts">View Posts</a>
HashRouter uses fragment links (Wikipedia), this type of links by definition will not trigger an HTTP request on the browser, this way you can support navigation while keeping everything locally, and without any special setup on the server.