Files

59 lines
3.6 KiB
Markdown

# Web App
The public website: a Next.js App Router app that server-renders the pages editors publish in [Web Manager](../../docs/core/web-manager/Readme.md). Everything visitors see comes from the backend's public API. Package: `@crux-kit/web-app`.
Architecture — request flow, layers, backend boundary, rendering registries, i18n, SEO — is documented in the [Next.js web architecture](../../docs/architecture/web/Readme.md).
| Tech | Version |
| ---------- | ---------------------------------------- |
| Next.js | 16 (App Router, React Server Components) |
| React | 19 |
| next-intl | 4 — locale routing and translations |
| Tailwind | 4 — CSS-first, no config file |
| TypeScript | 5.9, `strict`, `noEmit` |
| Vitest | 4 — node environment, no DOM tests |
## Configuration
| Variable | Required | Purpose |
| ------------------- | ----------------------- | --------------------------------------------------------------------- |
| `BACKEND_API_URL` | yes — throws without it | Backend origin. No `/web-manager/public` suffix. |
| `SITE_URL` | in production | Canonical origin. Missing → `http://localhost:3000` + a logged error. |
| `REVALIDATE_SECRET` | for purges to work | Must equal the backend's `WEB_REVALIDATE_SECRET`. |
Copy `.env.example` to `.env`. In Docker the backend URL must use the compose service name (`http://backend:3333`), not `localhost`.
## Development
```bash
npm run dev:web
```
```bash
npm run test:web
```
```bash
npm run build:web
```
`build` runs `tsc --noEmit` before `next build`, so a type error fails the build.
Under Docker the `web` service runs `npm run dev` on port 3000 with `.next` on an anonymous volume. That volume survives rebuilds and is the usual cause of a stale bundle — recreate with `docker compose up -d --force-recreate --renew-anon-volumes web`.
Path alias: `#*` maps to the app root. `vitest.config.ts` mirrors it by hand (Vitest ignores `tsconfig` paths) and stubs `server-only`.
## Tests
`tests/` covers the pure logic (normalization, cache tags, registries, metadata, sitemap, site URL, structured data) in a node environment — component rendering is not covered and would need `@testing-library/react`. `registry.test.ts` keeps a hand-mirrored list of the backend's block and page types and asserts the registries match exactly — the tripwire for a block the CMS can publish but the site cannot draw.
## Adding Things
| Task | Where |
| ---------------------------- | --------------------------------------------------------------------------------------------------------- |
| A new CMS block or page type | Spans three apps — follow [Web Manager → Extending](../../docs/core/web-manager/integration.md#extending) |
| A non-CMS route | A folder under `app/[locale]/`; keep the logic in `web-runtime/` |
| A shared UI primitive | `shared/ui/` — content-agnostic only |
| A new locale | Add it to `site.config.ts` and add the five `i18n/translations/*/<locale>.json` files |
| A new backend read | A method on `WebRuntimeAPI` with its tags, plus a normalizer for the response |