Component Decomposition

MANDATORY pattern for frontend files exceeding 600 lines. Splits complex components into focused modules with clear responsibilities.

Split Pattern

ModuleContainsReact Imports
fooService.tsPure functions, constants, typesNone
useFooState.tsState, queries, mutations, handlersYes (custom hook)
FooSection.tsxPresentational UI (props + useTranslation)Yes (minimal)
Foo.tsxOrchestrator (calls hook, composes sections)Yes

Real Examples

DomainDirectoryFiles
Document Obtainingfrontend/src/components/document-obtaining/60
Form Builderfrontend/src/components/form-builder/30
Building Componentsfrontend/src/components/building-components-redesign/18
Invoicesfrontend/src/components/invoices/16

Typical Decomposition

components/invoices/
  InvoiceList.tsx           # Orchestrator
  useInvoiceListState.ts    # Queries, mutations, handlers
  InvoiceTable.tsx          # Presentational table
  InvoiceFilters.tsx        # Filter UI section
  invoiceService.ts         # Helpers, formatters, types

Component Library

Shared UI primitives live in frontend/src/components/ui/ (102 Shadcn/Radix components). These are presentational by definition and do not need decomposition.

New components/ui/ components MUST have .stories.tsx files (CSF3, tags: ['autodocs']).

Rules

  • Orchestrator (Foo.tsx): calls the custom hook, passes data down to sections. No direct API calls.
  • Custom hook (useFooState.ts): all React Query usage, mutations, and event handlers.
  • Sections (FooSection.tsx): receive props, render UI. Only hook allowed is useTranslation.
  • Service (fooService.ts): zero React imports. Pure TypeScript functions and types.

See Also