Skip to content

Instantly share code, notes, and snippets.

  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save artginzburg/3ff7d5ffdac1738810bd01a8c316d70b to your computer and use it in GitHub Desktop.
react-router synchronised

GitHub Pages + React Router

The whole setting prevents static routing from ruining the UX by synchronizing your production and development implementations, so they act identically and effectively.

Just type to go here


# package.json

"homepage": "", # Your GH Pages deployment link (for %PUBLIC_URL%)
"scripts": {
  "postbuild": "ln -s index.html build/404.html" # Symlink — for "Not Found" to be served
// BrowserRouter.jsx

<Router basename={process.env.PUBLIC_URL}> {/* basename is responsible for "syncing" dev to prod */}

How do I know this file is useful to me?

Apparently, you were looking for a solution to a well-known problem of broken paths and routes in SPAs, and your journey has finally come to a logical conclusion. Give it a star if you think there is no better solution. Otherwise — the comments section is waiting for your heroic act.

What does this do?

  1. package.json:
  "homepage": "",
  "scripts": {
    "build": "react-scripts build",
    "postbuild": "ln -s index.html build/404.html",
    "predeploy": "npm run build",
    "deploy": "gh-pages -d build"
  "dependencies": {
    "react-router-dom": "^5.2.0",
    "react-scripts": "^4.0.3"
  "devDependencies": {
    "gh-pages": "^3.2.0"

"homepage" is used by react-scripts build to determine your %PUBLIC_URL% environment variable and should be set to your repo's GH Pages link.

In the current example, it would be /example-react-app

In other words, it prevents your GH Pages (or Netlify) deployment from assuming that is meant to be username.github.iowhich it's clearly not.

npm set-script postbuild "ln -s index.html build/404.html"

"postbuild" in "scripts" — symlinks index.html to 404.html, basically meaning any "non-really-existent" path in your deployment would now be served, acting as an entry point to your Multi-Page app component that takes care of dynamic routing (including the real 404 Not Found URLs) via React Router.

Previously, when GitHub pages had no support for symbolic links, we had to copy index to 404 or write crazy window.location splitting/replacing scripts (such as rafgraph/spa-github-pages, meaning increased build time and DRY violation :) But now 404 just uses content from index, which is almost the same as redirecting users back to /, though faster than ever.

  1. BrowserRouter.jsx - React Router DOM component:
<BrowserRouter basename={process.env.PUBLIC_URL}>{/* routes */}</BrowserRouter>

basename is a built-in property that prepends its value to <Route>, <Link>, <Redirect> or any other react-router-dom components provided as the <BrowserRouter>'s children.

Using process.env.PUBLIC_URL as its value makes relative paths like /login or /profile work stable and hassle-free — like you'd expect them to.

Say "Bye!" to confusions and divergences between the development server and production deployment.


  • The dissappointment of an app working perfectly in dev and failing in production

  • Tons of opened issues on the case:

    Start at facebook/create-react-app#1765 and scroll through endless references...

    You may regret seeing that

  • The lack of a single perfect solution that is used by everyone

  • Actively spreading unsustainable versions of code for "solving" the issue

Deprecated solutions

  • Using <HashRouter> instead of <BrowserRouter>.

    The reason for deprecation is the answer to "How often do you see website links looking like or even"

  • Prefixing the route for any route-able element like this:

    <Link to={process.env.PUBLIC_URL + '/'}>

    Elegant solution, eh? :) Quite sustainable...

npm set-script postbuild "ln -s index.html build/404.html"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment