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.

[gatsby-plugin-mdx] `html` field doesn't render custom components when those components are provided in a layout file via`MDXProvider shortcodes`

See original GitHub issue

Description

I have created some custom components and I use them in my blogpost markdown files. These custom components render fine in the browser when they’re supplied to MDXProvider via shortcodes in my layout file. Unfortunately, these custom components are not rendered properly in the html field that is used by gatsby-plugin-feed when generating the RSS feed.

I’ve done some digging, and it seems that the custom component will be rendered correctly if the component has been imported directly into the .mdx file, but the component will not be rendered correctly if it has been supplied via shortcodes to MDXProvider.

Here’s an example of one component, Caption:

import React from "react"

const Caption = ({children}) => {

    return(
        <div style={{
            fontSize:"14px",
            textAlign:"center",
            color:"grey",
            marginTop:"-1rem",
            marginBottom:"2rem"}}>
            {children}
        </div>
    )
}
export default Caption

It gets supplied to the blogPost.js layout file via a shortcode given to MDXProvider:

import React from 'react'
import { graphql } from 'gatsby'
import Caption from '../components/Caption'
import { MDXProvider } from "@mdx-js/react"
import { MDXRenderer } from "gatsby-plugin-mdx"

const shortcodes = { Caption }

export default function PageTemplate( { data, pageContext } ) {
    
    const { mdx } = data
    const title = mdx.frontmatter.title
    const timeToRead = mdx.timeToRead
    const relativeDate = mdx.frontmatter.date

    return (
          
        <div>
            <div id='middle-column'>
                <div>
                    <h1 style={{fontFamily:"Georgia", marginBottom:"8px"}}>{title}</h1>
                </div>
                <div className='blogpost'>
                    <MDXProvider components={shortcodes}>
                        <MDXRenderer>{mdx.body}</MDXRenderer>
                    </MDXProvider>
                </div>
            </div>        
            
        </div> 
    )
}

export const pageQuery = graphql`
    query($pathSlug:String!){
        mdx(fields: { slug: { eq: $pathSlug} }){
            id
            body
            frontmatter {
                title
                date (fromNow: true)
            }
            timeToRead
        }
    }
`

Here’s my plugin config in gatsby-config.js:

plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `pages`,
        path: `${__dirname}/src/pages`
      }
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `content`,
        path: `${__dirname}/content`
      }
    },
    {
      resolve: `gatsby-plugin-mdx`,
    },
    {
      resolve:`gatsby-plugin-feed`,
      options: {
        query: `
          {
            site {
              siteMetadata {
                title
                description
                siteUrl
                site_url: siteUrl
              }
            }
          }
        `,
        feeds: [
          {
            serialize: ({ query: { site, allMdx } }) => {
              return allMdx.edges.map(edge => {

                const siteUrl = site.siteMetadata.siteUrl
                const slug = siteUrl + edge.node.fields.slug
                const frontmatter = edge.node.frontmatter

                return Object.assign({}, edge.node.frontmatter, {
                  description: edge.node.excerpt,
                  date: frontmatter.date,
                  url: slug,
                  guid: slug,
                  custom_elements: [
                    { "content:encoded": edge.node.html }
                  ],
                })
              })
            },
            query: `
              {
                allMdx(
                  sort: { order: DESC, fields: [frontmatter___date] },
                ) {
                  edges {
                    node {
                      excerpt
                      html
                      fields { slug }
                      frontmatter {
                        title
                        date
                      }
                    }
                  }
                }
              }
            `,
            output: "/rss.xml",
            title: "RSS Feed",
          },
        ],
      },
    },
  ]
}

When the Caption component is imported directly into the Markdown file, i.e:

---
date: "2020-01-11"
title: "Blogpost test - title"
excerpt: "Excerpty text"
---
import Caption from "../../components/Caption"

## Something about this awesome image

Imagine an image goes here...
<Caption>A custom caption for my image!</Caption>

# And so it begins

Something or other about my blogpost.

the following is rendered in the allMdx.edges.node.html field, and thus my RSS feed (as expected):

<div style="font-size: 14px; text-align: center; color: grey; margin-top: -1rem; margin-bottom: 2rem;">The moguls below the ski lifts</div>

When the Caption component is supplied to MDXProvider via shortcodes (i.e. not imported directly), it is rendered as a div with none of the styling of the custom Caption component:

<div>The moguls below the ski lifts</div>

Additionally, I also get this error at gatsby build time:

warn Component Caption was not imported, exported, or provided by MDXProvider as global scope

It could be that I just don’t know how to properly supply shortcodes in a way that the html field knows about them. I’ve read the docs, but can’t find anyway of doing this.

Otherwise, it seems like a potential feature request to me.

Steps to reproduce

  1. Create a new gatsby project
  2. Install gatsby-plugin-mdx and gatsby-plugin-feed
  3. Create a custom layout file, and supply a custom to MDXProvider using shortcodes.
  4. Use createPages function in gatsby-node.js to generate pages using the above layout
  5. gatsby build – produces an warning in the console (see above), and the custom component styling isn’t applied to the html field in allMdx.edges.node.mdx

Repo with two blogs is here: https://github.com/jjdevenny/gatsby-rss-shortcodes-bug One blog imports the Caption directly and works (./src/pages/direct-import-test.mdx), the other imports the caption via shortcodes and doesn’t work (./content/shortcodes-test.mdx).

Expected result

The html field supplied to gatsby-plugin-feed should respect the custom components I’ve supplied to MDXProvider via shortcodes in my layout file. My RSS feed should include the custom component styling + layout.

Actual result

The html field supplied to gatsby-plugin-feed doesn’t include any of the custom components I have supplied via MDXProvider in my layout file.

I also get an error in the console on gatsby build:

warn Component Caption was not imported, exported, or provided by MDXProvider as global scope

Environment

System: OS: macOS 10.15.2 CPU: (12) x64 Intel® Core™ i9-8950HK CPU @ 2.90GHz Shell: 5.7.1 - /bin/zsh Binaries: Node: 13.6.0 - /usr/local/bin/node Yarn: 1.19.2 - /usr/local/bin/yarn npm: 6.13.4 - /usr/local/bin/npm Languages: Python: 2.7.16 - /usr/bin/python Browsers: Chrome: 79.0.3945.117 Safari: 13.0.4 npmPackages: gatsby: ^2.18.21 => 2.18.21 gatsby-cli: ^2.8.26 => 2.8.26 gatsby-plugin-feed: ^2.3.26 => 2.3.26 gatsby-plugin-mdx: ^1.0.67 => 1.0.67 gatsby-source-filesystem: ^2.1.46 => 2.1.46 npmGlobalPackages: gatsby-cli: 2.8.26 gatsby: 2.18.7

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:3
  • Comments:13 (3 by maintainers)

github_iconTop GitHub Comments

4reactions
Js-Brechtcommented, Apr 8, 2020

Hi @jjdevenny. I just had a closer look at the html resolver, and it looks like you just need to put the <MDXProvider> in wrapRootElement.

I have this .mdx page:

// ./content/blog/index.mdx
---
title: Test
date: '2020-04-08T04:37:20.569Z'
---

<FooComponent>This is a test!</FooComponent>

And this setup:

// ./src/components/FooComponent.js
import React from 'react';

export const FooComponent = ({ children }) => (
    <div style={{
        fontWeight: 900,
        color: 'green',
        fontSize: 24,
    }}>
        <p>RENDERED CHILDREN:</p>
        <div>
            {children}
        </div>
    </div>
)
// ./src/wrap-root-element.js
import React from 'react'
import { MDXProvider } from '@mdx-js/react'
import { FooComponent } from './components/FooComponent';

const components = {
  FooComponent,
}
export const wrapRootElement = ({ element }) => (
  <MDXProvider components={components}>{element}</MDXProvider>
)
// ./gatsby-ssr.js
export { wrapRootElement } from './src/wrap-root-element'

All of this results in this HTML (I’ve converted its encoding XML->JSON):

"encoded": {
    "__prefix": "content",
    "__text": "<div style=\"font-weight:900;color:green;font-size:24px\"><p>RENDERED CHILDREN:</p><div>This is a test!</div></div>"
}

That’s basically what you’re trying to do, yeah?

2reactions
danedencommented, Apr 8, 2020

+1, @Js-Brecht your solution worked for me too! Unlike @jjdevenny this was less about inline styles for me and more about correct semantic output; I had custom Image and Link/a components, for example, that were getting completely ignored/transformed into a div in RSS because of this issue.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Gatsby doesn't render MD in component inside of mdx file
Use the component directly in the MDX file instead of shortcode. Question: Is it not possible to render the MD correctly within the...
Read more >
Customizing Markdown Components - Gatsby
Using MDX, you can replace every HTML element that Markdown renders with a custom implementation. This allows you to use a set of...
Read more >
Working With MDX Custom Elements and Shortcodes
Create default Layout components that help format the MDX output. Replace default HTML elements rendered from Markdown with custom components ...
Read more >
gatsby-plugin-mdx - npm
MDX is markdown for the component era. ... Components. MDXProvider; Shortcodes ... Certain features like HTML syntax doesn't work in MDX.
Read more >
MDX "fold it in" - Paul Scanlon
The import statement works the same way in MDX and allows you to import React components and render them alongside the usual Markdown...
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