React 19 frontend for the Budget Buddy personal finance app. Features transaction and category management, a monthly spending dashboard with expenses by category, and real-time feedback with a global notification system.
- Node.js 20+
- pnpm
- A running budget-buddy-api instance (or
VITE_API_URLpointed elsewhere) - GitHub Packages read access for
@budget-buddy-org/budget-buddy-contracts
# One-time: add your GitHub token to ~/.npmrc
echo "//npm.pkg.github.com/:_authToken=ghp_<your-token>" >> ~/.npmrc
pnpm install
cp .env.example .env.local # set VITE_API_URL and OIDC vars
pnpm dev # http://localhost:5173Required OIDC environment variables:
VITE_OIDC_ISSUER— OIDC issuer URL (e.g. your Keycloak or Zitadel issuer)VITE_OIDC_CLIENT_ID— frontend SPA client IDVITE_OIDC_SCOPES— (optional) space-separated extra scopes appended to the OIDC protocol defaults (openid profile email offline_access). The defaults are always requested and cannot be disabled — use this only for IdP-specific audience or API resource scopes (e.g.urn:zitadel:iam:org:project:id:my-api:aud).VITE_OIDC_USER_MANAGEMENT_URL— (optional) URL for the identity provider's user management page (e.g. Account Center or Console).
pnpm dev # Vite dev server
pnpm build # type-check + production build
pnpm lint # ESLint + Biome lint
pnpm format # Biome auto-format
pnpm test # Vitest (run once)
pnpm test:watch # Vitest (watch mode)
pnpm test:a11y # Run accessibility tests
pnpm test:coverage # HTML coverage report in coverage/
pnpm type-check # tsc --noEmit
pnpm preview # Preview production build locally- Vite + React 19 + TypeScript (strict)
- TanStack Router v1 — file-based routing
- TanStack Query v5 — server state, caching, mutations
- react-oidc-context — OIDC authentication
- Zustand v5 — theme and appearance preferences
- shadcn/ui (Radix UI + Tailwind v4)
- ESLint + Biome — lint + format
- Vitest + Testing Library — unit tests
Multi-stage build: base → deps (pnpm install with BuildKit secret + pnpm store cache) → builder (Vite build) → production (nginx:1.29-alpine).
Runtime configuration is injected via docker-entrypoint.sh from environment variables using envsubst.
# Build image — GITHUB_TOKEN passed as a BuildKit secret (never stored in any layer)
docker build \
--secret id=github_token,env=GITHUB_TOKEN \
-t budget-buddy-web-app .
# Run locally with Docker Compose (app available at http://localhost:3000)
# VITE_API_URL and OIDC vars are injected into the container at runtime
GITHUB_TOKEN=$(gh auth token) \
VITE_API_URL=http://localhost:8080 \
VITE_OIDC_ISSUER=https://issuer.example.com \
VITE_OIDC_CLIENT_ID=web-client \
VITE_OIDC_USER_MANAGEMENT_URL=https://issuer.example.com/ui/console/users/me \
VITE_OIDC_JWT_AUD=test-audience \
docker compose up --buildVITE_API_URL, VITE_OIDC_ISSUER, VITE_OIDC_CLIENT_ID, and VITE_OIDC_USER_MANAGEMENT_URL are injected into the container at runtime — no need to rebuild the image when these values change.
Pre-built images are published to ghcr.io/budget-buddy-org/budget-buddy-web-app on every merge to main and every GitHub Release.
The app supports two deployment targets in parallel — neither path blocks the other.
Static dist/ is served from Cloudflare's edge. Runtime configuration is served by a Pages Function (functions/config.json.ts) that reads CF environment variables — same shape as the Docker envsubst model, no rebuild needed when env values change.
- Required CF Pages env vars (set per Production / Preview environment in the dashboard):
VITE_API_URL,VITE_OIDC_ISSUER,VITE_OIDC_CLIENT_ID,VITE_OIDC_SCOPES,VITE_OIDC_USER_MANAGEMENT_URL. - HTTP headers and SPA fallback come from public/_headers and public/_redirects.
- The Pages Function is exercised on CF preview deployments; locally,
loadConfig()falls back toimport.meta.env, so the Function isn't run duringvite dev/vite preview.
Multi-stage Docker image published to ghcr.io/budget-buddy-org/budget-buddy-web-app, served by nginx with runtime config injected by docker/docker-entrypoint.sh. Deployed via ../budget-buddy-deployment/deploy.sh. See docs/DEPLOYMENT.md for the Pi-side environment setup.
See CLAUDE.md for detailed guidance on the project structure, auth flow, adding features, and conventions.
The app consumes @budget-buddy-org/budget-buddy-contracts for typed API clients and model types. Currency amounts are stored as minor units (1299 = €12.99).