Implementing highlight.js in an Express app for a super fast pre-rendered code highlighting What we are Building A simple server side Node / Express code to transform Markdown content into a fully formatted HTML with highlighted code blocks. Technologies we use is a lightweight markup language with plain text formatting syntax. Its design allows it to be converted to many output formats. Markdown is a syntax highlighter written in JavaScript. It works in the browser as well as on the server. It works with pretty much any markup, doesn’t depend on any framework, and has automatic language detection. Highlight.js is a friendly interface backed by an ecosystem of plugins built for creating and manipulating content. Unified Unified plugins: , , , remark-parse remark-rehype rehype-stringify rehype-highlight Why Use server side code highlighting? At we decided to implement server side code highlighting to boost the loading time of our main website. We constantly benchmark our website with and aim for top performance scores. Regbrain Lighthouse Loading JavaScript to highlight code in the browser was taking too much time. First, the JavaScript files had to be fetched and then the browser was repainting the content resulting in a slower website. To improve speed, we decided to implement code highlighting on a server and now we send fully formatted html to the browser. At this point, you may be wondering, We will explore that in more details later on, but first, let's walk through our technical solution. how is highlighting code server side performant? Server side code highlighting Our articles are written in markdown so our workflow needs to take raw markdown as input and serve a fully formatted html. We do it in the following steps: 1. Fetch markdown content 2. Transform markdown into a markdown syntax tree using remark-parse 3. Transform markdown syntax tree to html syntax tree using remark-rehype 4. Traverse html syntax tree to apply code highlighting to content within <code> tags using rehype-highlight 5. Transform html syntax tree to string to send to the client using rehype-stringify We achieve all the above with framework and plugins as follows: unified Import required libraries We grab the unified framework and the required plugins unified = ( ) markdown = ( ) remark2rehype = ( ) highlight = ( ) html = ( ) let require 'unified' let require 'remark-parse' let require 'remark-rehype' let require 'rehype-highlight' let require 'rehype-stringify' Create a unified processor We create a processor which pipes together all the plugins above to achieve our chain of transformations from markdown to fully highlighted html: processor = unified() .use(markdown) .use(remark2rehype) .use(highlight) .use(html) let // Transform markdown into a markdown syntax tree // Transform markdown syntax tree to html syntax tree // Traverse html syntax tree to apply code highlighting to content within code tags // Transform html syntax tree to string to send to the client Transform! We now have the which can parse any markdown input as follows: processor input = some markdown content output = processor.process(input) let let await Express js router implementation example We implement the above steps in our Express app as follows: express = ( ) router = express.Router() unified = ( ) markdown = ( ) remark2rehype = ( ) html = ( ) highlight = ( ) router.get( , { input = article.from.database.in.markdown() processor = unified() .use(markdown) .use(remark2rehype) .use(highlight) .use(html) output = processor.process(input) res.render( , output) }) .exports = router let require 'express' let let require 'unified' let require 'remark-parse' let require 'remark-rehype' let require 'rehype-stringify' let require 'rehype-highlight' '/:slug' async ( ) function req, res, next let await let let await 'article' module Don't forget the CSS The last thing we need to do is to include highlight css styles on our pages. The easiest way would be to simply link them as external styles, but that would impair our website loading speed as fetching external styles blocks page rendering. To avoid the performance penalty, we include all css as an internal style on a page. <!doctype html> <head> <style> {all page's style including highlightjs css} </style> </head> <body> </body> < > html </ > html The performance question revisited How do we make server side rendering performant? Even though the above code highlighting slows down our server a little bit compared to sending 'clean' html, we implement a number of additional layers below which allow us achieve excellent page loading speed: - we serve our main content as AMP pages by default. That means that Google and Bing can cache our pages and serve it really fast on mobile devices. AMP - we do not use any blocking external resources such as styles, images or JavaScript files. This is already enforced by following the AMP specification, but even if we did not implement AMP, this would a good approach to take to improve page load speed. All our css is internal. We prepare css server side and make it specific to the type of content that we serve to avoid including unused styles (...within reason...). No external styles or JavaScript (other than async AMP) - we use css and html minification to further reduce the size of our pages. Minification - we use a global content distribution network and , we also configure asset compression for our CDN. CDN configure our HTTP headers to get benefits of CDN caching With the set up above we can serve even ten , which works out to be really cost attractive compared to various options of hosting individual apps separately as a service. Express apps on the smallest AWS EC2 instance