2.7 KiB
Frontend maintainability conventions
Conditional rendering
Use logical && or the when prop on Text when JSX has only a rendered state and an empty
state. Use a ternary only when both branches render meaningful alternatives.
{
error && <Alert severity="error">{error.message}</Alert>;
}
<Text variant="description" when={Boolean(description)}>
{description}
</Text>;
ESLint rejects condition ? <Element /> : null. This keeps one-sided conditions visually
distinct from real either-or UI decisions.
Typography and feedback
Use Text from @/components/ui/text for headings, paragraphs, descriptions, labels, captions,
code, and asynchronous feedback. It owns:
- semantic HTML for each visual variant;
- the display, body, and monospace font families;
- default, muted, error, success, and warning tones;
- accessible
alertandstatuslive regions for error and feedback text; - one-sided conditional text through
when.
ESLint rejects raw paragraph and heading elements. Existing MUI Typography usages remain valid,
but new shared UI should prefer Text so semantics and design tokens do not drift.
Forms and mutations
Use the libraries already established in the application:
- React Hook Form owns field registration, touched/dirty state, and client form lifecycle.
- Zod owns form validation and inferred form value types.
- TanStack Query owns server reads and mutations, including pending/error state, cache invalidation, and retries where safe.
Do not add TanStack Form alongside React Hook Form. It would create two form conventions without removing any current dependency. Reconsider only as a deliberate repository-wide migration with benchmarks, a codemod plan, and an approved deprecation path.
File uploads are not ordinary form fields. Keep file selection and client validation in a focused component, and use a TanStack Query mutation for upload progress, errors, completion refresh, and retry state. Do not place upload orchestration in a route-sized page component.
Page state
Pages should compose focused state components instead of accumulating unrelated booleans:
- query loading, error, and empty states stay adjacent to the query result;
- mutation pending/error state belongs to the component that initiated the mutation;
- route pages coordinate sections and navigation;
- reusable sections own their interaction details;
- errors render inline with accessible feedback, with toasts reserved for cross-page outcomes.
Enforcement and rollout
The lint rules are repository-wide and the initial violations were migrated in the same change.
npm run lint, npm run build, and the Text behavior tests are required gates. Future
maintainability rules must also land with a green migration rather than a warning-only backlog.