Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using React components in Markdown source #312

Closed
cutemachine opened this issue Jun 5, 2016 · 112 comments
Closed

Using React components in Markdown source #312

cutemachine opened this issue Jun 5, 2016 · 112 comments
Labels
type: question or discussion Issue discussing or asking a question about Gatsby

Comments

@cutemachine
Copy link

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

@ivanoats
Copy link
Contributor

ivanoats commented Jun 6, 2016

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

@andreypopp
Copy link

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.

@KyleAMathews
Copy link
Contributor

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

@benstepp
Copy link
Contributor

benstepp commented Jun 8, 2016

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

@cutemachine
Copy link
Author

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

@KyleAMathews
Copy link
Contributor

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 notifications@github.com 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
#312 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/AAEVh-2doZe_FXFMBxYTJ5FAVqo4XqxNks5qJlO6gaJpZM4Iubim
.

@SachaG
Copy link
Contributor

SachaG commented Sep 6, 2016

@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…

@KyleAMathews
Copy link
Contributor

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 syntax-tree/mdast#13

@SachaG
Copy link
Contributor

SachaG commented Sep 6, 2016

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.

@andreypopp
Copy link

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

@SachaG
Copy link
Contributor

SachaG commented Sep 8, 2016

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 :)

@emilyaviva
Copy link
Contributor

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

@andreypopp
Copy link

@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.

@Piyush3dB
Copy link
Contributor

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!

@KyleAMathews
Copy link
Contributor

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!

@Piyush3dB
Copy link
Contributor

Piyush3dB commented May 2, 2017

@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?

@KyleAMathews
Copy link
Contributor

KyleAMathews commented May 2, 2017 via email

@KyleAMathews
Copy link
Contributor

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.

@Piyush3dB
Copy link
Contributor

@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...

@Piyush3dB
Copy link
Contributor

@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!

@stereobooster
Copy link

stereobooster commented May 13, 2017

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

@timsuchanek
Copy link
Contributor

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.

@KyleAMathews
Copy link
Contributor

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.

@ChristopherBiscardi
Copy link
Contributor

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

@lindekaer
Copy link

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

@revolunet
Copy link

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

@stereobooster
Copy link

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

@revolunet
Copy link

revolunet commented Sep 22, 2017

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 ?

@abdulhannanali
Copy link

@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.

@timsuchanek
Copy link
Contributor

@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!

@silvenon
Copy link

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

@silvenon
Copy link

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>
)

@DylanVann
Copy link

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.

@KyleAMathews
Copy link
Contributor

@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

@KyleAMathews
Copy link
Contributor

Ideally of course we blend the both of both worlds.

@DylanVann
Copy link

@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.

@silvenon
Copy link

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.

@DylanVann
Copy link

DylanVann commented Jul 19, 2018

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.

@silvenon
Copy link

silvenon commented Jul 19, 2018

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. 😉

@ChristopherBiscardi
Copy link
Contributor

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 :)

@silvenon
Copy link

silvenon commented Jul 26, 2018

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

@ChristopherBiscardi
Copy link
Contributor

@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.

@avigoldman
Copy link
Contributor

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

@m-allanson
Copy link
Contributor

I think so, thanks everyone 🎉

@janosh
Copy link
Contributor

janosh commented Aug 28, 2018

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?

@silvenon
Copy link

silvenon commented Aug 28, 2018

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.

@CanRau
Copy link
Contributor

CanRau commented Aug 28, 2018

@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

@ChristopherBiscardi
Copy link
Contributor

@CanRau I'm rebasing that today (ChristopherBiscardi/gatsby-mdx#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.

@CanRau
Copy link
Contributor

CanRau commented Aug 28, 2018

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

@ChristopherBiscardi
Copy link
Contributor

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

@CanRau
Copy link
Contributor

CanRau commented Aug 28, 2018

kay got it, good to know

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

@silvenon
Copy link

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!

@ChristopherBiscardi
Copy link
Contributor

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 😝

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: question or discussion Issue discussing or asking a question about Gatsby
Projects
None yet
Development

No branches or pull requests