DEV Community

Cover image for Rewriting vue prism component in vue 3
shubhadip
shubhadip

Posted on • Updated on

Rewriting vue prism component in vue 3

With Vue 3 released, there will be many libraries running into port their vue 2 projects into vue 3. Recently while working on creating a library on vue 3 I needed a syntax highlighter for demo purpose, so thought of writing one with vue 3 Setup API.

for this to work we need prismjs so let's add prismJs library;

yarn add prismjs

We would be requiring code that is supposed to be used as a highlighter and the language in which the code would be.

import * as Vue from 'vue';
import Prism from 'prismjs';

export default Vue.defineComponent({
  props: {
    code: {
      type: String,
    },
    inline: {
      type: Boolean,
      default: false,
    },
    language: {
      type: String,
      default: 'markup',
    },
  }
})
Enter fullscreen mode Exit fullscreen mode

Now let's see how can we used the setup function to access props and children. Setup function provides props and setupContext as parameters, we can easily destructure setupContext to access attrs and slots.

...
setup(props, { slots, attrs }: { slots: Slots; attrs: Data }) {
    const { h } = Vue;
    const slotsData = (slots && slots.default && slots.default()) || [];
    const code = props.code || (slotsData.length > 0 ? slotsData[0].children : '');
    const { inline, language } = props;
    const prismLanguage = Prism.languages[language];
    const className = `language-${language}`;

...
Enter fullscreen mode Exit fullscreen mode

The above code will be access to props and children's passed to the prismJs.Also, h which was passed to render function but now it has to be imported from vue.

With this done let's see how can we pass {{code}} as well as language to prismJs so that it can return HTML back to us, that can be used in the render function.

    const d = Prism.highlight(code, prismLanguage);

Enter fullscreen mode Exit fullscreen mode

with everything in place, lets add our render function with these data.

...
return (): VNode =>
      h('pre', { ...attrs, class: [attrs.class, className] }, [
        h('code', {
          class: className,
          innerHTML: d,
        }),
      ]);
...
Enter fullscreen mode Exit fullscreen mode

In 3.x, the entire VNode props structure is flattened. you can read more about render function Vue 3 Render Function.

So this is how our code will look on completion.

// prismcomponent/index.ts
import * as Vue from 'vue';
import Prism from 'prismjs';
import { Slots, VNode } from 'vue';

declare type Data = Record<string, unknown>;

export default Vue.defineComponent({
  props: {
    code: {
      type: String,
    },
    inline: {
      type: Boolean,
      default: false,
    },
    language: {
      type: String,
      default: 'markup',
    },
  },
  setup(props, { slots, attrs }: { slots: Slots; attrs: Data }) {
    const { h } = Vue;
    const slotsData = (slots && slots.default && slots.default()) || [];
    const code = props.code || (slotsData.length > 0 ? slotsData[0].children : '');
    const { inline, language } = props;
    const prismLanguage = Prism.languages[language];
    const className = `language-${language}`;

    if (inline) {
      return (): VNode =>
        h('code', { ...attrs, class: [attrs.class, className], innerHTML: Prism.highlight(code, prismLanguage) });
    }

    const d = Prism.highlight(code, prismLanguage);
    return (): VNode =>
      h('pre', { ...attrs, class: [attrs.class, className] }, [
        h('code', {
          class: className,
          innerHTML: d,
        }),
      ]);
  },
});
Enter fullscreen mode Exit fullscreen mode

So in other components, all we need to add is

<template>
...
<Prism language="javascript" class="codesnippet">
  {{ code }}
</Prism>
...
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import Prism from '../prismcomponent';
import 'prismjs';
import 'prismjs/themes/prism.css';

export default defineComponent({
...
setup() {
    const code = `const c = a+b`;
    return {
      code,
    };
  },
...
})
</script>
Enter fullscreen mode Exit fullscreen mode

Top comments (0)