/vue-portable-text

A Vue component to render sanity.io's portable text.

Primary LanguageTypeScript

Vue Portable Text

Render an array of portable text from Sanity with Vue.

This package works for just Vue 2 for now. However when Nuxt 3 comes out, v2 will support Vue 3 as well. (Not sure if I will maintain Vue 2 support with vue-demi or support Vue 2 for v1 only)

Install

# NPM
npm install --save @bizurd/vue-portable-text

# Yarn
yarn add @bizurd/vue-portable-text

# PNPM
pnpm add @bizurd/vue-portable-text

Usage

Vue 2:

<template>
  <VuePortableText :blocks="blocks" :serializers="serializers" />
</template>

<script>
import VuePortableText from '@bizurd/vue-portable-text'

export default {
  components: {
    VuePortableText,
  },

  data() {
    return {
      blocks: [
        {
          style: 'normal',
          _type: 'block',
          children: [
            {
              _type: 'span',
              text: 'Hello World!',
            },
          ],
        },
      ],
      serializers: {
        styles: {
          // Just for example, I would not recommend doing this
          normal: 'h1',
        },
      },
    }
  },
}
</script>

Custom Serializers

You can create custom serializers using Vue components.

Example 1: Heading Component with id

Here's an example that adds id to headings.

(You'll need slugify installed for this example)

In MyHeading.vue:

<template>
  <component :id="id" :is="block.style">
    <slot />
  </component>
</template>

<script>
import slugify from 'slugify'
import { getTextOfBlocks } from '../portable-text/PortableText'

export default {
  props: {
    block: {
      type: Object,
      default: () => {},
    },
  },

  computed: {
    id() {
      if (!['h1', 'h2', 'h3'].includes(this.block.style)) return undefined

      const text = block.children.reduce((text, block) => text + block.text, '')

      return slugify(text.toLowerCase())
    },
  },
}
</script>

Wherever you're rendering the portable text:

<template>
  <VuePortableText :blocks="blocks" :serializers="serializers" />
</template>

<script>
import MyHeading from './MyHeading.vue'
import VuePortableText from '@bizurd/vue-portable-text'

export default {
  components: {
    VuePortableText,
  },

  data() {
    return {
      blocks: [
        {
          style: 'h1',
          _type: 'block',
          children: [
            {
              _type: 'span',
              text: 'Look the heading has an id!',
            },
          ],
        },
      ],
      serializers: {
        styles: {
          h1: MyHeading,
          h2: MyHeading,
          h3: MyHeading,
        },
      },
    }
  },
}
</script>