# How to Deploy React App on GitHub Pages: Best Practices and Common Pitfalls

> Learn how to deploy a React app on GitHub Pages. Discover best practices for production builds, package.json configuration, and client-side routing while avoiding common pitfalls.

- Repository: [Meta/react](https://github.com/facebook/react)
- Tags: best-practices
- Published: 2026-02-19

---

**Deploying a React app on GitHub requires shipping a production-optimized build, configuring the `homepage` field in [`package.json`](https://github.com/facebook/react/blob/main/package.json), and implementing a [`404.html`](https://github.com/facebook/react/blob/main/404.html) fallback to handle client-side routing, while keeping sensitive environment variables out of the static bundle.**

Deploying your first React application to GitHub Pages is a standard milestone for frontend developers, but the process involves specific architectural decisions documented throughout the `facebook/react` repository. While the React source code in [`packages/react/README.md`](https://github.com/facebook/react/blob/main/packages/react/README.md) emphasizes production build optimization, the repository's CI workflows in [`.github/workflows/runtime_build_and_test.yml`](https://github.com/facebook/react/blob/main/.github/workflows/runtime_build_and_test.yml) demonstrate automation patterns that translate directly to deployment pipelines. This guide covers how to deploy React app on GitHub infrastructure using patterns derived from the React source code itself.

## Ship a Production-Optimized Build

The React package documentation explicitly warns against deploying development builds to production. In [`packages/react/README.md`](https://github.com/facebook/react/blob/main/packages/react/README.md), the maintainers note that development builds contain extra warnings and are **significantly larger** than production bundles, which impacts load times on GitHub Pages' free hosting tier.

Always generate a production build before deploying:

```bash
npm run build

# or

yarn build

```

This creates a `build/` directory containing minified, static assets. According to the React repository's release process documented in [`scripts/release/README.md`](https://github.com/facebook/react/blob/main/scripts/release/README.md), these artifacts should be treated as disposable outputs rather than source code, meaning they should never be committed directly to your main branch.

## Configure the Homepage Field

GitHub Pages serves React applications from a subdirectory path (e.g., `https://username.github.io/repository-name/`) unless you use a custom domain. Without proper configuration, your assets will attempt to load from the root domain, resulting in 404 errors for JavaScript and CSS files.

Add the `homepage` field to your [`package.json`](https://github.com/facebook/react/blob/main/package.json):

```json
{
  "name": "my-react-app",
  "homepage": "https://username.github.io/my-react-app",
  "scripts": {
    "build": "react-scripts build",
    "deploy": "gh-pages -d build"
  }
}

```

This instructs the build tooling to prefix asset URLs correctly, ensuring that [`main.js`](https://github.com/facebook/react/blob/main/main.js) loads from [`/my-react-app/static/js/main.js`](https://github.com/facebook/react/blob/main//my-react-app/static/js/main.js) rather than [`/static/js/main.js`](https://github.com/facebook/react/blob/main//static/js/main.js).

## Handle Client-Side Routing

GitHub Pages is a static file host, which creates a critical issue for single-page applications (SPAs) using React Router. When a user navigates directly to a client-side route (e.g., `/about`) or refreshes the page, GitHub Pages attempts to serve a file at that path. Since the route exists only in the React application's memory, the server returns a 404 error.

Implement a fallback mechanism by creating a [`404.html`](https://github.com/facebook/react/blob/main/404.html) file in your `public/` directory:

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <script>
      // Preserve the original path and redirect to index.html
      const target = location.pathname + location.search + location.hash;
      location.replace('/?redirect=' + encodeURIComponent(target));
    </script>
  </head>
  <body></body>
</html>

```

Then modify your [`index.html`](https://github.com/facebook/react/blob/main/index.html) or root component to check for the redirect parameter and use the HTML5 History API to restore the original path without reloading.

## Secure Your Deployment

Environment variables used during the build process (e.g., `REACT_APP_API_KEY`) are **baked directly into the static JavaScript bundle**. Since GitHub Pages serves these files publicly, any secrets committed to the build will be exposed to anyone who inspects the page source.

Store sensitive values in **GitHub Secrets** rather than `.env` files, and inject them only in server-side processes or GitHub Actions workflows. If your application requires private API calls, implement a serverless function or proxy rather than calling the API directly from the React frontend.

## Automate with GitHub Actions

The React repository demonstrates robust CI/CD patterns in [`.github/workflows/runtime_build_and_test.yml`](https://github.com/facebook/react/blob/main/.github/workflows/runtime_build_and_test.yml), which includes a `yarn build` step at lines 55-56 and bundle size validation via the `sizebot` job at lines 66-70. You can adapt these patterns to automate your GitHub Pages deployment.

Create [`.github/workflows/deploy.yml`](https://github.com/facebook/react/blob/main/.github/workflows/deploy.yml):

```yaml
name: Deploy React to GitHub Pages
on:
  push:
    branches: [main]
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'
      - name: Install dependencies
        run: yarn install --frozen-lockfile
      - name: Build production bundle
        run: yarn build
      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./build

```

This workflow mirrors the React repository's approach of generating build artifacts on each push rather than committing the `build` directory to version control.

## Common Pitfalls to Avoid

| Pitfall | Why It Happens | How to Avoid |
|---------|----------------|--------------|
| **Forgetting the production build** | Running `npm start` creates a development server with extra warnings and larger bundle sizes | Always execute `npm run build` and deploy the resulting `build/` directory |
| **Missing `homepage` configuration** | Asset paths default to root-relative URLs that break in subdirectory deployments | Add `"homepage": "https://<user>.github.io/<repo>"` to [`package.json`](https://github.com/facebook/react/blob/main/package.json) |
| **404 errors on page refresh** | GitHub Pages serves static files only and knows nothing about React Router routes | Implement a [`404.html`](https://github.com/facebook/react/blob/main/404.html) fallback that redirects to [`index.html`](https://github.com/facebook/react/blob/main/index.html) with path preservation |
| **Exposing API keys** | Environment variables prefixed with `REACT_APP_` are embedded in the static bundle | Store secrets in GitHub Secrets and access them via server-side proxies only |
| **Committing `build/` or `node_modules/`** | Repository bloat and merge conflicts from generated files | Add both directories to `.gitignore` and use CI/CD to generate builds |
| **Ignoring bundle size** | Large JavaScript payloads cause slow loads on GitHub Pages' basic hosting | Enable code-splitting with `React.lazy` and monitor sizes with tools like `sizebot` |

## Summary

Deploying a React application to GitHub Pages successfully requires attention to build optimization, routing configuration, and security practices derived from the `facebook/react` source code:

- **Always generate production builds** using `npm run build` before deploying, as development bundles contain extra warnings and inflated sizes documented in [`packages/react/README.md`](https://github.com/facebook/react/blob/main/packages/react/README.md)
- **Configure the `homepage` field** in [`package.json`](https://github.com/facebook/react/blob/main/package.json) to ensure assets load correctly from subdirectory paths
- **Implement a [`404.html`](https://github.com/facebook/react/blob/main/404.html) fallback** to handle client-side routing in single-page applications
- **Never commit API keys or the `build/` directory**; use GitHub Actions workflows inspired by [`.github/workflows/runtime_build_and_test.yml`](https://github.com/facebook/react/blob/main/.github/workflows/runtime_build_and_test.yml) to automate deployment
- **Monitor bundle size** using CI checks similar to the React repository's `sizebot` job to ensure fast load times on GitHub Pages

## Frequently Asked Questions

### Why does my React app show a blank page after deploying to GitHub Pages?

This typically occurs when the `homepage` field is missing from [`package.json`](https://github.com/facebook/react/blob/main/package.json) or when you deployed a development build instead of a production build. Without the `homepage` configuration, the browser attempts to load JavaScript and CSS files from the root domain rather than the repository subdirectory, resulting in 404 errors for all assets. Always run `npm run build` and ensure your [`package.json`](https://github.com/facebook/react/blob/main/package.json) includes the correct GitHub Pages URL.

### How do I fix 404 errors when refreshing a page on my deployed React app?

GitHub Pages serves only static files and has no knowledge of React Router's client-side routes, so direct navigation to paths like `/about` returns a 404. To resolve this, create a [`404.html`](https://github.com/facebook/react/blob/main/404.html) file in your `public/` directory that redirects to [`index.html`](https://github.com/facebook/react/blob/main/index.html) while preserving the original path, or configure your GitHub Action to generate a fallback page that uses JavaScript to rewrite the URL before the React app mounts.

### Should I commit the build folder to my GitHub repository?

No, you should never commit the `build/` directory to version control. The React repository's CI workflow in [`.github/workflows/runtime_build_and_test.yml`](https://github.com/facebook/react/blob/main/.github/workflows/runtime_build_and_test.yml) demonstrates that build artifacts should be generated fresh during the deployment process rather than stored in the repository. Add `build/` to your `.gitignore` file and use GitHub Actions to run `npm run build` automatically on each push, then deploy the generated files to the `gh-pages` branch without polluting your main branch history.

### Can I use environment variables with GitHub Pages for API keys?

Environment variables prefixed with `REACT_APP_` are embedded directly into the static JavaScript bundle during the build process, making them publicly visible to anyone who inspects your site's source code. For GitHub Pages deployments, you should never store API keys in your React code or repository files; instead, store sensitive values in GitHub Secrets and access them through serverless functions or proxy servers that keep the credentials on the backend.