Faster syntax highlighting using Prism

15 Apr 2018

Prism is a lightweight syntax highlighter that makes your code snippets look cool/beautiful in your blogs and websites. It is built adhering to the proper HTML5 syntax recommendations (code.language-xxxx). It uses <code> tag and it doesn't require any crazy wrappers or tags around <code> tag.

The main advantage of this library is that., it allows you to customize the list of programming languages (out of 148 languages) that you need syntax highlighting  and also allows you to select the features you need (copy to clipboard, custom tool buttons, line-highlighting, line-number etc.,). This makes it as a perfect library for technical bloggers who to achieve decent look with a small sized library.

Lets get started - Grab on the required languages to support, features and theme from PrismJs custom build page. Then, you've couple of different ways to embed the Prism script based on your requirement as discussed below;

1. Synchronous script loading:

The basic usage requires us to embed a style-sheet, JavaScript and the <code> tag as follows

<!DOCTYPE html>
<html>
<head>
   <!-- You can select from the officially provided themes or the community ones -->
   <link href="themes/prism.css" rel="stylesheet" />
</head>
<body>
   <!-- It follows HTML5 spec format for embedding code in a webpage -->
   <pre>
       <code class="language-css">
           p { color: red }
       </code>
   </pre>

   <!-- Finally following script file contains the highlight rules based on the selected languages -->
   <script src="prism.js"></script>
</body>
</html>

Once above setup is done..,

The browser will load the script file synchronously i.e., blocking the page rendering for few seconds, till it completes the file download.
Again on page document ready event - Prism js will block the UI thread while it automatically scans the entire page for all <code> tags and applies the language syntax rules based on the classname.

This method is the easiest implementation which is best fit for highlighting small code snippets on your blog posts. 

The downside with this method is that., it absolutely blocks the browser JavaScript UI thread while downloading + highlighting phase. so, not suitable for highlighting huge code snippets.

2. Defer script loading:

This method uses the HTML attribute 'defer' which a synonym for post-pone. The major difference from above method is that, defer attribute will allow parallel downloading on the Prism js file without blocking the UI thread, while the rest of the webpage elements are still loading. Once document ready is hit & the Prism script is downloaded, it automatically starts scanning, parsing & initialization of code blocks.

<script src="prism.js" defer></script>

In-case you have a larger code snippet, this syntax highlight process would block the remaining JavaScript execution & component from loading on the page.., making your webpage page-load look sluggish.

This was the case for my blog, as I've shared lengthy code snippets on few posts. On low power devices like mobiles and lower version of IE, the browser User Interface will be frozen approx. for 8 secs and all following components will wait for the UI thread to complete the lengthier syntax highlighting. Any user interaction will result in Mobile browsers to prompt to kill the active tab. So it has become a destructive user experience behaviour from Prism-js.

So I browsed through their documentation on how to highlight lengthier code snippets. Which directed me to alternate implementation

3. Manual triggering of syntax highlighting:

Above implementation had a serious issue, as the code parsing happened at a undesired point of time in the page load & rendering cycle. So it would be really great if we can some how control the time when the parsing should happen i.e., preferred after entire page components are loaded. This is feasible with Prism-js.

For controlling the trigger, you need to decorate the script tag with an attribute 'data-manual'. This will stop the automatic syntax highlight processing phase. And you can trigger the syntax highlight using method - highlightAll() when the application is idle or free to process.

@Scripts.RenderFormat("<script src='{0}' data-manual='true'></script>", new string[] { "~/scripts/prism.js" })

<script type="text/javascript">
@* Once application is completely loaded, for e.g.., on window load event *@
window.onload(function(){
    Prism.highlightAll(false, function () {
        console.log('Syntax highlight completed');
    });
});
</script>

This method would require us to identify where & when to trigger the highlighter module. That case may vary from page to page and require code change on different trigger points.

Note: Prism.highlightAll() allows you to re-run the highlighter module on demand i.e., useful for cases when code part comes through AJAX.

4. Using web workers for syntax highlighting:

Till now the code parsing was done on the main UI thread, that results in either

  • blocking the UI thread synchronously or
  • forcing us to write code for choosing the perfect time to run the highlight parser.

With modern technologies & methods evolving, Web Workers offered a convenient way to move the heavy syntax highlighter's parsing out completely out of browser UI thread. The web workers can run in parallel as soon as the script gets downloaded, and can continue highlighting without interfering with the rest of the webpage's load cycle.

Web worker is currently being used in this blog for highlighting the lengthier code snippets efficiently. The usage is as below ** just watch out for the parameter passed to the manual trigger

@Scripts.RenderFormat("<script src='{0}' data-manual='true'></script>", new string[] { "~/scripts/prism.js" })

<script type="text/javascript">
    Prism.highlightAll(true, function () {
        console.log('Syntax highlight completed');
    });
</script>

When async is passed as 'true', PrismJs will validate the browser's web worker support and proceed with parallel initializing through Web Workers or else just fallback to the synchronous method.

Prism has really done heavy lifting by adopting the modern browser feature - Web Workers to perform the syntax highlighting process, which makes it feasible for technical bloggers like us to share lengthier source-codes on posts without making the page sluggish.

Hope this post helps you to sort out all Prism issues related to slow highlighting/unresponsiveness on pages, with huge amount of code parts & comments.