Skip to main content
A React app built with Vite is just a Node.js project that produces static files. Nubo runs your build, then a small server to serve the dist/ folder.

Set up

npm create vite@latest my-app -- --template react
cd my-app
npm install
npm install serve
serve is what runs in production. Vite is for building. In package.json, set a start script that boots serve against the build output:
package.json
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "start": "serve -s dist -l ${PORT:-3000}"
  }
}
The -s flag tells serve to fall back to index.html for unknown routes (so client-side routing works). The -l ${PORT:-3000} makes it respect Nubo’s port. Test locally:
npm run build
PORT=3000 npm start
# open http://localhost:3000

Deploy

Push to GitHub, then in the Nubo dashboard:
1

New Frame

Open your Project and Space, hit + Frame.
2

Pick the repo and branch

Connect your GitHub repo. Choose the branch Nubo should watch.
3

Confirm the port

Default 3000. Set it to whatever your start script listens on.
4

Deploy

Hit deploy. Nubo runs npm ci, npm run build, then npm start. (npm ci needs a committed package-lock.json.)

Client-side routing (React Router)

serve -s already redirects unknown paths to index.html. If you use a different static server, make sure it does the same. Otherwise hitting /dashboard directly returns 404.

Things that trip people up

  • No start script. Without npm start, Nubo tries node index.js which doesn’t exist in a Vite project. Define start explicitly.
  • vite preview in start. vite preview is for local checking only; it’s not a production server. Use serve or another static server.
  • Hardcoded serve -l 3000. Use ${PORT:-3000} so the Frame’s port setting actually applies.

Environment variables

Vite bakes VITE_-prefixed variables into the bundle at build time, but Nubo injects Project and Frame variables only at runtime. A VITE_ variable you set in the dashboard never reaches the built bundle: the build already finished before it exists. Either bake the value in at build time, or fetch config from your own runtime endpoint after the app loads. See Environment variables for details.

Going truly static

If you don’t need a Node container, build locally and deploy with the Static pack instead. The Static pack serves on port 80, so set the Frame port to 80. It also looks for index.html at the repo root or in public/, so committing dist/ on its own isn’t enough: rename dist/ to public/ (giving you public/index.html) or move the built files to the repo root, then remove package.json. Nubo will then ship the files behind nginx with no Node runtime at all. Useful for cheap landing pages; not useful when you want to redeploy from a git push.

How the build works

Uses the Node.js pack of Raypacks. Same flow as any Node app: install, build, start.