probot-self-hosted works in any React app. Because the browser can’t
safely hold your LLM key, the important part is the tiny backend that
proxies the chat call. This page shows Vite + Express as the reference
stack; the same pattern maps to Fastify, Hono, Next.js Pages Router, or
any other Node backend you prefer.
What you’ll build
- A Vite React SPA at
http://localhost:5173. - An Express backend at
http://localhost:3001that handlesPOST /api/probot-chat. - Vite’s dev-server proxy forwards
/api/*to Express so the SPA calls/api/probot-chatsame-origin.
1. Install
In your React app:2. Backend - Express proxy
Createserver/index.ts:
tsx:
3. Vite dev proxy
Invite.config.ts, forward /api/* to the Express server so the browser
calls same-origin /api/probot-chat and Vite forwards it to
http://localhost:3001:
4. React widget
src/BotWidget.tsx:
src/App.tsx:
5. Env vars in Vite
.env.local at the SPA root:
.env (or your shell env) holds OPENAI_API_KEY. Never mix
the two files: putting OPENAI_API_KEY in the Vite env leaks the key.
Headless usage
Skip<ProbotBot /> if you want to build your own UI. The
useProbotChat hook exposes everything you need:
Non-Express backends
Same pattern with any Node backend. Two examples:Fastify
Hono (works on Cloudflare Workers, Bun, Deno)
Deploying
- SPA + backend on the same host (recommended): serve Vite’s built
dist/from Express’s static middleware, so/and/api/*come from one origin. No CORS needed. - SPA on a static host (Netlify, Vercel) + backend elsewhere: set the
backend URL as
VITE_API_ORIGINand prefix every fetch. Add CORS on the backend to accept the SPA origin only.
Testing
Same recipe as Next.js: curl the proxy first, then check the widget.{ "reply": "OK" }, the React widget will just work.