Fallback handling for unknown elements
Explains how the RichText component safely renders unknown elements and text marks using fallback behavior and customizable React renderers.
The RichText component applies a safe fallback strategy when it encounters unknown elements or text marks. This strategy ensures valid HTML output, predictable rendering, and compatibility with React hydration.
Default behavior
The RichText component renders all unknown elements and text marks as <span> elements.
This ensures:
- Generates valid HTML in both inline and block contexts.
- Prevents React hydration and DOM warnings.
- Preserves layout stability.
- Allows styling through CSS when required.
How it works
- Known Elements: Use their default HTML tags (e.g.,
heading-one–><h1>,paragraph–><p>) - Unknown Elements: Automatically become
<span>tags - Known Text Marks: Use their default tags (e.g.,
bold–><strong>,italic–><em>) - Unknown Text Marks: Automatically become
<span>tags
Custom element handling
When rich text content contains unsupported or custom elements, you can override the default behavior by providing custom renderers through the elements and leafs props. This approach lets you control how unknown or custom content renders in React.
NoteUnknown elements and text marks most commonly originate from content created through the Integration API (REST API) rather than the CMS editor.
When content bypasses the CMS editor, the TinyMCE normalization and validation layer does not run. As a result, custom HTML elements, text marks, or attributes may appear that the SDK does not recognize by default.
Example: Handling unknown elements and text marks
import {
RichText,
ElementProps,
LeafProps,
} from '@optimizely/cms-sdk/react/richText';
// Custom component for unknown block elements
const UnknownElement = ({ children, element }: ElementProps) => (
<div className="unknown-element" data-type={element.type}>
{children}
</div>
);
// Custom component for unknown text marks
const UnknownLeaf = ({ children, leaf }: LeafProps) => (
<span className="unknown-leaf" data-marks={Object.keys(leaf).join(',')}>
{children}
</span>
);
// Custom component for a specific custom element
const CustomElement = ({ children, element }: ElementProps) => (
<div className="custom-element">{children}</div>
);
function Article({ content }) {
return (
<RichText
content={content.body?.json}
elements={{
'unknown-element': UnknownElement, // Handle specific unknown element type
'custom-element': CustomElement, // Your custom CMS element
}}
leafs={{
'unknown-leaf': UnknownLeaf, // Handle specific unknown text mark
highlight: ({ children }) => (
<mark className="highlight">{children}</mark>
),
}}
/>
);
}Example: Handling Custom CMS Elements
// Custom video element not supported by default
const VideoElement = ({ children, element }: ElementProps) => {
const videoData = element as { videoUrl?: string; autoplay?: boolean };
return (
<div className="video-container">
<video src={videoData.videoUrl} autoPlay={videoData.autoplay} controls>
{children}
</video>
</div>
);
};
// Custom callout box element
const CalloutElement = ({ children, element }: ElementProps) => {
const calloutData = element as { variant?: string };
return (
<div className={`callout callout-${calloutData.variant || 'info'}`}>
{children}
</div>
);
};
function Article({ content }) {
return (
<RichText
content={content.body?.json}
elements={{
'video-embed': VideoElement,
'callout-box': CalloutElement,
}}
/>
);
}Updated about 22 hours ago
