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
}
}
}
`;
Feel free to contact me. I am always open to discussing new projects, creative ideas or opportunities to be part of your visions.