Nuxt, Meet Prism
Tags: nuxt, javascript, vue, prism
Published: 2020-04-14 05:04:14

This article is part of a series on my experiences with Nuxt.js that I built into the [nuxt-toolkit](https://github.com/overscore-media/nuxt-toolkit) by [OverScore Media](https://overscore.media) {% github https://github.com/overscore-media/nuxt-toolkit no-readme %}

See a live example at https://nuxt-toolkit.overscore.media! :]

-----

Ah, syntax highlighting. The beauty of code made manifest!

Syntax highlighting can make or break a site in terms of UI, IMO, since all too often I see nasty or downright-broken code blocks on websites that should frankly know better... But every now and then there's something of such pure magic that it makes it all worth it.

There are a bunch of ways of getting syntax highlighting to work with JavaScript. This is one way of many, but it's the way I did it. Take that as you will.

## Look into the Prism

[Prism](https://prismjs.com/) is my favourite syntax highlighting library. It's really quite nice when you get it all set up. Admittedly, to get it to work the way I like it, I ended up importing a bunch of CSS and quite a bit of JS that I probably didn't end up needing, and which definitely added to my Nuxt project's bundle size considerably (and it's an absolute nightmare to try to make it play nice with things like PurgeCSS), but... Tradeoffs? I mean, would you rather have a slightly-slower-but-still-fairly-zippy site that looks beautiful or a super-fast site that looks "meh"? I don't know. Fight about it in the comments, I guess? \_(ツ)_/¯

## Ready?

If you are ready to start, and you want to do it my way for some reason, first import the libraries you'll need to make it happen.

`yarn add prismjs clipboard` or `npm install --save prismjs clipboard`

Next, bundle it all up in a Nuxt plugin, like `~/plugins/prism.js`. Choose your own Prism theme, language supports, and plugins. Fair warning, there are a lot of them from which to choose. You might be here for a while...


```js
/* eslint-disable no-template-curly-in-string */
/* eslint-disable no-undef */
import 'clipboard' // For the copy to clipboard plugin
import Prism from 'prismjs'
import Vue from 'vue'

// Include a theme:
import 'prismjs/themes/prism-tomorrow.css'

// Include language support: (optional)
import 'prismjs/components/prism-scss'
import 'prismjs/components/prism-markup'
import 'prismjs/components/prism-ruby'

// Include the toolbar plugin: (optional)
import 'prismjs/plugins/toolbar/prism-toolbar'
import 'prismjs/plugins/toolbar/prism-toolbar.css'

// Include the autolinker plugin: (optional)
import 'prismjs/plugins/autolinker/prism-autolinker'
import 'prismjs/plugins/autolinker/prism-autolinker.css'

// Include the line numbers plugin: (optional)
import 'prismjs/plugins/line-numbers/prism-line-numbers'
import 'prismjs/plugins/line-numbers/prism-line-numbers.css'

// Include the line highlight plugin: (optional)
import 'prismjs/plugins/line-highlight/prism-line-highlight'
import 'prismjs/plugins/line-highlight/prism-line-highlight.css'

// Include some other plugins: (optional)
import 'prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard'
import 'prismjs/plugins/highlight-keywords/prism-highlight-keywords'
import 'prismjs/plugins/show-language/prism-show-language'
import 'prismjs/plugins/normalize-whitespace/prism-normalize-whitespace'

Vue.component('prism', {
  props: {
    lang: {
      type: String,
      default: 'js'
    }
  },
  mounted () {
    Prism.highlightAll()
  },
  template: '<div class="prism"><pre class="line-numbers" :class="`language-${lang}`"><code><slot></slot></code></pre></div>'
})
```

Next, add it to your `nuxt.config.js` like so:

```js
// ...
plugins: [
  { src: '~/plugins/prism', mode: client' }
]
// ...
```

## Use it for realsies

Try out something like this to make sure it works:

```vue
<prism lang="scss">
.VueToNuxtLogo {
  display: inline-block;
  animation: turn 2s linear forwards 1s;
  transform: rotateX(180deg);
  position: relative;
  overflow: hidden;
  height: 180px;
  width: 245px;
}
</prism>
```

## Caveat(s)

Fair warning - if you're using this to display HTML or any sort of XML-y markup, you may run into issues if you don't put escaped markup inside the `<prism>` block. Try converting your markup to escaped markup with an [online converter](https://www.freeformatter.com/html-escape.html) or something.

Also, [PurgeCSS](https://purgecss.org) and Prism don't like each other all that much, so if you happen to be using them together, prepare to have your wonderful styling ripped away by Purge's unrelenting scourge, all in the name of bundle size reduction... Granted, there _is_ probably a ton of unused CSS if you happen to do it this way, but Purge is overly-aggressive. Now, you _can_ add some words to your PurgeCSS whitelist, but honestly it's a mess and I haven't figured out a better solution, and you'll probably have to manually add classes to make things work... Or just don't use Purge (or don't do it this way). Your call. Either way, almost every solution causes new problems, so you can't really avoid making these difficult choices if you want to have _some_ nice things.

```js
  purgeCSS: {
    enabled: true, // True means it's always on in dev/build/generate mode
    // The two below options achieve the happy medium between Prism.js and PurgeCSS
    // If you're having trouble with code highlighting, try adding the pertinent clases to the whitelist
    whitelistPatternsChildren: [/token$/],
    whitelist: [
      'pre', 'code', 'prism', 'line-numbers', 'tag', 'toolbar-item', 'toolbar',
      'code-toolbar', 'span', 'button', 'line-numbers-rows', 'url-link', 'attr-name', 'attr-value', 'punctuation',
      'keyword', 'keyword-let', 'operator', 'string'
    ]
  },
```

Also, if you're using Vuetify, this code will remove some of the default styling so your code blocks don't look weird:

```scss
code {
  box-shadow: 0 !important;
  &::before {
    content: '' !important;
  }
}
```

Anyway, stay safe and healthy, everybody. Keep on coding!