Skip to content
Engineering

Creating Rich & Interactive Blog Posts

Understanding unified, mdast, rehype, remark, MDX

When building this blog, I wanted posts that are easy to write and maintain while still offering interactivity and customization. My answer was the unified ecosystem, along with tools like Remark, Rehype, and MDX. In fact, the post you are reading right now is rendered by the very pipeline this article describes: this blog runs its own remark plugins (one that turns external links into preview cards, another that normalizes the legacy lang:filename code-fence syntax so the design system's code plugin can render the file name as the block title) alongside a rehype plugin that transforms internal post links into interactive preview cards.

Markdown has many different flavors and different people have different requirements for features they expect from it. unified provides a powerful and flexible framework for parsing, transforming, and rendering content in various formats, including Markdown, HTML, and JSX. It takes a string and turns it into structured data (syntax tree) that other plugins can work with. Plugins can do anything, from spellcheck to linting or transforming from one format to another (e.g. Markdown → HTML). Unified on its own doesn’t do much. It acts as a unifying package for all the other plugins out there.

By leveraging these tools, I get interactive and engaging blog posts that combine the simplicity of Markdown with the capabilities of modern JavaScript frameworks, and in this post I'll walk through how each piece fits together.

MDX: Markdown Meets JSX

MDX combines Markdown with JSX, allowing you to embed React components directly within your content. This enables interactive and dynamic elements, such as charts, forms, and widgets, to coexist with Markdown’s simplicity.

For instance, an MDX file might look like this:

# My Interactive Blog
This is a regular Markdown paragraph.
<Chart data={[1, 2, 3, 4]} />
Another paragraph follows the chart.

In this example, the <Chart /> component is a React element that renders an interactive chart. With MDX, you can seamlessly integrate such components into your posts without sacrificing readability or maintainability.

The Foundation: unified

At the core of this ecosystem is unified, a library that simplifies content processing by representing it as abstract syntax trees (ASTs) using the unist specification. These trees serve as structured representations of content, whether it's written in Markdown, HTML, or even JSX. By standardizing how content is processed, unified makes it possible to build reusable and extensible tools for transforming and manipulating text.

One of the standout features of unified is its pipeline architecture. A pipeline processes input content (such as Markdown) through various stages, including parsing, transforming, and stringifying. Each stage can be enhanced with plugins to introduce new functionality, making it easy to customize your content workflow.

Syntax trees are representations of source code or even natural language. These trees are abstractions that make it possible to analyze, transform, and generate code.

  • Concrete Syntax Trees (CST): structures that represent every detail (such as white-space in white-space insensitive languages)
  • Abstract Syntax Trees (AST): structures that only represent details relating to the syntactic structure of code (such as ignoring whether a double or single quote was used in languages that support both, such as JavaScript).

Remark: Markdown Processing Made Easy

Remark is a unified processor specifically designed for working with Markdown. It transforms Markdown text into a Markdown Abstract Syntax Tree (MDAST), which provides a structured format for analyzing and manipulating the content.

For example, consider a blog post written in MDX (remark learns the JSX syntax through the remark-mdx plugin):

# My Interactive Blog
Welcome to my blog. Here's an interactive chart:
<Chart data={[1, 2, 3, 4]} />

Remark parses this text into an MDAST, where each element (heading, paragraph, etc.) becomes a node in the tree. Plugins can then be applied to modify the structure, perhaps to add links, convert headings to anchor links, or inject custom elements. Once transformations are complete, Remark can serialize the MDAST back into Markdown, or hand it to Rehype (through remark-rehype) to produce HTML.

By combining Remark with plugins, you can:

  • Enforce consistent Markdown formatting.
  • Automatically generate tables of contents.
  • Convert Markdown into enriched formats such as HTML or MDX.
Any package that starts with remark-* is operating on the Markdown syntax tree (MDAST).

Breakdown of MDAST Structure

  • Root Node: The root node represents the entire document.
  • Heading Node: The first child is a heading (indicated by the # symbol in Markdown). It has a depth of 1, indicating that it is a top-level heading.
  • Paragraph Node: The next child is a paragraph, as indicated by the plain text following the heading. The paragraph is further broken down into text nodes for each segment of text.
  • MDX JSX Node: An mdxJsxFlowElement named Chart represents the <Chart data={[1, 2, 3, 4]} /> component; its props become attributes, with the data expression kept as raw source.

Each of the Markdown elements (headings, paragraphs, etc.) is mapped to a specific node in the MDAST. This enables manipulation, transformation, or further processing of the content in a programmatic way.

Rehype: HTML and Beyond

While Remark excels at handling Markdown, Rehype is the go-to tool for working with HTML. Like Remark, Rehype parses content into an abstract syntax tree, known as HAST (Hypertext Abstract Syntax Tree). This tree is ideal for making HTML-specific transformations, such as adding classes, optimizing images, or embedding scripts.

Rehype becomes particularly useful when converting Markdown to HTML. Using the remark-rehype plugin, you can transform an MDAST into a HAST, enabling a seamless transition from Markdown to HTML. From there, Rehype plugins can be used to enhance the HTML output for performance and accessibility.

Any package that starts with rehype-* is operating on the HTML syntax tree (HAST).
# My Interactive Blog
Welcome to my blog. Here's an interactive chart:
<Chart data={[1, 2, 3, 4]} />

Breakdown of HAST Structure

  • Root Node: Represents the HTML structure.
  • Element Node: Each h1 (heading) and p (paragraph) in the original MDX content is converted to corresponding HTML elements (<h1> and <p>).
  • JSX shown as a div: The <div class="chart"> standing in for <Chart /> is an illustration I invented for this example; no step in the pipeline produces it. In a real MDX pipeline the mdxJsxFlowElement passes through remark-rehype untouched and is compiled into a call to your Chart component.

Rehype vs. Remark: Choosing the Right Tool

There are several advantages to working with MDAST (Markdown Abstract Syntax Tree) over HAST (Hypertext Abstract Syntax Tree) in certain contexts, especially when you’re dealing with Markdown content and need to perform transformations or custom processing. Here are some reasons why MDAST might be preferable in specific use cases:

1. Markdown-Centric Workflow

  • Designed for Markdown: MDAST is specifically tailored for representing Markdown content, which means it’s optimized for the structure and elements found in Markdown (headings, paragraphs, lists, links, etc.). This makes it more intuitive and efficient when you are working directly with Markdown content.

  • Parsing and Transforming Markdown: If your starting point is Markdown, MDAST allows you to work directly with the parsed tree in a way that aligns closely with the way Markdown is structured. For example, it simplifies handling Markdown-specific elements like frontmatter, links, code blocks, and lists.

2. Flexibility for Custom Transformations

  • Ease of Transformation: Since MDAST is abstracted at a higher level (closer to the semantic structure of the content), it’s easier to perform transformations on the content. You can apply plugins to rewrite or extend Markdown content in a way that is agnostic of the eventual output format.

    • For example, you might want to automatically add anchors to all headings, or lint Markdown to ensure it follows certain formatting rules, which can be more straightforward using MDAST.
  • Simplifies Markdown-to-HTML Conversion: While MDAST focuses on the structure of Markdown, you can later convert it to HTML (or any other format) with tools like remark-rehype or other plugins. This gives you greater control over the transformation process.

3. Preserving the Semantic Structure

  • Better Content Integrity: MDAST allows you to maintain the integrity of the semantic structure of the content, such as distinguishing between different heading levels, paragraphs, and other semantic elements. This is crucial if you need to manipulate or analyze content while preserving its structure before converting it to another format like HTML.

    • For example, when working with Markdown, you can easily identify the headings, lists, and other elements that are important for tasks such as content generation, indexing, or creating a table of contents.

When HAST Might Be Better

  • HTML-Centric Transformations: If your main goal is to manipulate the HTML output directly (such as optimizing the final HTML for SEO or adding specific HTML elements like classes or ids), HAST might be more suited to your needs. HAST provides a detailed representation of the HTML structure, which is better for fine-tuning HTML attributes and working with HTML-specific content.

  • Final Output Target: If your main concern is producing high-quality, optimized HTML for rendering in browsers, then HAST is a natural choice. This is especially true if you're working in a more web-focused environment where your output needs to be fine-tuned for performance, accessibility, or other HTML-specific factors.

Building a Workflow for Interactive Blog Posts

The real power of these tools emerges when they are used together. Here’s a typical workflow for creating an interactive blog post:

  1. Write Content in Markdown: Start with simple Markdown to draft your content.
  2. Enhance with Remark: Use Remark plugins to lint your Markdown, generate a table of contents, or add custom syntax transformations.
  3. Transform to HTML with Rehype: Convert Markdown to HTML and apply Rehype plugins to add interactivity or optimize the output.
  4. Integrate JSX with MDX: Embed React components to introduce dynamic and interactive elements.
  5. Render in Your Application: Use a framework like Next.js to render your MDX content as part of a blog or documentation site.

Conclusion

unified offers a robust toolkit for creating modern, interactive, and engaging blog posts, and it is the toolkit this blog itself runs on: every post here, including the one you have just read, passes through the remark and rehype pipeline described above before it reaches your screen.

By leveraging this ecosystem, I have been able to turn static posts into richer, more interactive experiences while keeping my writing workflow as simple as editing a Markdown file.

The opinions and views expressed on this blog are solely my own and do not reflect the opinions, views, or positions of my employer or any affiliated organizations. All content provided on this blog is for informational purposes only.

I also offer consulting and mentoring in AI strategy, engineering leadership, and scaling teams. Get in touch if I can help.