Gatsby: Using React components in Markdown source

Created on 5 Jun 2016  ·  112Comments  ·  Source: gatsbyjs/gatsby

Is there an easy way to use React components in my Markdown source. Something like reactdown?

question or discussion

Most helpful comment

So I've been trying to think of a clean way to mix React & Markdown pretty much forever. Or at least since I started working on Gatsby.

And at last I think I have an idea that could work.

We'd use the same rule for differentiating between normal HTML elements and React components as JSX does i.e. React is capitalized.

So then you can include React components straight into your Markdown e.g.

# Hi folks

I'm writing some markdown about speaker <Person id="Paul Ryan">Paul Ryan</Person> who's the speaker of the house and other stuff.

<InteractiveComponent invertedColors={true} allTheProps={true} />

Then this would get parsed into an AST something like:

[
  [{ raw: true, value: "<h1>Hi Folks</h1>" }],
  [
    { raw: true, value: "<p>I'm writing some markdown about speaker " },
    {
      raw: false,
      value: {
        name: "Person",
        props: { id: "Paul Ryan", children: "Paul Ryan" }
      }
    },
    { raw: true, value: " who's the speaker of the house and other stuff.</p>" }
  ],
  [
    {
      raw: false,
      value: {
        name: "InteractiveComponent",
        props: { invertedColors: true, allTheProps: true }
      }
    }
  ]
];

And then in your component, instead of directly rendering (danger style) the HTML, you'd instead pass it to a helper function with a mapping to each React component something like:

import React from "react";
import renderHtmlReact from "gatsby-render-html-react";
import Person from "../components/Person";
import InteractiveComponent from "../components/InteractiveComponent";

export default ({ data }) => (
  <div>
    {renderHtmlReact({
      ast: data.markdownRemark.ast,
      componentMap: { Person, InteractiveComponent }
    })}
  </div>
);

// Your query goes down here

We'd need to do some prototyping to ensure this works with Remark but I'm pretty sure the React "Html" would get passed through untouched. So we'd just need a parser to split up the HTML and turn the React JSX parts into data as well as the frontend lib to render the resulting "AST".

Thoughts?

All 112 comments

That is a great idea. I would be willing to work on if it's not available yet.

I the developer of reactdown. It would be awesome to see reactdown working with Gatsby. Ping me if you have questions or need some assistance regarding integrating it.

👍 this is a common request so would be great to have something working that's widely available!

I set up a quick and dirty example repository of how to get reactdown working with gatsby. It's good to note that under the hood gatsby is just a very user friendly wrapper on top of webpack, so all of the power is still there to do just about anything.

https://github.com/benstepp/gatsby-reactdown

Thanks, I will try it and will let you know how it went.

Oh cool! Nice Ben. That's a lot simpler to setup than I'd been imaging
haha. Nice work Andrey.

On Tue, Jun 7, 2016 at 10:31 PM Jo Meenen [email protected] wrote:

Thanks, I will try it and will let you know how it went.


You are receiving this because you commented.

Reply to this email directly, view it on GitHub
https://github.com/gatsbyjs/gatsby/issues/312#issuecomment-224492088,
or mute the thread
https://github.com/notifications/unsubscribe/AAEVh-2doZe_FXFMBxYTJ5FAVqo4XqxNks5qJlO6gaJpZM4Iubim
.

@benstepp nice! This looks straightforward enough when using Markdown files through a wrapper, but would this also work when importing the markdown file to use inside a React component?

Edit: it seems like it should just work out of the box, but somehow the React component the md file is transformed into doesn't output anything…

I'm been thinking about a different approach that the new GraphQL layer (#420) opens up. I opened an issue asking about it in the mdash repo https://github.com/wooorm/mdast/issues/13

That'd be awesome! I felt I was so close with Reactdown though… The markdown files are properly transformed into React components by webpack (at least it looks like it), but it just gives me empty components when I use them in my code.

@SachaG have a repo somewhere I can took a look at?

Wow, really weird. I was trying to create a reproduction of the issue, and now it works! Not even sure what changed… Can't complain I guess :)

The gods of React came down in a golden chariot, and lo! the code worked

@SachaG I remember there were some issues with how reactdown references its runtime. Anyway if you hit some issues I would be flat do help.

Hello! Has there been any recent updates to this? What is the recommended method of using react components in markdown? Is there an example I can follow? Thanks!

Not yet :-( I have a good plan for how to do it — basically compile the markdown file into a React component file where you handle importing correctly all the referenced React components but haven't needed to build it quite yet. Feel free to start working on it if you need it!

@KyleAMathews do you mean I should run a converter script on my markdown file and then manually add the other react components I need to the output file produced by the conversion step? Once this is done then do the final build?

This would ideally be a plugin for Gatsby v1 that would do these steps
automatically.

On Tue, May 2, 2017, 12:50 PM Piyush Singh notifications@github.com wrote:

@KyleAMathews https://github.com/KyleAMathews do you mean I should run
a converter script on my markdown file and then add the other react
components I need to the output file produced by the conversion step? Once
this is done then do the final build?


You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub
https://github.com/gatsbyjs/gatsby/issues/312#issuecomment-298741837,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAEVh4riB8uXgeRUybcR6OxsC1EAKnkKks5r14kPgaJpZM4Iubim
.

A simple example of what I mean.

Imagine you had a markdown file at /my-blog/index.md that looked like the following:

---
title: "hi folks"
---

# This is my broomstick

Yo yo foo

<SweetComponent props="super cool" />

The body could then get converted into a react component which would get run through webpack/babel/etc. as normal.

import React from 'react'
import SweetComponent from 'auto-resolve-this-somehow'

class MarkdownBody extends React.Component {
  render () {
    return (
      <div>
        <h1>This is my broomstick</h1>
        <p>Yo yo foo</p>
        <Sweet Component props="super cool" />
      </div>
    )
  }
}

The trick part is how to integrate this with Gatsby's graphql system. Ideally you could just query for the "component" version of a markdown file similar to how you can query for the html today.

Anyways, that's a brief sketch of what I've thought about.

@KyleAMathews thanks, I think I get the idea now.

My aim is to be able to write content like this which leverage MathJax, D3.js visualisations and could also incorporate Three.js animation components within the markdown source.

If you see the html source here the page uses jQuery for some of the control sliders, though I imagine there might be better ways of doing jQuery stuff with React components?

From what I gather so far, I imagine producing such content with the Gatsby framework is achievable in a much neater and structured way, is that right?

I'm new to web development, but doing my best to learn how the Gatsby system works so that I can contribute to achieving these sorts of goals...

@KyleAMathews, I've been thinking about your proposed solution..

Do you think something like markdown-it-jsx could be used or improved upon to perform conversion to the desired MD JS component structure?

For resolving auto-resolve-this-somehow part, perhaps a second parser could scan the MD JS component file to look for JSX that match those in a component registry list (which could be a simple text file or file look-up in the directory where all components will be stored) and then add the relevant import line to the top of the file?

I'm going to start working on this so any feedback would be awesome!

Just a note there is another project which is doing this, maybe it will be useful as reference: react-styleguidist. Example of markdown file

Enabling components in markdown reminds me this masterpiece by Bret Victor.

UPD: and one more example: mdxc

And another one http://thejameskyle.com/react-markings.html

A solution is using react-jsx-parser.
At howtographql.com we're using it in production: https://github.com/howtographql/howtographql/blob/master/src/components/Tutorials/Markdown.tsx#L182

One disadvantage: It's of course slower than just using dangerouslySetInnerHTML, because it

  1. Parses the HTML
  2. Constructs a virtual dom tree
  3. Renders the virtual tree with React

One way to speed this up would be to not send the raw html with the graphql query, but actually send a format that the Inferno.js docs are using, as described here.

As I currently can't find the time to do this last step of optimization, anybody who is interested in it could go this approach.

That would mean to

  1. Construct proper GraphQL Types, that shouldn't be a problem as GraphQL Types can be recursive and represent a tree
  2. After the remark markdown parsing business is done, generate a serialized jsx format.
  3. In React deserialize this format.

Another option I've been eyeing is https://github.com/mapbox/jsxtreme-markdown

We could use a similar pattern. Just convert markdown to JSX components. Use custom delimiters to add JavaScript and JSX. We could use our existing Remark setup just fine. And there wouldn't be much of a performance penalty as it'd just be normal React pages.

There is also https://github.com/cerebral/marksy, which I used recently to good effect.

Is there a recommended approach for this in the context of Gatsby or is it still to be decided?

Hi, any recommendation to include customs components in our gatsby markdown files ?

One more option into the collection https://idyll-lang.github.io/

Looks like marksy works nicely for that use case

as it returns a react elements tree, i'm wondering how i could integrate that into a gatsby plugin ?

@KyleAMathews @revolunet What do you think about registering the components we need to use as custom elements, afterwards we can simple include them within the blog post and leave the rest of the work for browser to do?

This would eliminate any need to parse markdown as a react tree and keep our application performant, however, I don't know if there's going to be a performance cost, to do ReactDOM.render each time we need to add a new custom components, but still it would give us a pretty cool way to add dynamic functionality to the blog posts.

@abdulhannanali Registering components available in the markdown is a good idea, but the custom-elements stuff doesn't sound very server-side friendly :)

So I've been trying to think of a clean way to mix React & Markdown pretty much forever. Or at least since I started working on Gatsby.

And at last I think I have an idea that could work.

We'd use the same rule for differentiating between normal HTML elements and React components as JSX does i.e. React is capitalized.

So then you can include React components straight into your Markdown e.g.

# Hi folks

I'm writing some markdown about speaker <Person id="Paul Ryan">Paul Ryan</Person> who's the speaker of the house and other stuff.

<InteractiveComponent invertedColors={true} allTheProps={true} />

Then this would get parsed into an AST something like:

[
  [{ raw: true, value: "<h1>Hi Folks</h1>" }],
  [
    { raw: true, value: "<p>I'm writing some markdown about speaker " },
    {
      raw: false,
      value: {
        name: "Person",
        props: { id: "Paul Ryan", children: "Paul Ryan" }
      }
    },
    { raw: true, value: " who's the speaker of the house and other stuff.</p>" }
  ],
  [
    {
      raw: false,
      value: {
        name: "InteractiveComponent",
        props: { invertedColors: true, allTheProps: true }
      }
    }
  ]
];

And then in your component, instead of directly rendering (danger style) the HTML, you'd instead pass it to a helper function with a mapping to each React component something like:

import React from "react";
import renderHtmlReact from "gatsby-render-html-react";
import Person from "../components/Person";
import InteractiveComponent from "../components/InteractiveComponent";

export default ({ data }) => (
  <div>
    {renderHtmlReact({
      ast: data.markdownRemark.ast,
      componentMap: { Person, InteractiveComponent }
    })}
  </div>
);

// Your query goes down here

We'd need to do some prototyping to ensure this works with Remark but I'm pretty sure the React "Html" would get passed through untouched. So we'd just need a parser to split up the HTML and turn the React JSX parts into data as well as the frontend lib to render the resulting "AST".

Thoughts?

And then in your component, instead of directly rendering (danger style) the HTML, you'd instead pass it to a helper function with a mapping to each React component something like:
[…]
Thoughts?

This is something we have been doing too (very simplified, not actually safe and only designed for specific tags). Our use case was to style / lay out a JSON document:

<IconGrid src="sensors.json" />

This was really helpful to avoid duplicated content and we are contemplating using a similar mechanism for reusable API documentation components.

Something this solution lacks, which we also didn't solve, is the usage of markdown within component properties. So that something like this could be possible:

<Api method="post" description="
An API which uses **markdown**.

And having multi line parameters would be helpful as well.
" />

After trying tons of stuff in this space for a bit now I'm convinced that yeah it needs to really be solved at the markdown level. Meaning the markdown flavor really needs to understand jsx/react. The problem is really the javascript interpolation jsx supports and markdown chokes on. Jsxtreme-markdown gets close and works around this by processing them out first and then using markdown to parse the rest. Another approach is to lean on MD parsers natural ability to handle html and therefore straightforward jsx (Marked, for instance, will handle uppercase elements) but you quickly run into annoying limitations there being stuck on string only props and no nested markdown (e.g. Markdown inside, props.children). The other issue is that hydration on the client gets annoying bc you need a runtime (and generally a fairly large one) to convert a string into react elements, which isnt ideal for gatsby's focus on quick time to interaction and perf.

I do think the most promising way forward is an ast based approach, but for that you really need a markdown variant speed out that's different enough from existing ones to be a considerable project

Just throwing in my 2 cents!

Before I knew gatsby existed (2 years ago), I took a stab at making a react static site generator that behaved similarly to jekyll (rovr). I wanted to mix markdown, react, and html in my md files.

I opted to go with a regex to find <CapitalizedComponents/> within the markdown content and it worked out pretty well except it had a few edge cases. At the time, I was using marked which had some odd side effects around their parsing of html.

Also, my simplistic regex didn't allow for component end tags and therefor no children... so this didn't work:

<Component>
   <Child/>
</Component>

Anyway, I'm really excited for gatsby to support this! I'm in the process of converting my jekyll site to gatsby and i'm nearly complete. My next task is figuring out how to change my jekyll includes within md blog posts to components in my gatsby version.

@jquense agree that not support JS in markdown would be a limitation. But on the upside, this would fit nicely within our existing structure. The runtime lib would be pretty simple, just recursively loop over the AST creating elements. We could also set it up to auto parse any children text of components as markdown so components for this would just dangerouslySetInnerHTML on props.children.

It's not a 100% solution but it'd work for a lot of setups. I'm also not a huge fan of the idea of writing JS inside markdown. You lose all tooling support that way.

@jakedeichert yeah, simple regex only goes so far. Cool you got something similar to this working! That validates the concept. We'd want to use a JS parser that understands JSX to extract structured information. This way we could support nested components.

@KyleAMathews What's the progress on what you've proposed?

Few thoughts about this:

  1. I have a working example that uses remark to transform markdown containing arbitrary react components tmp-remark-jsx. I used remark-html and hast-util-to-html as base projects.
    Here is an example of usage + output: example
    I may provide some more information on how does it work if you're interested.
  2. MapBox created a package remark-react that transforms markdown into react elements using remark. It may take some work to adapt the package to accept custom react elements.

No one has started working on it afaik.

The problem with those two approaches is they don't work with Gatsby's GraphQL based data system since GraphQL queries can only return data not running code. remark-react for example does the markdown => html conversion in the client to get around this which is more expensive than ideal.

So to work with GraphQL, you need a client runtime to create the React components. My proposal came from working through a design that would enable the smallest possible runtime.

I think I get it now. So what's needed in your opinion are two pieces of code:

  1. markdown --> ready to use HAST that supports react components. (This code should run on build-time?)
  2. react enabled HAST --> react element (This should run on the client side with a minimal/trivial runtime)

Am I getting it right? Am I missing something?

Yup!

Ran across another project in this space — has some interesting ideas https://github.com/luiscarli/md-to-react

Hi,

I'm taking another stab at it. I have a working example of the first part that creates the HAST here remark-custom-element-to-hast. It still needs to support usage of markdown as child elements and maybe a webpack loader that uses it.

@KyleAMathews Do you think the approach matchs your view?

As for the second part, we can use syntax-tree/hast-to-hyperscript. Here is an example of usage wooorm/remark-vdom.

@fazouane-marouane cool! Yeah, from a quick look it seems really close. Great work!

We don't want to use hast-to-hyperscript though in the frontend as it has a bunch of dependencies and we want the runtime to be as light as possible — e.g. just recurse through the AST creating React elements. So move as much in the second library into your module.

@KyleAMathews That's great then! I'll try to do the remaining steps in the next few days. Hopefully this will get us a step closer to a practical solution.

Hi @fazouane-marouane & @KyleAMathews, I've read your discussion and implemented "simple" versions of the various parts you described here on this app I'm working on.

The interesting parts are the ones using @fazouane-marouane code in the form of a gatsby plugin here and the part where we render HAST here. It may also be interesting to see how the HAST is retrieved thanks to GraphQL.

Please note that at the time I'm writing this comment it's far from perfect, optimized & and clean.

EDIT: my renderHAST component is heavily inspired by the way phenomic deals with rendering markdown on client side: https://github.com/phenomic/phenomic/blob/master/packages/plugin-renderer-react/src/components/BodyRenderer.js

@pbellon Thanks for joining in! This will save everyone a lot of time. The code seems to be doing what's needed for the rest of the steps. I'll try it later this evening.

One thing that I need to debug is the way to handling components' children.
Right now if I set write in markdown This is a test <MyComponent>let's see how children are handled</MyComponent> the MyComponent's children property will be set to [0, null].

But I'm not sure if it comes from the parser or from the way I hydrate HAST.

@pbellon it comes from the parser. It's a minimalist parser to prototype this whole react in markdown thing. It will definitely need some fixes to be usable in all cases. Feel free to submit an issue if come across more examples. It will help make a good test base anyway.

@fazouane-marouane I see. I will try on my side to play with your code to see if I can implement this feature (and make a PR if I manage to do that) but it doesn't seem a trivial modification 😮
I've created an issue on your repo here

Chiming in with my $0.02. As part of developing GitDocs we needed a way to let users embed JSX inside markdown files. Here's what I've got so far.

Markdown Parsing

The markdown processing uses unified, remark, and rehype. A custom tokenizer was forked from the remark block HTML tokenizer with an extra regex to detect JSX. This tokenizer lets rehype detect JSX blocks as raw for further processing.

I wrote a custom rehype plugin that works similar to rehype-raw but that supports JSX and some basic expression evaluation. The plugin finds raw nodes and determines if they are JSX by attempting to parse the block using acorn-jsx. If parsing is successful, the JSX AST produced by acorn is converted into HAST.

Supported JSX

So far I can convert the following bits of JSX (example cases taken from the test suite)

<Test>
  <Test2></Test2>
</Test>
<Test>some text</Test>
<Test attr={{ prop: 400 }} />

Expressions

Simple compile time expression evaluation is supported.

<Test attr={1 + 2}/>

The created HAST node will have property 'attr' set to 3.

Array Expressions

Use of Array.prototype.map() is allowed in properties:

<Test attr={ [1,2,3].map(x => 2 * x) } />

You can even map over an array and create JSX children

<Test>{ [1,2,3].map(x => (<Test2>{x}</Test2>)) }</Test>

will be translated to HAST as the equivalent of

<Test>
  <Test2>1</Test2>
  <Test2>2</Test2>
  <Test2>3</Test2>
</Test>

Limitations

The expression support is quite limited and hasn't been security reviewed. The code does not use eval and accessing things like window should not be possible but I can't make any guarantees.

I did an experiment in this area where you can fully use the gatsby remark plugins as normal. You pass the graphql html to a webpack loader which produces react.js code. Unfortunately, it has several issues, and breaks the single production builds.

          createPage({
            path: edge.node.fields.slug,
            component: getCdResolve(blogPost, edge.node.fileAbsolutePath, {
              html: edge.node.html,
              site: result.data.site,
            }),
            context: {
              slug: edge.node.fields.slug,
            },
          })

Where cdResolve returns a string like !!cd-resolve-loader!foo.md. Gatsby does not like that. It at least works in the development server.

Rest of the code is here.

The build output is a simple component with a set of static nodes for children.

var h = require('react').createElement;
var n1 = h('div', { key: 1, dangerouslySetInnerHTML: { __html: '<h1>Hello</h1>' } });
var n2 = h(CustomComponent, { key: 2, foo: 1 });

module.exports = () => h('div', null, [n1, n2]);

Huh interesting!

This unfortunately would also break hot reloading of the markdown in development as far as I can tell.

Yep, that was another issue. I think if the issues were somehow worked out, it'd be one of the best end results, as far as bundle size and time to interactive. Of course, it's quite far off from being working solution. Any tips on how to get compilation to JS files after remark working?

@brigand I think the easiest solution is what I outlined in an earlier comment https://github.com/gatsbyjs/gatsby/issues/312#issuecomment-336681894

@KyleAMathews For the solution you proposed, we now know how to transform markdown containing jsx into html AST using remarkjs. We've solved the last blocking bug. All we need now is to create the code that can transform this AST into a react component and push it into npm. I'll have some free time to do this by the end of next week.

Don't hesitate to show up at my doorstep with a baseball bat if I don't push something by then 😅.

@r24y got this working!!! Checkout his PR https://github.com/gatsbyjs/gatsby/pull/3732 and example page on the using-remark example site!

https://using-remark.gatsbyjs.org/custom-components/

So, reporting my progress here. I had the time to finish the second step.

All in all, we have two packages:
The parsing into HAST: @dumpster/remark-custom-element-to-hast
The trivial rendering into react component: @dumpster/hast-react-renderer

@pbellon proposed a Gatsby plugin here.

This pipeline should be more permissive than rehype. If my memory serves me right, rehype uses parser5 which doesn't accept <Note value="my content" /> and I can't remember if it accepts bodies for custom elements.

@KyleAMathews I'm not sure what's the next step from here.

@fazouane-marouane awesome!!!

I think next steps would be to add an example site to the repo so we could show off your work as well — then we need to get a docs page going on gatsbyjs.org to talk about the different options and trade-offs they have.

Sweet ! Thanks for the clarifications.

I’ll try to have something for next week or the week after (starting on a new job this monday 😅).

Chiming in. After reading https://using-remark.gatsbyjs.org/custom-components/ and trying it out, I made a small package named gatsby-remark-component to avoid validateDOMNesting warnings that you get if your component inside your markdown contains block-level elements (div...). Custom components are by default wrapped into a paragraph, while with this package they get wrapped into a div.
It's pretty easy to use

//In your gatsby-config.js
plugins: [
  {
    resolve: "gatsby-transformer-remark",
    options: {
      plugins: [
        {
          resolve: "gatsby-remark-component",
          options: { components: ["my-component", "other-component"] }
        }
      ]
    }
  }
]

I hope this is useful to someone.

@Hebilicious thanks for the plugin; yeah, I did realize all the custom-components were rendered inside <p> element and I was using,

<div>
  <custome-component></custom-component>
</div>

as a workaround.

However, it is not easy to include all the custom-components into the config file again, like what I already did in the markdown template file.

@li-xinyang I agree with you. I added an auto-detection feature so you don't have to manually add them.
Make sure to upgrade the package to the latest version 1.1.0 if you want to try it.
It works out of the box with :

//In your gatsby-config.js ...
plugins: [
  {
    resolve: "gatsby-transformer-remark",
    options: {
      plugins: ["gatsby-remark-component"]
    }
  }
]

This is a bit of a tangent, but folks here might be interested in coordinating with @rauchg: see proposal

@r24y sounds nifty!

I think there's several ways this can be solved that have different tradeoffs.

I don't know if you're aware of the project called Catalog which also supports to embed React components into Markdown. There is also a Gatsby plugin called gatsby-remark-design-system that tries to adapt the concept of Catalog.

This solution looks perfect @arcticicestudio

Catalog looks nice; And the gatsby-remark-design-system plugin is cool, but it does not have support for React components. I do think that it does something right though, it makes use of the existing code block syntax to add more complex functionality to Markdown.

As discussed a few times within this thread, I think the use of the existing parser isn't quite the functionality that is desired. Perhaps instead of of free floating React elements, it would be possible to more clearly denote complex React components explicitly, for example a markdown file may look like:

# Some Heading
... etc etc ...

&lt;Example>
   &lt;Etc />
&lt;/Example>

When we want an embedded react element to be rendered, we specify the language as react, and a JSX aware parser kicks in and generates a valid AST that can be used with rehype. If we want a normal JavaScript prism example, then just use the normal javascript language.

Hi guys, great discussion here.
I´m using the gatsby-remark-component and I have some questions:

  1. Is there a way to "auto detect" components? That would remove the necessity to explicity inform my components in the components object:
const renderAst = new RehypeReact({
  createElement: React.createElement,
  components: {

  },
}).Compiler
  1. Is there a way to integrate some images with the gatsby-remark-images? One of my goals is that the user can add a image and send it to my component via props, for example. Something like:
    <my-component header-image="[](./image.png)"><my-component>

Thanks!

@ThiagoMiranda Yes, (2) would be great! My use case is an image gallery:

<gallery>
  <item source="image1.jpg" caption="One"></item>
  <item source="image2.jpg" caption="Two"></item>
</gallery>

This JSX aware markdown parser by Zeit looks promising: https://github.com/mdx-js/mdx

MDX is superset of the CommonMark specification that adds embedded JSX and import/export syntax.

It's built upon remark and rehype, so might be a nice drop in replacement for use within gatsby projects

cool, wanted to try that with Gatsby :)

Using @nhducit's mdx plugin, it's pretty straightforward to wire up MDX pages!!

  1. Add in the config for mdx and source in the pages/ directory.

gatsby-config.js

module.exports = {
  plugins: [
    'gatsby-plugin-mdx',
    {
      resolve: 'gatsby-source-filesystem',
      options: {
        name: 'pages',
        path: `${__dirname}/src/pages/`,
      },
    },
  ],
}
  1. Query all markdown files and create pages for each of them.

gatsby-node.js

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

  return new Promise((resolve, reject) => {
    graphql(`
      { 
        allFile(filter:{extension:{eq:"md"}}) {
          edges {
            node {
              absolutePath
              relativeDirectory
              name
            }
          }
        }
      }
    `).then(result => {
      if (result.errors) {
        return reject(result.errors)
      }

      // Create markdown pages.
      result.data.allFile.edges.forEach(({ node: {
        absolutePath,
        relativeDirectory,
        name
       } }) => {
        createPage({
          path: `/${relativeDirectory}/${name}`,
          component: absolutePath
        })
      })
    })
    .then(resolve)
  })
}
  1. Use MDX 🎉

src/pages/my-markdown-page.md

import MyComponent from '../components/MyComponent'

# Title

_some content_

<MyComponent />

@avigoldman Nice! It'd be great to see your example added to the README of @nhducit's plugin.

@avigoldman PR is welcome! 👍

@avigoldman looks like this approach conflicts with frontmatter?

@nhducit I'll make a PR!

@lintonye can you explain a bit more about what you mean. Are you referring to the exports from the MDX?

@lintonye have you tried to use mdx exports? It seems to be an equivalent for mdx: https://github.com/mdx-js/mdx#exports

Correct me if I'm wrong, but it looks like we need to wait for Gatsby 2.0 with a later webpack version to pass in remark plugins to MDX.

cc @KyleAMathews

@avigoldman @nhducit When exporting data out of the mdx files using the export syntax, how can we access that in our UI when the components are rendered?

same question here, with MDX currently, wasn't able to:

  • apply custom styles to markdown pages
  • inject frontmatter content as pathContext from named mdx exports
    This could be done actually, but this requires creating a .js file to import the .md file, for every .md file, so it duplicates the work...

@slorber I have a pretty hacky solution in this repo: https://github.com/avigoldman/avigoldman.com

Definitely not ideal. I'm going to revisit this once Gatsby V2 is out.

Hi and thanks @avigoldman

I also did try to do that here https://github.com/nhducit/gatsby-plugin-mdx/issues/13#issuecomment-392334706

It seems you have found the missing piece I didn't have to extract correctly the exports: babel-plugin-config-export

You create one layout per blog post page, not sure exactly if this is supposed to be done this way or what kind of impact it may have on Gatsby's performance, as people using Remark usually have a single layout instead...

What about publishing your plugin to NPM? this seems more usable than the one @nhducit made regarding frontmatter

Also, where does the ...mdx graphql fragment come from?

About MDX, they just added a "MDXProvder" (based on React Context) which may be helpful to integrate custom tags with Gatsby, cc @avigoldman

@slorber I created a gatsby-transformer-mdx plugin so that I could query against the MDX files.

I am treating each layout as a page, and each MDX file as the content. So the layout gets the page context and then the MDX is dropped in (like you would do with the remark-generated html).
This won't work in Gatsby V2 since the special layout components are going away.

The MDXProvider looks promising!

Adding my 2¢ here, I've managed to hack most of my own website (the repo is still using Jekyll, will soon be updating or adding a new repo) over to Gatsby using mdx thanks to @avigoldman’s gatsby-transformer-mdx!

That's great! Should have some time this weekend/upcoming week to make it into a package that plays nicely with Gatsby V2.

@avigoldman one thing I couldn't figure out from your gatsby-transformer-mdx is the rendering step. gatsby-transformer-remark uses the html field (which is a string), what does your transformer use? How does the webpack loader work if they aren't being required anywhere?

Oh, I just read the comments above, the trick is that Gatsby v1 automatically wraps everything in a layout component, so you're using absolutePath to target the MDX file. 😃 Thanks for this trick!

@avigoldman any news on the Gatsby 2 plugin? It would also be awesome if you could give rough directions on how you would build it without the layout component that doesn't exist in Gatsby 2 anymore.
Thanks!

I was hoping I could somehow use the export default feature of MDX, but passing any additional props other than children doesn't seem to be possible ATM. mdx-js/mdx#187

If my PR gets merged (mdx-js/mdx#189), I think we'll be able to use MDX with existing gatsby-plugin-mdx and gatsby-transformer-mdx. The only required change will be on our end, and that's exporting our post template from our .mdx files:

src/posts/hello-world.mdx:

import Post from '../components/post'

# Hello World

Lorem ipsum.

export default Post

gatsby-node.js:

const path = require('path')

exports.createPages = ({ actions }) => {
  actions.createPage({
    path: '/posts/hello-world',
    component: path.join(__dirname, 'src', 'posts', 'hello-world.mdx'),
    context: { /* passed as pageContext */ }
  })
}

src/components/post.js:

import React from 'react'
import Layout from './layout'

export default ({ children, pageContext }) => (
  <Layout>
    {children}
  </Layout>
)

I think this is really critical. Right now all the gatsby-remark-* plugins are ~reimplementing a bunch of rendering logic that could and probably should be in React. This could simplify things a lot.

@DylanVann that kinda depends. gatsby-remark-* plugins do a lot of build-time transformations that if we did those in React would mean shipping a lot of JS to browsers. Also things that are impossible to do in React like https://www.gatsbyjs.org/packages/gatsby-remark-images/?=remark-image

Ideally of course we blend the both of both worlds.

@KyleAMathews Yeah it would mean some more JS on the client side, although it would still be first delivered as static HTML. I think a lot of people are probably shipping gatsby-image for some parts of their site anyways, so in that case duplication is less of an issue.

I do understand that there is utility in the gatsby-remark-* plugins. The rendering logic specifically, which is currently done with strings, seems like it could be done in React though.

I've got this sort of working with the htmlAst/rehype-react method.

It seems that way, but in practice it's much more complicated. If you believe you're onto something, you could raise a new issue with a focused proposition.

It seems like a hard problem to solve. Unfortunately I don't think I'll have time to implement anything or make a proposal. The idea of doing remark plugins partially using React custom components definitely works, I have code using it.

This is generated by @dylanvann/gatsby-remark-cloudinary from markdown images ending in mp4. The idea is to optimize videos (sizing at build time, adding posters). I wanted the rendering logic in React though. Processing of the video is done in another function but this is part of what the node's HTML is replaced with.

export const videoHTML = ({
    srcVideo,
    srcVideoPoster,
    base64,
    paddingBottom,
    presentationWidth,
}) =>
    `<cloud-video srcvideo="${srcVideo}" srcvideoposter="${srcVideoPoster}" base64="${base64}" paddingbottom="${paddingBottom}" presentationwidth="${presentationWidth}"></cloud-video>`

Then using a custom component with rehype-react.

import React from 'react'
import rehypeReact from 'rehype-react'
import CloudVideo from './CloudVideo'

const renderAst = new rehypeReact({
  createElement: React.createElement,
  components: {
    'cloud-video': CloudVideo,
  },
}).Compiler

const Markdown = ({ ast }) => renderAst(ast)

Markdown.propTypes = {
  ast: PropTypes.object,
}

export default Markdown

The ast can be pulled out of GraphQL.

So this component works for SSR and client side. Anyways I know how it is with OSS. I'm just saying I think it would be a great feature and could reduce code complexity, so it would be great if anyone has time to figure out better solutions.

I'm not disagreeing with anything you are saying, I think that starting a good discussion would be much better in its own issue, rather than at the end of a very loaded thread of 60+ comments. 😉

Me and @avigoldman built gatsby-mdx to house ambitious 2.0 compatible MDX integrations and utils.

Currently gatsby .mdx pages work by default after enabling the plugin and the following additional features have been added on top of mdx:

  • Use and query classic and JSON style frontmatter
  • Define default layouts for mdx files that don't define one

We're also planning more sophisticated integrations for

  • advanced image processing
  • custom md and hast plugins.
  • compatibility with all the same fields as gatsby-transformer-remark

We're still pretty early in the lifecycle, so let us know if you run into any issues and we'll get it done :)

@ChristopherBiscardi is it meant to be used in combination with gatsby-plugin-mdx or instead of it?

@silvenon It looks like gatsby-plugin-mdx will be deprecated and thus stop at 1.0, while gatsby-mdx will move forward with 2.0 and beyond.

@m-allanson think its safe to close this issue now that we have gatsby-mdx?

I think so, thanks everyone 🎉

So is gatsby-mdx now to be preferred over rehype-react in conjunction with gatsby-transformer-remark as described here?

If the answer is not a clear-cut yes, could someone explain the advantages and drawbacks of both approaches?

I think this part of that blog post answers your question. rehype-react provides custom HTML elements which map to React components, but MDX actually is JSX inside Markdown, it's more predictable and has less caveats.

I'm not in the Gatsby team, but I would say yes, gatsby-mdx is the preferred way of React in Markdown.

@janosh as far as I know gatsby-mdx can't replace gatsby-transformer-remark completely yet
seems to still miss gatsby-remark-images & gatsby-remark-copy-linked-files for example and other gatsby-remark plugins..
I think they're working on it but not sure about the current state

But if you're not in the need of those plugins or can wait then I would say yes, at least I'll prefer it, seems cleaner to me

@CanRau I'm rebasing that today (https://github.com/ChristopherBiscardi/gatsby-mdx/pull/68) with the intent to merge and release. There will probably be some edge cases we have to deal with but I'll be going over some of them today on stream before I merge.

The state of the PR is that gatsby-remark-* plugins are applied properly, but there are differences in how the output of say, the gatsby-remark-prismjs plugin (which currently produces HTML output) is handled by transformer-remark vs mdx's pipeline. I view support for plugins like gatsby-remark-prismjs as important, but also a suboptimal approach. A better approach in the MDX world is to use something like prism-react-renderer as the code element in an MDXProvider, which would give you full flexibility and control over the rendering when compared to using remark plugins to achieve a similar effect (and also allow you to share that component with non-mdx content like .js pages).

I'm far more concerned with the copy-linked-files and image processing working than I am with prismjs working for the first release of gatsby-remark-* plugin support.

Sounds awesome @ChristopherBiscardi especially copy-linked-files and image support, I would love to help but realistically speaking I don't think I can handle it right now as we're really packed^^
Maybe I can share some feedback, I think I'll give it a try for some smaller meta pages to try things out

interesting to know that you're streaming your work..I'm quite new to live stuff and don't yet understand how to know when you're going life..probably works only with an account I guess

Happy to have you help at any point in the future if you find the time, feel free to ping me if you have questions :) Feedback itself helps a bunch either way though so if you try it out be sure to file issues!

interesting to know that you're streaming your work..I'm quite new to live stuff and don't yet understand how to know when you're going life..probably works only with an account I guess

I set up a "calendar" of streaming times at the bottom of my twitch channel. I know if someone has a twitch account and they follow me they'll get notifications when I go live, but otherwise the schedule is where to look. Pretty sure you can watch whether you have an account or not. I'm a bit new to streaming myself (only been doing it a couple weeks now) so always open to better ways to do this kind of stuff. I've had a few people come around repeatedly and hang out/watch/talk in chat which is pretty fun :)

screen shot 2018-08-28 at 1 17 05 pm

kay got it, good to know

and I'll surely post issues when I encounter any ;)

Btw, since Prism was mentioned, I'd just like to add that ideally remark/rehype plugins should be used directly, MDX supports that via mdPlugins and hastPlugins options (this can be passes through gatsby-mdx). Plugins like @mapbox/rehype-prism can be added to hastPlugins, but there were some whitespace issues that I fixed in mdx-js/mdx#226, so once that gets merged and released, syntax highlighting will be good to go!

yeah, totally. gatsby-mdx already supports passing remark/rehype plugins through to mdx core. Here's @mapbox/rehype-prism for example (I yanked that example from some of your PR/Issue discussions originally @silvenon, thanks). AFAIK rehype prism doesn't support prism plugins so it's always a tradeoff depending on use cases (I believe using rehype plugins for prism means you will have a harder time integrating something like react-live, for example)

gatsby-remark-prismjs and other gatsby-remark-* plugin support was released in 0.1.1 today, so now there's at least 3 options for syntax highlighting between rehype/remark/react-components 😝

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rossPatton picture rossPatton  ·  3Comments

magicly picture magicly  ·  3Comments

3CordGuy picture 3CordGuy  ·  3Comments

benstr picture benstr  ·  3Comments

hobochild picture hobochild  ·  3Comments