Michal Miškerník

Rendering HTML in Gatsby

Posted on 16 December, 2018

The Gatsby blog starter uses the dangerouslySetInnerHTML prop to render HTML for blog posts written in Markdown. While this works, there is a better solution.

Along with the html property of each blog post, there is the htmlAst property which contains the AST (abstract syntax tree) of the rendered markdown document. For example, the first few words of this post look like this:

{
  "type": "root",
  "children": [
    {
      "type": "element",
      "tagName": "p",
      "properties": {},
      "children": [
        {
          "type": "text",
          "value": "The Gatsby blog starter ..."
        }
      ]
    }
  ],
  "data": {
    "quirksMode": false
  }
}

We can take the AST and turn it into React components, in this case it would become

<p>The Gatsby blog starter ...</p>

To do this we can use the hast-to-hyperscript module, which takes a HAST node and returns hyperscript. HAST is a format of abstract syntax trees that can be used to represent documents, and it is what we get from the htmlAst property. While the hast-to-hyperscript utility is originally meant for Hyperscript, it also supports creating React components using React.createElement. We can create a simple function to convert HAST to React components:

import React from "react";
import hastToHyperscript from "hast-to-hyperscript";

const renderHtmlToReact = node => {
  return hastToHyperscript(React.createElement, node);
};

Then we can use the function instead of the dangerouslySetInnerHTML attribute in Gatsby component:

import React from "react";
import { graphql } from "gatsby";
import { renderHtmlToReact } from "./utils/html";

const BlogPostTemplate = ({ data }) => (
  <article>
    <h2>{data.markdownRemark.frontmatter.title}</h2>
    <section>{renderHtmlToReact(data.markdownRemark.htmlAst)}</section>
  </article>
);

export default BlogPostTemplate;

export const query = graphql`
  query($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      htmlAst
      frontmatter {
        title
      }
    }
  }
`;
Served by Vercel