Frontend Deployment
Deploy to production: build the static bundle and serve dist/, with an optional Docker recipe.
Just want to run it locally? See QUICKSTART.md —
npm install, set two env vars,npm run dev. That's the whole story for development. This guide is for deploying to production.
The DARE frontend is a static single-page app built with Vite. Deploying it is deliberately
simple: build the bundle once, then serve the generated dist/ folder from any static host.
There is no server runtime to manage.
For the full environment variable reference, see docs/configuration.md.
Prerequisites
| Requirement | Version | Notes |
|---|---|---|
| Node.js | 18+ | 20 LTS recommended — build-time only |
| npm | 9+ | (or pnpm / yarn if you adapt scripts) |
A running DARE backend | — | Reachable from the browser via the configured URLs |
Node is needed only to build the bundle. Once you have
dist/, the host serving it needs nothing but the ability to serve static files.
1. Configure environment
cp .env.example .env
# edit .env — see docs/configuration.md for the full referenceMinimum for a production build:
VITE_APP_ENVIRONMENT=production
VITE_DJANGO_BACKEND_URL=https://api.example.com
VITE_WEBSOCKET_URL=wss://api.example.comImportant: Vite inlines
VITE_*values at build time. Changing them after the build has no effect — you must rebuild for each target environment.
2. Build
npm ci # respect the lockfile
npm run build # produces ./distdist/ is a self-contained static bundle: hashed JS/CSS assets plus index.html.
3. Serve dist/
Pick whichever fits your infrastructure — they are all equivalent from the app's point of view:
- Managed static hosting (simplest) — upload
dist/to Netlify, Vercel, Cloudflare Pages, S3 + CloudFront, or Cloud Storage. The one required setting: SPA-style routing — rewrite all unmatched paths to/index.htmlso React Router can handle deep links. - Nginx / Apache / Caddy — copy
dist/to the web root and add the same SPA fallback. A minimal Nginx config is in the appendix below. - Quick check on a box —
npx serve -s dist -l 5173(the-sflag enables SPA fallback). Fine for staging; use a real web server or CDN for production.
4. CORS and CSRF
The backend must include the frontend's origin in CORS_ALLOWED_ORIGINS and
CSRF_TRUSTED_ORIGINS. See the backend configuration reference.
For Socket.IO to connect, VITE_WEBSOCKET_URL must be reachable from the browser and accept the
same auth token used for REST.
Verifying the deployment
- Visit the deployed URL — the login screen should load.
- Open dev tools → Network, sign in, and confirm:
- The REST request hits
VITE_DJANGO_BACKEND_URL. - The Socket.IO upgrade succeeds (a
wss://request with a101status).
- The REST request hits
- Send a chat message — you should see streamed tokens arrive.
- Upload a file — it should move from
processingtocompleted.
If any step fails, check the browser console for CORS or auth errors and confirm the backend's
CORS_ALLOWED_ORIGINS includes your origin.
Upgrading
git pull
npm ci # respect lockfile
npm run build
# redeploy dist/ via your usual mechanismAppendix: Nginx & Docker
Serving a static bundle needs no container, so Docker is optional for the frontend — reach for it only if a container is how the rest of your stack ships. If so, a minimal multi-stage build:
# build stage
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
ARG VITE_DJANGO_BACKEND_URL
ARG VITE_WEBSOCKET_URL
ENV VITE_DJANGO_BACKEND_URL=$VITE_DJANGO_BACKEND_URL
ENV VITE_WEBSOCKET_URL=$VITE_WEBSOCKET_URL
RUN npm run build
# serve stage
FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY devops/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80A SPA-friendly Nginx config (devops/nginx.conf) — also usable standalone, without Docker:
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Cache static assets aggressively (they are content-hashed)
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# SPA fallback — let React Router handle the path
location / {
try_files $uri $uri/ /index.html;
}
}Build and run:
docker build \
--build-arg VITE_DJANGO_BACKEND_URL=https://api.example.com \
--build-arg VITE_WEBSOCKET_URL=wss://api.example.com \
-t dare-frontend .
docker run -d -p 80:80 --name dare-frontend dare-frontendBecause Vite inlines
VITE_*at build time, pass the per-environment URLs as build args — changing them on the running container has no effect.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| White screen, console error about asset 404s | Server not serving SPA fallback (deep links 404) | Configure try_files $uri /index.html or the host's rewrite-to-index.html setting |
Network Error on every request | Wrong VITE_DJANGO_BACKEND_URL, or CORS not allowed by backend | Verify URL; add origin to backend CORS_ALLOWED_ORIGINS |
| Chat connects but no streamed tokens | Socket.IO URL or auth issue | Check VITE_WEBSOCKET_URL; inspect WS frames in dev tools |
| Build fails with TS errors | Type drift after backend API changes | Update redux/types/; run npx tsc --noEmit for a full report |
| Old code shipped after deploy | CDN or browser cache | Ensure index.html is Cache-Control: no-cache; assets fingerprinted (Vite default) |
VITE_* env var ignored at runtime | Vite inlines at build time | Rebuild with the desired value |
For local development, see QUICKSTART.md; for contributing, see CONTRIBUTING.md.