I started working in react js with react router 4, so everything was working fine on the development server and it was pretty fast and fascinating experience while working. On the production server, it shows 404 error.
Understanding of Server Side Rendering & Client Side Rendering
There are two things to understand in terms of URL interpretation, whereas there used to be only 1 in ‘the old days’. In the past, when life was simple, some user sent a request for https://swatantra.xyz/about to the server, which inspected the path part of the URL, determined the user was requesting the about page and then sent back that page.
If we are going to use client-side rendering and using react router then things are not as simple as we think At first, the client does not have any JS code loaded yet. So the very first request will always be to the server. That will then return a page that contains the needed script tags to load React and React Router etc. Only when those scripts have loaded does phase 2 start. In phase 2, when the user clicks on the ‘About us’ navigation link, for example, the URL is changed locally only to https://swatantra.xyz/about (made possible by the History API), but no request to the server is made. Instead, React Router does its thing on the client side, determines which React view to render and renders it. Assuming your about page does not need to make any REST calls, it’s done already. You have transitioned from Home to About Us without any service request having fired.
Now we need to consider if we want to share code via email or other messaging app to your friends. Your friend has not loaded your website yet.In other terms, she/He is still in phase 1. No React Router is running on her/His machine yet. So her/his browser will make a server request to https://swatantra.xyz/about.
After this our main problem starts from here. Until now, you could get away with just placing a static HTML at the webroot of your server. But that would give
404 errors for all other URLs when requested from the server. Those same URLs work fine on the client side, because there React Router is doing the routing for you, but they fail on the server side unless you make your server understand them.
Concatenating server- and client-side routing
If you want the https://swatantra.xyz/about URL to work on both the server- and the client-side, you need to set up routes for it on both the server- and the client side. Makes sense right?
And this is where your choices begin. Solutions range from bypassing the problem altogether, via a catch-all route that returns the bootstrap HTML, to the full-on isomorphic approach where both the server and the client run the same JS code.
How to Solve this problem : Hash History
using Hash History instead of Browser history , current URL for the about page would look something like this https://swatantra.xyz/about The Part after the hash (#) symbol is not sent to server. So the server https://swatantra.xyz/ and sends the index page as expected. React-Router will pickup the #/about part and show the correct page.
- Bad Url
- SSR(Server side rendering) is not possible with this way and Search Engine Optimization is concerned,your website consists of single page with hardly any content on it.
With this approach you do use Browser History, but just set up a catch-all on the server that sends
index.html, effectively giving you much the same situation as with Hash History. You do have clean URLs however and you could improve upon this scheme later without having to invalidate all your user's favorites.
- More complex to set up
- Still no good SEO
In the hybrid approach you expand upon the catch-all scenario by adding specific scripts for specific routes. You could make some simple PHP scripts to return the most important pages of your site with content included, so Googlebot can at least see what’s on your page.
- Even more complex to set up
- Only good SEO for those routes you give the special treatment
- Duplicating code for rendering content on server and client
What if we use Node JS as our server so we can run the same JS code on both ends? Now, we have all our routes defined in a single react-router config and we don’t need to duplicate our rendering code. This is ‘the holy grail’ so to speak. The server sends the exact same markup as we would end up with if the page transition had happened on the client. This solution is optimal in terms of SEO.
- Server must (be able to) run JS. I’ve experimented with Java i.c.w. Nashorn but it’s not working for me. In practice it mostly means you must use a Node JS based server.
- Many tricky environmental issues (using
windowon server-side etc)
- Steep learning curve
Which should I use?
Choose the one that you can get away with. Personally I think the catch-all is simple enough to set up that that would be my minimum. This setup allows you to improve on things over time. If you are already using Node JS as your server platform, I’d definitely investigate doing an isomorphic app. Yes it’s tough at first but once you get the hang of it it’s actually a very elegant solution to the problem.
So basically, for me, that would be the deciding factor. If my server runs on Node JS, I’d go isomorphic, otherwise I would go for the Catch-all solution and just expand on it (Hybrid solution) as time progresses and SEO requirements demand it.
If you’d like to learn more on isomorphic (also called ‘universal’) rendering with React, there are some good tutorials on the subject:
- React to the future with isomorphic apps
- The Pain and the Joy of creating isomorphic apps in ReactJS
Also, to get you started, I recommend looking at some starter kits. Pick one that matches your choices for the technology stack (remember, React is just the V in MVC, you need more stuff to build a full app). Start with looking at the one published by Facebook itself:
Or pick one of the many by the community. There is a nice site now that tries to index all of them:
I started with these:
Currently I am using a home-brew version of universal rendering that was inspired by the two starter kits above, but they are out of date now.
Best of luck~~~~~~~~