GraphQL queries inside MDX files

When you want to query data with GraphQL inside your MDX pages, you have the following options:

Default MDX config

If you are using the default configuration of gatsby-plugin-mdx, just export a GraphQL query as you would do in a regular Gatsby page. By default configuration, I mean that you installed gatsby-plugin-mdx, @mdx-js/mdx, and @mdx-js/react, and finally, you added gatsby-plugin-mdx as a plugin in gatsby-config.js. Then, you can access the query data from props.data.yourdata.

In the following example, I render the astronaut image from the default gatsby starter, and I display the props:

<!-- src/pages/example.mdx -->

# Example

Example MDX page. Here's the image of an astronaut:

import { graphql } from "gatsby";import Image from "gatsby-image";export const query = graphql`  query {    astronaut: file(relativePath: { regex: "/gatsby-astronaut/i" }) {      childImageSharp {        fluid(maxWidth: 600) {          ...GatsbyImageSharpFluid_tracedSVG        }      }    }  }`;

<Image title="astronaut" fluid={props.data.astronaut.childImageSharp.fluid} />
Log the `props.data`:

<pre>{JSON.stringify(props, null, 2)}</pre>

Custom templates

If you create the MDX pages programmatically, and you render them with a custom template, create a new component that uses a StaticQuery, then import that component in your MDX page and render it.

If you try to create a page query inside the MDX file or try to render a StaticQuery, you’ll get the following error:

Error: It appears like Gatsby is misconfigured. Gatsby related graphql calls are supposed to only be evaluated at compile time, and then compiled away…

Recommended folder structure:

__ src
____ components
______ AstronautImage.jsx
____ pages
______ index.js
______ example.mdx

The MDX file:

<!-- src/pages/example.mdx -->

# Example

Here's the image of an astronaut:

import AstronautImage from "../components/AstronautImage"

<AstronautImage />

And finally, the custom component that uses the StaticQuery:

// src/components/AstronautImage.jsx

import React from "react";
import { graphql, StaticQuery } from "gatsby";
import Image from "gatsby-image";

export default () => (
  <StaticQuery
    query={graphql`
      query {
        astronaut: file(relativePath: { regex: "/gatsby-astronaut/i" }) {
          childImageSharp {
            fluid(maxWidth: 600) {
              ...GatsbyImageSharpFluid_tracedSVG
            }
          }
        }
      }
    `}
    render={data => (
      <Image title="astronaut" fluid={data.astronaut.childImageSharp.fluid} />
    )}
  />
);

Extra

In this section you can find an MDX template, a gatsby-node.js file that creates programmatically MDX pages, and some links.

Custom template file

// src/templates/MDXTemplate.jsx
import React from "react";
import { graphql } from "gatsby";
import { MDXRenderer } from "gatsby-plugin-mdx";
import Layout from "../components/layout";

export default ({
  data: {
    mdx: { body }
  }
}) => {
  return (
    <Layout>
      <h1>Hello from the template</h1>
      <MDXRenderer>{body}</MDXRenderer>
    </Layout>
  );
};

// Notice the slug field in the MDX type you created
// in onCreateNode.
// The $slug variable comes from the page context
// you passed while creating the page.
export const query = graphql`
  query MDXBySlug($slug: String!) {
    mdx(fields: { slug: { eq: $slug } }) {
      body
    }
  }
`;

gatsby-node.js

// gatsby-node.js
const { createFilePath } = require("gatsby-source-filesystem");
const mdxTemplate = require.resolve("./src/templates/MDXTemplate.jsx");

exports.onCreatePage = ({ page, actions: { createPage, deletePage } }) => {
  const { frontmatter } = page.context;
  // Assuming only MDX pages have a frontmatter field...
  if (frontmatter) {
    deletePage(page);
    createPage({
      ...page,
      component: mdxTemplate,
      context: {
        ...page.context,
        slug: page.path
      }
    });
  }
};

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions;
  if (node.internal.type === "Mdx") {
    const value = createFilePath({ node, getNode });
    createNodeField({
      name: "slug",
      node,
      value
    });
  }
};

Other things to read

Popular posts

Other notes