Implementing Syntax Highlighting

Using Prism.js for XML, PUG, YAML, C#, and Python

When building a website or documentation portal that involves a lot of code snippets, syntax highlighting becomes essential to enhance readability. Prism.js is a lightweight and customizable library for syntax highlighting.

In this guide, we will walk through setting up Prism.js for XML, PUG, YAML, C#, and Python and discuss automating this process using a custom build tool, `render-scripts.js`.

Adding Prism.js to Your website project

Step 1: Setting Up Prism.js
First, include Prism.js in your project. You can use the CDN or download it locally. Below is the CDN setup for the Coy theme.
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/themes/prism-coy.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/prism.min.js"></script>
Step 2: Adding Support for Specific Languages
To highlight specific languages like XML, PUG, YAML, C#, and Python add their corresponding language components.
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/components/prism-xml.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/components/prism-pug.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/components/prism-yaml.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/components/prism-csharp.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/components/prism-python.min.js"></script>
Step 3: Creating Code Blocks for Each Language

Wrap your code in <pre> and <code> tags with the appropriate language class for Prism.js to highlight them.

XML Example:

<rss version="2.0">
  <channel>
    <title>Latest Blog Posts</title>
    <link>https://markhazleton.com</link>
    <description>Recent blog posts from Mark Hazleton</description>
  </channel>
</rss>

PUG Example:

html
  head
    title Prism.js Highlight Example
  body
    h1 This is a PUG template
    p Use Prism.js for highlighting PUG syntax.

YML/YAML Example:

name: Update README with Latest Blog Posts
on:
  schedule:
    - cron: '0 0 * * *'
jobs:
  update-readme:
    runs-on: ubuntu-latest

C#/csharp Example:

using System;
public class HelloWorld {
  public static void Main(string[] args) {
    Console.WriteLine("Hello, World!");
  }
}
Step 4: Automating with render-scripts.js
In my build process, the `render-scripts.js` script bundles Prism.js, Bootstrap, and custom JavaScript files into a single, minified file for deployment. Here's the key part of the script that handles the combination of these scripts.
// Path to Bootstrap JS in node_modules (adjust according to your Bootstrap version)
const bootstrapJSPath = upath.resolve(upath.dirname(__filename), '../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js');

// Path to PrismJS and the YAML language component in node_modules
const prismJSPath = upath.resolve(upath.dirname(__filename), '../node_modules/prismjs/prism.js');
const prismYAMLPath = upath.resolve(upath.dirname(__filename), '../node_modules/prismjs/components/prism-yaml.min.js');
const prismXMLPath = upath.resolve(upath.dirname(__filename), '../node_modules/prismjs/components/prism-xml-doc.min.js');
const prismPUGPath = upath.resolve(upath.dirname(__filename), '../node_modules/prismjs/components/prism-pug.min.js');
const prismCSHARPPath = upath.resolve(upath.dirname(__filename), '../node_modules/prismjs/components/prism-csharp.min.js');
const prismPYTHONPath = upath.resolve(upath.dirname(__filename), '../node_modules/prismjs/components/prism-python.min.js');

// Paths for scripts.js and its destination
const sourcePathScriptsJS = upath.resolve(upath.dirname(__filename), '../src/js/scripts.js');
const destPathScriptsJS = upath.resolve(upath.dirname(__filename), '../docs/js/scripts.js');

// Read the Bootstrap JS, PrismJS, Prism YAML, and your scripts.js content
const [bootstrapJS, prismJS, prismYAML, prismXML, prismPUG, prismCSHARP,prismPYTHON, scriptsJS] = await Promise.all([
  fs.readFile(bootstrapJSPath, 'utf8'),
  fs.readFile(prismJSPath, 'utf8'),
  fs.readFile(prismYAMLPath, 'utf8'),
  fs.readFile(prismXMLPath, 'utf8'),
  fs.readFile(prismPUGPath, 'utf8'),
  fs.readFile(prismCSHARPPath, 'utf8'),
  fs.readFile(prismPYTHONPath, 'utf8'),
  fs.readFile(sourcePathScriptsJS, 'utf8')
]);

// Combine Bootstrap JS, PrismJS, Prism YAML, and your scripts.js
const combinedScripts = bootstrapJS + '\n' + prismJS + '\n' + prismYAML + '\n' + prismXML + '\n' + prismPUG + '\n' + prismCSHARP + '\n' + prismPYTHON + '\n'+ scriptsJS;
Step 5: Verifying and Customizing Highlighting
You can verify that Prism.js is working by viewing the page in your browser. Optional customizations can be applied using CSS to tweak the appearance. For example:
.token.keyword {
  color: #569cd6;
}

Other Considerations

Optimizing Performance with Prism.js

To optimize performance, consider using Prism.js’s autoloader to load only the necessary languages at runtime. Add the autoloader script like this:

Prism.js's autoload feature dynamically loads only the language components needed for a specific page, optimizing performance by reducing initial file size and improving load times. Instead of including all supported languages at once, the autoload plugin detects the language specified in the class attribute of a >code< block (e.g., language-js or language-csharp) and fetches the corresponding language component asynchronously from the server.

This is particularly useful for websites that support multiple languages but may not use all of them on every page. To enable this feature, you include the autoloader plugin script in your project, and Prism.js will handle the rest by loading languages as they are required, ensuring your site loads quickly while still supporting syntax highlighting for all necessary languages.

<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/plugins/autoloader/prism-autoloader.min.js"></script>
Theming with Prism.js

Theming with Prism.js allows bloggers to align the appearance of their code samples with the overall design and branding of their site. Customizing how code looks enhances both the visual appeal and readability, which can improve user experience and engagement. Prism.js provides a variety of out-of-the-box themes, such as Coy, Okaidia, and Solarized Light, that make it easy to integrate beautiful syntax highlighting with minimal effort.

However, bloggers often want more control over the colors and styles used for keywords, strings, comments, and other code elements. Prism.js supports a "roll your own" approach, enabling full customization of themes through CSS overrides. This flexibility allows bloggers to tailor code snippets to match their site’s style, making the presentation of technical content both consistent and aesthetically pleasing.

Prism.js offers various themes to fit the style of your website. Themes control the appearance of highlighted code elements like keywords, strings, comments, and more. Here are some popular Prism.js themes:

  • Coy
  • Okaidia
  • Solarized Light
  • Twilight
  • Funky
  • Dark

Each theme provides a different look and feel, giving flexibility in matching your site’s design. To switch between themes, simply replace the theme’s CSS file in your HTML with the new one. For example:

<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/themes/prism-twilight.min.css" rel="stylesheet" />

If the available themes don’t match your style, you can create a custom theme by overriding Prism.js’s default CSS. Here’s a simple guide to writing your own theme:

/* Customizing the color for keywords */
.token.keyword {
  color: #ff79c6;
}

/* Changing the background color of code blocks */
pre {
  background-color: #282a36;
  color: #f8f8f2;
}

/* Customize string tokens */
.token.string {
  color: #50fa7b;
}
You can override any token type supported by Prism.js. Common token types include `.token.keyword`, `.token.string`, `.token.comment`, and `.token.function`. Once your CSS is ready, include it in your project to apply the new styles:
<link rel="stylesheet" href="/path/to/your/custom-theme.css" />
Writing your own theme allows you to fully control the appearance of code snippets and seamlessly integrate them into your site's design.
Embedding Code in Markdown Blogs

If your blog uses Markdown, you can still use Prism.js for syntax highlighting by specifying the language after the opening backticks. Here's how to add different language code blocks in Markdown:

```xml
<rss version="2.0">
  <channel>
    <title>Latest Blog Posts</title>
    <link>https://markhazleton.com</link>
  </channel>
```

```pug
html
  head
    title Prism.js Highlight Example
  body
    h1 This is a PUG template
```

After generating your Markdown content, Prism.js will automatically highlight the code samples according to the specified language when rendered on your site.

Adding Copy-to-Clipboard Functionality

Prism.js offers a plugin to add a "copy to clipboard" button for each code block. Simply add the following script to enable this functionality:

<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>

Once included, the "copy" button will automatically appear in each code block, making it easy for your readers to copy code snippets with one click.

Adding Line Numbers to Code Blocks

If you want to display line numbers alongside your code, Prism.js has a line-number plugin. Include the plugin with the following CDN:

<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/plugins/line-numbers/prism-line-numbers.min.css" rel="stylesheet" />

To apply line numbers, add the `line-numbers` class to your `pre` tags:

SEO Considerations for Code Samples

Code samples can impact SEO if not optimized properly. Here are a few tips:

  • Use `<pre>` tags appropriately to indicate that it's source code.
  • Ensure code snippets are properly formatted and not too long to avoid readability issues.
  • Add descriptive titles and meta tags to help search engines understand your content.
Collapsible Code Blocks for Large Samples

For long code snippets, you can make the block collapsible to keep your blog post more readable. Here’s an example using Bootstrap's collapse component:

<button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#codeBlock" aria-expanded="false" aria-controls="codeBlock">
  Show/Hide Code
</button>
<div class="collapse" id="codeBlock">
</div>
Making Code Blocks Mobile Responsive

To ensure code snippets are readable on mobile devices, you can add responsive styles. Prism.js by default handles code wrapping, but you can ensure better readability by adding custom CSS:

pre {
  white-space: pre-wrap;
  word-wrap: break-word;
}
Embedding Code from GitHub Gists

Another option for sharing code snippets is embedding them directly from GitHub Gists. To do this, simply paste the script URL from the Gist into your HTML:

<script src="https://gist.github.com/username/gist-id.js"></script>

This will automatically render the Gist’s code in your blog post, allowing readers to view and copy it.

In conclusion, Prism.js is a powerful tool for syntax highlighting, and by automating its integration with a custom script like `render-scripts.js`, you can streamline the process and ensure all necessary languages are supported across your site.