Skip to main content
The project is a Vite + React SPA. The output of npm run build is a static dist/ folder that can be deployed to any static host.

Build for production

npm run build
Vite compiles and bundles the app into dist/:
dist/
├── index.html
├── assets/
│   ├── index-[hash].js
│   ├── index-[hash].css
│   └── ... (fonts, images, 3D model)

Preview the production build locally

Before deploying, verify the build looks correct:
npm run preview
This starts a local server at http://localhost:4173 serving the dist/ folder.

Deploy to Vercel

Vercel is the easiest option — zero configuration needed for Vite projects.
1

Push your code to GitHub

Make sure the project is in a GitHub (or GitLab/Bitbucket) repository.
2

Import the repository on Vercel

Go to vercel.com/new, click Add New Project, and import your repository.
3

Confirm the framework preset

Vercel auto-detects Vite. Confirm the settings:
SettingValue
Framework PresetVite
Build Commandvite build
Output Directorydist
Install Commandnpm install
No environment variables are required.
4

Deploy

Click Deploy. Vercel builds the project and provides a live URL (e.g., https://your-project.vercel.app).Subsequent pushes to the default branch trigger automatic re-deployments.

Deploy to Netlify

1

Connect your repository

Log in to netlify.com, click Add new site → Import an existing project, and connect your Git provider.
2

Set build settings

SettingValue
Branch to deploymain (or your default branch)
Build commandnpm run build
Publish directorydist
3

Deploy

Click Deploy site. Netlify builds and publishes the site. Future pushes to the configured branch trigger automatic deploys.

Deploy to GitHub Pages

This project uses React Router with BrowserRouter, which relies on server-side URL rewriting. GitHub Pages is a pure static host and does not rewrite URLs to index.html. Navigating directly to a non-root route (e.g., /projects) will return a 404.
There are two workarounds:
Replace BrowserRouter with HashRouter in src/App.jsx. Routes will use hash-based URLs (/#/projects instead of /projects).
src/App.jsx
// Before
import { BrowserRouter } from 'react-router-dom';

// After
import { HashRouter as BrowserRouter } from 'react-router-dom';
No other changes are needed — the rest of the routing code stays the same.
1

Configure the base path in vite.config.js

If your GitHub Pages site is hosted at a sub-path (e.g., https://username.github.io/repo-name/), set the base option:
vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  base: '/repo-name/', // replace with your repository name
});
If the site is at the root (user/org pages with a username.github.io repo), leave base as '/' or omit it.
2

Build and push to the gh-pages branch

npm run build
npx gh-pages -d dist
This uses the gh-pages npm package to push the dist/ folder to the gh-pages branch. Enable GitHub Pages in your repository settings and set the source to the gh-pages branch.

Important: iframe CORS and embedding

The 3D monitor renders your portfolio inside an <iframe>. The embedded site must permit being framed.
If the embedded URL responds with the header X-Frame-Options: DENY or Content-Security-Policy: frame-ancestors 'none', the monitor will show a blank white rectangle. This is a browser security restriction — the 3D portfolio cannot override it.
To check whether a URL allows embedding:
curl -I https://your-portfolio.vercel.app/ | grep -i 'x-frame-options\|content-security-policy'
If the header is absent or set to SAMEORIGIN / frame-ancestors *, the iframe will work. Vercel-hosted React apps typically do not set X-Frame-Options by default.

Build docs developers (and LLMs) love