Skip to content

Commit d58b20e

Browse files
committed
feat(web): browser environment assertion
1 parent d547bff commit d58b20e

6 files changed

Lines changed: 27 additions & 4 deletions

File tree

docs/WEB.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,9 @@ On web, HTML is sanitized automatically with [DOMPurify](https://github.com/cure
5959
### Custom mention attributes
6060

6161
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.
62+
63+
## Client-only rendering (no SSR)
64+
65+
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)**.
66+
67+
If your application uses SSR (Next.js, Remix, Gatsby, etc.), make sure these components only render on the client.

src/web/EnrichedText.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { useImageErrorFallback } from './useImageErrorFallback';
2020
import { usePressInteractions } from './usePressInteractions';
2121
import { adaptWebToNativeEvent } from './adaptWebToNativeEvent';
2222
import { useStableRef } from './useStableRef';
23+
import { assertBrowserEnvironment } from './assertBrowserEnvironment';
2324

2425
export const EnrichedText = memo(
2526
({
@@ -35,6 +36,8 @@ export const EnrichedText = memo(
3536
onLinkPress,
3637
onMentionPress,
3738
}: EnrichedTextProps) => {
39+
assertBrowserEnvironment('EnrichedText');
40+
3841
const containerRef = useRef<HTMLDivElement>(null);
3942

4043
useImperativeHandle(ref, () => ({

src/web/EnrichedTextInput.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ import {
8484
checkMentionAttributes,
8585
sanitizeMentionAttributes,
8686
} from './sanitization/htmlSanitizer';
87+
import { assertBrowserEnvironment } from './assertBrowserEnvironment';
8788

8889
function runFocused(
8990
editor: Editor,
@@ -125,6 +126,8 @@ export const EnrichedTextInput = ({
125126
htmlStyle,
126127
useHtmlNormalizer = ENRICHED_TEXT_INPUT_DEFAULT_PROPS.useHtmlNormalizer,
127128
}: EnrichedTextInputProps) => {
129+
assertBrowserEnvironment('EnrichedTextInput');
130+
128131
const tiptapContent =
129132
defaultValue != null
130133
? prepareHtmlForTiptap(defaultValue, useHtmlNormalizer)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* `EnrichedText` and `EnrichedTextInput` rely on browser-only APIs (DOMParser,
3+
* DOMPurify, TipTap) and therefore cannot render without a DOM — e.g. during
4+
* server-side rendering (SSR). They are client-only components.
5+
*
6+
* This asserts a DOM is available and throws a clear error otherwise.
7+
*/
8+
export function assertBrowserEnvironment(componentName: string): void {
9+
if (typeof window === 'undefined' || typeof document === 'undefined') {
10+
throw new Error(
11+
`[react-native-enriched] ${componentName} is a client-only component and cannot be rendered without a DOM. ` +
12+
`If you are running an SSR application, make sure the component is only rendered on the client.`
13+
);
14+
}
15+
}

src/web/normalization/htmlNormalizer.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -679,8 +679,6 @@ function walkNode(node: Node, out: { buf: string }): void {
679679
}
680680

681681
export function normalizeHtml(html: string): string {
682-
if (typeof DOMParser === 'undefined') return html;
683-
684682
const parser = new DOMParser();
685683
const doc = parser.parseFromString(`<body>${html}</body>`, 'text/html');
686684
const body = doc.body;

src/web/normalization/prepareHtmlForWeb.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ export function prepareHtmlForWeb(
44
html: string,
55
useHtmlNormalizer: boolean | undefined
66
): string {
7-
if (typeof DOMParser === 'undefined') return html;
8-
97
if (useHtmlNormalizer) {
108
html = normalizeHtml(html);
119
}

0 commit comments

Comments
 (0)