Created
September 29, 2022 21:19
-
-
Save SebbeJohansson/31b64c5c4f914abfec2660210ec7795a to your computer and use it in GitHub Desktop.
In-Line Storyblok block rendering for Nuxt3 with SSR and Prerendering
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script setup lang="ts"> | |
import { Richtext } from 'storyblok-js-client'; | |
const props = defineProps({ blok: Object }); | |
const nuxtApp = useNuxtApp(); | |
const textObject = { ...props.blok.text }; | |
const nodes = []; | |
// Proof of concept for custom handling of inline blok nodes. | |
Object.entries(textObject.content).forEach(([key, node]) => { | |
if (node.type === 'blok') { | |
const blok = { | |
content: node.attrs?.body?.[0], | |
}; | |
nodes.push({ | |
key, | |
type: 'blok', | |
content: { | |
blok, | |
}, | |
}); | |
} else { | |
nodes.push({ | |
key, | |
type: 'html', | |
content: nuxtApp.$formatRichText(useStoryblokApi().richTextResolver.render({ | |
type: 'doc', | |
content: [ | |
node, | |
], | |
} as Richtext)), | |
}); | |
} | |
}); | |
</script> | |
<template> | |
<div v-editable="blok" class="text"> | |
<div v-for="node in nodes" :key="node.key"> | |
<component | |
:is="$resolveStoryBlokComponent(node.content.blok)" | |
v-if="node.type === 'blok'" | |
:blok="node.content.blok.content" | |
/> | |
<div v-else v-html="node.content" /> | |
</div> | |
</div> | |
</template> | |
<style> | |
.text img { | |
max-width: 100%; | |
} | |
</style> |
@marvr/storyblok-rich-text-vue-renderer Does this, but it hasn't been touched in a while and I'm not sure if there's a good way to set it up for Nuxt. I used it with iles but I had to register each component that could be used in it manually like this:
import { defineApp } from 'iles'
import { StoryblokVue, apiPlugin } from '@storyblok/vue';
import { plugin as VueRichTextRenderer, RichTextRenderer, defaultResolvers } from '@marvr/storyblok-rich-text-vue-renderer';
import { h, VNode } from 'vue';
import Grid from "@/components/Grid.vue";
import Page from "@/components/Page.vue";
import Teaser from "@/components/Teaser.vue";
import Feature from "@/components/Feature.vue";
export default defineApp({
enhanceApp({app}) {
/*
* Register components for automatic import for use in the rich text renderer and StoryblokComponent
* since they can't just use unplugin-vue-components
*/
// LIST ALL COMPONENTS HERE 👇
const components: Record<string, any> = {
Grid, Page, Teaser, Feature, RichTextRenderer
}
// List of components for use by rich text renderer. (generated later)
const componentRenderers: Record<string, (data: any) => VNode> = {}
// The render function used by the rich text renderer for our components
function componentRenderer ({id, component, _uid, fields} : {id:string, component: string, _uid: string, fields: Record<string, any>}) {
return h(components[component], {blok:{id, component, _uid, ...fields}})
}
// Iterate through all the component we listed so we can register them
Object.entries(components).forEach(([name, component]) => {
// Register component for use inside other components without needing to import
app.component(name, component)
// Register component for use by rich text renderer
componentRenderers[name] = componentRenderer
})
app.use(VueRichTextRenderer({
resolvers: {
...defaultResolvers,
// 👇 list of component renderers we just generated
components: componentRenderers
}
}))
},
})
I'm honestly REALLY surprised there hasn't been an official library/plugin that does this at this point. It's an extremely common use case and no one has an amazing solution.
@joezimjs Have you tried raising a feature request specifically for this?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@SebbeJohansson No, it means you can't use
renderRichText
. You have to be able to render every kind of node yourself via a Vue component that is also able to traverse the rich text data structure and display each of its child nodes in the same way.