React Routing works in local machine but not Heroku

How to fix client-side routing errors (Heroku 404 errors):

React Browser Router

If you're using React Browser Router, as an npm module with create-react-app, then the solution (which works for me) is to create a static.json file (within the same directory as package.json).

{
  "root": "build/",
  "clean_urls": false,
  "routes": {
    "/**": "index.html"
  }
}

Here is why this solution works:

Create-react-app is for the most part a Node.Js server which serves client-side React. The public static directory is mapped to the / endpoint, and visiting this endpoint from a browser will download the index.html webpage. This webpage in turn loads the React components. And because React Browser Router is a React component, the routes are loaded dynamically after visiting the / endpoint. In other words, before the index.html webpage is loaded all our React Browser Router routes will result in 404 errors on Heroku. To resolve this issue, a static.json file can be used to map any endpoints with the following pattern /** to the index.html file, which in turn will load React Browser Router and correctly load the react components for that route.

From an Apache HTTP server:

Likewise, on an Apache HTTP server creating an .htaccess file in the public directory, will remap all endpoints that match /** to the index.html file.

Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]

More resources

Also read the "Deployment" section of the create-react-app README, which has a ton of good information on how to reconfigure the server to use client-side routing.

https://facebook.github.io/create-react-app/docs/deployment

React Static Router

Lastly, React Router offers a static router, React Static Router, which can be used with the "react-dom/server" npm module on a Node.js server, to render JSX server-side, and doesn't need static.json or .htaccess reconfiguration.


I actually came across this post first before 3 hours of searching through react-router and heroku documentation. For swyx, and anyone else having the same problem, I'll outline the minimum of what you need to do to get this working.

router.js - (Obviously change AppSplash and AppDemo to your components)

export default <Router history={hashHistory}>
  <Route path="/" component={App}>
    <IndexRoute component={AppSplash}/>
    <Route path="demo" component={AppDemo}/>
  </Route>
</Router>

app.js

import React, { Component } from 'react'

class App extends Component {
static propTypes = {
  children: PropTypes.node
}

render() {
  const { children } = this.props
  return (
    <div>
      {children}
    </div>
  )
}
}

export default App

Create a new file in the root of your home directory and name it static.json. Put this into it.

{
  "root": "build/",
  "clean_urls": false,
  "routes": {
    "/**": "index.html"
  }
}

Push to heroku again. The routes should work this time.

Explanation:

You need to modify Heroku's default webpack, otherwise the service gets confused with how to handle the client-side routing. Essentially what static.json does. The rest is just the correct way to handle the routing according to the 'react-router' documentation.