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 git.io/gh-pages-with-react-router
to go here
TL;DR
# package.json
"homepage": "https://username.github.io/some-react-app/", # 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?
package.json
:
{
"homepage": "https://username.github.io/example-react-app/",
"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 username.github.io/example-react-app/
is meant to be username.github.io
— which 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 now404
just uses content fromindex
, which is almost the same as redirecting users back to/
, though faster than ever.
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.
Reasons
-
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
example.com/users#login
or evenexample.com/users#/top/3/score
?" -
Prefixing the route for any route-able element like this:
<Link to={process.env.PUBLIC_URL + '/'}>
Elegant solution, eh? :) Quite sustainable...