Add a new page
How do I add a page that reads real data and matches the rest of the site?
The canonical page pattern — typed API client, query keys, loading/error/empty states, permission gating — so a new page looks and behaves like it shipped with the template.
Typical effort: 30 minutes
The recipe — paste this to your AI
# Task prompt — "Add a page / module" Copy the block below into your AI assistant **together with this repo** and fill in the blanks. It builds a new page the house way — same look, same security, upgrade-safe. Want to SEE what a finished add-a-page diff looks like first? Read the worked example: [`example-add-a-page.md`](./example-add-a-page.md) — a real shipped page, file by file. (The rules it follows: [`master-prompt.md`](./master-prompt.md); the patterns by name: [`../conventions.md`](../conventions.md); the look: [`../design.md`](../design.md).) --- > This is the Genesis frontend-template. Read `docs/prompts/master-prompt.md` and > follow it exactly. Then add a page for me: > > - What it shows: **___** (e.g. "my open jobs with their next visit date") > - Who sees it: **___** (operators in the app / public marketing / member portal) > - Data domain: **___** (if you're unsure, pick the closest client in `lib/api/` — > see `docs/api.md` §8 — and tell me what you chose) > - Main action on the page (one): **___** > > Do exactly this: > 1. Find the **nearest existing page** for that audience (a list screen → copy > `app/(dashboard)/orders/page.tsx`; a detail → `orders/[id]`; marketing → > `app/(marketing)/services`) and copy its shape — header comment, fetch → states → gate → > CTA, tokens, kit components. > 2. Call the API **only** through the domain client (`lib/api/<domain>.ts`), query key > `['<domain>', '<entity>', …]`. If the endpoint doesn't exist, STOP and tell me — do not > fake data or compute it client-side (see `docs/api.md` §9). > 3. Gate the action with `<Can permission="…">` (find the key on the nearest page using that > domain). Money renders via `<MoneyDisplay>`, status via `<StatusBadge>`. > 4. If it's an app module: add a nav entry in `components/shell/nav-config.tsx` with a stable > `key` + an entry in `site.config.ts` comments, and register the feature in > `docs/features.json` + `docs/features.md` (a feature isn't done without its docs entry). > 5. If the page is **public** (marketing, landing, token link — anything a visitor with no > login should see): add its path to `PUBLIC_PATHS` in `middleware.ts`, or it will redirect > to `/login`. Use a trailing slash for segment prefixes (`'/lp/'`) — a bare prefix would > accidentally expose operator pages that share the letters (the `/p` vs `/pricing` trap, > documented in the file). > 6. Operator-app chrome (nav label, shared kit strings) gets i18n keys: add > `module.component.element` keys to `lib/i18n.tsx` with BOTH an EN and an ES value > (i18n-L2/L3, documented at the top of that file). Per-tenant copy (member portal, > landing pages) stays literal — the company's own words, not the template's. > 7. If you added a **new client method** in `lib/api/`, the endpoint must also be documented > in the platform's OpenAPI spec — in the Genesis monorepo, `scripts/check-openapi-coverage.mjs` <!-- docs-truth: allow --> > fails CI on any client call missing from the spec. Standalone: confirm the endpoint exists > at `GET /api/v1/openapi.json` before wiring it. > 8. Check it against `docs/design.md` §9's test: same width, type scale, spacing, one filled > button. > 9. Prove it: `pnpm verify` green + `npm run build` green, then show me the page on a phone > viewport and desktop. --- Want a whole new *domain* (a new `lib/api/` client) rather than a page? Say so explicitly — that's an extension of the API surface and your AI should copy the smallest existing client file and confirm the endpoints with Genesis first.
Source of truth
This recipe ships inside the template as docs/prompts/add-a-page.md — this page renders that exact file. The repository is always the newest version: https://github.com/srisenmay/bringthefront-template. Clone the template and you get every recipe locally, versioned with the release you are on.