Code Syntax Highlighting for Prismic in Vue & Nuxt

Prismic CMS
Vue
Nuxt
April 24, 2022

Learn how to implement a Code Syntax Highlighter using highlight.js for your preformatted data from Prismic, including a language selector.

Code Syntax Highlighting for Prismic in Vue & Nuxt

💡

You should be familiar with the basic concepts of Prismic. If not, get started reading the Prismic Docs.

Prepare CMS

To achieve our goal, we will create a Slice in Prismic. We could also achieve Code Highlighting without using Slices. The drawback would be, that you would rely on automatic language detection, which can fail easily. We will use a list of languages to select from for each code block using Slices. This will improve flexibility and modularity.

💡

Learn more about Slices and how to use them.

Create Slice

In this example, the Slice will be named CodeBlock. Just add a new Slice into your Slice library or any document you will use it. It's up to you if you implement the Slice in the Prismic Dashboard or in the local Slice Machine.

Slice content

First, add a Select field called language to your Slice and add the languages you need for the Code Highlighting into the options. Define one language per line and check the "Define first value as default" field.

Secondly, add a Rich Text field called preformatted, which has preformatted as the only active format option.

The Slice content should look like the following:

Prepare Frontend

⚠️

I assume, that you have already integrated Prismic into your Frontend. If not, check out Get Started with Vue or Get Started with Nuxt.

For the implementation in the Frontend, you will need to create the Slice, add highlight.js and use a custom HTML Serializer to highlight the code.

Create Slice

We will use the Prismic Rich Text Component to render the preformatted Rich Text field we defined in the CMS.

Vue
CodeBlock.vue
<template>
  <PrismicRichText
    :field="slice.primary.rich_text"
  />
</template>

<script>
  export default {
    props: {
      slice: {
        type: Object,
        required: true
      }
    }
  };
</script>

Add highlight.js

To Syntax Highlight the code, we will need to install highlight.js.

bash
NPM
npm i highlight.js

Then, you need to register all languages, you have already defined in your Slice. Import and register each language manually with hljs.registerLanguage() method. Check the list of all supported languages with its aliases for the import.

⚠️

The first argument of hljs.registerLanguage() should reflect the exact name of the language you defined in the Select Field in your CMS, as we will use it directly to identify the language.

Vue
CodeBlock.vue
<script>
  import hljs from 'highlight.js/lib/core';
  import bash from 'highlight.js/lib/languages/bash';
  import javascript from 'highlight.js/lib/languages/javascript';
  import xml from 'highlight.js/lib/languages/xml';
  import json from 'highlight.js/lib/languages/json';

  hljs.registerLanguage('bash', bash);
  hljs.registerLanguage('vue', xml);
  hljs.registerLanguage('javascript', javascript);
  hljs.registerLanguage('json', json);

  export default {
    props: {
      slice: {
        type: Object,
        required: true
      }
    }
  };
</script>

💡

Unfortunately, there is no language support for Vue in highlight.js. Use XML as seen in the example to get Code Highlighting for Vue.

Custom HTML Serializer

The last step is to customize the HTML output with Syntax Code Highlighting. We will create a method called htmlSerializer and pass it to the html-serializer Prop in the <PrismicRichText /> Component to override the default HTML Serializer.

Our custom HTML Serializer will check for the preformatted element in the Rich Text field and use the hljs.highlight() method to highlight the code. The method takes an input and an object for the options as arguments. In the options, we will pass the language field from the Slice data.

💡

You can read more about highlight.js usage.

💡

Here, you can read about all accessible arguments for the htmlSerializer() method.

Vue
CodeBlock.vue
<template>
  <PrismicRichText
    :field="slice.primary.rich_text"
    :html-serializer="htmlSerializer"
  />
</template>

<script>
  import { Element as Elements } from '@prismicio/helpers';

  import hljs from 'highlight.js/lib/core';
  import bash from 'highlight.js/lib/languages/bash';
  import javascript from 'highlight.js/lib/languages/javascript';
  import xml from 'highlight.js/lib/languages/xml';
  import json from 'highlight.js/lib/languages/json';

  hljs.registerLanguage('bash', bash);
  hljs.registerLanguage('vue', xml);
  hljs.registerLanguage('javascript', javascript);
  hljs.registerLanguage('json', json);

  export default {
    props: {
      slice: {
        type: Object,
        required: true
      }
    },

    methods: {
      htmlSerializer(type, element, content, children) {
        if (type === Elements.preformatted) {
          this.text = element.text;
          return `<pre>${
            hljs.highlight(
              element.text,
              { language: this.slice.primary.language }
            ).value
          }</pre>`;
        }
      }
    }
  };
</script>

Themes

Don't forget to include a theme to see any results. There is a list of all themes in action. The import names/source for all themes can be found here. For example, I use the GitHub Dark theme.

JavaScript
import 'highlight.js/styles/github-dark.css';

Conclusion

Now, you can customize your Slice for your needs and add additional features like a copy code button.

The concepts for Vue or Nuxt you just saw can be easily translated to any other Frontend using Prismic as CMS. You just need a customized HTML Serizalizer and highlight.js to highlight the code for preformatted Rich Text fields.