Nyx47
Nyx47

Reputation: 13

How to render json rich text from editor-js strapi to HTML in Nuxt 3?

Here is the output from the rich-text editor that I created on Strapi.

enter image description here

"{"time":1679496880268,"blocks":[{"id":"82C13q17QU","type":"paragraph","data":{"text":"Title"}},{"id":"aQ7sSPshJO","type":"paragraph","data":{"text":"Description"}},{"id":"P0cESzO_a-","type":"image","data":{"file":{"url":"/uploads/render_3fc01e6769.png","mime":"image/png","height":1000,"width":1000,"size":672.3,"alt":"render.png","formats":{"thumbnail":{"name":"thumbnail_render.png","hash":"thumbnail_render_3fc01e6769","ext":".png","mime":"image/png","path":null,"width":156,"height":156,"size":49.79,"url":"/uploads/thumbnail_render_3fc01e6769.png"},"small":{"name":"small_render.png","hash":"small_render_3fc01e6769","ext":".png","mime":"image/png","path":null,"width":500,"height":500,"size":529.93,"url":"/uploads/small_render_3fc01e6769.png"},"medium":{"name":"medium_render.png","hash":"medium_render_3fc01e6769","ext":".png","mime":"image/png","path":null,"width":750,"height":750,"size":1200.69,"url":"/uploads/medium_render_3fc01e6769.png"}}},"caption":"a cup of coffee","withBorder":false,"stretched":false,"withBackground":false}}],"version":"2.23.2"}"

I want to dismay as HTML in Nuxt 3.

Upvotes: 0

Views: 3616

Answers (2)

Boris Kamp
Boris Kamp

Reputation: 338

I solved this by creating RichTextBlocks.vue component in my Nuxt3 TS project. It uses Tailwind's typography plugin. Here is my component:

<script lang="ts" setup>
import type { RichTextBlockChild, RichTextBlocks } from '@/ts/interfaces/strapi-models/strapiRichText'
import { RichTextBlockChildType, RichTextBlockFormat, RichTextBlockType } from '@/ts/interfaces/strapi-models/strapiRichText'

defineProps<{
  data?: RichTextBlocks[]
}>()

function renderList(block: RichTextBlocks) {
  let html = ''
  block.children.forEach((child) => {
    html += '<li>'
    child.children.forEach((childChild) => {
      if (childChild.type === RichTextBlockChildType.text) {
        if (childChild.bold)
          html += `<strong>${childChild.text}</strong>`
        else if (childChild.italic)
          html += `<i>${childChild.text}</i>`
        else if (childChild.underline)
          html += `<u>${childChild.text}</u>`
        else if (childChild.strikethrough)
          html += `<s>${childChild.text}</s>`
        else
          html += childChild.text
      }
    })
    html += '</li>'
  })

  return html
}

function renderText(children: RichTextBlockChild[]) {
  let html = ''
  children.forEach((childChild) => {
    if (childChild.type === RichTextBlockChildType.text) {
      if (childChild.bold)
        html += `<strong>${childChild.text}</strong>`
      else if (childChild.italic)
        html += `<i>${childChild.text}</i>`
      else if (childChild.underline)
        html += `<u>${childChild.text}</u>`
      else if (childChild.strikethrough)
        html += `<s>${childChild.text}</s>`
      else
        html += childChild.text
    }
  })

  return html
}
</script>

<template>
  <div class="prose prose-slate prose-a:text-blue-600">
    <template v-for="(block, index) in data" :key="index">
      <!-- Paragraph -->
      <p v-if="block && block.type === RichTextBlockType.paragraph">
        <template v-for="(child, childIndex) in block.children" :key="`${childIndex}-${index}`">
          <strong v-if="child.type === RichTextBlockChildType.text && child.bold">{{ child.text }}</strong>
          <i v-else-if="child.type === RichTextBlockChildType.text && child.italic">{{ child.text }}</i>
          <u v-else-if="child.type === RichTextBlockChildType.text && child.underline">{{ child.text }}</u>
          <s v-else-if="child.type === RichTextBlockChildType.text && child.strikethrough">{{ child.text }}</s>
          <code v-else-if="child.type === RichTextBlockChildType.text && child.code">{{ child.text }}</code>
          <a v-else-if="child.type === RichTextBlockChildType.link && child.url" :href="child.url" v-html="renderText(child.children)" />
          <template v-else-if="child.type === RichTextBlockChildType.text">
            {{ child.text }}
          </template>
        </template>
      </p>

      <!-- Lists -->
      <ul v-if="block && block.type === RichTextBlockType.list && block.format === RichTextBlockFormat.unordered" v-html="renderList(block)" />
      <ol v-if="block && block.type === RichTextBlockType.list && block.format === RichTextBlockFormat.ordered" v-html="renderList(block)" />

      <!-- Headings -->
      <h1 v-if="block && block.type === RichTextBlockType.heading && block.level === 1" v-html="renderText(block.children)" />
      <h2 v-if="block && block.type === RichTextBlockType.heading && block.level === 2" v-html="renderText(block.children)" />
      <h3 v-if="block && block.type === RichTextBlockType.heading && block.level === 3" v-html="renderText(block.children)" />
      <h4 v-if="block && block.type === RichTextBlockType.heading && block.level === 4" v-html="renderText(block.children)" />
      <h5 v-if="block && block.type === RichTextBlockType.heading && block.level === 5" v-html="renderText(block.children)" />
      <h6 v-if="block && block.type === RichTextBlockType.heading && block.level === 6" v-html="renderText(block.children)" />

      <!-- Image -->
      <nuxt-img
        v-if="block && block.type === RichTextBlockType.image && block.image"
        provider="imagekit"
        :src="getPathForImage(block.image)"
        width="1000"
        :placeholder="15"
        :modifiers="{ f: 'webp' }"
        lazy="true"
        :alt="block.image.alternativeText ?? ''"
        :title="block.image.name"
        class=""
      />
    </template>
  </div>
</template>

It has support for TS, here is my strapiRichText.ts file that contains the interfaces:

import type StrapiImage from "./image";

export enum RichTextBlockType {
  paragraph = 'paragraph',
  list = 'list',
  heading = 'heading',
  image = 'image',
}

export enum RichTextBlockFormat {
  unordered = 'unordered',
  ordered = 'ordered',
}

export enum RichTextBlockChildType {
  text = 'text',
  list_item = 'list-item',
  link = 'link',
}

export interface RichTextBlocks {
  type: RichTextBlockType;
  children: RichTextBlockChild[];
  format?: RichTextBlockFormat
  level?: number
  image?: StrapiImage
}

export interface RichTextBlockChild {
  text: string
  url: string
  type: RichTextBlockChildType
  children: RichTextBlockChild[]
  bold?: boolean;
  underline?: boolean;
  italic?: boolean;
  strikethrough?: boolean;
  code?: boolean;
}

In my other components or pages I can then use my component like so: <RichTextBlocks v-if="props.data?.text" :data="props.data?.text" class="mb-10" />

I hope this helps anyone in the future!

Upvotes: 0

David
David

Reputation: 948

There should be a way to query rich text as markdown, see: https://strapi.io/blog/build-a-blog-with-next-react-js-strapi

Then you should be able to use vue-markdown to render the rich text content as formatted HTML:

<vue-markdown>{{ markdownResponse }}</vue-markdown>

Upvotes: -1

Related Questions