remix: getting syntax highlighting working

2022-01-30

 | 

~3 min read

 | 

559 words

In working through Remix’s Developer Blog tutorial, I saw an opportunity for this website. There’s not a lot on here other than some markdown files. And the markdown itself is fairly simple - though there’s quite a bit of code snippets that I wanted to be able to style.

There are a lot of options when it comes to parsing markdown, but the tutorial recommended marked, so that’s where I started.

Once I had the words on the page, there was the matter of styling it however.

The Marked docs discuss highlighting on the Advanced Usage page and demonstrate two separate ways of applying highlighting:

  1. Synchronously
  2. Asynchronously

I started with the synchronous approach using highlight.js largely because the other recommendation was pygmentize.js which has a hard dependency on pygments, a python package. Ensuring that my build environment has python and a compatible version of pygments didn’t sound appealing.

In my app/posts.tsx file, I have:

app/posts.tsx
marked.setOptions({
  highlight: function (code, lang) {
    const hljs = require("highlight.js/lib/common")
    const language = hljs.getLanguage(lang) ? lang : "plaintext"
    return hljs.highlight(code, { language }).value
  },
  langPrefix: "hljs language-",
})

export async function getPost(slug: string) {
  const filepath = path.join(postsPath, slug + ".md")
  const file = await fs.readFile(filepath)
  const { attributes, body } = parseFrontMatter(file.toString())
  invariant(
    isValidPostAttributes(attributes),
    `Post ${filepath} is missing attributes`,
  )

  const html = marked.parse(body)
  return { slug, title: attributes.title, html }
}

Okay, so at this point we’re parsing the markdown but the styling isn’t being applied.

The last step is to load the CSS. This Stack Overflow post gave me the clue on what I’d need to do, however, since I’m using Remix, instead of using the package, I thought I’d use the <link> tag, which is the recommended approach:

The primary way to style in Remix (and the web) is to add a to the page. In Remix, you can add these links via the Route Module links export at route layout boundaries. When the route is active, the stylesheet is added to the page. When the route is no longer active, the stylesheet is removed.

import { LinksFunction } from "remix"
export const links: LinksFunction = () => {
  return [
    {
      rel: "stylesheet",
      href: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/monokai.min.css",
      integrity:
        "sha512-RLF8eOxhuwsRINc7r56dpl9a3VStqrXD+udWahutJrYdyh++2Ghnf+s4jFsOyryKZt/GNjPwbXVPH3MJpKrn2g==",
      crossorigin: "anonymous",
    },
  ]
}
Note: Finding the style sheet

Figuring out where to go to find the style sheet actually wasn’t particularly straight forward, but I knew I wanted to use a CDN if it was available, so that was my Google search.

From there, I found https://cdnjs.com/libraries/highlight.js which had a number of links. After that it was just looking for the particular styles that I wanted to use.

Note: Integrity key

The integrity key in the link is a subresource integrity hash.

Initially, I put the links function in the app/root.tsx. This seems reasonable since nearly all of my posts have code snippets and the site is 98% posts.

But, this also means that anyone who visits the site always downloads the css even if they never want to use it.

So, I ran an experiment and moved it to app/routes/posts/$slug.tsx - the route which actually needs the styling.

The waterfall shows that we only download the CSS once but each subsequent page that needed it had the appropriate styling!

Et voilá! Hooray for using the platform!


Related Posts
  • Exploring Remix
  • The Basics For Using Subresource Integrity


  • Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!