By: John Furrow

Recently, the Mapbox Design team was exploring ways to leverage the power of Javascript and React components without losing the ease-of-use of markdown and Jekyll. What we ended up creating was jsxtreme-markdown, a tool to transform markdown with interpolated Javascript expressions and React components into pure JSX. If a challenge like this interests you, check out our opening for a front-end engineer position.

We’ve been using Jekyll to build Mapbox websites for a long time. Jekyll is a static website generator that transforms markdown (using the Liquid templating engine), HTML, and CSS into static, ready-to-serve files. Everyone at Mapbox learns to write markdown due to our extensive use of Github, and it’s incredibly powerful. With markdown, our content writers can focus exclusively on the content and its prose.

Our goal was to find a way to retain the same features that Jekyll affords us but also leverage the power of dynamically rendered, component-based Javascript applications (for robust interactivity, repeatable design patterns, client-side routing, code splitting, better development tooling, and more).

React and Webpack are two other tools we use to build robust and performant web applications. And with such active open source communities, they provide a fantastic developer experience. We didn’t want to abandon what markdown is really good at — building templated, repetitious content web pages. So how do we leverage the power of interactive web applications and the development tools for our static websites without forcing everyone to learn a new syntax (JSX and Javascript)?

There are several really useful tools that can convert markdown to Javascript files. They almost provided everything we needed, but not quite — the ideal solution had to encourage people to continue contributing content in markdown without discouraging the use of reusable React components. This is why we thought it would be smart to use React components and Javascript expressions in the same way our content writers use Liquid templating directly in markdown. So David Clark embarked on the journey of building this tool, and thus jsxtreme-markdown was gifted to the world.

jsxtreme-markdown provides flexible, low-level tools that transform markdown into pure JSX, optionally interspersed with run-time Javascript expressions and JSX components. This combines all of the things we love about markdown, liquid templating, React components, and modern tooling. In effect, it allows our content writers to mix interactive elements of arbitrary complexity with natural prose.

Here’s a basic example:

{{ <FancyReactContentWrapper> }} This is a **Markdown** paragraph inside our `FancyReactContentWrapper`. We can even access our frontmatter variables in our Javascript expressions, like this: {{ `Foo is ${frontMatter.foo}` }} We can just as easily render a standalone React component: {{ <StandaloneReactComponent title={fontMatter.title} isInteractive={fontMatter.category === 'interactive'} /> }} {{ </FancyReactContentWrapper> }}

You can experiment with jsxtreme-markdown live, in your browser, at our REPL environment.

The repository contains three independently published packages:

jsxtreme-markdown, a script which does the actual conversion of markdown, optionally with interpolated run-time Javascript expressions and JSX) into JSX, and creates a React component module which renders the result of the transformation

babel-plugin-transform-jsxtreme-markdown, a Babel plugin that compiles tagged template literals of jsxtreme-markdown into raw JSX

jsxtreme-markdown-loader, a Webpack loader that uses jsxtreme-markdown to transform markdown and return valid React component modules

We split up these three packages to avoid locking users into any particular build tool. If you don’t use Webpack or Babel, that’s no problem! You can still use jsxtreme-markdown on its own, or build a plugin for your workflow of choice (e.g. gulp-jsxtreme-markdown).

jsxtreme-markdown exposes two functions, toJsx and toComponentModule .

toJsx accepts a markdown string and returns a JSX string, retaining any run-time JSX and Javascript expressions.

Input:

I love **markdown**! *So easy* to write. But I also love {{ <ReactComponent> }}React components{{ </ReactComponent> }}!

Output:

toComponentModule creates a React component module containing the JSX returned by toJsx . You can provide your own module template, but jsxtreme-markdown ships with a sane default template.

Input:

---

title: Everything is ok

quantity: 834

prependJs:

- "const Timer = require('./timer')"

- "import { Watcher } from './watcher'"

--- # {{ frontMatter.title }} Some introductory text. The quantity is {{ frontMatter.quantity }} {{ <Watcher /> }} This paragraph includes a {{ <Timer /> }}.

Output:

babel-plugin-transform-jsxtreme-markdown exposes a “fake” module, which can be used as follows:

Input:

const md = require('babel-plugin-transform-jsxtreme-markdown/md'); const foo = md` # Title This is **bold.** Here is a [link](/some/url). `;

Output:

jsxtreme-markdown-loader allows you to leverage the use of Webpack, so that your markdown files can be treated just like any other Javascript components.

One of the challenges faced in developing this tool was interpolating the Javascript expressions and JSX in the markdown.

During implementation, we realized it would be necessary to replace the Javascript expressions and JSX elements with unique placeholder IDs, in order to accurately re-insert into the interpolated markup. Some clever tricks were employed to ensure this solution worked with our chosen syntax highlighter, highlights, which runs at compile time. Check out the placeholders logic.

Another challenge was the seemingly daunting task of creating the Webpack loader and Babel plugin. As it turns out, they don’t have to be complicated. Both tools make the process simple and straightforward, allowing the loader and plugin logic to be simple as well. Check out the code for yourself: Webpack loader and Babel plugin

Many Mapbox websites are already using jsxtreme-markdown. We recently ported our Android documentation, and we’re pleased with the results so far.

Interested in helping us building tools like this? Check out our jobs page. Currently, we’re looking for a front-end engineer on the design team.