Gatsby: Passing variables to use inside MDX file

Created on 11 Sep 2019  路  7Comments  路  Source: gatsbyjs/gatsby

Hi,
I'm using gatsby-plugin-mdx to add rich markdown to my site.

I want to access the frontmatter of the markdown within the markdown itself.

The docs say this is available in my MDX file with {props.pageContext.frontmatter} but this returns an error "TypeError: Cannot read property 'frontmatter' of undefined".

I presume this is because, rather than rendering as an individual page, I'm trying to render this mardown inside a component using MDXRenderer. I'm guessing that in this case "props.pageContext..." doesn't work.

(The actual use case is for a portfolio site - I'm trying to loop through all my "Work" markdown files on a single "Featured Work" page and render each one individually)

I expect that the way to do this is by passing the a variable using the MDXRenderer component. Something like:

<MDXRenderer category={work.frontmatter.category}>{work.body}</MDXRenderer>

But then I'm not sure how to access the category property inside my MDX markdown.

Question:

Is this the correct way to pass variables into my MDX code? Is what I want to do even possible? And if so, how do I then access the variables inside my MDX file?

Thanks in advance,
B

MDX question or discussion

Most helpful comment

Update:

After doing more searching I've found this thread on Spectrum that explained that any properties passed in are available in the {props} object in the mdx file.

e.g. in my case I can do

<MDXRenderer frontmatter={work.frontmatter}>{work.body}</MDXRenderer>

and then in the mdx markup I can add a component and use the {props} object. E.g.

<Socials links={props.frontmatter.socials}/>

Funny, I could have sworn I tried that while I was debugging but apparently not well enough.

All 7 comments

The actual use case is for a portfolio site - I'm trying to loop through all my "Work" markdown files on a single "Featured Work" page and render each one individually

If I understand you correctly, isn't this what you want to do?
https://www.gatsbyjs.org/docs/mdx/programmatically-creating-pages/#bonus-make-a-blog-index

Query all mdx data and display it?

Hi
Thanks for getting back to me.
This is close - and I've got something similar to this working fine.
But what I really want to be able to do is to access the frontmatter variables from _inside_ my MDX markdown. The reason being is I'd like to slightly customise my layout and components on a post by post basis by changing markdown.
The docs suggest this is possible. But this method isn't working for me.

Can you provide a minimal reproduction?
We'd highly appreciate that!

I can do that yes but just in case I'm able to save a bit of time by describing my case in a bit more depth here I will just do that first if that's OK - I don't think this is a bug - I just think there's something I'm not quite getting about the way MDX works:

I have a graphql query that returns all the "Featured Work" mdx files. This site is for a music producer. So each Featured Work post is an artist the producer has worked with. In the frontmatter for each post is the artist name, social links etc.


const data = useStaticQuery(graphql`
query { 
        allMdx(filter: {frontmatter: {type: {eq: "featuredWork"}}}) {  
          nodes {  
            id  
            frontmatter {  
              genre  
              artist  
              socials {  
                spotify
                instagram
                facebook
                twitter
                soundcloud
              }
              spotifyEmbed
              title
            }
            excerpt
            body
          }
        }
    }
    )

I then loop through data.allMdx.nodes, and pass the content through to a "Featured Work" component.

const featuredWorkData = data.allMdx.nodes
{featuredWorkData.map((work, index) => <FeaturedWork work={work} key={work.id} index={index}  /> )}

My FeaturedWork component looks something like this:

const featuredWork = ({work, index}) => {
return (
<>
       <FeaturedWorkContent className={featuredWorkStyles.featuredWorkContent}>
           <h3>{work.frontmatter.genre}</h3> 
           <h2>{work.frontmatter.artist}</h2> 
           <MDXRenderer>{work.body}</MDXRenderer>
</>
)
}

export default featuredWork

This is all working just great. It reads the mdx files just fine.

But - you see how I added "socials" in my frontmatter - I would love to be able to create a component called "SocialLinks" that renders out some social media links and have the option of inserting this _inside my .mdx markdown_

I'm able to add components in my markdown:

import {SocialLinks} from '../../components/miscBJComponents'

Lorem Ipsum ...

<SocialLinks />

This will output the content of the component if it's a simple static component.

But I need to pass the frontmatter data to it.

Something like

---
"socials": {
    "spotify": "https://open.spotify.com/artist/6jCJXKWlZky694nYGcJlm1?si=NUKotT5jR5OYBRCx3dUKiQ",
    "instagram":"https://www.instagram.com/childcareband",
    "facebook":"https://www.facebook.com/childcareband",
    "twitter":"https://www.twitter.com/childcareband",
    "soundcloud":"https://soundcloud.com/childcare",
}

---
import {SocialLinks} from '../../components/miscBJComponents'

Lorem Ipsum ...

<SocialLinks links={frontmatter.socials} />

I've tried using {props.pageContext.frontmatter.socials} as it says in the docs. But I get an error - "TypeError: Cannot read property 'frontmatter' of undefined".

I presumed there was something I could do like

<MDXRenderer frontmatter={work.frontmatter}>{work.body}</MDXRenderer>

And I still suspect this might be the case - but what I'm not sure of is how you access those passed variables from _within the markdown body._

Apologies for the long reply. I hope that makes sense. Let me know if you'd like anything clarified.

Thanks again,
Ben

Update:

After doing more searching I've found this thread on Spectrum that explained that any properties passed in are available in the {props} object in the mdx file.

e.g. in my case I can do

<MDXRenderer frontmatter={work.frontmatter}>{work.body}</MDXRenderer>

and then in the mdx markup I can add a component and use the {props} object. E.g.

<Socials links={props.frontmatter.socials}/>

Funny, I could have sworn I tried that while I was debugging but apparently not well enough.

Update:

After doing more searching I've found this thread on Spectrum that explained that any properties passed in are available in the {props} object in the mdx file.

e.g. in my case I can do

<MDXRenderer frontmatter={work.frontmatter}>{work.body}</MDXRenderer>

and then in the mdx markup I can add a component and use the {props} object. E.g.

<Socials links={props.frontmatter.socials}/>

Funny, I could have sworn I tried that while I was debugging but apparently not well enough.

Thx for the solution! That's working!
How to suggest add this code to the official docs?
<MDXRenderer frontmatter={work.frontmatter}>{work.body}</MDXRenderer>

I still don't see this information in the official docs, and it confused me until I find this thread. Mind I ask if I may add this as documentation for mdx in the Programmatically creating pages section as suggested by n-laverenko? I think it would help a lot of people.

Was this page helpful?
0 / 5 - 0 ratings