ChristopherBiscardi / gatsby-mdx

Gatsby+MDX • Transformers, CMS UI Extensions, and Ecosystem Components for ambitious projects
https://gatsby-mdx.netlify.com/
715 stars 100 forks source link

Missing markdown content from props.children? #179

Closed craigmcginley closed 6 years ago

craigmcginley commented 6 years ago

Describe the bug When trying to render this.props.children (the markdown content from file) in my layout component, it's coming in as undefined. I could be in error, but check my playground project. LMK if you need assistance updating the docs anywhere as well.

To Reproduce https://github.com/craigmcginley/gatsby-mdx-children

  1. yarn develop
  2. Click link on homepage to "Test Markdown page"

Expected behavior Markdown file content should be visible, but it is not

craigmcginley commented 6 years ago

gatsby-mdx 0.2.0

arcticicestudio commented 6 years ago

I ran into this too yesterday after trying to migrate from my custom private plugin (waited for gatsby-mdx to be more stable and mature, now it includes more features than my implementation). I checked the source and I think this is related to the latest changes like #123 regarding the usage of the MDXRenderer component along with the componentWithMDXScope function to inject the data from within GraphQL. Unfortunately the docs haven't been updated so new users will run into this.

You're using the componentWithMDXScope function in your gatsby-node.js, but passes the children in the posts-page-layout.js instead of the data from a GraphQL query in combination with the MDXRenderer component.

Check out the demo repo ChristopherBiscardi/gatsby-mdx-minimal-repro to see how to use the new feature.

craigmcginley commented 6 years ago

I'm a bit confused since #123 isn't merged yet.

Either way, I still can't get that to work. Gatsby tells me the GraphQL query from a non-page (in this case, the posts-page-layout.js component) won't be run. I tried using StaticQuery inside the component instead, but GraphQL says no $id was passed in. I'm not sure where that id is configured/passed other than in gatsby-node.js, which I have.

Am I missing something?

rdrey commented 6 years ago

You can ignore these warnings: The GraphQL query in the non-page component "...src/templates/x-page.js" will not be run. gatsby-mdx does actually run pageQueries. But it does not yet re-evaluate them for us, so hot reloading of the md(x) content doesn't work. Here is a minimal example that I've got working:

class SomePage extends React.Component {
  render() {
    const mdx = this.props.data.mdx
    return (
      <MDXRenderer>{mdx.code.body}</MDXRenderer>
    )
  }
}
export default SomePage

export const pageQuery = graphql`
  query($id: String!) {
    mdx(id: { eq: $id }) {
      code {
        body
      }
    }
  }
`
ChristopherBiscardi commented 6 years ago

@craigmcginley It looks like you're trying to use an "mdx layout" (which does use children) as a gatsby page layout (which has no children).

The component for a page needs to look more like this one. Especially note the MDXRenderer and the pageQuery usage.

import React from 'react'
import { graphql } from 'gatsby'
import MDXRenderer from 'gatsby-mdx/mdx-renderer'
import { MDXProvider } from '@mdx-js/tag'

import Header from './header'

export default ({ data }) => (
  <MDXProvider components={{}}>
    <Header siteTitle={data.site.siteMetadata.title} />
    <MDXRenderer>{data.mdx.code.body}</MDXRenderer>
  </MDXProvider>
)

export const pageQuery = graphql`
  query($id: String!) {
    site {
      siteMetadata {
        title
      }
    }
    mdx(id: { eq: $id }) {
      id
      code {
        body
      }
    }
  }
`

componentWithMDXScope is a bit confusing if you don't know what it does, but it's safe to treat it mentally as the same as passing in the gatsby page layout path. It injects the mdx scope in a wrapper component and then uses that component as the page layout instead, but it's transparent and you should never have to deal with it. The page layout you use still needs to query the content it wants to render, as typical of gatsby apps.

craigmcginley commented 6 years ago

Thanks @rdrey and @ChristopherBiscardi. Had to step away for a bit but I think I have things set up correctly now. Yet using components through MDXProvider does not seem to work. Check out this example using the gatsby-mdx-minimal-repro: https://github.com/craigmcginley/gatsby-mdx-provider

Try going to page 3 and you'll see the component I'm trying to reference is undefined.

Could be related to https://github.com/mdx-js/mdx/issues/206

Should I open a new gatsby-mdx issue for this?

rdrey commented 6 years ago

I haven't been able to pass in components like that either, I've been using import statements at the start of my .mdx files. (After making them globally accessible with https://github.com/craigmcginley/gatsby-mdx-provider/blob/master/gatsby-node.js#L50)

craigmcginley commented 6 years ago

Excellent. Thanks so much @rdrey!

ChristopherBiscardi commented 6 years ago

@craigmcginley The approach in https://github.com/craigmcginley/gatsby-mdx-provider/commit/563a00ee4d9901c780591db2acf3f7e1bcb527dd is unsupported. If you use a component in an MDX file, you have to import it to use it.

MDXProvider works by allowing you to take control of rendering for primitive tags like <div>, <a>, <p>, etc. It does not allow you to define new elements and use them in MDX files.

ChristopherBiscardi commented 6 years ago

The issue that would support global usage of components without explicitly importing them is #138

craigmcginley commented 6 years ago

That would be a useful feature for gatsby-mdx @ChristopherBiscardi. And it appears I misinterpreted the mdxjs docs here: https://mdxjs.com/getting-started/#example-usage

I imagined inlineCode was a custom component but is actually probably just inline code elements ;)

ChristopherBiscardi commented 6 years ago

@craigmcginley ah, of course. I was confused about a similar thing when I started getting involved with mdx. There are two non-primitives that you can replace, inlineCode and wrapper. These are created by the HAST transformations and the transformation to JSX in mdx core. inlineCode represents inline code and wrapper is the wrapping element containing all of the MDX.