Getting Gatsby Images from Generated Fields

Ever since I wrote SEO Images with Unsplash I've been making images for my posts, pulling the branch down to my local computer, adding the image to the branch, editing the frontmatter to point to the image (always a png with the same name as the post file) and pushing it back up to merge.

This is kind of an annoying workflow to do every day, so I wanted to automate the finding and processing of the image files. If I automated it instead of placing it in the frontmatter, I could push the images up on master and merge the blog posts in later without having to pull them down. I handled the automation in this commit, which also merged the Getting Started with Emotion post (the first post to use the automatic featured image functionality.

Generating Fields

The first change I needed to make was to generate the featuredImage field on my content nodes. I use MDX so the resulting field will apply to MDX nodes in the Gatsby node system.

Here is the code from the change I made that checks for Mdx nodes with File node parents. On these nodes it checks for the existence of a file with the same name as the File node that has a .png extension instead of a .mdx extension. If the featured image file exists, we add a new field called featuredImage to the MDX node with the absolute path to the image.

// gatsby-node.js
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions;
if (node.internal.type === `Mdx`) {
const { frontmatter } = node;
const parent = getNode(node.parent);
if (parent.internal.type === "File") {
const ext = path.extname(parent.absolutePath);
const featuredImage = parent.absolutePath.replace(ext, ".png");
if (fs.existsSync(featuredImage)) {
createNodeField({
name: `featuredImage`,
node,
value: featuredImage
});
}
}
}
};

Strings and Sharp Objects

Now, that gives us a field on Mdx nodes that can be queried as such:

{
mdx {
fields {
featuredImage
}
}
}

The problem is that featuredImage is an absolute path to a png file and thus, is a string. We need to associate this string with a File node so that we can make interesting queries through the sharp transformer. To do this, we can take advantage of a gatsby-config.js key that I don't see used as often as other features: mapping.

To associate our field with the absolute path of the File node we stick the path to our field in the mapping object as a key and the path to the corresponding field on the File nodes as the value. These strings are nodeType.path so for Mdx nodes, we want the fields.featuredImage property.

module.exports = {
mapping: { "Mdx.fields.featuredImage": `File.absolutePath` },
plugins: [...]
}

This is the magic piece that lets us query File nodes for ImageSharp children.

export const query = graphql`
query {
allMdx(limit: 10) {
edges {
node {
id
excerpt
fields {
slug
featuredImage {
id
childImageSharp {
fluid(maxWidth: 700) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
}
}
`;

and now I can drop in a file without having to update the frontmatter for my blog posts and get a nice SEO image to use when I post on Twitter and query for more interesting images to use on the front page or blog post page template too.

Fin

Now that it all works, I can go back and simplify the code that queries for images on the frontmatter and the fields so that it just needs to look for fields.featuredImage. I can do this by pulling the processing back into the onCreateNode lifecycle and checking to see if there's a frontmatter.featuredImage.


Web Mentions

Adam Rackis

How though? The image prop is never passed to the SEO component. Is there some Gatsby magic happening there?

github.com/gaearon/overre…

Levi ᕙ(⇀‸↼‶)ᕗ

For tomorrow levi

Adam Rackis

I assume you meant “adding an image” but thank you.

Holy shit I’ve been on this platform for how many years and I didn’t know that 😐😑

Kyle Mathews

No just when he creates the tweet he's adding a screenshot

Kyle Mathews

No image ^^^

Adam Rackis

Hmm - the card never showed up for me when I composed my tweet...

Will take a fresh look tomorrow. Thanks a ton for the info!

Adam Rackis

🤦‍♂️🤦‍♂️🤦‍♂️

LOL. I need to go to bed. So it seems he’s just terminating the default Twitter card altogether and adding his own screen shot. What’s the secret sauce for killing the card?

Kyle Mathews

Dan's just adding a screenshot though it seems

:party-corgi:

I mean, the tweet from Dan is just a pasted screenshot into the tweet.

Kyle Mathews

I think you can just delete it when creating the tweet :-)

𝙎𝙘𝙤𝙩𝙩 𝙎𝙥𝙚𝙣𝙘𝙚 👨‍💻

Awesome!! Thanks @chrisbiscardi 🙏

So I won't have to retrospectively go change all my historical frontmatter now?