Gatsby GraphQL — Querying Data with GraphQL in Gatsby
In this tutorial, you will learn about Gatsby Graphql. We cover key concepts, practical examples, and best practices to help you master this topic.
Gatsby GraphQL provides a unified query layer for sourcing, transforming, and querying data from all connected plugins and sources.
What You'll Learn
By the end of this tutorial, you'll understand Gatsby's GraphQL data layer, how to write page queries and static queries, use GraphQL fragments, filter and sort data, and optimize query performance.
Why It Matters
Gatsby's GraphQL layer is the central nervous system of your site. Every piece of content — from blog posts to images to CMS data — flows through it. Efficient querying directly impacts build speed and site performance.
Real-World Use
A multi-language documentation site queries content from 2000 markdown files, filters by language and category, orders by version, and retrieves featured images. A single GraphQL query replaces what would require multiple API calls.
Query Architecture
graph TD
A[Gatsby GraphQL
Schema] --> B[Node Types]
A --> C[Connections]
B --> D[MarkdownRemark]
B --> E[ContentfulProduct]
B --> F[WordPressPost]
B --> G[ImageSharp]
C --> H[allMarkdownRemark]
C --> I[allContentfulProduct]
C --> J[allWordPressPost]
D --> K[Page Queries]
E --> K
F --> K
G --> L[Static Queries]
K --> M[Component Props]
L --> N[useStaticQuery Hook]
style A fill:#4a90d9,color:#fff
style K fill:#e67e22,color:#fff
style L fill:#27ae60,color:#fff
Page Queries
// src/templates/blog-post.js
import React from 'react';
import { graphql } from 'gatsby';
export default function BlogPost({ data }) {
const { markdownRemark } = data;
const { frontmatter, html, fields } = markdownRemark;
return (
<article>
<h1>{frontmatter.title}</h1>
<div className="meta">
<span>{frontmatter.date}</span>
<span>{fields.readingTime.readingTime} min read</span>
<div className="tags">
{frontmatter.tags?.map(tag => (
<span key={tag} className="tag">{tag}</span>
))}
</div>
</div>
<div dangerouslySetInnerHTML={{ __html: html }} />
</article>
);
}
// Page query (runs at build time, tied to page)
export const query = graphql`
query BlogPostBySlug($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
html
frontmatter {
title
date(formatString: "MMMM DD, YYYY")
tags
}
fields {
readingTime {
readingTime
}
}
}
}
`;
Static Queries
// src/components/header.js
import React from 'react';
import { useStaticQuery, graphql, Link } from 'gatsby';
export default function Header() {
// Static query (available in any component, no context needed)
const data = useStaticQuery(graphql`
query SiteMetadata {
site {
siteMetadata {
title
description
}
}
allNavigationYaml {
nodes {
name
path
}
}
}
`);
const { title } = data.site.siteMetadata;
const navItems = data.allNavigationYaml.nodes;
return (
<header>
<Link to="/" className="logo">{title}</Link>
<nav>
{navItems.map(item => (
<Link key={item.path} to={item.path}>
{item.name}
</Link>
))}
</nav>
</header>
);
}
Advanced Filtering and Sorting
// src/pages/blog.js — Advanced query patterns
export const query = graphql`
query BlogList(
$category: String = "all"
$tag: String = ""
$sortField: String = "date"
$sortOrder: SortOrderEnum = DESC
$limit: Int = 10
$skip: Int = 0
) {
# Filter by category
featuredPost: markdownRemark(
frontmatter: {
featured: { eq: true }
category: { eq: $category }
}
) {
frontmatter {
title
excerpt
slug
}
}
# Paginated, sorted posts with tag filter
allMarkdownRemark(
filter: {
frontmatter: {
category: { eq: $category }
tags: { in: [$tag] }
draft: { ne: true }
}
}
sort: { frontmatter: { date: $sortOrder } }
limit: $limit
skip: $skip
) {
totalCount
nodes {
id
frontmatter {
title
date(formatString: "MMMM DD, YYYY")
slug
tags
}
excerpt(pruneLength: 200)
fields {
slug
}
}
}
}
`;
GraphQL Fragments
// fragments/post-fields.js
// Reusable fragment for post metadata
import { graphql } from 'gatsby';
export const PostFields = graphql`
fragment PostFields on MarkdownRemark {
id
frontmatter {
title
date(formatString: "MMMM DD, YYYY")
slug
tags
author
}
fields {
slug
readingTime {
readingTime
}
}
excerpt(pruneLength: 200)
}
`;
// Usage in a page query
export const query = graphql`
query BlogList {
allMarkdownRemark(
sort: { frontmatter: { date: DESC } }
limit: 10
) {
nodes {
...PostFields
# Fragment includes all fields above
# Add page-specific fields here
frontmatter {
featuredImage {
publicURL
}
}
}
}
}
`;
Common Mistakes
- Not using aliases for ambiguous queries. When querying the same type twice, use aliases (featuredPost, latestPost) to avoid conflicts.
- Over-fetching nested data. Request only the fields you render. Each extra field increases build time and memory usage.
- Forgetting to use formatString for dates. Raw date strings are unformatted. Always specify the output format you need.
- Using page queries in non-page components. Page queries only work in top-level page files. Use useStaticQuery for components.
- Not filtering out draft content. Drafts should be filtered with
draft: { ne: true }. Otherwise they're included in production builds.
Practice Questions
- What is the difference between a page query and a static query in Gatsby?
- How do you pass variables to a Gatsby page query?
- What are GraphQL fragments and why are they useful?
- How do you filter and sort data in Gatsby GraphQL queries?
- Can you use the same GraphQL query in multiple components?
Challenge: Build a blog with advanced GraphQL queries: implement pagination using skip/limit, filter by category and tags, use fragments for consistent post fields, and display reading time from computed fields.
FAQ
Mini Project
Create a Gatsby site with advanced GraphQL queries: 10+ markdown posts with categories and tags, a filtered blog listing page, pagination with skip/limit, reusable GraphQL fragments, and a featured post section using query aliases.
What's Next
You've mastered Gatsby GraphQL. Now explore an alternative SSG approach with Hugo — the world's fastest static site generator built in Go.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro