DEPLOYING/MIGRATING static create-react-app project to Heroku-22 stack
Recently I had to migrate an older static Create React App project to the latest Heroku-22 stack. Migrating to a new Heroku stack can be very easy if your app is up to date with the latest programming language version and packages or it might require a dedicated project of updating the codebase. In the specific scenario of migrating a static create-react-app project, I found the hard way that the existing Heroku buildpack is no longer supported and there is no streamlined way of migration. This article explains a way to migrate your static Create React App to the Heroku-22 stack with minimal effort.
If you have applications on Heroku, sooner or later, you get a variant of those notifications.
The Moving Parts:
Heroku & Heroku Stacks
Heroku is an app-centric platform where developers can deploy, scale and manage their apps with almost no infrastructure knowledge. With Heroku Add-ons a developer can attach a database, security service, image processing, or scalable search service. Heroku apps run on Dynos. Dynos are (very simply put) dedicated virtual environments that run your code. The dynos have OS “versioning” called stacks. Once in a while, reasonably so Heroku updates the stacks in order to provide access to the new OS, newest programming language, and security updates. New stacks do not need to be replaced that often, but once they do it might require the developer to upgrade their app in order to be supported by the newest one.
More info on Heroku Stacks
Create-react-app (and other static react apps)
My particular used case here is around create-react-app as it comes with scripts to build static assets — npx run build
and does not have a production web server out of the box. Though, this guide can be revised for any React App that can “build” static assets via assets compiler as webpack, gulp.js, or others.
The Problem
All the current guides that explain deploying create-react-app to Heroku do not consider the Heroku-22 stack. Outdated documentation (at the time of writing):
- https://create-react-app.dev/docs/deployment/#heroku
- https://blog.heroku.com/deploying-react-with-zero-configuration
The current documentation uses a build pack that has reached and of life — https://github.com/mars/create-react-app-buildpack
Even the buildpack author himself has the below message when you use the buildpack on Heroku-22 stack.
=====! create-react-app-buildpack has reached end-of-life 🌅
This build may succeed, but the buildpack is no longer maintained.
On the Heroku-22 stack and beyond, this may fail to build at all.
Please consider migrating to https://nextjs.org or https://remix.run to develop React apps which are deployable using Heroku’s Node.js buildpack https://github.com/heroku/heroku-buildpack-nodejs, or you may develop your own create-react-app deployment with Node.js and Nginx buildpacks.
*Source: https://github.com/mars/create-react-app-buildpack/blob/master/bin/compile#L29
As the buildpack author suggests, you can consider migrating your whole app to https://nextjs.org/ or remix.run. I personally did not go that route as my app is small and the task at hand is to just update the infrastructure. If you have an ongoing frequently touched or updated app, you might consider that path.
To be noted
- Deploying a create-react-app out of the box to Heroku-22 or any other stack will WORK. That will be supported via
npx start
script which will run the app in development mode. Development mode is much slower, exposes your source code, and is likely to cause huge memory issues and crash the Heroku dyno altogether. That’s why you need a different approach to run in production mode. - Another option that create-react-app suggest is using serve, a static assets server. That package would require a global install so it will not work out of the box on Heroku. It can work if it’s implemented via serve-handler and at that point, it becomes similar to the express server solution below.
The Solution
A simple express.js web server that can run the static pre-build assets it’s all we need.
Step 1:
Add the express server package to the project
Step 2:
Create Express Server for static assets in the file scripts/heroku-start.js
// Create new file scripts/heroku-start.jsconst express = require('express');
const path = require('path');
const app = express();
const port = process.env.PORT || 3000;app.use(express.json());// Your static pre-build assets folder
app.use(express.static(path.join(__dirname, '..', 'build')));// Root Redirects to the pre-build assets
app.get('/', function(req,res){
res.sendFile(path.join(__dirname, '..', 'build'));
});// Any Page Redirects to the pre-build assets folder index.html that // will load the react app
app.get('*', function(req,res){
res.sendFile(path.join(__dirname, '..', 'build/index.html'));
});app.listen(port, ()=>{
console.log("Server is running on port: ", port)
})
Step 3:
Create a Procfile
file that defines the web process that runs the web server.
web: node scripts/heroku-start.js
More on Heroku Procfile
Step 4:
Deploy your code. Heroku will automatically run npx run build
first. That will compile your assets and get them ready for production.
Complete Source Code: https://github.com/velles/create-react-app-heroku-22-stack
Conclusion
Hopefully, this will unblock your path to upgrading to the Heroku-22 stack. I will not be surprised if, in the near future, there is a new Heroku buildpack supporting out-of-the-box deployment.
Contacts
I am a Software, Data, and Machine Learning Consultant, certified in AWS Machine Learning & AWS Solutions Architect — Professional. Get in touch if you need help with your next Machine Learning project.
To stay updated with my latest articles and projects, follow me on Medium.