Skip to main content
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:3001 that handles POST /api/probot-chat.
  • Vite’s dev-server proxy forwards /api/* to Express so the SPA calls /api/probot-chat same-origin.

1. Install

In your React app:
In your backend (may be the same package.json for a monorepo, or a separate one):

2. Backend - Express proxy

Create server/index.ts:
Run it with tsx:

3. Vite dev proxy

In vite.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:
Mount it in src/App.tsx:

5. Env vars in Vite

.env.local at the SPA root:
The backend’s .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:
Hook return values:

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_ORIGIN and 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.
If curl returns { "reply": "OK" }, the React widget will just work.