Access dynamic data from GraphQL in a component that only has access to a Static Query

July 25, 2020

A component in gatsby only has access to a static query by which it can access data in the siteMetadata section of gatsby-config.js or any hard coded query without passing in a variable. How do you pass dynamic data from GraphQL to this component?

We will learn how to do this by adding keywords to the SEO component of every blog post. These keywords reside in the frontmatter of each blog post.

File content/blog/dir-which-becomes-slug-of-blog-post/index.mdx:

---
title: title of post
date: 'YYYY-MM-DD'
keywords: [key1, key2, key3]
---

Here is how my project is structured:

.-content
. |---assets
. | |---image1.jpg
. |---blog
. |---dir-which-becomes-slug-of-blog-post
. |-- index.mdx <--- has frontmatter 'keywords' that get put into graphql
.-src
. |---components
. | |---seo.js <--- SEO component only has access to a StaticQuery,
. | accessing siteMetadata from gatsby-config.js or
. | a hard coded query in 'allMdx'
. | <--- I want access to 'keywords' here for specific blog posts
. | to plug it into the <head> tag.
. |---templates
. | |---blog-post.js <--- createPages API in gatsby-node.js calls
. | blog-post.js for every .mdx file
. | Can get a variable passed into graphql query
. | from gatsby-node.js
. |---pages
. |---index.js <---LANDING PAGE IS HERE
. |---blogindex.js <---Page showing list of all blog posts
.-gatsby-config.js
.-gatsby-node.js

The SEO component in src/components/seo.js returns JSX with key=value pairs that are plugged into the <head> html tag of the JSX. React-helmet is a library that adds these key-value pairs to the <head> of your html document.

This is simplified version of it for illustrative purposes:

File src/components/seo.js:

function SEO (){
return(
<Helmet
title={title} <-- from StaticQuery, accessing siteMetadata for the website
description={description} <-- from StaticQuery, accessing siteMetadata for the website
keywords=??
/>
)
export default SEO

How do we get the keywords into the SEO component? We can achieve this by running a pageQuery in src/templates/blog-post.js, that pulls the keywords from the frontmatter of the .mdx file for each blog post. It then creates an SEO component passing the keywords as props to the SEO component for a specific blog post.

File src/templates/blog-post.js:

class BlogPostTemplate extends React.Component {
render() {
const post = this.props.data.mdx
return(
<Layout ...>
<SEO title={post.frontmatter.title} <--- props,overwrites siteMetadata in SEO
description={post.excerpt} <--- props,overwrites siteMetadata in SEO
keywords={post.frontmatter.keywords}/> <--- Passed as props to SEO
</Layout>
<h1> {post.frontmatter.title}</h1>
<p> {post.frontmatter.date} </p>
<MDXRenderer> {post.body}</MDXRenderer>
);
}
}
export default BlogPostTemplate
export const pageQuery = graphql`
query($slug: String!) { <--- $slug: a variable that got passed by the
createPages API in gatsby-node.js
site {
siteMetadata {
title
author
}
}
mdx(fields: { slug: { eq: $slug } }) { <--- $slug: "
id
excerpt(pruneLength: 160)
frontmatter {
title
date(formatString: "MMMM DD, YYYY")
keywords <--- Keywords for specific blog post
}
body
}
}
`

The variable $slug above would not have been available to a component to use in a StaticQuery.

Here is a good video on an introduction to StaticQuery.

The following is a simplified version of what the SEO component looks like, with the StaticQuery in place:

function SEO({ description, keywords, title, meta }) { <---these coming
<--- from props, for a single blog post
return (
<StaticQuery
query={detailsQuery}
render={data => {
const metaDescription =
description || data.site.siteMetadata.description
return (
<Helmet
htmlAttributes={{
lang,
}}
title={title}
meta={[
{
name: `description`,
content: metaDescription,
},
{
property: `og:title`,
content: title,
},
]
//.. other fields...
.concat(
(keywords && (keywords.length > 0)) <--- KEYWORDS COMING FROM PROPS
? {
name: `keywords`,
content: keywords.join(`, `),
}
: []
)
.concat(meta)} //end of concat and meta
/> //end of Helmet
) //end of return
}} //end of render and data
/> //end of Static Query
) //end of return
}
export default SEO
const detailsQuery = graphql`
query DefaultSEOQuery {
site {
siteMetadata {
title
description
author
}
}
}
`

If I now go to that blog post and inspect the Elements, go to the head section and search for keywords, I find this:

<head>
<meta name="keywords" content="key1, key2, key3" data-react-helmet="true">
</head>

tufan.io
Making it simple to create, manage & secure cloud applications.