Published 18 Nov 2020
The use of GraphQL in Gatsby brings many advantages, but it can lead to duplication of query fields across pages and templates. This is a quick guide on a pattern for using fragments inside your React components to avoid duplication. It is one of those things that just seems like an obviously strong pattern, but I couldn't find it clearly advocated anywhere. It is mentioned on the Gatsby documentation but the example isn't clear and does not explain the benefits well.
First let me set up the problem with an example from this website. The code is available on a GitHub repo. I have an article summary component which is used in multiple places (the index page, journal template and tag template). This summary needs fields from the article type and the standard pattern that I see is for each page/template to have those fields in their queries. Maybe this is because the philosophy behind GraphQL is very page-centric in that you request all of the information you need to render a page in a single request. However, when all of these pages are passing the data to the same component this seems like poor design. If I refactor the content type in Kontent then I have to update the code in multiple places. I was aware of the concept of GraphQL fragments, but nothing explaining exactly how to use them with Kontent on Gatsby. The component knows what fields it needs, so it is a much stronger design to allow the component to manage this responsibility itself, making the dependency clearer and easier to manage. Here is how to implement this approach.
First we need to create a GraphQL fragment in the component itself to capture the fields that are required:
import { graphql } from 'gatsby';
const ArticleSummary = ({ article }) => (
<div className="featured-article">
...
</div>
);
export const pageQuery = graphql`
fragment ArticleSummaryInfo on kontent_item_article {
elements {
title {
value
}
summary {
value
}
article_url_slug {
value
}
article_topics {
value {
name
codename
}
}
publish_date {
value
}
}
}
`;
export default ArticleSummary;
It does not matter what the const is called, Gatsby will pick up the fragment regardless. What does matter is the fragment name, ArticleSummaryInfo
, as this is what will be used to reference it in the pages and templates that use the component. The naming convention is your choice, whatever makes sense to you. The next bit, the kontent_item_article
, needs to be the name of the type from which the fields are taken in the GraphQL schema. In Kontent you can easily see this by looking at the internal field for the type in the GraphQL Explorer. In my case:
query myQuery {
kontentItemArticle {
internal {
type
}
}
}
You can see the pattern is easy to guess and also appears under modular content on rich text fields where you are using the type as a Kontent component. Once you have the type name you can then capture the element and its fields that are necessary for the component.
Now that we have this fragment, it is a simple matter to use it in the consuming page as it will be automagically available via Gatsby. Here is it used on the home page:
export const pageQuery = graphql`
query IndexQuery {
kontentItemHome {
elements {
featured_articles {
value {
... on kontent_item_article {
...ArticleSummaryInfo
}
}
}
introduction {
value
}
meta_data__keywords {
value
}
meta_data__description {
value
}
}
system {
name
}
}
}
`;
And there you have it, that simple. If you follow this pattern for all of your components dependent on content from Kontent then you will be able to keep the GraphQL nicely organised and duplication free alongside the consuming code. Refactoring the Kontent schema becomes a much quicker and smoother process.