[Solved] React app bundled with Parcel would not deploy correctly in GitHub Pages
Gabriel Diem
Posted on February 8, 2023
I was learning ReactJS, making my first dynamic-ish frontend and I was happy enough with that little project as a first React project. It worked perfectly in the local dev server provided by Parcel. I bundled the project and run a simple HTTP Python local server with the command:
python -m http.server 3000
It worked perfectly.
So I was ready to deploy the app in GitHub Pages, see if I could even do that. I pushed the changes and the dist
folder as well, configured a default GitHub Actions script for a static webapp deployment and waited. It did not work, the js and css files were not being received and the console showed a couple of 404 errors. I found out that the page was trying to pull files from my root url, that being https://gabrieldiem.github.io. So I tried making a couple of changes to the GitHub Actions script but I couldn't make it work.
After that I turned to a package called gh-pages, which pushed the bundled files to a new branch and deployed the Github Pages website from there, and I gave it a try.
First I tried deploying a simple create-react-app app to know what I was trying to do was even possible (I was not using create-react-app btw). And it worked! So it had to be something about my bundling config.
I found out that I had to add to the package.json the entry:
"homepage": "http://gabrieldiem.github.io/Portfolio_like_project"
With Portfolio_like_project being the repo name. So I bundled the project as I was doing before with the script:
"build": "parcel build src/index.html"
And set up the gh-pages required scripts:
"predeploy": "npm run build",
"deploy": "gh-pages -d dist"
I gave it another try, same as before, it did not work again.
Then I tried a couple things with absolute routes and ended up in a GitHub issue of the Parcel repo saying that Parcel and absolute routes don't mix well. So I ditched that. Okay, maybe hardcoding the absolute route directly into the HTML and JS, I ditched that as well (not only because it was awful but also because it didn't work).
Eventually found out that what could solve my problem was to add a --public-url
flag to the parcel bundle script so it ended looking like this:
"build": "parcel build src/index.html --public-url /Portfolio_like_project/"
Again, not using absolute routing, instead using the relative route of the root url, which I guessed was https://gabrieldiem.github.io so adding the repo name would make the fetching request of the js and css files work properly.
This time it kind of worked. I solved the problem of the requests ending in a 404 error, so it was serving the files as it should. The problem was I still had a blank page staring at me, nothing at all was displayed.
By this time, I had already spent a couple of hours making pointless changes, debugging, bundling in different ways (with no cache access, without optimizations, etc) and reading tons of docs, Stack Overflow posts and GitHub issues. I took a little rest and came back to it. I kept investigating and found these two posts that gave me the key of what was happening:
Building a Static Single Page Application with JavaScript, for GitHub pages and more … written by Weiyuan.
So you want to host your Single Page React App on GitHub Pages? written by Brendan McIlhenny.
And the key was that besides the general routing of the application, I had also used the React components BrowserRouter
, Route
and Link
, so it appeared that the breaking point could have been there. Spoiler alert: it was.
So I fixed the routes and prepended a /Portfolio_like_project
to the destiny routes of Routes and Links, deployed it and it worked! I was happy that it was finally live and I managed to find the ways things work.
Until, I run the dev local server and it crashed, which was logical because it couldn't find the files and routes pointed by the new location I prepended before. I tried a couple of workarounds and settled with the next solution:
- I created a
.env
file which contained the relative path I wanted to add:
BASE_RELATIVE_PATH="/Portfolio_like_project"
- I created a new js file which was in charge of detecting if I was in production mode and at the same time not in localhost, the code looks like this:
const isEnvProduction = process.env.NODE_ENV === 'production';
const isEnvNotLocalhost = window.location.hostname !== 'localhost' && !window.location.href.includes('127.0.0.1');
const baseRelativePath = (isEnvProduction && isEnvNotLocalhost) ? process.env.BASE_RELATIVE_PATH : '';
export default baseRelativePath;
A little note here: Parcel sets the NODE_ENV variable to 'production' or 'development' depending on the build (if it's been built and bundled, or if it's running on the dev server, respectively). So I took advantage of that. If I was in a production environment I prepended the relative path and if I wasn't I prepended an empty string.
Then all I had to do was use this basePath before every redirection, like this:
import baseRelativePath from 'path.js';
<Link to={`${baseRelativePath}/`}>Home</Link>
<Route path={`${baseRelativePath}/`} element={/*some element*/} />
After making that change I managed to make the app work in a local development, local production and remote production environment. Success!
It is not the most elegant solution though, making a template literal for every path in order to prepend this basePath sounds like something that could be solved in other way. As a React beginner I find it good enough for the moment, and I hope I can find a better way of doing this, if I do I will be pasting a link for the update here. As for now, it is staying like this.
Thanks for reading! And if you came here and I managed to help you, I'm glad I could serve you useful 😊.
Posted on February 8, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
February 8, 2023