Skip to content

Rich Text

A standalone rich text renderer that accepts a normalized RichTextNode[] tree (typically produced by a CMS mapper) and renders semantic HTML output.

The component uses only React.createElement to walk and render the node tree. No hooks, no state, no use client. It renders complete HTML on the server with zero JavaScript shipped.

Hello, world!

import { RichText } from "@/rokkit200-ui/components/rich-text/rich-text";
import { mapCmsType } from "@/rokkit200-ui/lib/content-mapper/content-mapper-util";
import type { RichTextCmsField } from "@/rokkit200-ui/lib/rich-text-mapper/rich-text-mapper-types";
import { richTextCmsMappers } from "@/rokkit200-ui/lib/rich-text-mapper/rich-text-mapper-util";
const source = {
json: {
type: "richText",
children: [
{
type: "paragraph",
children: [
{
text: "Hello, world!",
bold: true,
italic: true,
},
],
},
],
},
};
const cmsField = source.json as RichTextCmsField;
const content = mapCmsType(richTextCmsMappers, cmsField);
return <RichText content={content} />;

Renders structured content mapped from a CMS rich text field (Slate AST). The mapper translates CMS-specific node types (e.g. heading-two, bulleted-list) into normalized RichTextNode[] that the component walks recursively. By default this component applies no styling opinions to the rendered markup.

A Short Guide to Clear Writing

Writing well isn't about using complicated words — it's about communicating ideas clearly.
A good document uses structure, emphasis, and visual elements to guide the reader.

Sometimes that means linking to helpful resources, like the
Plain Language Guidelines.


Why Structure Matters

Large blocks of text are hard to read. Breaking ideas into sections makes content easier to scan.

“Good design is as little design as possible.”
— Dieter Rams

That idea applies to writing too: clarity comes from restraint.


Key Principles

  • Start with the main idea

  • Add supporting details

  • Highlight important phrases

  • Use subtle emphasis where appropriate

  • Avoid unnecessary complexity


A Simple Process

  1. Write a rough first draft

  2. Remove unnecessary words

  3. Improve clarity and flow

  4. Add formatting where it helps readers


Inline Code Example

Sometimes you may want to show a command or snippet like:

npm install example-package


Including Images

Images can help break up text and illustrate ideas.

Lorem ipsum

A short caption or explanation often helps readers understand why the image is relevant.


Comparison Table

FeatureBasic TextRich Text
FormattingLimitedFlexible
StructureMinimalHeadings, lists, tables
MediaNoneImages, embeds

Final Thoughts

Clear communication isn't just about the words themselves — it's about how those words are presented.

If you're curious to learn more about writing for the web, the
Nielsen Norman Group has excellent research on how people read online.


Sometimes a simple divider helps separate ideas:


And that's a quick example of a document that exercises many common rich text rendering features.

Overrides the default rendering for links, images, text marks, and specific element tags using the components prop. This example integrates existing registry components (Link, Image) and applies custom styling to blockquotes, horizontal rules, and table cells.

Each override function receives the typed node and (where applicable) pre-rendered children. Returning undefined from an override falls back to the default rendering for that node, allowing selective customization.

A Short Guide to Clear Writing

Writing well isn't about using complicated words — it's about communicating ideas clearly.
A good document uses structure, emphasis, and visual elements to guide the reader.

Sometimes that means linking to helpful resources, like the
Plain Language Guidelines.


Why Structure Matters

Large blocks of text are hard to read. Breaking ideas into sections makes content easier to scan.

“Good design is as little design as possible.”
— Dieter Rams

That idea applies to writing too: clarity comes from restraint.


Key Principles

  • Start with the main idea

  • Add supporting details

  • Highlight important phrases

  • Use subtle emphasis where appropriate

  • Avoid unnecessary complexity


A Simple Process

  1. Write a rough first draft

  2. Remove unnecessary words

  3. Improve clarity and flow

  4. Add formatting where it helps readers


Inline Code Example

Sometimes you may want to show a command or snippet like:

npm install example-package


Including Images

Images can help break up text and illustrate ideas.

Lorem ipsum

A short caption or explanation often helps readers understand why the image is relevant.


Comparison Table

FeatureBasic TextRich Text
FormattingLimitedFlexible
StructureMinimalHeadings, lists, tables
MediaNoneImages, embeds

Final Thoughts

Clear communication isn't just about the words themselves — it's about how those words are presented.

If you're curious to learn more about writing for the web, the
Nielsen Norman Group has excellent research on how people read online.


Sometimes a simple divider helps separate ideas:


And that's a quick example of a document that exercises many common rich text rendering features.

CapabilityDescription
Structured inputAccepts a normalized RichTextNode[] tree from a CMS mapper
Server-side first paintUses only React.createElement - no hooks, no state, no use client
Semantic renderingPreserves headings, lists, blockquotes, tables, and links as authored
Safe by constructionOutput is built from typed node objects, not injected as raw HTML
Link safetyExternal links get safe defaults (noopener noreferrer, configurable)
Component overridesReplace rendering for any node type or specific element tags via components prop
Fail safeUnknown nodes, malformed links, or partial input never crash rendering
PropertyTypeRequiredDescription
contentRichTextNode[] | undefinedYesThe normalized rich text node tree
classNamestringNoApplied to the outer wrapper element
componentsPartial<RichTextComponentMap>NoNode-level render overrides by node type
linkPolicyPartial<RichTextLinkPolicy>NoOverride external link safety defaults
htmlEntityMapRecord<string, string>NoOverride the default HTML entity map for structured text node values, such as &nbsp;

A discriminated union of the four renderable node types:

TypeDescriptionKey Fields
elementBlock or inline HTML element (p, h1, ul, table, etc)tag, attrs?, children?
textText leaf with optional formatting marksvalue, marks? (bold, italic, underline, code)
linkAnchor wrapping child nodeshref, target?, rel?, isExternal?, className?, children
imageSelf-contained imageurl, alt?, width?, height?

Override renderers keyed by node type. Each function receives the typed node and (for link and element) pre-rendered children. Return undefined to fall back to the default rendering for that node.

{
text?: (node: RichTextText) => React.ReactNode;
image?: (node: RichTextImage) => React.ReactNode;
link?: (node: RichTextLink, children?: React.ReactNode) => React.ReactNode;
element?: (node: RichTextElement, children?: React.ReactNode) => React.ReactNode;
}

Controls external link behavior. Merged with safe defaults at render time.

FieldTypeDefaultDescription
externalTargetstring"_blank"Target for external links
externalRelstring"noopener noreferrer"Rel attribute for external links
allowedSchemesstring[]["http","https","mailto","tel"]Permitted URL schemes. Links with disallowed schemes render as <span>.

The RichText component preserves the semantic structure of CMS-authored content. The following guarantees are enforced by design:

  • Heading hierarchy is preserved as authored. The component does not remap heading levels (h1-h6).
  • List semantics (ul, ol, li) and table semantics (table, thead, tbody, tr, th, td) are rendered with their correct HTML elements, maintaining screen reader navigation.
  • Links render as semantic <a> elements, ensuring they are keyboard reachable and correctly announced by assistive technology.
  • Meaningful text nodes are never removed or hidden during rendering. Unknown node types are skipped with a dev-mode warning, but their absence does not affect valid content.
  • The component is non-interactive by default. No ARIA widget roles are applied. No event handlers are attached.
  • Because there is no hydration step (no hooks, no state, no use client), the rendered HTML is immediately available to assistive technology with no flash of empty content waiting for JavaScript.