Back to Blog

Remove runts from your Markdown with ~15 lines of code

Runts are a small but annoying pet peeve of mine — luckily, we can fix them.

·1,274 views

This tutorial is a super quick one, because you only need about 15 lines of code to fix pesky typographic runts.

What the hell are runts?

Have you ever noticed a paragraph of text that ends with a word all by its lonesome on the last line? Here’s an example:

A visual demonstration of a typographic runt. There is paragraph of lorem ipsum text, and on the
last line is the word libero all by itself.

I don’t know about you, but that really grinds my gears.

Let’s fix ’em

Astro makes it super easy to add remark plugins to your projects. By default, Astro uses a package called smartypants to turn straight quotes and apostrophes into curly ones. We can extend this base configuration with our own, custom plugins.

This post will assume you already have your Astro project set up for Markdown (.md or .mdx). If you need help setting that up, I recommend following Astro’s “Build a Blog” tutorial.

Getting started

We’re only going to need one package, unist-util-visit:

pnpm add unist-util-visit

This package lets you “walk the tree,” by visiting each DOM node. We will do that so we can parse the text content of each node and transform it by adding a non-breaking space between the last two words.

Writing our plugin

Now, we’ll create the JS file that will actually do the transformation. I like to store my custom plugins in a folder called /remark-plugins at the root of my project, but you’re welcome to put them wherever.

remark-deruntify.mjs
import { visit } from 'unist-util-visit';
 
export function remarkDeruntify() {
  function transformer(tree) {
    visit(tree, 'text', function (node) {
      let wordCount = node.value.split(' ').length;
 
      if (wordCount >= 4) {
        node.value = node.value.replace(/ ([^ ]*)$/, '\u00A0$1');
      }
    });
  }
 
  return transformer;
}

So, what’s going on here? 🤔

  1. We’re exporting a custom function that goes through each node’s text content,
  2. and checks if there are more than 4 words;
  3. if there are, it inserts a non-breaking space entity between the last two words.

Now we need to import our function into our Astro config file and enable it for Markdown content:

astro.config.mjs
import mdx from '@astrojs/mdx';
import { defineConfig } from 'astro/config';
 
import { remarkDeruntify } from './remark-plugins/remark-deruntify.mjs';
 
export default defineConfig({
  integrations: [mdx()],
  markdown: {
    remarkPlugins: [remarkDeruntify],
  },
});

That’s it!

Feel free to tailor the wordCount in the remark-deruntify.mjs file to your liking — 4 was a decent starting point for me, personally.

Want this in Next.js?

Here’s how you could configure a Next.js project using Contentlayer to do the same thing:

contentlayer.config.js
import { remarkDeruntify } from './lib/remark-deruntify.js';
 
export default makeSource({
  contentDirPath: './content',
  documentTypes: [Blog], // your doc types
  mdx: {
    remarkPlugins: [remarkDeruntify],
  },
});

Feel free to drop me a line or tweet at me. I am always open to improving this tutorial or my code examples.

Further reading