{"id":28528,"date":"2022-03-23T22:22:17","date_gmt":"2022-03-23T22:22:17","guid":{"rendered":"http:\/\/Featured-Image-with-MDX-and-Gatsby"},"modified":"2022-03-23T22:22:17","modified_gmt":"2022-03-23T22:22:17","slug":"featured-image-with-mdx-and-gatsby","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/","title":{"rendered":"Featured Image with MDX and Gatsby"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>MDX, the powerful authoring format that lets you write JSX content inside of your markdown content, is an amazing tool to deliver interactive experiences to your readers.<\/p>\n<blockquote>\n<p>What is MDX? You can learn more by checking the <a href=\"https:\/\/mdxjs.com\">official site<\/a> and\/or this <a href=\"https:\/\/www.youtube.com\/watch?v=d2sQiI5NFAM&amp;list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u\">youtube video<\/a>.<\/p>\n<\/blockquote>\n<p>MDX is compatible with different tools, and Gatsby is one of those.<\/p>\n<p>When writing in Markdown (and for extension, with MDX), you use the <code>frontmatter<\/code> section to add metadata for your content. Usually, one of those attributes is the featured image URL (or cover image, banner, etc).<\/p>\n<p>There are a few problems that you can encounter when using Gatsby and MDX, however. For example, if you want to use Gatsby image preprocessing capabilities with a featured image from your <code>frontmatter<\/code>, you need to tap into the Gatsby build system through the <code>gatsby-node.js<\/code> file to retrieve the image and create the corresponding Node. But what if you want to use the filesystem routing API from Gatsby instead?<\/p>\n<p>In this article we will learn:<\/p>\n<ul>\n<li>How to use the Gatsby filesystem route API to automatically create pages for your MDX content file.<\/li>\n<li>How to create a plugin by using the <code>createResolvers<\/code> API from Gatsby to create an image node from the featured image defined in the <code>frontmatter<\/code> section of the MDX file.<\/li>\n<li>How to use the new <code>gatsby-image-plugin<\/code> to use the new image node created by the plugin in your site optimally.<\/li>\n<\/ul>\n<h1>What is the Gatsby filesystem route API?<\/h1>\n<p>A benefit of using Gatsby is the ability to create static pages based on the data that you can source. One way to accomplish this is by using the Gatsby API through the <code>gatsby-node<\/code> by querying the data programmatically and somehow imperatively creating the pages. But Gatsby offers what can be considered a simple, more declarative way to accomplish the same thing: the filesystem route API.<\/p>\n<p>This is a way to create pages from your GraphQL data by using certain file naming notations that will allow you to control the page path and the queried data without touching the <code>gatsby-node<\/code> file. Since the publication of this API, it has become the recommended process for creating pages. If for some reason, your use case is not covered by this API, you can always jump into <code>gatsby-node<\/code> and use the <code>createPages<\/code> API.<\/p>\n<h2>How can I create pages?<\/h2>\n<p>Let\u2019s assume that you have a simple Gatsby site that is sourcing data from the filesystem, like a collection of MDX files. Since MDX is an extension for Markdown, you can use <code>frontmatter<\/code> to define some attributes for your files. In this case, you have the following in the header of every MDX file.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">---\ntitle: Some Title\ncover: some url to an image\ndate: 04\/30\/2021\ndescription: Some description\n---\n<\/code><\/span><\/pre>\n<p>These files are stored inside <code>src\/content\/posts<\/code>.<\/p>\n<blockquote>\n<p>Assume that you are already able to source the MDX files into your Gatsby schema. If that is not is the case, you need to set up the <code>gatsby-source-filesystem<\/code> plugin in your <code>gatsby-config.js<\/code> file <a href=\"https:\/\/github.com\/matiasfha\/gatsby-starter-twin-macro-mdx\/blob\/main\/gatsby-config.js#L69\">like this<\/a> or you can use <a href=\"https:\/\/github.com\/matiasfha\/gatsby-starter-twin-macro-mdx\">this starter to set things up<\/a>.<\/p>\n<\/blockquote>\n<p>Now, we want to create pages for each of these posts. By default, Gatsby assumes that the content of the <code>src\/pages<\/code> folder will become a statically generated page. We will create a file under that folder and use some naming conventions to pull the data from the GraphQL layer.<\/p>\n<p>Let\u2019s create the file:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ touch src\/pages\/{mdx.slug}.js\n<\/code><\/span><\/pre>\n<p>Here, we used the curly braces <code>{}<\/code> convention in the name. This tells Gatsby that this is a dynamic URL, generated by querying the GraphQL schema, and retrieves the <code>mdx<\/code> nodes from both inside the node reading the  <code>slug<\/code> attribute of the node.<\/p>\n<p>This is Similar to a GraphQL query that looks like this:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">query MyQuery {\n\tmdx {\n\t\tslug\n\t}\n}\n<\/code><\/span><\/pre>\n<p>For a file named \u2018Some Title\u2019, the corresponding route\u2019s output will be \u2018\/some-title\u2019.<\/p>\n<p>The notation means:<\/p>\n<ul>\n<li>Using \u2018.\u2019 (period), you signify that you want to access a field on a node of a type.<\/li>\n<li>Using \u2018__\u2019 (double underscore), you signify that you want to access a nested field on a node.<\/li>\n<\/ul>\n<h2>Page implementation<\/h2>\n<p>Now that the file we just created will become the <code>template<\/code> for the content we want to render, go ahead and open the file, and let\u2019s add a basic definition for it.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> { graphql } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"gatsby\"<\/span> <span class=\"hljs-comment\">\/\/ We need to explicitly import GraphQL here.<\/span>\n<span class=\"hljs-keyword\">import<\/span> { MDXRenderer } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"gatsby-plugin-mdx\"<\/span>; <span class=\"hljs-comment\">\/\/Since we want to render MDX, we need this plugin.<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">PostPage<\/span>(<span class=\"hljs-params\">props<\/span>) <\/span>{\n\t<span class=\"hljs-keyword\">return<\/span> (\n\t\t<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>{props.data.frontmatter.title}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{props.data.cover}<\/span> <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">{props.data.frontmatter.title}<\/span> \/&gt;<\/span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>{props.data.frontmatter.date}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">MDXRenderer<\/span>&gt;<\/span>{props.data.body<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">MDXRenderer<\/span>&gt;<\/span>\n\t\t<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n\t)\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> query = graphql<span class=\"hljs-string\">`\n  query($id: String) {\n    mdx(id: { eq: $id }) {\n      frontmatter {\n\t\t\t\tcover\n\t\t\t\tdate\n\t\t\t\tdescription\n      }\n\t\t\tbody\n    }\n  }\n`<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This defines the component that will be rendered, and also the data that will be used.\nThe filesystem route API naming will automatically create a route for your page for each node, and will automatically pass the queried field <code>slug<\/code> via <code>props.params<\/code> to your component. Gatsby will also automatically give you the <code>id<\/code> for the data you are querying.<\/p>\n<p>So, you are able to access the queried data through the <code>data<\/code> prop. This data is the result of the graphql query that filter the data by <code>id<\/code>. In this example, we queried the whole data of the post and rendered that in the component.<\/p>\n<blockquote>\n<p>For more information about how the filesystem route API is designed and works, please check the <a href=\"https:\/\/www.gatsbyjs.com\/docs\/reference\/routing\/file-system-route-api\">Gatsby documentation site<\/a>.<\/p>\n<\/blockquote>\n<h1>How to manage the image of the post<\/h1>\n<p>One important thing to notice in the previous example is that we are querying the <code>cover<\/code> attribute from the <code>frontmatter<\/code> as a string attribute and rendering that as a simple  <code>img<\/code> tag. Additionally, if you are using a media service, such as Cloudinary, your image can be optimized by the service and even cached, but <strong>What if you want more control over the image?<\/strong><\/p>\n<blockquote>\n<p>Gatsby allows powerful image processing features using the Sharp library to automatically process images to be performant, using features like lazy-loading. That said, this only works if the image is a File node in the GraphQL layer.<\/p>\n<\/blockquote>\n<p>Gatsby v3.0 was released along with a new amazing plugin to handle and optimize images. <code>gatsby-plugin-image<\/code> is now the default way to manage your media files.<\/p>\n<p>This plugin handles the hard parts of producing images in multiple sizes to accomplish your responsiveness needs while maintaining high-performance scores without any hassle.<\/p>\n<p>To use it, we just need to install it, and add it to the <code>gatsby-config<\/code> file:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">npm install gatsby-plugin-image gatsby-plugin-sharp gatsby-source-filesystem gatsby-transformer-sharp\n<\/code><\/span><\/pre>\n<p>Then update your config file:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-built_in\">module<\/span>.exports = {\n  <span class=\"hljs-attr\">plugins<\/span>: &#91;\n    <span class=\"hljs-string\">`gatsby-plugin-image`<\/span>,\n    <span class=\"hljs-string\">`gatsby-plugin-sharp`<\/span>,\n    <span class=\"hljs-string\">`gatsby-transformer-sharp`<\/span>,\n  ],\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Unfortunately, our query now just retrieves the <code>cover<\/code> image as a string. The <code>mdx<\/code> node is not actually doing anything special with the image data, so we can\u2019t use the power of this plugin. What can we do?<\/p>\n<h2>Use <code>createResolvers<\/code> to create an image node<\/h2>\n<p>We will need to tap into the Gatsby build system in the <code>gatsby-node<\/code> API to add a simple function that will read the image attribute that we have and to create the required node to allow us to use the new image plugin.<\/p>\n<blockquote>\n<p>This is already available as a plugin. You can <a href=\"https:\/\/www.npmjs.com\/package\/@matiasfha\/gatsby-plugin-frontmatter-featured-image\">install it directly from npm<\/a>.<\/p>\n<\/blockquote>\n<p>The <code>createResolvers<\/code> function is part of the <code>gatsby-node<\/code> API. It allows us to add additional customizations on top of a pre-made data schema. As mentioned by the Gatsby documentation:<\/p>\n<blockquote>\n<p>This is an \u201cescape hatch\u201d API, as it allows us to modify any fields or types in the Schema, including Query type.<\/p>\n<\/blockquote>\n<blockquote>\n<p>You can learn more by reading the Gatsby <a href=\"https:\/\/www.gatsbyjs.com\/docs\/reference\/graphql-data-layer\/schema-customization\/#createresolvers-api\">documentation about <code>createResolvers<\/code> here<\/a>.<\/p>\n<\/blockquote>\n<p>We will use the API to read the <code>mdx<\/code> node that we are using, retrieve the <code>cover<\/code> attribute from the <code>frontmatter<\/code>, and create a new field by using <code>createRemoteNodeField<\/code>. This allows us to read the image as an external resource, and then add it back to the schema. Since it is an image, it will go through the <code>sharp<\/code> transformation and create a queryable node that we can use with <code>gatsby-plugin-image<\/code>. To do this, let\u2019s add some code to the <code>gatsby-node.js<\/code> file:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">exports.createResolvers = ({\n  actions,\n  cache,\n  createNodeId,\n  createResolvers,\n  store,\n  reporter,\n}, pluginOptions) =&gt; {\n  <span class=\"hljs-keyword\">const<\/span> { createNode } = actions\n  \n  createResolvers({\n    <span class=\"hljs-attr\">Mdx<\/span>: {\n      <span class=\"hljs-attr\">cover<\/span>: {\n        <span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-string\">`File`<\/span>,\n        resolve(source, args, context, info) {\n          <span class=\"hljs-keyword\">return<\/span> createRemoteFileNode({\n            <span class=\"hljs-attr\">url<\/span>: source.frontmatter.cover,\n            <span class=\"hljs-attr\">parentNodeId<\/span>: source.id,\n            cache,\n            createNode,\n            createNodeId,\n            reporter,\n          })\n\n        }\n      },\n    },\n  })\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>And that\u2019s it! Here, we are telling Gatsby to create a new node for the <code>mdx<\/code> nodes. Essentially, every time you read an <code>mdx<\/code> node, you add a new attribute called <code>cover<\/code>, which will be a <code>File.<\/code> This file will be created by <code>createRemoteFileNode<\/code>, which reads the <code>source.frontmatter.cover<\/code> string.<\/p>\n<blockquote>\n<p>Read more about <a href=\"https:\/\/www.gatsbyjs.com\/docs\/how-to\/images-and-media\/preprocessing-external-images\/\">\u2019createRemoteFileNode` in the Gatsby official documentation<\/a>.<\/p>\n<\/blockquote>\n<p>Now, we can use the powerful Gatsby image processing features in our post component.<\/p>\n<h1>How to use <code>gatsby-plugin-image<\/code><\/h1>\n<p>The <code>gatsby-plugin-image<\/code> that we already installed and configured offers a way to tap into the Gatsby image processing features by exposing two components: <code>StaticImage<\/code> and <code>GatsbyImage<\/code>.<\/p>\n<p>The <code>StaticImage<\/code> component is supposed to be used with images that are not meant to change, like a logo on your site. On the other hand, <code>GatsbyImage<\/code> is meant to be used with dynamic data, like the images that come from a CMS, or in our case, from the file system.<\/p>\n<p>To use this plugin, we need to update our <code>PostPage<\/code> component to retrieve the correct information in the query. Let\u2019s do it!.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> query = graphql<span class=\"hljs-string\">`\n  query($id: String) {\n    mdx(id: { eq: $id }) {\n      frontmatter {\n      date\n\t\t\t\tdescription\n      }\n\t\t\tcover {\n\t      childImageSharp {\n\t        gatsbyImageData(width: 600)\n\t      }\n\t    }\n\t\t\tbody\n    }\n  }\n`<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Here, we remove the <code>cover<\/code> attribute from the <code>frontmatter<\/code> query. It was a new attribute that reads the <code>childImageSharp<\/code> information and retrieves a \u201cfunction\u201d named <code>gatsbyImageData<\/code> that returns a data structure with all the information and processing on the image. You can configure even further by, for example, adding a placeholder.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">cover {\n    childImageSharp {\n      gatsbyImageData(width: 600, placeholder: BLURRED)\n    }\n  }\n<\/code><\/span><\/pre>\n<p>This will add a very low-resolution version of the source image and display it as a blurred background.<\/p>\n<blockquote>\n<p>Find more information about the different configurations for <a href=\"https:\/\/www.gatsbyjs.com\/docs\/reference\/built-in-components\/gatsby-plugin-image#gatsbyimage\">\u2018gatsbyImageData` in Gatsby\u2019s documentation<\/a>.<\/p>\n<\/blockquote>\n<p>Our last step is to update the actual component code to use the new <code>gatsbyImageData<\/code>. For that, we will use a helper that is provided by the plugin called <code>getImage<\/code>. This helper takes a <code>File<\/code> node as our <code>cover<\/code>attribute and returns the  <code>file.childImageSharp.gatsbyImageData<\/code> information, so our component will look like this:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">... <span class=\"hljs-comment\">\/\/the previous imports<\/span>\n<span class=\"hljs-keyword\">import<\/span> { GatsbyImage, getImage } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"gatsby-plugin-image\"<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">PostPage<\/span>(<span class=\"hljs-params\">{ data: { mdx} }<\/span>) <\/span>{\n\t<span class=\"hljs-keyword\">const<\/span> image = getImage(mdx.cover)\n\t<span class=\"hljs-keyword\">return<\/span> (\n\t\t<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>{mdx.frontmatter.title}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">GatsbyImage<\/span> <span class=\"hljs-attr\">image<\/span>=<span class=\"hljs-string\">{image}<\/span> <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">{mdx.frontmatter.title}<\/span> \/&gt;<\/span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">br<\/span> \/&gt;<\/span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>{mdx.frontmatter.date}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">MDXRenderer<\/span>&gt;<\/span>{mdx.body}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">MDXRenderer<\/span>&gt;<\/span>\n\t\t<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n\t)\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Now, you have access to the processing power of Gatsby in your MDX files while using the Filesystem route API.<\/p>\n<p>You can check this example in the <a href=\"https:\/\/github.com\/matiasfha\/featured-images-example\">following repository<\/a>, visit the <a href=\"https:\/\/featuredimagesexample.gatsbyjs.io\">deployed example here<\/a>  or fork this codesandbox<\/p>\n<\/div>\n  \n  <div class=\"wp-block-cloudinary-code-sandbox \">\n    <iframe\n      src=\"https:\/\/codesandbox.io\/embed\/trusting-gagarin-w9zyb?theme=dark&amp;codemirror=1&amp;highlights=&amp;editorsize=50&amp;fontsize=14&amp;expanddevtools=0&amp;hidedevtools=0&amp;eslint=0&amp;forcerefresh=0&amp;hidenavigation=0&amp;initialpath=%2F&amp;module=%2Fgatsby-node.js&amp;moduleview=0&amp;previewwindow=&amp;view=&amp;runonclick=1\"\n      height=\"500\"\n      style=\"width: 100%;\"\n      title=\"Featured Images with Gatsby and MDX\"\n      loading=\"lazy\"\n      allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n      sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n    ><\/iframe>\n  <\/div>\n\n  <div class=\"wp-block-cloudinary-markdown \"><h1>Conclusion<\/h1>\n<p>We just learned how to use the Filesystem Route API from Gatsby, a powerful convention for declaratively creating pages. But we found an issue. We can\u2019t use the image processing features of Gatsby with the <code>frontmatter<\/code> data that we have by using this approach. Because of this, we tapped into the Gatsby build system by adding a resolver for our <code>Mdx<\/code> files that creates a new <code>File<\/code> node from the data of the image URL. This new <code>File<\/code> field allows us to use the <code>gatsby-image-plugin<\/code> capabilities to get responsive images in our <code>PostPage<\/code> component.<\/p>\n<p>It is worth mentioning that this will slow the build process since it means that for each MDX file, Gatsby needs to download the <code>cover<\/code> image and process it to store it in the filesystem and make it available to your GraphQL query.<\/p>\n<p>If you are using an image storage service such as Cloudinary that pre-processes your image, and performs transformations on the fly, this process isn\u2019t necessarily required. You will need to evaluate your performance requirements, however.<\/p>\n<h2>Resources<\/h2>\n<ul>\n<li>\n<code>gatsby-plugin-image<\/code> <a href=\"https:\/\/www.gatsbyjs.com\/docs\/reference\/built-in-components\/gatsby-plugin-image\">reference guide<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/www.gatsbyjs.com\/docs\/how-to\/images-and-media\/preprocessing-external-images\/\"><code>createRemoteFileNode<\/code> documentation<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/www.gatsbyjs.com\/docs\/reference\/graphql-data-layer\/schema-customization\/#createresolvers-api\"><code>createResolvers<\/code> API documentation.<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/www.npmjs.com\/package\/@matiasfha\/gatsby-plugin-frontmatter-featured-image\">Featured image plugin that implements the same approach<\/a>\n<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":28529,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[378,379,134,370,175,388,407,371],"class_list":["post-28528","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-gatsbyjs","tag-graphql","tag-guest-post","tag-image","tag-jamstack","tag-optimize","tag-sharp","tag-under-review"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Featured Image with MDX and Gatsby<\/title>\n<meta name=\"description\" content=\"MDX, the powerful authoring format that lets you write JSX content inside of your markdown content, is an amazing tool to deliver interactive experiences to your readers. There are a few problems that you can encounter when using Gatsby and MDX, however. For example, if you want to use Gatsby image preprocessing capabilities with a featured image from your `frontmatter`, you need to tap into the Gatsby build system through the `gatsby-node.js` file to retrieve the image and create the corresponding Node. But what if you want to use the filesystem routing API from Gatsby instead?\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Featured Image with MDX and Gatsby\" \/>\n<meta property=\"og:description\" content=\"MDX, the powerful authoring format that lets you write JSX content inside of your markdown content, is an amazing tool to deliver interactive experiences to your readers. There are a few problems that you can encounter when using Gatsby and MDX, however. For example, if you want to use Gatsby image preprocessing capabilities with a featured image from your `frontmatter`, you need to tap into the Gatsby build system through the `gatsby-node.js` file to retrieve the image and create the corresponding Node. But what if you want to use the filesystem routing API from Gatsby instead?\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-03-23T22:22:17+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924358\/Web_Assets\/blog\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407.jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"5385\" \/>\n\t<meta property=\"og:image:height\" content=\"3029\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Featured Image with MDX and Gatsby\",\"datePublished\":\"2022-03-23T22:22:17+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/\"},\"wordCount\":6,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924358\/Web_Assets\/blog\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407.jpg?_i=AA\",\"keywords\":[\"GatsbyJS\",\"GraphQL\",\"Guest Post\",\"Image\",\"JAMStack\",\"Optimize\",\"Sharp\",\"Under Review\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/\",\"name\":\"Featured Image with MDX and Gatsby\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924358\/Web_Assets\/blog\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407.jpg?_i=AA\",\"datePublished\":\"2022-03-23T22:22:17+00:00\",\"description\":\"MDX, the powerful authoring format that lets you write JSX content inside of your markdown content, is an amazing tool to deliver interactive experiences to your readers. There are a few problems that you can encounter when using Gatsby and MDX, however. For example, if you want to use Gatsby image preprocessing capabilities with a featured image from your `frontmatter`, you need to tap into the Gatsby build system through the `gatsby-node.js` file to retrieve the image and create the corresponding Node. But what if you want to use the filesystem routing API from Gatsby instead?\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924358\/Web_Assets\/blog\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924358\/Web_Assets\/blog\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407.jpg?_i=AA\",\"width\":5385,\"height\":3029},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Featured Image with MDX and Gatsby\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Featured Image with MDX and Gatsby","description":"MDX, the powerful authoring format that lets you write JSX content inside of your markdown content, is an amazing tool to deliver interactive experiences to your readers. There are a few problems that you can encounter when using Gatsby and MDX, however. For example, if you want to use Gatsby image preprocessing capabilities with a featured image from your `frontmatter`, you need to tap into the Gatsby build system through the `gatsby-node.js` file to retrieve the image and create the corresponding Node. But what if you want to use the filesystem routing API from Gatsby instead?","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/","og_locale":"en_US","og_type":"article","og_title":"Featured Image with MDX and Gatsby","og_description":"MDX, the powerful authoring format that lets you write JSX content inside of your markdown content, is an amazing tool to deliver interactive experiences to your readers. There are a few problems that you can encounter when using Gatsby and MDX, however. For example, if you want to use Gatsby image preprocessing capabilities with a featured image from your `frontmatter`, you need to tap into the Gatsby build system through the `gatsby-node.js` file to retrieve the image and create the corresponding Node. But what if you want to use the filesystem routing API from Gatsby instead?","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/","og_site_name":"Cloudinary Blog","article_published_time":"2022-03-23T22:22:17+00:00","og_image":[{"width":5385,"height":3029,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924358\/Web_Assets\/blog\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407.jpg?_i=AA","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/"},"author":{"name":"","@id":""},"headline":"Featured Image with MDX and Gatsby","datePublished":"2022-03-23T22:22:17+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/"},"wordCount":6,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924358\/Web_Assets\/blog\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407.jpg?_i=AA","keywords":["GatsbyJS","GraphQL","Guest Post","Image","JAMStack","Optimize","Sharp","Under Review"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/","name":"Featured Image with MDX and Gatsby","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924358\/Web_Assets\/blog\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407.jpg?_i=AA","datePublished":"2022-03-23T22:22:17+00:00","description":"MDX, the powerful authoring format that lets you write JSX content inside of your markdown content, is an amazing tool to deliver interactive experiences to your readers. There are a few problems that you can encounter when using Gatsby and MDX, however. For example, if you want to use Gatsby image preprocessing capabilities with a featured image from your `frontmatter`, you need to tap into the Gatsby build system through the `gatsby-node.js` file to retrieve the image and create the corresponding Node. But what if you want to use the filesystem routing API from Gatsby instead?","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924358\/Web_Assets\/blog\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924358\/Web_Assets\/blog\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407.jpg?_i=AA","width":5385,"height":3029},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/featured-image-with-mdx-and-gatsby\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Featured Image with MDX and Gatsby"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924358\/Web_Assets\/blog\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407\/ec0fc750f9a8e268453ca0686583619e8efe0c63-5385x3029-1_285294d407.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28528","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=28528"}],"version-history":[{"count":0,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28528\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/28529"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=28528"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=28528"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=28528"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}