A selfie of me hiking on snowy Mt. Rainier. I am smiling while wearing my hat and sunglasses. Behind my head, in the distance, you can see the looming peak of Mt. Adams.
0 min read
Published · Updated

Remove runts from Astro Mark­down files with ~15 lines of code

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

This tu­to­r­ial is a super quick one, be­cause you only need about 15 lines of code to fix pesky ty­po­graphic runts.

What the hell are runts?

Have you ever no­ticed a para­graph of text that ends with a word all by its lone­some on the last line? Here’s an ex­am­ple:

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.

(Cour­tesy: opus­de­sign.us)

I don’t know about you, but that re­ally grinds my gears.

Let’s fix ’em

Astro makes it super easy to add re­mark plu­g­ins to your projects. By de­fault, Astro uses a pack­age called smartypants to turn straight quotes and apos­tro­phes into curly ones. We can ex­tend this base con­fig­u­ra­tion with our own, cus­tom plu­g­ins.

This post will as­sume you al­ready have your Astro project set up for Mark­down (.md or .mdx). If you need help set­ting that up, I rec­om­mend fol­low­ing Astro’s “Build a Blog” tu­to­r­ial.

Get­ting started

We’re only going to need one pack­age, unist-util-visit:

pnpm add unist-util-visit

This pack­age lets you “walk the tree,” by vis­it­ing each DOM node. We will do that so we can parse the text con­tent of each node and trans­form it by adding a non-​breaking space be­tween the last two words.

Writ­ing our plu­gin

Now, we’ll cre­ate the JS file that will ac­tu­ally do the trans­for­ma­tion. I like to store my cus­tom plu­g­ins in a folder called /remark-plugins at the root of my project, but you’re wel­come to put them wher­ever.

remark-plugins/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 ex­port­ing a cus­tom func­tion that goes through each node’s text con­tent,
  2. and checks if there are more than 4 words;
  3. if there are, it in­serts a non-​breaking space en­tity be­tween the last two words.

Now we need to im­port our func­tion into our Astro con­fig file and en­able it for Mark­down con­tent:

astro.config.mjs
import { defineConfig } from "astro/config"

import mdx from "@astrojs/mdx"

import { remarkDeruntify } from "./remark-plugins/remark-deruntify.mjs"

export default defineConfig({
	integrations: [mdx()],
	markdown: {
		remarkPlugins: [remarkDeruntify],
	},
})

That’s it! Feel free to tai­lor the wordCount in the remark-deruntify.mjs file to your lik­ing — 4 was a de­cent start­ing point for me, per­son­ally.

Work­ing ex­am­ple

Feel free to drop me a line or tweet at me. I am al­ways open to im­prov­ing this tu­to­r­ial or my code ex­am­ples.


Fur­ther read­ing

Interested in working together?

Reach out to learn more about me and/or my work.