Frontend
Adding a frontend feature
Build a React feature with typed API calls, translated forms and the shared authentication flow.
On this page
Keep a feature’s API calls, wire types, hooks, schemas and UI together under frontend/src/features/<slice>/.
Start with Frontend overview for the stack and routing conventions.
Wire types and API calls
Define camelCase request and response types from the endpoint’s transport records. Commands can include values supplied by the server, such as the current user ID, so their fields are not automatically request fields. Preserve nullable response values and distinguish form input values from the request sent over the wire.
Use frontend/src/features/api-keys/types.ts and api.ts as the current example, together with
api/src/Slicekit.Api/Endpoints/v1/ApiKeys/CreateApiKeyEndpoint.cs. For downloads, use the data-export slice.
All calls go through the API client: JSON uses apiFetch, downloads use apiBlob, and uploads
pass FormData without setting a multipart Content-Type manually.
Run node scripts/check-contracts.mjs from the repository root after changing a wrapper or wire type. It checks
those application types against a fresh API export; no generated client needs updating.
Form schemas and errors
Create Zod schemas as factories taking i18next’s TFunction, and rebuild them when the locale changes. Use keys
that exist in both locales and provide every interpolation value the message requires. Follow
Forms and Internationalisation.
Read the current API-key schemas.ts and components/CreateApiKeyDialog.tsx for the working form and request
conversion. Do not copy API-key constraints into a feature that does not share them: a key needs at least one permission,
and credential creation requires step-up before submission. Keep password and re-auth proof fields in
the request when the operation needs them. See Step-up re-authentication.
Use React Hook Form with showFormError from frontend/src/shared/ui/form-error.ts. It applies server field errors
and shows a translated fallback when no field error was handled. A catch block that only calls applyToForm can
silently swallow a non-field API error. Keep challenge and re-authentication transitions in the owning feature
before applying the ordinary form-error helper.
Server state and UI
Use TanStack Query for server data. Include pagination, filters and other request inputs in query keys, export
keys for related mutations, and invalidate the data a successful mutation changes. The API-key hooks.ts shows
list keys and invalidation. Reuse meQueryKey from features/account/hooks.ts for current-user invalidation and
currentUserQueryOptions from features/account/queries.ts when fetching that same data outside a hook.
Render failed queries as retryable errors, including failed refetches with old data still cached. A failure does
not establish that a list is empty or a credential is unlinked. Keep search and filter controls available while
the failed result area shows LoadError.
Use translated labels, shared confirmation dialogs for irreversible actions and the existing step-up flow for credential or identity changes. Avoid duplicating those flows inside a new feature.
Routes and permissions
Create or extend a file under frontend/src/routes/. Underscore-prefixed segments are pathless layouts.
_app.settings.api-keys.tsx shows route composition with feature components. Vite generates routeTree.gen.ts
during development and builds; never edit the generated file.
Place navigation in the relevant workflow, such as the settings layout. Mirror new permissions in
shared/auth/permissions.ts and follow Frontend permissions when hiding actions or
navigation. The server still enforces authorization on every request.
Verify
- Run the contract checker after changing wire types or endpoint wrappers.
- From
frontend/, runpnpm typecheck,pnpm lintandpnpm test. - Exercise valid and invalid submissions, non-field API failures, language changes and permission-denied behavior.
- Manually verify cookies, re-authentication, redirects and interactive state transitions when changing those flows.