Frontend service template: Vite + React 19 + React Router, nginx-unprivileged
Find a file Use this template
nholtware d267ae2b93 ci: use https://git.lab.holtlabs.xyz for workflows and registry (Plan 12)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-11 11:55:15 -06:00
.forgejo ci: use https://git.lab.holtlabs.xyz for workflows and registry (Plan 12) 2026-11-11 11:55:15 -06:00
docker-entrypoint.d feat: Unleash feature flags via src/lib/flags.tsx, hello-banner on home page 2026-11-05 11:19:45 -06:00
nginx feat: JSON access log in the platform logging contract 2026-11-05 10:54:10 -06:00
public feat: initial Vite/React frontend service template 2026-11-05 02:27:23 -06:00
src feat: Unleash feature flags via src/lib/flags.tsx, hello-banner on home page 2026-11-05 11:19:45 -06:00
.dockerignore feat: initial Vite/React frontend service template 2026-11-05 02:27:23 -06:00
.gitignore feat: initial Vite/React frontend service template 2026-11-05 02:27:23 -06:00
.nvmrc feat: initial Vite/React frontend service template 2026-11-05 02:27:23 -06:00
.prettierignore feat: initial Vite/React frontend service template 2026-11-05 02:27:23 -06:00
.prettierrc.json feat: initial Vite/React frontend service template 2026-11-05 02:27:23 -06:00
CLAUDE.md feat: Unleash feature flags via src/lib/flags.tsx, hello-banner on home page 2026-11-05 11:19:45 -06:00
commitlint.config.js feat: initial Vite/React frontend service template 2026-11-05 02:27:23 -06:00
Dockerfile feat: JSON access log in the platform logging contract 2026-11-05 10:54:10 -06:00
eslint.config.js feat: initial Vite/React frontend service template 2026-11-05 02:27:23 -06:00
index.html feat: initial Vite/React frontend service template 2026-11-05 02:27:23 -06:00
package-lock.json feat: Unleash feature flags via src/lib/flags.tsx, hello-banner on home page 2026-11-05 11:19:45 -06:00
package.json feat: Unleash feature flags via src/lib/flags.tsx, hello-banner on home page 2026-11-05 11:19:45 -06:00
README.md ci: use https://git.lab.holtlabs.xyz for workflows and registry (Plan 12) 2026-11-11 11:55:15 -06:00
tsconfig.json feat: initial Vite/React frontend service template 2026-11-05 02:27:23 -06:00
vite.config.ts feat: initial Vite/React frontend service template 2026-11-05 02:27:23 -06:00

${REPO_NAME}

Frontend service generated from nholtware/service-template-react: a Vite + React 19 + React Router (data mode) SPA, served as static files by unprivileged nginx.

Develop

npm install
npm run dev          # http://localhost:5173, /api proxied to API_PROXY_TARGET (default http://localhost:3000)
npm test
npm run lint
npm run format

Runtime config

/config.js sets window.__APP_CONFIG__. In the container, docker-entrypoint.d/10-config.sh writes it at start from env vars, so one image runs in every environment. Read values through getConfig() in src/lib/config.ts.

Env var Default Meaning
API_BASE_URL /api Backend base URL; /api is the same-host route
APP_NAME app Display name
UNLEASH_URL (empty) Unleash base URL for the browser; empty = flags off
UNLEASH_FRONTEND_TOKEN (empty) Unleash frontend token (public by design)

Everything in config.js is public. Never put secrets there.

Feature flags

Flags come from Unleash (UI: https://unleash.lab.holtlabs.xyz, environment development) through its Frontend API, using the official @unleash/proxy-client-react SDK. src/lib/flags.tsx wraps it: FlagsProvider is mounted once in main.tsx; components call the wrappers, never the SDK directly:

import { useFlag, useVariant } from "../lib/flags";
const showBanner = useFlag("hello-banner");
  • In the cluster the deployment sets UNLEASH_URL=https://unleash.lab.holtlabs.xyz and UNLEASH_FRONTEND_TOKEN from Secret unleash-client-token (key frontendToken).
  • The SDK fetches on load and refreshes every 15 s; components re-render on change. A reload always picks up the current state.
  • With UNLEASH_URL unset (local npm run dev, tests) every flag reads false, nothing is fetched.
  • Tests: vi.mock("@unleash/proxy-client-react") and control useFlag (see src/app.test.tsx).
  • hello-banner on the home page is the platform demo flag; delete it when you don't need it.

Container

  • GIT_SHA build arg is baked in as import.meta.env.VITE_GIT_SHA.
  • nginx listens on 8080: /healthz returns 200, unknown paths fall back to index.html, /assets/* are cached as immutable, and index.html and config.js are not cached.
  • Access log: one JSON line per request in the platform logging contract (level, time, service = APP_NAME, version = GIT_SHA, req.id (echoes X-Request-Id), req.method, req.url, res.statusCode, responseTime in ms, msg: "request completed"), the same shape as the backend template. In Kibana (https://kibana.lab.holtlabs.xyz, data view kube-*): service:"${REPO_NAME}" and res.statusCode >= 400. /healthz isn't logged. nginx's own startup/error lines stay plain text (field log).

Deploy

Push to main: Forgejo CI runs lint/test/format, builds and pushes git.lab.holtlabs.xyz/nholtware/${REPO_NAME}:<sha>, and bumps the tag in the gitops repo. ArgoCD then syncs it. Manifests come from gitops templates/frontend/. The repo needs Actions secrets REGISTRY_USER, REGISTRY_TOKEN, and GITOPS_TOKEN.