Extract logic to hooks/sub-components when approaching limits
Component organization:
Use sub-components over render functions
One component = one concern
Compose from smaller pieces
π¨ ALL HOOKS MUST BE UNIT TESTED π¨
File structure:
GraphQL files co-located with components
Hooks co-located with components
Shared components in components/, not shared/
Sub-components Over Render Functions
Use sub-components (e.g., <Icon />) instead of render functions (e.g., getIcon()).Benefits:
Better readability (step-down rule)
Improved performance (no new functions on each render)
Easier testing
Props: IDs Over Objects
For GraphQL data, pass the fragment reference (FragmentType), not an id and not the raw object. An id makes the child fetch for itself, which means a query below the query root. A raw result object ties the child to the parent's query. The child declares a Message_message fragment, takes message: FragmentType<typeof MessageMessageFragmentDoc>, and reads it with useFragment. See GraphQL Fragments, and Testing a Fragment-Owning Component on Its Own for how to test such a component.
File Size Limits
React component files must not exceed 500 lines.Extract logic to hooks, sub-components, or utilities when approaching this limit.
File size guidance:
React components: Max 500 lines
Hooks: Max 200 lines
Utilities: Max 200 lines
File Organization
GraphQL files MUST be co-located with the component/hook that uses them (GraphQL near operation pattern).
Feature Folder Structure
GraphQL File Naming
Queries: queries.graphql (any number of queries)
Mutations: mutations.graphql
Subscriptions: subscriptions.graphql
Fragments: fragments.graphql, with one fragment per component named Component_prop
Only the route, or the feature's top-level view or page, calls useQuery. A component below it declares a fragment in its own fragments.graphql and reads it with useFragment. See the Fragments guide.
Module Structure
For larger features:
Key principles:
GraphQL near operation: Files live next to the component/hook using them
No separate graphql/ folder: Don't create module-level graphql/ folders
Hooks co-located: Feature-specific hooks live with components
Sub-components: Live in same folder as parent
Single Responsibility
One component/hook = one concern.
The UserDashboard query selects id on user and spreads ...UserProfileSection_user and ...PostsSection_user. A section reads its data from its fragment, so it never calls useQuery itself.
Composition Over Inheritance
Favor small, composable components over "god" components.
Error Boundaries
Use Error Boundaries to protect pages from crashes. Always log errors to Datadog.
Error Handling in Hooks
A hook returns an Apollo error for the component to show. It doesn't report it: the shell's Apollo client reports a network or transport failure once, and the backend has already reported an error it returned in the response (Who reports an error).
Testing
π¨ ALL HOOKS MUST BE UNIT TESTED π¨See Unit Testing for comprehensive testing patterns.