add-web-page
Add a page, route or UI component to the web app (apps/web) or a primitive to packages/ui. Use when the user wants a new screen, a new section of settings, a form, or a reusable component on the web.
SKILL.md.claude/skills/add-web-page/SKILL.md
GitHubAdd a web page or component
The web app renders and nothing else: data comes from @repo/client hooks, text from
packages/i18n, primitives from packages/ui. .claude/rules/web.md has the rules.
A page
- Data first. If the page reads or writes anything new, the procedure and its hook
come first (the add-feature skill). Pages never call
fetch. - Module.
apps/web/src/modules/<feature>/pages/<page>.tsx(a client component when it uses hooks:"use client"), its parts incomponents/, and the page exported fromapps/web/src/modules/<feature>/index.ts. Copy the shape ofapps/web/src/modules/dashboard/. - Route.
apps/web/src/app/(app)/<route>/page.tsxfor signed-in pages (or(auth)for sign-in flows), two lines likeapps/web/src/app/(app)/dashboard/page.tsx:export const generateMetadata = pageTitle("<namespace>.<key>")andexport default <Page>. Dynamic segments usePageProps<"/route/[id]">andawait params. - Access. A new top-level signed-in path goes in
APP_PATHSinapps/web/src/lib/routes.ts, or the proxy won't send signed-out users to sign in. Links in the header or settings navigation:apps/web/src/modules/shell/orapps/web/src/app/(app)/settings/layout.tsx. - Text. Every string, including
aria-label,alt, placeholders and the page title, in everypackages/i18n/messages/*.json(the add-translation skill). Errors render ast(errorMessageKey(error), errorParams(error)). - Forms. Validate with the contract's input schema, as
apps/web/src/modules/dashboard/components/todo-list.tsxdoes; shared fields are inapps/web/src/components/form-fields.tsx. - Weight. Server components for static content; lazy-load heavy client code.
- Tests. Pure hooks and helpers as
src/**/*.test.ts. The user flow as a Playwright spec inapps/web/e2e/<feature>.spec.ts, with the helpers inapps/web/e2e/support.tsandexpectAccessible(page)on each screen.
A component
- Used by one module:
apps/web/src/modules/<feature>/components/. - Used by two modules, or a missing primitive:
packages/ui/src/components/<name>.tsx(bunx shadcn@latest add <name> --cwd packages/uifor one from the registry) with<name>.stories.tsxbeside it, aplayfunction if it's interactive (.claude/rules/ui.md).
Done when
bun run lint(render-only and boundary checks),bun run check-types,bun run testpass.bun run test:e2e --app web e2e/<feature>.spec.tspasses (stopbun devfirst), andbun run --cwd apps/web budgetafter it stays within the budget.- For a
packages/uicomponent:bun run --cwd packages/ui test:stories, andtest:visual:updatefor its new screenshots. - The
frontend-reviewerandi18n-checkeragents report nothing blocking.