How to Deploy Your Vite React App to GitHub Pages (With and Without React Router)

This article will guide you to deploy existing Vite React app to GitHub. The Vite documentation has already written the steps, but I personally think it misses some details. Let us address it.
Configure Vite Config
First of all, add a property called base
with the value of our repository name on vite.config.js
or vite.config.ts
. For example, if our repository name is react-vite-gh-pages
, then we set the configuration as follows:
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig(({ command }) => {
const config = {
plugins: [react()],
base: '/',
}
if (command !== 'serve') {
config.base = '/react-vite-gh-pages/'
}
return config
})
The deployment URL would be https://<OUR_GITHUB_USERNAME>.github.io/react-vite-gh-pages
. Note that we have a conditional config based on our Vite command. This means when we are running npm run dev
, the page will start serving on path http://localhost:5173/
instead of http://localhost:5173/react-vite-gh-pages/
Create GitHub Action Workflow
On our GitHub repository, navigate to “Pages” menu inside “Settings” tab. Set build and deployment source to “GitHub Actions”.

We need to create a Github Action workflow configuration to automate the deployment for every push in main
branch or manually triggered from Actions tab. Create the following directory:
react-vite-gh-pages/
├─ .github/
│ ├─ workflows/
│ │ ├─ deploy.yml
Inside deploy.yml
, we set the following command from Vite documentation:
# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ['main']
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets the GITHUB_TOKEN permissions to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow one concurrent deployment
concurrency:
group: 'pages'
cancel-in-progress: true
jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'npm'
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Setup Pages
uses: actions/configure-pages@v3
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
# Upload dist repository
path: './dist'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
Stage all changes in the current directory using git add .
, commit, and push it to our GitHub repository. Under the “Actions” tab, we will see our workflow appear based on our recent push. Congrats, your app is successfully deployed!

If you’re unsure about the address, head back to Settings → Pages and you will find the URL.

If We Have React Router Installed…
When we use React Router on our project, the workflow will run without any problems. But we will get this error when we open our page:

To add more confusion, the console does not show any errors. The actual root cause is that the router’s basename
is still set to /
when the root of our deployment is /react-vite-gh-pages/
. In order to fix that, we need to update it as we did on vite.config.js
— adding a conditional base path. import.meta.env.DEV
is a built-in Vite environment variable that returns true
when npm run dev
is running.
// App.jsx
import { createBrowserRouter, RouterProvider, Link } from 'react-router-dom'
function App() {
// your routes here
// const routes = [...]
const router = createBrowserRouter(routes, { basename: import.meta.env.DEV ? '/' : '/react-vite-gh-pages/' })
return <RouterProvider router={router} />
}
Or, if you write routes as components:
// App.jsx
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'
function App() {
return (
<BrowserRouter
basename={import.meta.env.DEV ? '/' : '/react-vite-gh-pages/'}
>
<Routes>
<Route
path='/'
element={
<div>
<h1>Hello World</h1>
<Link to='about'>About Us</Link>
</div>
}
/>
<Route path='/about' element={<div>About</div>} />
</Routes>
</BrowserRouter>
)
}
One More Thing!
There is a GitHub’s 404 error when we refresh the page on children path relative to the basename
route (e.g. /about
). This is because GitHub Pages is looking for about.html
while our app is a single page application which has only index.html
. To prevent this, update the following line on our deploy.yml
:
- Before:
run: npm run build
- After:
run: npm run build && cp ./dist/index.html ./dist/404.html
By default, GitHub Pages will look for 404.html
if there is no specified document provided. What happens here is we create a copy of our index.html
as 404.html
.
The source code of this guide can be found here.
Caveats
- This method is only for those who want to deploy their Vite React app to
https://<OUR_GITHUB_USERNAME>.github.io/<REPO_NAME>/
path. - If we build locally using
npm run build
, it will show a blank page whendist
directory is served locally. - We are locked in GitHub Pages deployment. If we try to deploy it on Netlify or Vercel, it will show a blank page as if we serve the build locally like number 2.
- It is recommended to use npm or yarn as the package manager and has its
package-lock.json
oryarn.lock
lockfile present in the repository. GitHub Actions does not support pnpm’spnpm-lock.yaml
at the moment of this writing.