This package provides utilities for transforming Kontent.ai rich text into structured formats suitable for resolution and rendering in various environments.
Install the package via npm
npm i @kontent-ai/rich-text-resolver
The tool provides environment-aware (browser or Node.js) parseHTML
function to transform HTML into an array of DomNode
trees. Any valid HTML is parsed, including all attributes. Together with built-in transformation methods, this tool is a suitable option for processing HTML and rich text from external sources, to make it compatible with Kontent.ai rich text format. See dedicated HTML transformer docs for further information.
Portable Text is a universal standard for rich text representation, with tools available for its transformation and rendering in majority of popular frameworks and languages:
- React: react-portabletext
- HTML: to-html
- Svelte: svelte-portabletext
- Vue: vue-portabletext
- Astro: astro-portabletext
Tip
This module re-exports modified toHTML
function and <PortableText>
component from to-html
and react-portabletext
packages respectively. These modified helpers provide default resolution for tags which are either unsupported or only partially supported in the original packages (sub
and sup
tags, images, tables and links).
Make sure to use these re-exports if you want to take advantage of the default resolution.
The tool provides transformToPortableText
function to convert rich text content into an array of Portable Text blocks, with custom blocks defined for Kontent.ai-specific objects.
Combined with a suitable package for the framework of your choice, this makes for an optimal solution for resolving rich text.
Important
The provided Portable Text transformation functions expect a valid Kontent.ai rich text content, otherwise you risk errors or invalid blocks in the resulting array.
Besides default blocks for common elements, Portable Text supports custom blocks, which can represent other entities. Each custom block should extend ArbitraryTypedObject
to ensure _key
and _type
properties are present. Key should be a unique identifier (e.g. guid), while type should indicate what the block represents. Value of _type
property is used for mapping purposes in subsequent resolution.
This package comes with built-in custom block definitions for representing Kontent.ai rich text entities:
rich-text-resolver-js/showcase/showcase.ts
Lines 3 to 12 in c62306f
rich-text-resolver-js/showcase/showcase.ts
Lines 14 to 24 in c62306f
rich-text-resolver-js/showcase/showcase.ts
Lines 26 to 34 in c62306f
rich-text-resolver-js/showcase/showcase.ts
Lines 36 to 62 in c62306f
HTML resolution using a slightly modified version of toHTML
function from @portabletext/to-html
package.
import {
transformToPortableText,
resolveTable,
resolveImage,
PortableTextHtmlResolvers,
toHTML
} from "@kontent-ai/rich-text-resolver";
const richTextValue = "<rich text html>";
const linkedItems = ["<array of linked items>"]; // e.g. from SDK
const portableText = transformToPortableText(richTextValue);
const resolvers: PortableTextHtmlResolvers = {
components: {
types: {
image: ({ value }) => {
// helper method for resolving images
return resolveImage(value);
},
componentOrItem: ({ value }) => {
const linkedItem = linkedItems.find(
(item) => item.system.codename === value.component._ref
);
switch (linkedItem?.system.type) {
case "component_type_codename": {
return `<p>resolved value of text_element: ${linkedItem?.elements.text_element.value}</p>`;
}
default: {
return `Resolver for type ${linkedItem?.system.type} not implemented.`;
}
};
},
table: ({ value }) => {
// helper method for resolving tables
const tableHtml = resolveTable(value, toHTML);
return tableHtml;
},
},
marks: {
contentItemLink: ({ children, value }) => {
return `<a href="https://website.com/${value.reference._ref}">${children}</a>`;
},
link: ({ children, value }) => {
return `<a href=${value?.href} data-new-window=${value?.["data-new-window"]}>${children}</a>`;
},
},
},
};
const resolvedHtml = toHTML(portableText, resolvers);
React, using a slightly modified version of PortableText
component from @portabletext/react
package.
import { toPlainText } from "@portabletext/react";
import {
PortableTextReactResolvers,
PortableText,
TableComponent,
ImageComponent,
} from "@kontent-ai/rich-text-resolver/utils/react";
import {
transformToPortableText,
} from "@kontent-ai/rich-text-resolver";
// assumes richTextElement from SDK
const resolvers: PortableTextReactResolvers = {
types: {
componentOrItem: ({ value }) => {
const item = richTextElement.linkedItems.find(item => item.system.codename === value.component._ref);
return <div>{item?.elements.text_element.value}</div>;
},
// Image and Table components are used as a default fallback if a resolver isn't explicitly specified
table: ({ value }) => <TableComponent {...value} />,
image: ({ value }) => <ImageComponent {...value} />,
},
marks: {
link: ({ value, children }) => {
return (
<a href={value?.href} rel={value?.rel} title={value?.title} data-new-window={value?.['data-new-window']}>
{children}
</a>
)
},
contentItemLink: ({ value, children }) => {
const item = richTextElement.linkedItems.find(item => item.system.id === value?.reference._ref);
return (
<a href={"https://website.xyz/" + item?.system.codename}>
{children}
</a>
)
}
}
}
const MyComponent = ({ props }) => {
// https://github.com/portabletext/react-portabletext#customizing-components
const portableText = transformToPortableText(props.element.value);
return (
<PortableText value={portableText} components={resolvers} />
);
};
Using @portabletext/vue
package
<script setup>
import {
PortableText,
PortableTextComponentProps,
PortableTextComponents,
toPlainText,
} from "@portabletext/vue";
import {
resolveTableVue as resolveTable,
resolveImageVue as resolveImage,
toVueImageDefault,
} from "@kontent-ai/rich-text-resolver";
const components: PortableTextComponents = {
types: {
image: ({ value }: PortableTextComponentProps<PortableTextImage>) =>
resolveImage(value, h, toVueImageDefault),
table: ({ value }: PortableTextComponentProps<PortableTextTable>) =>
resolveTable(value, h, toPlainText),
},
// marks etc.
};
</script>
<template>
<PortableText :value="props.value" :components="components" />
</template>
Package exports a traversePortableText
method, which accepts an array of PortableTextObject
and a callback function. The method recursively traverses all nodes and their subnodes, optionally modifying them with the provided callback:
import {
PortableTextObject,
transformToPortableText,
traversePortableText,
} from "@kontent-ai/rich-text-resolver";
const input = `<figure data-asset-id="guid" data-image-id="guid"><img src="https://asseturl.xyz" data-asset-id="guid" data-image-id="guid" alt=""></figure>`;
// Adds height parameter to asset reference and changes _type.
const processBlocks = (block: PortableTextObject) => {
if (block._type === "image") {
const modifiedReference = {
...block.asset,
height: 300
}
return {
...block,
asset: modifiedReference,
_type: "modifiedImage"
}
}
// logic for modifying other object types...
// return original block if no modifications required
return block;
}
const portableText = transformToPortableText(input);
const modifiedPortableText = traversePortableText(portableText, processBlocks);
toManagementApiFormat
is a custom transformation method built upon toHTML
package, allowing you to restore portable text previously created from management API rich text back into MAPI supported format.
const richTextContent =
`<p>Here is an <a data-item-id="12345"><strong>internal link</strong></a> in some text.</p>`;
const portableText = transformToPortableText(richTextContent);
// your logic to modify the portable text
const validManagementApiFormat = toManagementApiFormat(portableText);
Important
MAPI transformation logic expects Portable Text that had been previously created from management API rich text and performs only minimal validation. It doesn't provide implicit transformation capabilities from other formats (such as delivery API).
If you're interested in transforming external HTML or rich text to a MAPI compatible format, see HTML transformer docs instead.