I recently wanted to host my React portfolio on GitHub Pages, but with a twist. Most tutorials show you how to deploy to [username.github.io/repository-name], which is straightforward. However, I wanted my portfolio to live at the root domain: username.github.io.
But, I didn't want my GitHub Pages repository cluttered with React source code, node_modules, and development files. I wanted clean separation:
- One repository for my React source code (development, version control, collaboration)
- Another repository (username.github.io) with only the production build
Manually building and pushing every time I made changes? Not ideal. There had to be a better way.
Cross-Repository GitHub Actions
After some tinkering, I discovered GitHub Actions could automate deployments between repositories. Here's what I learned.
Understanding GitHub Pages Root Domain Deployment
Option A: Put React code in username.github.io
❌ Messy: Source code mixed with builds
❌ Unprofessional: Shows development artifacts
Option B: Build locally, push to username.github.io
❌ Manual process every deployment
❌ Error-prone: Forget to build? Push wrong files?
❌ Not scalable: No CI/CD pipeline
Option C: Separate repos + GitHub Actions
✅ Clean separation of concerns
✅ Automated builds and deployments
✅ Professional CI/CD workflow
The Solution: Two-Repository Architecture
Repository 1: my-portfolio (Private/Public)
- React source code
- Development dependencies
- GitHub Actions workflow
- Version control for features
Repository 2: username.github.io (Public)
- Only production build files
- Automatically updated by CI/CD
- Serves the live website
Step 1: Setting Up the Workflow File
In my React repository, I created .github/workflows/deploy.yml:
name: Deploy to GitHub Pages on: push: branches: - main # Triggers on every push to main workflow_dispatch: # Allows manual deployment jobs: build-and-deploy: runs-on: ubuntu-latest steps: - name: Checkout source repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '18' cache: 'npm' - name: Install dependencies run: npm ci - name: Run tests run: npm test -- --watchAll=false --passWithNoTests continue-on-error: false - name: Build React app run: npm run build env: CI: true - name: Verify build output run: | echo "Build completed successfully" ls -la ./build du -sh ./build - name: Deploy to GitHub Pages repository uses: peaceiris/actions-gh-pages@v3 with: personal_token: ${{ secrets.PAGES_DEPLOY_TOKEN }} external_repository: username/username.github.io publish_branch: main #branch name of repo u want to deploy to publish_dir: ./build #directory of the action user_name: 'github-actions[bot]' user_email: 'github-actions[bot]@users.noreply.github.com' commit_message: 'Deploy from ${{ github.repository }}@${{ github.sha }}' Step 2: Authentication Setup
GitHub Actions needs permission to push to another repository. Here's what I learned about authentication options:
Option A: Personal Access Token (What I Used)
Creating the token:
GitHub Settings → Developer settings → Personal access tokens → Tokens (classic)
Generate new token (classic)
Name it descriptively: "Portfolio Deploy to GitHub Pages"
Expiration: Choose based on security needs
Required scopes:
repo - Full control of repositories
workflow - Update GitHub Action workflows
Adding to repository secrets:
Go to React repository → Settings → Secrets and variables → Actions
New repository secret
Name: PAGES_DEPLOY_TOKEN
Value: Paste the token
Add secret
Option B: Deploy Keys (More Secure Alternative)
I also explored deploy keys, which are SSH-based and repository-specific:
# Generate SSH key pair ssh-keygen -t ed25519 -C "deploy-key-pages" -f deploy_key -N "" Then:
Add deploy_key.pub to username.github.io → Settings → Deploy keys (with write access)
Add deploy_key (private) to React repo → Settings → Secrets as PAGES_DEPLOY_KEY
Update workflow to use deploy_key: ${{ secrets.PAGES_DEPLOY_KEY }} instead of personal_token.
Why I chose Personal Access Token:
Simpler for a single-user portfolio. Deploy keys are better for team projects.
Configuring the Build
Important customizations I had to make:
yaml# Node version matching my local environment node-version: '18' # Check with: node --version # Build output directory publish_dir: ./build # React uses 'build', Vite uses 'dist' # Package manager run: npm ci # Or 'yarn install --frozen-lockfile' for Yarn Pro tip: Always verify your build directory locally:
npm run build ls -la # Check if 'build' or 'dist' folder was created Step 4: Testing the Workflow
First deployment:
git add .github/workflows/deploy.yml git commit -m "Add automated deployment workflow" git push origin main Then I monitored it:
- React repository → Actions tab
- Watched the "Deploy to GitHub Pages" workflow
- Each step showed real-time logs
- Green checkmarks = success!
Within 5 minutes, username.github.io received the build files and my portfolio was live!
How It Works: The Complete Flow
Developer pushes code ↓ GitHub Actions triggers in React repo ↓ 1. Checkout source code 2. Install dependencies 3. Run tests 4. Build React app (npm run build) 5. Generate ./build folder ↓ Workflow authenticates with token ↓ Push build files to username.github.io ↓ GitHub Pages auto-deploys ↓ Site live at username.github.io Troubleshooting Issues I Encountered
Issue 1: Build Fails in CI but Works Locally
Problem: Different Node versions
Solution: Match Node version in workflow to local:
node --version # Check local version # Update workflow: node-version: '18' Issue 2: 404 on GitHub Pages
Problem: Routing issues with React Router
Solution:
- Make sure your homepage for the react is set correctly.
- Add 404.html that redirects to index.html or use hash routing:
<BrowserRouter basename="/">// For username.github.io
Resources
GitHub Actions Documentation
peaceiris/actions-gh-pages
GitHub Pages Documentation
Top comments (0)