Vue.js > Structured Text fields

    Structured Text fields

    Rich text in DatoCMS is stored in Structured Text fields, which lets us use it in many different contexts, from HTML in the browser to speech fulfillments in voice interfaces, if that's what you want.

    There's a lot to be said about Structured Text and the extensibility of it, but for now let's just say that it returns content in a particular JSON format called dast which will resemble this example:

    {
    "schema": "dast",
    "document": {
    "type": "root",
    "children": [
    {
    "type": "heading",
    "level": 1,
    "children": [
    {
    "type": "span",
    "marks": [],
    "value": "Hello world!"
    }
    ]
    }
    ]
    }
    }

    To make it easy to convert this format in HTML inside your Vue.js projects, we released a package called vue-datocms that exposes a <datocms-structured-text /> component that does all the tedious work for you.

    To take advantage of it, install the vue-datocms package if you haven't already:

    yarn add vue-datocms

    Then, inside your page, make a GraphQL query to fetch a Structured Text field, and feed the result to the data prop of a <datocms-structured-text /> component:

    <template>
    <div>
    <div v-if="loading">
    Loading...
    </div>
    <div v-else-if="error">
    Something bad happened!
    </div>
    <div v-else>
    <article v-for="blogPost of data.allBlogPosts" v-bind:key="blogPost.id">
    <h6>{{blogPost.title}}</h6>
    <datocms-structured-text :data="blogPost.content" />
    </article>
    </div>
    </div>
    </template>
    <script setup>
    import { ref, onMounted } from 'vue'
    import { StructuredText as DatocmsStructuredText } from "vue-datocms";
    import { request } from "./datocms";
    const data = ref(null);
    const error = ref(null);
    const loading = ref(true);
    const HOMEPAGE_QUERY = `query HomePage($limit: IntType) {
    allBlogPosts(first: $limit) {
    id
    title
    content {
    value
    }
    }
    }`;
    onMounted(async () => {
    try {
    data.value = await request({
    query: HOMEPAGE_QUERY,
    variables: {
    limit: 10
    }
    });
    } catch (e) {
    error.value = e;
    }
    loading.value = false;
    })
    </script>

    Rendering special nodes

    Other than "regular" formatting nodes (paragraphs, lists, etc.), Structured Text documents can contain three special types of node:

    • itemLink nodes are just like regular HTML hyperlinks, but point to other records instead of URLs;

    • inlineItem nodes lets you directly embed a reference to a record in-between regular text;

    • block nodes lets you embed a DatoCMS block record in-between regular paragraphs;

    If a Structured Text document contains one of these nodes, then we need to change the GraphQL query, so that we also fetch all the records and blocks it references. As an example, if the field can link to other Blog posts, and can embed blocks of type "Image block", then the query should change like this:

    const HOMEPAGE_QUERY = `query HomePage($limit: IntType) {
    allBlogPosts(first: $limit) {
    id
    title
    content {
    value
    blocks {
    __typename
    ... on ImageRecord {
    id
    image {
    responsiveImage { src srcSet sizes width height base64 }
    }
    }
    }
    links {
    __typename
    ... on BlogPostRecord {
    id
    slug
    title
    }
    }
    }
    }
    }`;

    We also need to tell <StructuredText /> how you want such nodes to be rendered:

    <template>
    <div>
    <div v-if="loading">
    Loading...
    </div>
    <div v-else-if="error">
    Something bad happened!
    </div>
    <div v-else>
    <article v-for="blogPost of data.allBlogPosts" v-bind:key="blogPost.id">
    <h6>{{blogPost.title}}</h6>
    <datocms-structured-text
    :data="blogPost.content"
    :render-inline-record="renderInlineRecord"
    :render-link-to-record="renderLinkToRecord"
    :render-block="renderBlock"
    />
    </article>
    </div>
    </div>
    </template>
    <script setup>
    import { ref, onMounted, h } from 'vue'
    import {
    StructuredText as DatocmsStructuredText,
    Image as DatocmsImage,
    } from "vue-datocms";
    import { request } from "./datocms";
    const data = ref(null);
    const error = ref(null);
    const loading = ref(true);
    const renderInlineRecord = ({ record }) => {
    if (record.__typename === 'BlogPostRecord') {
    return h('a', { href: `/blog/${record.slug}` }, [record.title]);
    }
    return null;
    };
    const renderLinkToRecord = ({ record, children }) => {
    if (record.__typename === 'BlogPostRecord') {
    return h('a', { href: `/blog/${record.slug}` }, children);
    }
    return null;
    };
    const renderBlock = ({ record, key }) => {
    if (record.__typename === 'ImageBlockRecord') {
    return h(DatocmsImage, { key, props: { data: record.image.responsiveImage } });
    }
    return null;
    };
    const HOMEPAGE_QUERY = ...;
    onMounted(async () => {
    try {
    data.value = await request({
    query: HOMEPAGE_QUERY,
    variables: {
    limit: 10
    }
    });
    } catch (e) {
    error.value = e;
    }
    loading.value = false;
    })
    </script>