Skip to content

Sidenotes for Markdown

March 28, 2022

Update (April 25, 2022)

This site has since been rebuilt with Next.js using MDX v2. The post below references MDX v1 when markdown could be passed into a React component as a prop and correctly converted to HTML at build time. This is no longer possible.

I recently wanted to add sidenotes (also called margin-notes) to my blog. This site is built with Gatsby.js and content is written in Markdown. If you’ve searched for a solution to this problem before you have likely ran into Gwern’s post. They cover known sidenote implementations comprehensively, so we don’t need to do our own homework. We do, however, need to figure out a way to make this accessible in Markdown.

The solution I was looking for entailed:

  1. No JavaScript after build-time
  2. Simple syntax for using sidenotes
  3. Resulting markup is somewhat semantic

Quick Demo

On desktop this text fills the right margin and on mobile it simply collapses into a box below the relevant paragraph.

Remark Plugin (Not Recommended)

I originally wrote this blog using Remark. The common, hacky way to build custom components using Remark is to create a codeblock with the language set to a specifier for your component instead of a real programming language.

Take for example, if we wanted to add a newsletter button in our page:

# My title

This is a button:

```newsletter-button
Subscribe to my newsletter!
```

We would then write a Remark plugin that checks all code blocks in the syntax tree where lang is newsletter-button and then swap the code block’s node out for a custom HTML node.

This is pretty much exactly what I did when building sidenotes with Remark. If you just want to see the Gatsby plugin itself, all of it can be found here. I no longer recommend this approach. Keep reading for a more idiomatic method of implementing sidenotes.

With MDX

MDX is a markup format that allows you to use JSX within markdown. It blends rich-text and dynamic presentational methods. As a result, we only using forceful syntax when necessary. If you are used to writing React like I am, this leads to a very comfortable writing experience.

Below is an example of a React component (Sidenote) being used in an MDX file.

<Sidenote content={"An interesting tidbit about foo!"}>
  Something about foo.
</Sidenote>

If you need it the Sidenote component is implemented as follows:

const Sidenote: FC<SidenoteProps> = ({ children, content }) => (
  <div className="sidenote-wrapper">
    {children}
    <aside>{content}</aside>
  </div>
)

Limitations

While this approach is simple, it is also naive. It requires some manual restraint as overlaying is not dealt with in any way. In other words, if there are many sidenotes in the page gutter, then they will stack on top of one another. My writing style does not lean on sidenotes too hard, so this works out.