Editor’s note: This article was last updated on 27 November 2023 to explore how to automate the process of deploying with GitHub Actions.
The simplicity of deploying a static website with GitHub Pages is a process that can be easily transferred to React applications. With just a few steps, it’s easy to host a React app on GitHub Pages for free or build it to deploy on your custom domain or subdomain.
In this article, we’ll explore how to deploy React apps on GitHub Pages. We’ll also demonstrate how to create a custom domain on GitHub Pages for our static website and automate the process using GitHub Actions.
Let’s get started!
GitHub Pages is a service from GitHub that enables you to add HTML, JavaScript, and CSS files to a repository and create a hosted static website.
The website can be hosted on GitHub’s github.io
domain (e.g., https://username.github.io/repositoryname
) or your custom domain. A React app can be similarly hosted on GitHub Pages.
To deploy your React application to GitHub Pages, follow these steps:
Let’s get started by creating a new React application. For this tutorial, we’ll be using create-react-app
but you can set up the project however you prefer.
Open the terminal on your computer and navigate to your preferred directory. For this tutorial, we’ll set up the project in the desktop directory, like so:
cd desktop
Create a React application using create-react-app
:
npx create-react-app "your-project-name"
In just a few minutes, create-react-app
will have finished setting up a new React application! Now, let’s navigate into the newly created React app project directory, like so:
cd "your-project-name"
This tutorial is limited to demonstrating how to deploy a React application to GitHub Pages, so we’ll leave the current setup as it is without making any additional changes.
The next step is to create a GitHub repository to store our project’s files and revisions. In your GitHub account, click the + icon in the top right and follow the prompts to set up a new repository:
After your repository has been successfully created, you should see a page that looks like this:
Now that the GitHub remote repository is set up, the next step is to initialize Git in the project so that we can track changes and keep our local development environment in sync with the remote repository.
Initialize Git with the following command:
git init
Now, we’ll commit our code and push it to our branch on GitHub. To do this, simply copy and paste the code received when you created a new repository (see the above repo image):
git commit -m "first commit" git branch -M main git remote add origin https://github.com/nelsonmic/testxx.git git push -u origin main
Next, we’ll install the gh-pages
package in our project. The package allows us to publish build files into a gh-pages
branch on GitHub, where they can then be hosted.
Install gh-pages
as a dev dependency via npm:
npm install gh-pages --save-dev
Now, let’s configure the package.json
file so that we can point our GitHub repository to the location where our React app will be deployed.
We’ll also need to add predeploy
and deploy
scripts to the package.json
file. The predeploy
script is used to bundle the React application, and the deploy
script deploys the bundled file. In the package.json
file, add a homepage
property that follows this structure: http://{github-username}.github.io/{repo-name}
.
Now, let’s add the scripts. In the package.json
file, scroll down to the scripts
property and add the following commands:
"predeploy" : "npm run build", "deploy" : "gh-pages -d build",
Here’s a visual reference:
That’s it! We‘ve finished configuring the package.json
file.
Now, let’s commit our changes and push the code to our remote repository, like so:
git add . git commit -m "setup gh-pages" git push
We can deploy our React application by simply running: npm run deploy
. This will create a bundled version of our React application and push it to a gh-pages
branch in our remote repository on GitHub.
To view our deployed React application, navigate to the Settings tab and click on the Pages menu. You should see a link to the deployed React application:
We can deploy our React app to GitHub’s domain for free, but GitHub Pages also supports custom subdomains and apex domains. Here are examples showing what each type of subdomain looks like:
Supported custom domain | Example |
---|---|
www subdomain |
www.logdeploy.com |
Custom subdomain | app.logdeploy.com |
Apex domain | logdeploy.com |
Right now, if we navigate to https://nelsonmic.github.io/logdeploy/, we’ll see our recently published website. But, we could also use a custom subdomain or an apex domain instead. Here are the steps to set those up:
CNAME
file at the root of your repository:CNAME
record on your domain service provider points to the GitHub URL of the deployed website (in the case of this example, nelsonmic.github.io/logdeploy/). To do so, navigate to the DNS management page of the domain service provider and add a CNAME
record that points to username.github.io
where username
is your GitHub username.To deploy to an apex domain, follow the first two steps above for deploying to a custom subdomain but substitute the third step with the following:
ALIAS
record or ANAME
record that points your apex domain to your GitHub Pages IP addresses, as shown:If you’ve previously deployed a React app that uses React Router for routing to Netlify, you’re aware that you need to configure redirects for your URLs. Without redirects, users will get a 404 error when they try to navigate to different parts of your application.
Netlify makes it simple to configure redirects and rewrite rules for your URLs. All you need to do is create a file called _redirects
(without any extensions) in the app’s public folder.
Then, simply add the following rewrite rule within the file:
/* /index.html 200
No matter what URL the browser requests, this rewrite rule will deliver the index.html
file instead of returning a 404.
If we want to handle page routing when we deploy to GitHub Pages, we’ll need to do something similar. Let’s configure routing for our previously deployed project.
First, we need to install a router. Start by installing React Router in the project directory, like so:
npm install react-router-dom
Then, follow the next steps.
Step 1: Connect a HashRouter
to the application to enable client-side routing:
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; import { HashRouter as Router } from "react-router-dom"; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Router> <App /> </Router> ); // If you want to start measuring performance in your app, pass a function // to log results (for example, reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals reportWebVitals();
Our index.js
file should look like the above code block. Because GitHub Pages does not support browser history, we’re employing a HashRouter
. Our existing path does not assist GitHub Pages in determining where to direct the user (because it is a frontend route).
To solve this issue, we must replace our application’s browser router with a HashRouter
. The hash component of the URL is used by this router to keep the UI in sync with the URL.
Step 2: Create the routes:
Create a routes
folder and the required routes. These routes can be configured in the app.js
file. But first, let’s create a Navbar
component that can be visible on all pages.
Step 3: Create a Navbar
component:
>import { Link } from "react-router-dom" const Navbar =()=>{ return ( <div> <Link to="/">Home</Link> <Link to="/about">About</Link> <Link to="/careers">Careers</Link> </div> ) } export default Navbar;
Now we can add the Navbar
component alongside the configured routes in the app.js
file.
Step 4: Set up routes in the app.js
file:
import './App.css'; import { Routes, Route} from "react-router-dom"; import About from "./routes/About"; import Careers from "./routes/Careers"; import Home from "./routes/Home"; import Navbar from './Navbar'; function App() { return ( <> <Navbar /> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/careers" element={<Careers />} /> </Routes> </> ); } export default App;
Now that we’re done with the setup, let’s push our code, like so:
>git add . git commit -m "setup gh-pages" git push
Next, we simply deploy and our app should route properly:
>npm run deploy
Once these steps are completed, our deployed application will correctly route the user to any part of the application they desire.
When we configure Netlify deployments, we’re given a preview link to view our deployment before it is merged into the main branch. Let’s create the same for GitHub Pages.
We’ll use a simple tool called Livecycle for this, which saves us the trouble of having to do this using GitHub Actions. Every time we make a pull request in our repository, Livecycle assists us in creating a preview environment.
To create a preview environment, follow these steps:
create-react-app
:Once deployment is successful, anytime we make a PR or push a commit to that PR, we’ll get a preview link.
We would proceed to integrate GitHub Actions for automated deployments. This streamlines the deployment process and enhances efficiency. Before we can deploy the app using this approach, we’ll create a workflow file in .github/workflows/deploy.yml
:
name: Deploy to GitHub Pages on: push: branches: - main workflow_dispatch: permissions: contents: read pages: write id-token: write concurrency: group: "pages" cancel-in-progress: false jobs: build: runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@v4 - name: Detect package manager id: detect-package-manager run: | if [ -f "${{ github.workspace }}/yarn.lock" ]; then echo "manager=yarn" >> $GITHUB_OUTPUT echo "command=install" >> $GITHUB_OUTPUT echo "runner=yarn" >> $GITHUB_OUTPUT exit 0 elif [ -f "${{ github.workspace }}/package.json" ]; then echo "manager=npm" >> $GITHUB_OUTPUT echo "command=ci" >> $GITHUB_OUTPUT echo "runner=npx --no-install" >> $GITHUB_OUTPUT exit 0 else echo "Unable to determine package manager" exit 1 fi - name: Setup Node uses: actions/setup-node@v4 with: node-version: "14" cache: ${{ steps.detect-package-manager.outputs.manager }} - name: Install dependencies run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} - name: Build project run: ${{ steps.detect-package-manager.outputs.runner }} npm run build - name: Upload artifact uses: actions/upload-pages-artifact@v2 with: path: ./build # Deployment job deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v3
This is called a workflow file and is used by GitHub Actions to determine how to run the jobs in it. This has two jobs: a build, and a deploy job.
actions/deploy-pages@v3
action to deploy the build artifact from the build stage to GitHub pagesNext, run this:
git add . && git commit -m "Adds Github Actions" && git push
This will start the pipeline and proceed to deploy the built files to pages:
If you get an error: Branch "main" is not allowed to deploy to github-pages due to environment protection rules
, follow these steps:
This should allow deployments from the main branch to your github-pages environment:
After adding this, retry the deploy stage, and this time it should pass successfully:
Sometimes, we need to add sensitive data to applications. But to do it securely, we don’t want to hardcode it directly into the application. Let’s see how to do this with the help of GitHub Actions.
To demonstrate this, we’ll update the About.js
pages to include an environment variable, add a React environment variable to GitHub as a secret, and finally add this sensitive data securely to the workflow file:
To add a new secret to GitHub, go to:
New repository secret
buttonREACT_APP_API_KEY
as the secret name, and 12345
as the secret valueAdd Secret
button:Next, modify the workflow file to include this environment variable. This enables the variable REACT_APP_API_KEY
to be available to pages within the app:
name: Deploy to GitHub Pages on: push: branches: - main workflow_dispatch: permissions: contents: read pages: write id-token: write concurrency: group: "pages" cancel-in-progress: false jobs: build: runs-on: ubuntu-latest env: REACT_APP_API_KEY: ${{ secrets.REACT_APP_API_KEY }} steps: - name: Checkout Repository uses: actions/checkout@v4 - name: Detect package manager id: detect-package-manager run: | if [ -f "${{ github.workspace }}/yarn.lock" ]; then echo "manager=yarn" >> $GITHUB_OUTPUT echo "command=install" >> $GITHUB_OUTPUT echo "runner=yarn" >> $GITHUB_OUTPUT exit 0 elif [ -f "${{ github.workspace }}/package.json" ]; then echo "manager=npm" >> $GITHUB_OUTPUT echo "command=ci" >> $GITHUB_OUTPUT echo "runner=npx --no-install" >> $GITHUB_OUTPUT exit 0 else echo "Unable to determine package manager" exit 1 fi - name: Setup Node uses: actions/setup-node@v4 with: node-version: "14" cache: ${{ steps.detect-package-manager.outputs.manager }} - name: Install dependencies run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} - name: Build project run: ${{ steps.detect-package-manager.outputs.runner }} npm run build - name: Upload artifact uses: actions/upload-pages-artifact@v2 with: path: ./build # Deployment job deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v3
Finally, update the About.js
page to this:
const apiKey = process.env.REACT_APP_API_KEY; const About = () => { return <h1>About page 1.0, Secret: {apiKey}</h1>; }; export default About;
Once deployed, we can see the environment variable displayed on the page:
GitHub Pages is easy to get started with and free to use, making it a desirable option for developers of all skill levels.
In this article, we demonstrated how to use GitHub Pages to convert a React app into a static website. We showed how to deploy the React app to GitHub’s domain, as well as to a custom subdomain. We also explored how to automate the process by using GitHub Actions. If you’re looking for an easy way to share your code with the world, GitHub Pages is a great option.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowJavaScript’s Date API has many limitations. Explore alternative libraries like Moment.js, date-fns, and the new Temporal API.
Explore use cases for using npm vs. npx such as long-term dependency management or temporary tasks and running packages on the fly.
Validating and auditing AI-generated code reduces code errors and ensures that code is compliant.
Build a real-time image background remover in Vue using Transformers.js and WebGPU for client-side processing with privacy and efficiency.
14 Replies to "Deploying React apps to GitHub Pages"
The deploy script code has an error. There should be a space before -d
Hello Kevin,
We’ve corrected the typo. Thanks for reading LogRocket’s blog!
Thank you for pointing this out.
this didn’t work for me. I’m seeing the html of my index.js file instead of the actual site
Hi Henry, thank you for reading. Can you provide more context to this problem?
“gh-pages-d build”, there is a space in -d. if you would not add space you can get message that “‘gh-pages-d’ is not recognized as an internal or external command,
operable program or batch file. ”
should be like this
“deploy”: “gh-pages -d build”
Hi Fazeelat, thanks for pointing out the typo. We’ve fixed it. Thanks for reading LogRocket’s blog!
Thank you for the artice, very well put together.
Thank you! I’m glad it was a good read.
Thank you for putting everything very well. I could easily go through the steps and got the deploying done. A big thank you
Hi Michael, thank for posting this. I’m getting an error near the beginning.
npm install gh-pages –save-dev
Unsupported engine {
npm WARN EBADENGINE package: ‘@csstools/[email protected]’,
npm WARN EBADENGINE required: { node: ‘^14 || ^16 || >=18’ },
npm WARN EBADENGINE current: { node: ‘v17.9.1’, npm: ‘8.11.0’ }
I think this is connected to problems I’m having using the command line to manage my repository on github… but maybe I fixed that and this is a new problem…?
Also, I’d like to subscribe to your blog, but I’m not sure what logrocket is and how to subscribe without paying a membership fee.
Hey, how come you listed the ‘git branch -M main’ command during setup? How does that affect the process?
When I try this, I end up with an empty page that just says “This site is open source. Improve this page.”
I looked at the `artifact.zip` file that the ci built, and it appears to have created this index.html with just that content in it at the top level.
My project runs just fine locally with `npm run start` for example.
Any idea why this would happen?
In my case I had forgot to add the homepage property in the package.json.
“In the package.json file, add a homepage property that follows this structure: http://{github-username}.github.io/{repo-name}.”