From a 300-line query to colocated, maintainable building blocks
A page query that lists every field for ten components in one block turns into a merge conflict magnet with every change. GraphQL fragments break queries into named, reusable building blocks that live right next to the component consuming them, solving maintainability problems that become unavoidable as an application grows.
Table of Contents
- 1. Why large queries become unmaintainable without fragments
- 2. Fragment syntax: fragment Name on Type
- 3. Fragment colocation: fragments next to components
- 4. Combining fragments: spreads and nesting
- 5. Applying fragments to interfaces and union types
- 6. Avoiding fragment duplication
- 7. Codegen from fragments: type-safe fragment types
- 8. Fragment masking in Apollo Client and urql
- 9. Fragments compared to inline queries
- 10. Summary
- 11. FAQ
1. Why large queries become unmaintainable without fragments
A typical page query in a growing React application collects fields for every component rendered on the page, header, product card, cart widget, footer. Without GraphQL fragments, all these fields end up in a single, monolithic query at the page's entry point, often hundreds of lines long. Every change to a single component requires a change to this central query, regardless of how deeply the component is nested in the tree.
The result is merge conflicts between team members working on different components of the same page, and a query that no one fully understands anymore. GraphQL fragments solve this problem structurally: each component declares exactly the fields it needs itself, in its own named fragment. The parent query merely combines these fragments, without needing to know each individual component's field list.
2. Fragment syntax: fragment Name on Type
The basic syntax of a GraphQL fragment is refreshingly simple: fragment FragmentName on TypeName { fields }. The type name after on defines which GraphQL type the fragment applies to, the field list inside the curly braces follows exactly the same syntax as in a normal query. A fragment can then be included in any query or any other fragment expecting the same or a compatible type, using the spread syntax ...FragmentName.
fragment ProductCardFields on Product {
id
name
sku
price
thumbnailUrl
}
query ProductListPage {
products(first: 20) {
edges {
node {
...ProductCardFields
}
}
}
}
The benefit shows up immediately on reuse: ProductCardFields can be included in any query that renders a product card, whether it's a product list, a search result, or related products. When the product card gains a new field, only the fragment gets updated once, and every query that includes it automatically gets the new field, without needing to change itself.
3. Fragment colocation: fragments next to components
The fragment colocation pattern goes a step further than plain reuse: instead of collecting fragments in a central file, a component's fragment gets defined directly in the same file as the component itself. The component declares exactly which data it needs, right next to the code that renders that data. This reduces the cognitive distance between data requirement and data usage to zero.