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.