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)