Reputation: 13
Here is the output from the rich-text editor that I created on Strapi.
"{"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
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
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