As apps grow, the same groups of fields appear in query after query: every card that shows a user wants id, name, and avatar. Copy-pasting those selections invites drift and bugs. GraphQL solves this with fragments: named, typed, reusable selection sets. This lesson covers fragment syntax, inline fragments, and the colocation pattern that modern clients are built around.
A fragment declares a selection set on a specific type, and is spread into queries with ...:
fragment UserCard on User {
id
name
avatar(size: 64)
}
query Dashboard {
me { ...UserCard }
post(id: "7") {
title
author { ...UserCard }
}
}The server expands each spread as if the fields were written inline. Change UserCard once and every usage updates. Fragments can spread other fragments, but cycles are forbidden and validated against, and every declared fragment must be used.
The on User type condition matters: spreading UserCard inside a Post selection is a validation error caught before execution. Fragments are not text templates; they participate fully in type checking, which is why they beat string concatenation for composing queries.
Sometimes you need a one-off type condition without naming a fragment, most commonly when a field returns an interface or union (fully covered next lesson):
query Search {
search(term: "graphql") {
... on Post { title }
... on User { name }
}
}The ... on Type { } syntax applies those fields only when the runtime object is of that type. Add __typename to the selection so the client can tell which case it received.
The deepest use of fragments is architectural. In React codebases using Relay or Apollo, each component declares a fragment describing exactly the data it renders, colocated in the same file:
// UserCard.jsx
export const USER_CARD_FRAGMENT = gql`
fragment UserCard on User {
id
name
avatar(size: 64)
}
`;Pages then compose their query from the fragments of the components they render. The benefits compound:
undefined props).Normalized client caches key objects by __typename plus id. Because fragments encourage consistently selecting id everywhere an entity appears, cache updates after mutations propagate to every component automatically. This is a practical reason to include id in every fragment even if the UI never displays it.
...Name, and fully type-checked.... on Type) branch on runtime type, essential for interfaces and unions.id (and rely on __typename) so normalized caches work for you.Next lesson: Interfaces, Unions and Enums, the type-system features that make inline fragments shine.