TL;DR When I started learning Gatsby, I wanted to understand the magic of GraphQL queries passing their results back to the React component. My secondary focus was to use React functional components instead of React class components as much as possible. In this blogpost, I have summarized my findings.
Most of the information comes from Gatsby Documentation and from my personal test projects.
Page Query
Suppose we have our site metadata defined in gatsby-config.js
:
module.exports = {
siteMetadata: {
title: "My Personal website",
description: "The most wonderful description of my website goes here.",
},
}
Then in the src/pages/HomePage.js
(or index.js
) we can write the following code, which will show the description The most wonderful description of my website goes here.
on the page:
import React from 'react'
import { graphql } from 'gatsby'
const HomePage = ({data}) => {
return (
<div>
{data.site.siteMetadata.description}
</div>
)
}
export const query = graphql`
query HomePageQuery {
site {
siteMetadata {
description
}
}
}
`
export default HomePage
So, where does the HomePage
function get its data
from? Here we have the famous "Convention over Configuration" principle at work:
- the
site
field in the GraphQL query refers to thegatsby-config.js
file above; thesiteMetadata
object with thedescription
field gets exported there; - the GraphQL query itself is (and should be!) also exported:
export const query = graphql
; - as a result, the query response is passed as the
data
prop to theHomePage
function and can be destructured as{ data }
from its props.
Page Query with Query Variables
With page queries we can go a step further and add query variables to the GraphQL query. The following example shows how it can be done. It is extra interesting because it is also an example of how to query the .mdx
pages in our project.
Let us look at this query in the src/templates/blog-post.js
file:
export const query = graphql`
query MdxBlogPost($title: String) {
mdx(title: {eq: $title}) {
id
title
}
}
`
It uses $title
as a variable and searches the MDX-nodes in the project site with the title
that equals the $title
string value.
For this to work,
the
MdxBlogPost
query should havegatsby-plugin-mdx
plugin installed, andserving of the
.mdx
files should be configured viagatsby-source-filesystem
plugin in thegatsby-config.js
file.
When a page is created dynamically from the blog post template in gatsby-node.js
, you can provide an object as part of the page’s context. Keys in the context object that match up with arguments in the page query (in this case: "title"
), will be used as variables. Variables are prefaced with $
, so passing a title
property will become$title
in the query.
posts.forEach(({ node }, index) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/blog-post.js`),
// values in the context object are passed in as variables to page queries
context: {
title: node.title, // "The most wonderful description ..."
},
})
})
StaticQuery
Gatsby v2 introduced StaticQuery
, a new API that allowed not only pages but any component to retrieve data via a GraphQL query.
The table below shows the differences between page queries and the StaticQuery element.
Page Query | StaticQuery |
---|---|
works only in Pages | works in any React component including Pages |
accepts variables via pageContext | accepts no variables |
works with React.createElement | does not work with React.createElement , instead it is itself a JSX <StaticQuery /> element |
default export: the page query itself | default export: an anonymous function, which returns the StaticQuery element |
Simply said the StaticQuery
is a JSX-element with 2 parameters:
query
that holds a query definition, andrender
that holds a function returning a JSX-element
<StaticQuery query={graphql`...`} render={...} />
It handles the query result in a different way than a page query. Let us consider the following code snippet:
import React from "react"
import { StaticQuery, graphql } from "gatsby"
export default () => (
<StaticQuery
query={graphql`
query HeadingQuery {
site {
siteMetadata {
title
}
}
}
`}
render={data => (
<header>
<h1>{data.site.siteMetadata.title}</h1>
</header>
)}
/>
)
How it works is that the result of the query in the query
parameter automagically becomes the value of the render
function data
argument.
Notice the
export default () => (...)
signature similar to that of the page query except that here we have an anonymous default export.
From StaticQuery to useStaticQuery hook
React hooks are becoming more and more popular, and Gatsby has created its own: useStaticQuery hook. It is based on the StaticQuery
element and requires at least version 16.8.0 of React
and ReactDOM
packages. Let us rewrite the previous example using this hook:
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
export default () => {
const data = useStaticQuery(graphql`
query HeaderQuery {
site {
siteMetadata {
title
}
}
}
`)
return (
<header>
<h1>{data.site.siteMetadata.title}</h1>
</header>
)
}
Here, we extract data
object from the useStaticQuery
hook and then use it in the JSX-element we return from the default anonymous export.
Known Limitations of useStatic Query
As Gatsby documentation states, useStaticQuery
- does not accept variables (hence the name “static”), but can be used in any component, including pages;
- because of how queries currently work in Gatsby, only a single instance of
useStaticQuery
per file is supported.
Closing Words
We have considered three options how to use GraphQL in Gatsby:
- Page Queries to use only on pages
- StaticQuery to use in any JSX-component
- useStaticQuery hook to use in any JSX-component
For every option we have shown how the query result is passed to the corresponding presentational component.