question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

MDXRenderer: components available globally as shortcodes not working

See original GitHub issue

Description

I am trying to get JSX components to work globally in my markdown files for my blog as per the documentation: Importing and Using Components in MDX

The proper html is not being rendered from my custom component inthe markdown, Rather I get a plain <div> tag with the attributes set in the custom component.

When I import my custom component into a .mdx file in src/pages and import the component from within the .md file as per import-components-for-use-from-another-library the proper html is rendered:

  <blockquote class='sc-bdVaJa jKmSoD'>
    <p>
      <small>
        This is a foobar<cite>by some genius</cite>
      </small>
    </p>
  </blockquote>

I have simplified my test to putting a <MDXRenderer> right in the blog.js file which resides in src/pages to rule out any issues with pathing and createPages in gatsby-node.js The contents of my blog.js is:

import Bio from "../components/bio"
import Layout from "../components/layout"
import SEO from "../components/seo"
import { rhythm } from "../utils/typography"
import Button from "../components/button"

import { MDXRenderer } from "gatsby-plugin-mdx"
import { ImgCaption } from "../components/ImgCaption"

class Blog extends React.Component {
  render() {
    const { data } = this.props
    const siteTitle = data.site.siteMetadata.title
    const posts = data.allMdx.edges

    const shortcodes = { ImgCaption }

    return (
      <Layout location={this.props.location} title={siteTitle}>
        <SEO title="All posts" />
        <Bio />
        <div style={{ margin: "40px 0 40px" }}>
          {posts.map(({ node }) => {
            const title = node.frontmatter.title || node.fields.slug
            return (
              <React.Fragment>
                <div key={node.fields.slug} style={{paddingLeft: '15px'}}>
                <MDXRenderer
                components={shortcodes}    
                >
                  {node.body}
                </MDXRenderer>
                  <h3
                    style={{
                      marginBottom: rhythm(1 / 8),
                    }}
                  >
                    <Link
                      style={{ boxShadow: `none` }}
                      to={`blog${node.fields.slug}`}
                    >
                      {title}
                    </Link>
                  </h3>
                  <small>{node.frontmatter.date}</small>
                  <p
                    style={{
                      marginBottom: rhythm(1 / 2),
                      marginTop: rhythm(1 / 4)
                    }}
                    dangerouslySetInnerHTML={{
                      __html: node.frontmatter.description || node.excerpt,
                    }}
                  /> 
              </div>
              <hr style={{borderTop: '1px solid #bbb', margin: '0 0 15px'}} />
              </React.Fragment>
            )
          })}
        </div>
        <Link to="/">
          <Button marginTop="85px">Go Home</Button>
        </Link>
      </Layout>
    )
  }
}

export default Blog

export const pageQuery = graphql`
  query {
    site {
      siteMetadata {
        title
      }
    }
    allMdx(sort: { fields: [frontmatter___date], order: DESC } filter: {fileAbsolutePath: {regex: "/content/blog/"}}) {
      edges {
        node {
          excerpt
          fields {
            slug
          }
          frontmatter {
            date(formatString: "MMMM DD, YYYY")
            title
            description
          }
          body
        }
      }
    }
  }
`

My custom component is here: src/components/ImgCaption.js and looks like this:

import React from 'react'
import styled from 'styled-components'
import colors from '../../content/assets/style/colors.scss'

export const ImgCaption = ({description, citation}) => {
  return (
    <Container>
      <p>
        <small>
          {description} 
          <cite>
            {citation}
          </cite>
        </small>
      </p>
    </Container>
  )
}

const Container = styled.blockquote`
  border-left: 1px solid ${colors.charm_pink};
  padding-left: .35em !important;
  p {
    small {
      font-size: 65%;
      cite {
        font-size: xx-small;
      }
    }
  }
`

My gatsby-config.js looks like this:

module.exports = {
  siteMetadata: {
    // edit below
    title: `blah`,
    author: `blah bah`,
    description: `blah blah blah blah blah`,
    siteUrl: `https://blah.com/`,
    social: {
      twitter: `blahblah`,
    },
  },
  plugins: [
    {
      resolve:`gatsby-source-filesystem`, // this entry has to be the first or will not work as per FAQ 
      options:{
        path:`${__dirname}/static/assets`,
        name:`assets`
      }
    },
    `gatsby-plugin-sass`,
    `gatsby-plugin-netlify-cms`,
    `gatsby-plugin-styled-components`,
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    `gatsby-plugin-offline`,
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-feed-mdx`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/blog`,
        name: `blog`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/data`,
        name: `content`,
      },
    },
    {
      resolve: `gatsby-plugin-mdx`,
      options: {
        extensions: [".mdx", ".md"],
        gatsbyRemarkPlugins: [
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 590,
            },
          },
          {
            resolve: "gatsby-remark-external-links",
            options: {
              target: "_blank",
              rel: "nofollow"
            }
          },
          {
            resolve: `gatsby-remark-responsive-iframe`,
            options: {
              wrapperStyle: `margin-bottom: 1.0725rem`,
            },
          },
          {
            resolve: `gatsby-remark-vscode`,
          },
          {
            resolve: `gatsby-remark-copy-linked-files`,
          },
          {
            resolve: `gatsby-remark-smartypants`,
          },
        ],
      },
    },
    {
      resolve: `gatsby-plugin-google-analytics`,
      options: {
        // edit below
        // trackingId: `ADD YOUR TRACKING ID HERE`,
      },
    },
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `blah`,
        short_name: `blah`,
        start_url: `/`,
        background_color: `#ffffff`,
        theme_color: `#663399`,
        display: `minimal-ui`,
        // edit below
        icon: `content/assets/gatsby-icon.png`,
      },
    },
    /*{
      resolve: `gatsby-plugin-typography`,
      options: {
        pathToConfigModule: `src/utils/typography`,
      },
    },*/
  ],
}

Steps to reproduce

  • Place a file named test.md in /content/assets/blog
  • Ensure that test.md contains the following content:
---
path: 03-10-20-1
date: 2020-13-10T16:51:48.059Z
title: this is a test
description: 'testing a possible bug'
---
This is regular content
<ImgCaption description='This is a foobar' citation='by some genius' />
This is more regular content

Expected result

The following html should be rendered from the custom component:

  <blockquote class='sc-bdVaJa jKmSoD'>
    <p>
      <small>
        This is a foobar<cite>by some genius</cite>
      </small>
    </p>
  </blockquote>

Actual result

The following html is rendered for the html component:

<div description="This is a foobar" citation="by some genius"></div>

Environment

System: OS: Windows 10 10.0.18362 CPU: (8) x64 Intel® Core™ i7-6700HQ CPU @ 2.60GHz Binaries: Node: 12.4.0 - C:\Program Files\nodejs\node.EXE Yarn: 1.19.1 - ~\AppData\Roaming\npm\yarn.CMD npm: 6.13.2 - C:\Program Files\nodejs\npm.CMD Languages: Python: 2.7.16 - /c/Python27/python Browsers: Edge: 44.18362.449.0 npmPackages: gatsby: ^2.3.25 => 2.19.34 gatsby-image: ^2.0.39 => 2.2.43 gatsby-plugin-feed-mdx: ^1.0.0 => 1.0.1 gatsby-plugin-google-analytics: ^2.0.18 => 2.1.37 gatsby-plugin-manifest: ^2.0.29 => 2.2.45 gatsby-plugin-mdx: ^1.0.52 => 1.0.78 gatsby-plugin-netlify-cms: ^4.0.0 => 4.1.41 gatsby-plugin-offline: ^2.0.25 => 2.2.10 gatsby-plugin-react-helmet: ^3.0.12 => 3.1.23 gatsby-plugin-sass: ^2.1.30 => 2.1.30 gatsby-plugin-sharp: ^2.0.35 => 2.4.8 gatsby-plugin-styled-components: ^3.0.7 => 3.1.20 gatsby-plugin-typography: ^2.2.13 => 2.3.24 gatsby-remark-copy-linked-files: ^2.0.11 => 2.1.39 gatsby-remark-external-links: ^0.0.4 => 0.0.4 gatsby-remark-images: ^2.0.6 => 2.0.6 gatsby-remark-responsive-iframe: ^2.1.1 => 2.2.33 gatsby-remark-smartypants: ^2.0.9 => 2.1.22 gatsby-remark-vscode: ^1.0.4 => 1.4.0 gatsby-source-filesystem: ^2.0.29 => 2.1.51 gatsby-transformer-sharp: ^2.1.18 => 2.3.17

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:5 (4 by maintainers)

github_iconTop GitHub Comments

7reactions
JSinkler713commented, Apr 28, 2021

Looks like everyone here gets it, but I’ll just post this for the next soul trying to use it with MDXRenderer. You want your MDXProvider to wrap your MDXRenderer. Then you will have access to all your components

import React from "react"
import { MDXRenderer } from 'gatsby-plugin-mdx'
import { MDXProvider } from "@mdx-js/react"
import { Chart, Pullquote, Message } from "./ui"

const shortcodes = { Chart, Pullquote, Message }

export default ({ children }) => (
  <MDXProvider components={shortcodes}>
    <MDXRenderer>{children}</MDXRenderer>
  </MDXProvider>
)
1reaction
lukePeaveycommented, Mar 16, 2020

To expand on @flikteoh’s comment…

In your code, you are passing your components to MDXRenderer. Instead, you should pass custom component to MDXProvider.

From the docs:

All MDX components passed into the components prop of the MDXProvider will be made available to MDX documents that are nested under the provider.

// src/components/layout.js

import React from "react"
import { MDXProvider } from "@mdx-js/react"
import { Chart, Pullquote, Message } from "./ui"

const shortcodes = { Chart, Pullquote, Message }

export default ({ children }) => (
  <MDXProvider components={shortcodes}>{children}</MDXProvider>
)

The MDXProvider in this example is in a layout component that wraps all MDX pages, you can read about this pattern in the layout section of the gatsby-plugin-mdx README.

You can specify the default layout component for different types of mdx pages in the plugin options.

// in gatsby-config.js

plugins: [
  {
    resolve: `gatsby-plugin-mdx`,
    options: {
      defaultLayouts: {
        default: require.resolve("./src/components/layout.js"),
        posts: require.resolve("./src/components/posts-layout.js"),
      },
    },
  },
]
Read more comments on GitHub >

github_iconTop Results From Across the Web

Adding MDX Pages | Gatsby
mdx . To learn more about this, head to the programmatically creating pages section just below. Make components available globally as shortcodes.
Read more >
Make React components globally available as shortcodes in ...
Learn how to make imported components globally available across all of your MDX pages. The MDXProvider is able to "provide" MDX children nodes...
Read more >
Working With MDX Custom Elements and Shortcodes
If we are programmatically creating pages, we'd have to use a component named MDXRenderer to achieve the same thing, as specified in the...
Read more >
How to embed React components in markdown using Gatsby ...
By providing an array of shortcodes and passing them to the MDXProvider component, we can support globally available components to avoid using ...
Read more >
Nesting MDX content - reactjs - Stack Overflow
import * as React from "react"; import { MDXRenderer } from ... i fixed the problem. to render content of Test components as...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found