Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/WEB.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,9 @@ On web, HTML is sanitized automatically with [DOMPurify](https://github.com/cure
### Custom mention attributes

To attach custom data to a mention, use the `data-` prefix (e.g. `data-user-id`) to make sure they survive sanitization. Attributes passed to the `setMention` ref method are properly sanitized.

## Client-only rendering (no SSR)

Both `EnrichedText` and `EnrichedTextInput` are **client-only** components. They rely on browser-only APIs (`DOMParser`, `DOMPurify`, `TipTap`) and are **not designed for server-side rendering (SSR)**.

If your application uses SSR (Next.js, Remix, Gatsby, etc.), make sure these components only render on the client.
3 changes: 3 additions & 0 deletions src/web/EnrichedText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { useImageErrorFallback } from './useImageErrorFallback';
import { usePressInteractions } from './usePressInteractions';
import { adaptWebToNativeEvent } from './adaptWebToNativeEvent';
import { useStableRef } from './useStableRef';
import { assertBrowserEnvironment } from './assertBrowserEnvironment';

export const EnrichedText = memo(
({
Expand All @@ -35,6 +36,8 @@ export const EnrichedText = memo(
onLinkPress,
onMentionPress,
}: EnrichedTextProps) => {
assertBrowserEnvironment('EnrichedText');

const containerRef = useRef<HTMLDivElement>(null);

useImperativeHandle(ref, () => ({
Expand Down
3 changes: 3 additions & 0 deletions src/web/EnrichedTextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import {
checkMentionAttributes,
sanitizeMentionAttributes,
} from './sanitization/htmlSanitizer';
import { assertBrowserEnvironment } from './assertBrowserEnvironment';

function runFocused(
editor: Editor,
Expand Down Expand Up @@ -125,6 +126,8 @@ export const EnrichedTextInput = ({
htmlStyle,
useHtmlNormalizer = ENRICHED_TEXT_INPUT_DEFAULT_PROPS.useHtmlNormalizer,
}: EnrichedTextInputProps) => {
assertBrowserEnvironment('EnrichedTextInput');

const tiptapContent =
defaultValue != null
? prepareHtmlForTiptap(defaultValue, useHtmlNormalizer)
Expand Down
15 changes: 15 additions & 0 deletions src/web/assertBrowserEnvironment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* `EnrichedText` and `EnrichedTextInput` rely on browser-only APIs (DOMParser,
* DOMPurify, TipTap) and therefore cannot render without a DOM — e.g. during
* server-side rendering (SSR). They are client-only components.
*
* This asserts a DOM is available and throws a clear error otherwise.
*/
export function assertBrowserEnvironment(componentName: string): void {
if (typeof window === 'undefined' || typeof document === 'undefined') {
throw new Error(
`[react-native-enriched] ${componentName} is a client-only component and cannot be rendered without a DOM. ` +
`If you are running an SSR application, make sure the component is only rendered on the client.`
);
Comment thread
hejsztynx marked this conversation as resolved.
Comment thread
Copilot marked this conversation as resolved.
}
Comment thread
hejsztynx marked this conversation as resolved.
}
Comment thread
Copilot marked this conversation as resolved.
2 changes: 0 additions & 2 deletions src/web/normalization/htmlNormalizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -679,8 +679,6 @@ function walkNode(node: Node, out: { buf: string }): void {
}

export function normalizeHtml(html: string): string {
if (typeof DOMParser === 'undefined') return html;

const parser = new DOMParser();
const doc = parser.parseFromString(`<body>${html}</body>`, 'text/html');
const body = doc.body;
Comment thread
hejsztynx marked this conversation as resolved.
Expand Down
2 changes: 0 additions & 2 deletions src/web/normalization/prepareHtmlForWeb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ export function prepareHtmlForWeb(
html: string,
useHtmlNormalizer: boolean | undefined
): string {
if (typeof DOMParser === 'undefined') return html;

if (useHtmlNormalizer) {
html = normalizeHtml(html);
}
Expand Down