skip to content

What is fragment colocation in a GraphQL client, and what problem does it solve?

level: juniorimportance: must knowfreq 58%

answer

  1. Where the data requirement is written
  2. Beside the view that renders it
  3. Parents compose children upward
  4. A client convention, not the specification
  5. One screen, one request

basics

~10 s

Fragment colocation is a client convention: every view declares the fields it needs as its own GraphQL fragment, stored beside that view, and a parent composes those fragments upward into one operation per screen.

solid answer

~40 s

Colocation is a **client-side convention**, not anything the GraphQL specification defines - the specification knows documents, operations and fragments, and has never heard of a view or a screen. The rule is: each view that renders data owns a fragment declaring exactly the fields it renders, written in the same file as the view; the parent view spreads its children's fragments into its own fragment; the screen root spreads those into a single operation and sends it once. The payoff is local reasoning. Adding a field to a view is a one-file change, deleting the view deletes its fields from the request, and no one hand-maintains a screen-wide query that slowly drifts from what the UI actually reads. It also means one request per screen instead of one per view.

code

graphql · 27 lines
graphql
fragment OrderStatusChip_order on Order {
  status
  placedAt
}

fragment CourierCard_order on Order {
  courier {
    displayName
    etaMinutes
  }
}

fragment OrderLines_order on Order {
  lines {
    quantity
    menuItem { name priceCents }
  }
}

query OrderTrackingScreen($orderId: ID!) {
  order(id: $orderId) {
    id
    ...OrderStatusChip_order
    ...CourierCard_order
    ...OrderLines_order
  }
}

go deeper

for a junior

Be ready to say what colocation is in one sentence and why it beats a hand-written screen query: the fields live beside the view that renders them, and the parent composes them into one operation.

for a middle

An interviewer expects the mechanics: composition runs upward through every ancestor, duplicate fields merge, the response never mentions fragment names, and nothing here is enforced by the specification.

for a senior

Show the operational payoff and the limits - one request per screen, self-correcting over-fetch for fields that have an owner, and the fact that invisible views still ship their fields unless the spread is conditioned.

for a principal

Own it as a codebase policy: who may share a fragment, how naming stays collision-free across teams, and what enforcement (build-time composition checks) makes the convention hold when nobody is reviewing it.

## The convention, stated plainly GraphQL's specification defines an executable document: operation definitions and fragment definitions, sent to a server, validated against a schema, executed. It contains no notion of a view, a component, a screen, a file or a folder. **Fragment colocation is a convention layered on top of fragments by client codebases**, and any answer that calls it "part of GraphQL" is wrong on the first sentence. The convention has three parts: 1. **Every view that renders server data declares a fragment** listing exactly the fields it renders - no more, no less. 2. **The fragment lives next to the view**, usually in the same file, so the data requirement and the markup that consumes it are edited together. 3. **Composition goes upward.** A parent view spreads each child's fragment inside its own; the screen root spreads the top-level ones into a single operation and issues that one request. In a restaurant ordering graph, the order-tracking screen renders a status chip, a courier card and the line items: ```graphql fragment OrderStatusChip_order on Order { status placedAt } fragment OrderLines_order on Order { lines { quantity menuItem { name priceCents } } } query OrderTrackingScreen($orderId: ID!) { order(id: $orderId) { id ...OrderStatusChip_order ...OrderLines_order } } ``` The `Owner_propName` naming - the view that owns the fragment, then the thing it is handed - is a widespread house style, not a rule. The specification only requires that fragment names be unique inside the one document being sent, which is exactly why a mechanical prefix is worth having once 14 views on one screen each contribute a fragment. ## What it actually buys **Local reasoning.** The question "what does this view need from the server?" has one answer, in the file you are already looking at. Without colocation the answer lives in a screen-level query somebody else wrote, and you learn what your view reads by running it. **Deletion safety.** Remove the view, remove its fragment, and its fields leave the request automatically. The classic failure of hand-written screen queries is the opposite: fields accumulate, nobody dares delete them, and the screen fetches columns no pixel has depended on for a year. Colocation makes over-fetching self-correcting for fields that had an owner - proving a field has *no* consumer at all is a different exercise and needs server-side usage evidence. **One-file change for one-file features.** Adding a delivery ETA to the courier card touches the courier card. Under a central query it touches the courier card and the screen query, and the second edit is the one people forget. **Findability.** A schema change to `MenuItem` has a mechanical blast radius: every fragment whose type condition is `MenuItem`. **One request per screen.** Because the whole tree's needs are known statically, before rendering, the screen can ask for everything at once instead of discovering its needs level by level as components mount. ## What it costs **Threading discipline.** Composition only works if every ancestor spreads its children. Miss one link and the field never reaches the server; the view then renders with the data absent, which is a runtime surprise rather than a compile error unless a typed client generator is enforcing it for you. **Invisible views still cost bytes.** A fragment belonging to a modal that the user never opens is part of the document all the same, unless the spread is put behind a condition. **It is not a fragment library.** Moving every fragment into one shared `fragments.graphql` file and importing them is the opposite of colocation: the fragment is no longer owned by anything, so it grows fields for callers that no longer exist. Sharing one fragment between two views that genuinely render the same block is fine; sharing it because it is convenient is how the drift comes back. ## What it is not It is not a caching mechanism and not a server optimisation. Spreading a fragment resolves precisely the fields it lists, exactly as if they had been typed inline; there is no reuse across requests, no reduction in resolver calls, and no key in the response named after the fragment - the fields land flattened under their own response keys. The savings are round trips and human maintenance, nothing else. One measured shape, from an order-tracking screen with 14 data-bound views: 9 separate operations issued as components mounted, versus one composed operation selecting 118 fields across 6 levels. The composed document is bigger than any one of the nine. It is still the cheaper screen, because the cost that mattered was the number of sequential round trips, not the size of the request.

  • If two views on the same screen both select `menuItem.priceCents`, is it fetched twice?
    No. Identical fields selected on the same object at the same response position merge into one entry, so the field is resolved once and appears once in the response. That is why duplication between colocated fragments is cheap: the overlap collapses at execution, and the only real cost is a slightly larger request document.
  • What happens if a parent forgets to spread a child's fragment?
    The server never sees those fields, so they are simply absent from the response and the child renders with missing data. Nothing fails validation - the document is perfectly legal, it just asks for less than the UI needs. Catching this at build time is the job of a typed client generator, not of GraphQL itself.
  • Does colocation reduce the work the server does?
    Not by itself. The same fields are requested and the same resolvers run. What changes is that they run inside one request instead of several, so the request pays authentication, parsing and validation once, and any per-request batching the server does now spans the whole screen instead of a fraction of it.

It is a packing list per traveller rather than one list written by whoever books the trip: each person writes what they need beside their own bag, and the organiser staples the lists together into a single order.

saying these in an interview costs you the question

  • Says colocation is defined by the GraphQL specification
  • Expects the response to nest data under fragment names
  • Claims colocation reduces the number of resolver calls
  • Calls one shared fragments file colocation
  • Thinks a fragment is scoped to the view's file

context