8453. React Router and Client Side Routing
Routing, CSR, Webpack, and Nginx


Introduce client side routing and how to fix the issue when using React Router.

1. Routing Issue

When using React Router and Webpack for my React app, I encountered a routing issue. When accessing the homepage http://localhost:3000/, everything looks fine. image However, when clicking on the Code Editor button, we are navigated to http://localhost:3000/editor. The routing is not working, instead, “Cannot GET /URL Error” appears. image

2. Client Side Routers

In the old days, things were simple. If you wanted to get the contents of /dashboard, the browser would make a GET request to your server, by inspecting the path portion of the URL the server would figure out that the user was requesting the /dashboard page. It would then grab that page and send back to the browser as a response. Then these things called client side routers (CSR) came into the picture. With a CSR (like React Router), you’re no longer making requests to your server every time you change routes. Instead, your CSR is just handling that for you locally on the browser. So when you go to /dashboard, instead of making a GET request to your server, your CSR is using a browser API called history.pushState to manually change the URL and then it renders the View for that specific route - all without causing a page refresh.

3. Solutions

The solution is to force web browser access the index page. This can be done at server side.

3.1 Webpack-Dev-Server

Add publicPath and historyApiFallback into the webpack configuration.

publicPath: '/',
historyApiFallback: true,

Sample webpack.config.js.

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

const outputDirectory = 'dist';

module.exports = {
  entry: './src/client/index.js',
  output: {
    path: path.join(__dirname, outputDirectory),
    filename: 'bundle.js',
    publicPath: '/',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
  devServer: {
    port: 3000,
    open: true,
    proxy: {
      '/api': 'http://localhost:8080',
    },
    historyApiFallback: true,
  },
  plugins: [
    new CleanWebpackPlugin([outputDirectory]),
    new HtmlWebpackPlugin({
      template: './public/index.html',
      favicon: './public/favicon.ico',
    }),
  ],
};

Rebuild with webpack and refresh the browser. You may need to clean web browser cache. image

3.2 Nginx Server

Add following line to the Nginx configuration file /usr/local/etc/nginx/nginx.conf.

try_files $uri /index.html;

This lets nginx serve static asset files and serves your index.html file when any file isn’t found on the server.

server {
    listen       9096;
    server_name  localhost;

    #charset koi8-r;

    #access_log  logs/host.access.log  main;

    location / {
        try_files $uri /index.html;
        root   /nginx/www;
        index  index.html index.htm;
    }

4. Reference