Back to Blog
Tutorial10 min read2026-08-03

Deploying a Vite+React SPA with One Click

Add a deploy button to your Vite+React repo that auto-detects build settings and puts your SPA on a CDN in under two minutes, no dashboard required.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

Most Vite+React starter templates ship without a deploy story. You get a local dev server and a npm run build command, but no clear path from dist/ to a live URL. A deploy button in your README closes that gap—anyone with a PandaStack account can click the badge and have a running SPA in under two minutes, without touching the dashboard or writing a config file.

This post shows how to add the deploy button to a Vite+React repo, how it auto-detects your build settings, and how to override the defaults with a pandastack.json file when needed.

The deploy button syntax

The button is a Markdown badge that links to https://dashboard.pandastack.io/deploy?repo=owner/repo:

[![Deploy to PandaStack](https://dashboard.pandastack.io/deploy-button.svg)](https://dashboard.pandastack.io/deploy?repo=yourusername/vite-react-starter)

When someone clicks it, PandaStack reads the repo's package.json from GitHub's public API, detects the build command and output directory, and pre-fills the deploy form. For a standard Vite setup, it infers:

  • Build command: npm run build
  • Output directory: dist
  • Type: static

The user confirms the settings, connects their GitHub account if they haven't already, and the deploy starts. No manual field entry required.

What happens during the deploy

PandaStack clones the repo, runs npm install && npm run build in an ephemeral build pod, and uploads the dist/ directory to a CDN. The build pod is destroyed after the upload, so there's no idle cost. The CDN serves the SPA from edge locations with HTML edge-cached and hashed assets served immutable—cache is purged automatically on the next deploy.

Free-tier static sites are unlimited in number, so you can deploy every experimental SPA without worrying about limits. Paid plans increase the bandwidth allowance (100 GB free, 500 GB on Pro at $15/mo, unlimited on higher tiers).

Overriding auto-detection with pandastack.json

If your Vite config uses a custom output directory (build instead of dist) or you want to force a specific Node version, add a pandastack.json file to the repo root:

{
  "type": "static",
  "language": "nodejs",
  "buildCommand": "npm run build",
  "outputDir": "build"
}

The deploy page reads this file via GitHub's API and uses its values instead of the inferred ones. The language field is optional—defaults to auto, which detects Node.js from package.json. Other valid values: python, go, docker.

Deploying from a monorepo subdirectory

If your Vite app lives in apps/web/ within a monorepo, use the rootDir field:

{
  "type": "static",
  "rootDir": "apps/web",
  "buildCommand": "npm run build",
  "outputDir": "dist"
}

This sets the working directory to apps/web/ before running the build command. The output directory is relative to that path.

Alternatively, pass it as a query param in the deploy button URL:

[![Deploy to PandaStack](https://dashboard.pandastack.io/deploy-button.svg)](https://dashboard.pandastack.io/deploy?repo=acme/monorepo&rootDir=apps/web)

Both work. The pandastack.json approach is cleaner because it keeps the configuration in version control.

Prompting for environment variables

If your Vite app reads VITE_API_URL at build time, you want the deploy page to prompt for it instead of building with missing config. Add an env array to pandastack.json:

{
  "type": "static",
  "buildCommand": "npm run build",
  "outputDir": "dist",
  "env": [
    {
      "key": "VITE_API_URL",
      "description": "Backend API endpoint"
    }
  ]
}

Now the deploy page shows an input field labeled "VITE_API_URL" with the description as help text. The user fills it in, and the value is injected during the build. This is the same pattern Vercel uses—PandaStack has Vercel-parity here.

You can also pre-fill a default:

{
  "key": "VITE_API_URL",
  "description": "Backend API endpoint",
  "value": "https://api.example.com"
}

The user can override it if needed.

The CLI alternative

If you prefer scripting over buttons, the panda CLI achieves the same result:

panda login
panda projects create \
  --name vite-react-app \
  --repo yourusername/vite-react-starter \
  --branch main \
  --type static \
  --build-cmd "npm run build" \
  --output-dir dist

This creates the project and starts the first deploy. Subsequent deploys happen automatically on push if you set autoDeploy: true in the API payload or check "Auto-deploy on push" in the dashboard.

The CLI is better for scripting in CI or when you're deploying many repos at once. The deploy button is better for README discoverability—visitors can deploy your demo in one click without reading docs.

The API approach

For programmatic deploys, use the REST API:

curl -X POST https://api.pandastack.io/v1/projects \
  -H "Authorization: Bearer psk_live_your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "static",
    "name": "vite-react-app",
    "repositoryName": "yourusername/vite-react-starter",
    "branch": "main",
    "autoDeploy": true,
    "env": [
      { "name": "VITE_API_URL", "value": "https://api.example.com" }
    ]
  }'

The response includes the project ID and the first deployment UUID. You can poll the deployment status to confirm it succeeded.

Why this matters

Netlify and Vercel popularized the deploy button pattern, but they lock you into their platform-specific workflows. PandaStack's deploy button works the same way—auto-detects build settings, prompts for env vars, deploys to a CDN—without vendor lock-in. The underlying API is open and documented, so you can script deploys in CI if you outgrow the button workflow.

The free tier includes unlimited static sites and 100 GB bandwidth per month, which is enough for most personal projects. When you hit the bandwidth limit, the Pro plan at $15/mo raises it to 500 GB and adds 1000 build minutes.

Common issues

Build fails with "command not found: vite": The builder runs npm install before npm run build, but if your package.json has vite in devDependencies and you're using npm v7+, it should work. If it fails, check that your package.json doesn't skip dependency installation with a postinstall script.

404 on all routes except index.html: Vite SPAs need a catch-all redirect so /about serves index.html and React Router handles the route. PandaStack's static hosting automatically configures this for SPAs—all 404s serve index.html. If it's not working, verify the output directory is correct (dist, not build).

Environment variable not injected: Vite only injects vars prefixed with VITE_ at build time. If your var is named API_URL, rename it to VITE_API_URL in both your code and pandastack.json.

Full example repo

Here's a minimal Vite+React repo with the deploy button configured:

package.json:

{
  "name": "vite-react-starter",
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  },
  "dependencies": {
    "react": "^18.3.0",
    "react-dom": "^18.3.0"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^4.2.0",
    "vite": "^5.0.0"
  }
}

pandastack.json:

{
  "type": "static",
  "buildCommand": "npm run build",
  "outputDir": "dist",
  "env": [
    { "key": "VITE_API_URL", "description": "Backend API" }
  ]
}

README.md:

# Vite+React Starter

[![Deploy to PandaStack](https://dashboard.pandastack.io/deploy-button.svg)](https://dashboard.pandastack.io/deploy?repo=yourusername/vite-react-starter)

Click the button to deploy this app to PandaStack in under 2 minutes.

Push this to GitHub, and the deploy button works immediately. No further setup needed.

References

  • [Vite Documentation](https://vitejs.dev/guide/)
  • [PandaStack Deploy Button Docs](https://docs.pandastack.io/projects/deploy-button)
  • [Vite Environment Variables](https://vitejs.dev/guide/env-and-mode.html)

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also