Search code examples
javascriptreactjsgatsby

Gatsby doesn't render MDX, instead gives plain text


I'm building a blog with Gatsby, I've made some progress but when I try to render mdx in my blog template, it doesn't render to anything. enter image description here I use createPages api, and everything works accordingly except this. When I wanted to use it gives an error 'Element type is invalid'. From my research, it is because its outdated with mdx v2?

This is the bit where I create pages and pass nodes

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions

  const result = await graphql(
    `
    query AllQueries {
      site {
        siteMetadata {
          title
        }
      }
      allMdx(sort: {fields: [frontmatter___date], order: DESC}) {
        nodes {
          fields {
            slug
          }
          excerpt
          frontmatter {
            date(formatString: "Do MMMM YYYY ")
            title
            description
          }
          body
        }
      }
    }
  `
  )



  const blogPostTemplate = path.resolve(`./src/templates/blog-post.js`)
  const articles = result.data.allMdx.nodes
  result.data.allMdx.nodes.forEach((node, index) => {
    console.log('node', node)
    createPage({
      path: `/blog${node.fields.slug}`,
      component: blogPostTemplate,
      context: {
                post: node,
                prev: index == 0 ? null : articles[index -1],
                next: articles.length -1 ? articles[index + 1] : null
      },
    })
  })

I can get frontmatter, title, date etc. without any problems.

node [Object: null prototype] {
  fields: [Object: null prototype] { slug: '/eighth-post' },
  excerpt: 'Heading Level Two Some text\n\nHeading Level Three\n\nSome more text',
  frontmatter: [Object: null prototype] {
    date: '5th October 2022 ',
    title: 'Eighth Post',
    description: 'This article is the truth'
  },
  body: '\n' +
    '\n' +
    '\n' +
    '## Heading Level Two\n' +
    '\n' +
    'Some text\n' +
    '\n' +
    '### Heading Level Three\n' +
    '\n' +
    'Some more text'
}

This is the example of a node that I'm getting. Thank you. This is the template I'm using to render posts.

import React from "react";
import { Link } from "gatsby";
import  Layout from "../components/layout";

function BlogPost({ pageContext }) {
   
  const { post } = pageContext
  const prev = pageContext.prev 
  ? {
    url : `${pageContext.prev.fields.slug}`,
    title : pageContext.prev.frontmatter.title 
  } : null

  const next = pageContext.next 
  ? {
    url : pageContext.next.fields.slug,
    title : pageContext.next.frontmatter.title 
  } : null

  return (

   <Layout>
      <div className="card" key={post.fields.slug}>
         <Link className="card-link" to={`.${post.fields.slug}`}>              
         <h2 className="card-title">{post.frontmatter.title}</h2>
         </Link>
         <p className="card-date">{post.frontmatter.date}</p>
         <p className="card-description">{post.frontmatter.description}</p>
         
         <p>{post.body}</p>
         <nav>
         {next && 
         <Link to={`..${next.url}`} className="previous">
            <span>&lt; {next.title} </span>
         </Link>
         }
         {prev && 
            <Link to={`..${prev.url}`} className="next">
               <span>{prev.title} &gt;</span>
            </Link>
         }
         </nav>
      </div>
   </Layout>
  )
}


export default BlogPost

This is the gatsby-config file

module.exports = {
  siteMetadata: {
    title: `My Online Portfolio`,
    description: `my home on the web`,
    siteUrl: `https://evilsaint.cloud`,
    social: {
      twitter: `https://twitter.com/_EvilSaint_`
    },
  },
  plugins: [
  `gatsby-plugin-image`, 
  `gatsby-plugin-sitemap`, 
  `gatsby-plugin-mdx`, 
  `gatsby-plugin-sharp`, 
  `gatsby-transformer-sharp`,
  {
    resolve: 'gatsby-plugin-mdx',
    options: {
      extensions: [`.mdx`, `md`],
      gatsbyRemarkPlugins: [
        {
          resolve: `gatsby-remark-highlight-code`,
          options: {
            terminal: "ubuntu",
          },
        },
      ],
    },
  },
  {
    resolve: 'gatsby-source-filesystem',
    options: {
      "name": "images",
      "path": "./src/images/"
    },
    __key: "images"
  },
  {
    resolve: 'gatsby-source-filesystem',
    options: {
      "name": "pages",
      "path": "./src/pages/"
    },
    __key: "pages"
  },
  {
    resolve: `gatsby-transformer-remark`,
    options: {
      footnotes: true,
      gfm: true,
      plugins: [],
    },
  },
  {
    resolve: 'gatsby-source-filesystem',
    options: {
      "name": `blogs`,
      "path": `${__dirname}/src/blog/`,
    },
    __key: "blogs"
  },
  'gatsby-transformer-remark',
  
]
};

Solution

  • The problem is that you are just rendering the body as is, as a plain text:

     <p>{post.body}</p>
    

    If you want to render the content as MDX you have two options:

    • Using the built-in plugin by Gatsby (see docs):

      1. Install gatsby-transformer-remark
      2. Add it to your gatsby-config.js:
      module.exports = {
        plugins: [
          {
            resolve: `gatsby-source-filesystem`,
            options: {
              name: `markdown-pages`,
              path: `${__dirname}/src/markdown-pages`,
            },
          },
       `gatsby-transformer-remark`,
        ],
      }
      

      Important: add it below gatsby-source-filesystem instance

      1. Use React's dangerouslySetInnerHTML to display the internal content. Change your current <p> to:
      <p dangerouslySetInnerHTML={{ __html: post.body }}/>
      

      Caveats: using dangerouslySetInnerHTML you are potentially exposing your site as XSS (Cross-Site Scripting)

    • Use another library like markdown-to-jsx (or similar)

      import Markdown from 'markdown-to-jsx';
      
      ...
      
      <Markdown options={{ wrapper: 'p' }}>{post.body}</Markdown>