@lightpohl

Simple Markdown Wrapper Component

April 29th, 2017

Marked and Prism

When creating this website, I wanted each blog entry to be a separate markdown file, and simply have them correspond to a specific path. Since the website is built with React, it made sense to create a component that would accept a string of markdown and handle the conversion for rendering the markdown on the page.

Thankfully, there are a couple open source libraries that make this rather easy: Marked (v0.6.0) for converting the string to HTML, and Prism (v1.15.0) for providing syntax highlighting if you need it.

Creating our Basic Component

The component is very easy to setup, and only requires a few lines of code outside of normal React boilerplate. Install Marked as a dependency and you're ready to go. We will be adding lifecycle methods later, so I'll go ahead and use React.Component now instead of our other options.

import React from 'react';
import PropTypes from 'prop-types';
import marked from 'marked';

export default class MarkdownWrapper extends React.Component {
  static propTypes = {
    markdown: PropTypes.string,
  };

  static defaultProps = {
    markdown: '',
  };

  render() {
    let markdown = marked.parse(this.props.markdown, {sanitize: true});

    return (
      <div
        // We will need the ref for syntax highlighting
        ref={(el) => (this.markdownWrapperRef = el)}
        className="markdownWrapper"
        dangerouslySetInnerHTML={{__html: markdown}}
      />
    );
  }
}

In order to insert the generated HTML from Marked with React, we need to use dangerouslySetInnerHTML. It shouldn't be used with unsanitized user input, but since I'll be writing all the markdown content that will be passed into this component, I'm not worried about it.

Marked has a variety of settings for parsing. You can change them from their default values by passing a config object as the second parameter. The only setting I've changed from the default value is sanitize, which when set to true will ignore any HTML in the input string.

Adding Syntax Highlighting

Marked has great support for highlight.js. After installing, you would tell Marked to use highlight.js in the constructor of the component, and that would be it. There are a few examples of that in the Marked docs. Unfortunately, highlight.js lacks support for JSX, so I needed to find another option.

Prism works quite well, and has support for JSX. It takes a little bit more time to setup, and I did have issues with the npm version at first, but it wasn't too much of a hassle. I would recommend installing Prism via npm and downloading the styles you want from their website.

Let's put the rest of the component together and talk about it.

...

import Prism from "prism";

// Downloaded from prismjs.com
import "./prism.css";

class MarkdownWrapper extends React.Component {
  componentDidMount() {
    prism.highlightAllUnder(this.markdownWrapperRef);
  }

  componentDidUpdate() {
    prism.highlightAllUnder(this.markdownWrapperRef);
  }

  ...
}

Prism's highest level function is highlightAll(), which uses document.querySelectorAll(), and highlights every matching code block on the page--regardless if it is in the MarkdownWrapper component or not.

Instead, we are using highlightAllUnder() and passing a reference to our containing div, which will only highlight code blocks found in our markdown component.

Only Including Some Languages

Using Prism via npm is very convenient, but if we're not careful, we can add a sizeable amount of unneeded bloat to our JavaScript bundle by including more language support than we need.

We can use babel-plugin-prismjs to specify the languages we want to support with highlighting, and the rest will not be included in our bundle. After installing the plugin, we'll add the plugin to our list of plugins in our Babel config file and specify a list of languages like so:

{
  "plugins": [
    [
      "prismjs",
      {
        "languages": ["javascript", "jsx", "css", "markup", "bash", "json"]
      }
    ]
  ]
}

Conclusion

Using Marked and Prism you can fairly easily create a React component to render text written in markdown with syntax highlighting and all.

Next
Async Components in React Router 4
Written by Joshua Pohl
Software Engineer in San Antonio, Texas. Find me on Instagram.