Fragments
Fragments are one of Relay's most powerful features, enabling component composition, data masking, and efficient data fetching. This guide covers how to use fragments effectively in Solid Relay.
Basic Fragments with createFragment
Use createFragment
to read fragment data within a component:
import { createFragment } from 'solid-relay';import { graphql } from 'relay-runtime';import type { UserProfile_user$key } from "./__generated__/UserProfile_user.graphql";
function UserProfile(props: { $user: UserProfile_user$key }) { const user = createFragment( graphql` fragment UserProfile_user on User { id name email avatar } `, () => props.$user, );
return ( // user() is undefined when loading <Show when={user()}> {(user) => ( <div> <img src={user().avatar} alt={user().name} /> <h2>{user().name}</h2> <p>{user().email}</p> </div> )} </Show> );}
Fragment Composition
Compose fragments from child components in parent queries:
// UserCard.tsxexport function UserCard(props: { $user: UserCard_user$key }) { const user = createFragment( graphql` fragment UserCard_user on User { id name avatar } `, () => props.$user, );
return ( <Show when={user()}> {(user) => ( <div> <img src={user().avatar} alt={user().name} /> <span>{user().name}</span> </div> )} </Show> );}
// UserList.tsximport { UserCard } from './UserCard';
const UserListQuery = graphql` query UserListQuery { users { id ...UserCard_user # Compose fragment from UserCard } }`;
function UserList() { const data = createLazyLoadQuery(UserListQuery, {});
return ( <Show when={data()}> {(data) => ( <div> <For each={data().users}> {(user) => <UserCard $user={user} />} </For> </div> )} </Show> );}
Data Masking
Fragments provide data masking, ensuring components only access the data they declare:
function UserProfile(props: { $user: UserProfile_user$key }) { const user = createFragment( graphql` fragment UserProfile_user on User { name email # This component can't access 'avatar' or other unspecified fields } `, () => props.$user, );
onMount(() => { // ✅ This works - name is in the fragment console.log(user()?.name);
// ❌ This would cause a TypeScript error - avatar not declared // console.log(user()?.avatar); });
return ( <Show when={user}> {(user) => <div>{user().name}</div>} </Show> );}
Refetchable Fragments
Make fragments refetchable to update their data independently:
import { createRefetchableFragment } from 'solid-relay';
function UserProfile(props: { $user: UserProfile_user$key }) { const [user, refetch] = createRefetchableFragment( graphql` fragment UserProfile_user on User @refetchable(queryName: "UserProfileRefetchQuery") { id name email posts(first: 5) { edges { node { id title } } } } `, () => props.$user, );
const handleRefresh = () => { refetch({}); };
return ( <Show when={user()}> {(user) => ( <div> <h2>{user().name}</h2> <button onClick={handleRefresh}>Refresh Profile</button> <div> <For each={user().posts.edges}> {(edge) => ( <div>{edge.node.title}</div> )} </For> </div> </div> )} </Show> );}
Pagination Fragments
Use pagination fragments for efficient list loading:
import { createPaginationFragment } from 'solid-relay';
function UserPosts(props: { $user: UserPosts_user$key }) { const data = createPaginationFragment( graphql` fragment UserPosts_user on User @argumentDefinitions( count: { type: "Int!", defaultValue: 10 } cursor: { type: "String" } ) @refetchable(queryName: "UserPostsPaginationQuery") { posts( first: $count after: $cursor ) @connection(key: "UserPosts_posts") { edges { node { id title content createdAt } } } pageInfo { hasNextPage endCursor } } `, () => props.$user );
const handleLoadMore = () => { if (data.hasNext && !data.isLoadingNext) { data.loadNext(10); // Load 10 more items } };
return ( <Show when={data()}> {(user) => ( <div> <For each={user().posts.edges}> {(edge) => ( <article> <h3>{edge.node.title}</h3> <p>{edge.node.content}</p> <small>{edge.node.createdAt}</small> </article> )} </For>
<Show when={data.hasNext}> <button onClick={handleLoadMore} disabled={data.isLoadingNext} > {data.isLoadingNext ? 'Loading...' : 'Load More'} </button> </Show> </div> )} </Show> );}
Conditional Fragments
Use conditional fragments based on GraphQL types:
function FeedItem(props: { $item: FeedItem_item$key }) { const item = createFragment( graphql` fragment FeedItem_item on FeedItem { __typename ... on Post { id title content author { name } } ... on Comment { id text author { name } post { title } } } `, () => props.$item );
return ( <Show when={item()}> {(item) => { const data = item(); switch (data.__typename) { case 'Post': return <PostItem post={data} />; case 'Comment': return <CommentItem comment={data} />; } }} </Show> );}
Fragment Variables
Pass variables to fragments for dynamic behavior:
fragment UserPosts_user on User @argumentDefinitions( count: { type: "Int", defaultValue: 5 } includeContent: { type: "Boolean", defaultValue: false } ) { posts(first: $count) { edges { node { id title content @include(if: $includeContent) } } }}
// Usage in parent queryconst UserQuery = graphql` query UserQuery($userId: ID!, $showContent: Boolean!) { user(id: $userId) { ...UserPosts_user @arguments( count: 10, includeContent: $showContent, ) } }`;
Best Practices
Colocate Fragments with Components
Keep fragments close to the components that use them:
export function UserProfile(props: { $user: UserProfile_user$key }) { // component implementation const user = createFragment( graphql` fragment UserProfile_user on User { # fragment definition } `, () => props.$user, );}
Use Descriptive Fragment Names
Follow the naming convention ComponentName_propName
:
# ✅ Goodfragment UserProfile_user on User { ... }
# ❌ Badfragment UserFragment on User { ... }
Compose Fragments Hierarchically
Build complex UIs by composing fragments from child components:
fragment UserPage_user on User { ...UserProfile_user ...UserPosts_user ...UserFriends_user}
Use TypeScript for Type Safety
Import generated types for better development experience:
import type { UserProfile_user$key } from './__generated__/UserProfile_user.graphql';
function UserProfile(props: { $user: UserProfile_user$key }) { const user = createFragment( graphql` fragment UserProfile_user on User { # fragment definition } `, () => props.$user, ); // TypeScript knows the exact shape of user()}
Prefer Fragments Over Inline Fields
Use fragments even for simple cases to improve maintainability:
# ✅ Good - using fragmentfragment UserName_user on User { name}
# ❌ Avoid - inline fields in queriesquery SomeQuery { user { name # Better to use fragment }}
Last updated: 8/13/25, 1:20 AM
Edit this page on GitHub