Skip to content

NativeProseViewer Reference

Jayden Smith edited this page May 2, 2026 · 4 revisions

Overview

NativeProseViewer renders read-only native rich text from either ProseMirror document JSON or HTML. It uses the same rendering stack as the editor, minus selection, text input, and toolbar state.

import { NativeProseViewer } from '@apollohg/react-native-prose-editor';

<NativeProseViewer contentJSON={documentJson} />;
<NativeProseViewer contentHTML="<p>Hello from HTML</p>" />;

Unlike NativeRichTextEditor, the viewer is read-only. Pass exactly one of contentJSON or contentHTML — the props are mutually exclusive.

Props

interface NativeProseViewerProps {
  contentJSON?: DocumentJSON | string;
  contentHTML?: string;
  contentRevision?: string | number;
  contentJSONRevision?: string | number;
  schema?: SchemaDefinition;
  theme?: EditorTheme;
  style?: StyleProp<ViewStyle>;
  allowBase64Images?: boolean;
  collapseTrailingEmptyParagraphs?: boolean;
  enableLinkTaps?: boolean;
  contentId?: string;
  containerWidth?: number;
  mentionPrefix?:
    | string
    | ((mention: NativeProseViewerMentionRenderContext) => string | null | undefined);
  resolveMentionTheme?: (
    mention: NativeProseViewerMentionRenderContext
  ) => EditorMentionTheme | null | undefined;
  onPressLink?: (event: NativeProseViewerLinkPressEvent) => void;
  onPressMention?: (event: NativeProseViewerMentionPressEvent) => void;
}

Prop Table

Prop Type Default Description
contentJSON DocumentJSON | string Document JSON to render. A string value must be serialized ProseMirror JSON, not HTML. Mutually exclusive with contentHTML.
contentHTML string HTML rendered through the editor's native HTML parser. Mutually exclusive with contentJSON.
contentRevision string | number Revision hint paired with contentJSON or contentHTML. Bump it to force a fresh render cycle even when the input value reference hasn't changed.
contentJSONRevision string | number Deprecated alias for contentRevision. If both are set, contentRevision wins.
schema SchemaDefinition tiptapSchema Schema used to normalize and render the document.
theme EditorTheme Theme object for text, lists, mentions, and other rendering tokens. See EditorTheme Reference.
style StyleProp<ViewStyle> Style applied to the native viewer surface (width, margin, border, background, etc.).
allowBase64Images boolean false Opt-in support for data:image/... sources. Mirrors the editor's image behavior.
collapseTrailingEmptyParagraphs boolean true Drops trailing top-level empty paragraphs from the render output. The source contentJSON is not modified.
enableLinkTaps boolean true Makes rendered link text tappable. With no onPressLink, taps open the URL through the platform default handler.
contentId string Stable identifier for this content (e.g. message ID). Paired with containerWidth, the viewer pre-measures and caches its height so it doesn't flash at zero height on mount. See Eliminating Height Flash in Lists.
containerWidth number Container width in points. Used with contentId for height pre-measurement.
mentionPrefix string | ((mention) => string | null | undefined) Prefix prepended to the rendered mention label. Skipped when the label already starts with it.
resolveMentionTheme (mention) => EditorMentionTheme | null | undefined Per-mention theme override, resolved from the mention node's attrs and document position.
onPressLink (event: NativeProseViewerLinkPressEvent) => void Called when the user taps a rendered link. Providing this handler suppresses the platform default open behavior — the callback owns the tap.
onPressMention (event: NativeProseViewerMentionPressEvent) => void Called when the user taps a rendered mention node.

Input Behavior

  • Pass exactly one of contentJSON or contentHTML.
  • contentJSON accepts a DocumentJSON object or a serialized JSON string. Whole-document inputs normalize { type: 'doc', content: [] } to a schema-valid empty document for the active schema.
  • contentHTML accepts an HTML string parsed by the native HTML parser before rendering.
  • Pair the input with contentRevision to force a re-render without changing the value reference.
  • Trailing empty paragraphs are dropped from the render output by default. Set collapseTrailingEmptyParagraphs={false} to keep them.
  • Link taps are on by default. enableLinkTaps={false} disables them entirely (no platform open, no onPressLink).
  • Mention support is merged into the render schema, so mention nodes render and onPressMention fires for taps.
  • mentionPrefix, resolveMentionTheme, and the attrs on onPressMention resolve from the source JSON. With contentHTML, mention taps still fire but attrs is empty — there is no source JSON to enrich from.
  • onPressMention receives the label after any mentionPrefix has been applied.
  • Malformed JSON strings fall back to an empty render and log an error.

HTML Input Example

<NativeProseViewer
  contentHTML={`
    <p>Hello <strong>world</strong></p>
    <p><a href="https://example.com">Example link</a></p>
  `}
/>

Sizing Behavior

The viewer measures its rendered native content and grows to match that height. There is no internal scrolling mode.

If the viewer mounts before its final width is known, it discards the stale measurement and remeasures once the layout width changes. This matters for message previews, modals, and other containers that settle their width late.

Wrap the viewer in your screen's ScrollView, FlatList, or other container when the document should scroll as part of a larger layout.

Eliminating Height Flash in Lists

In a FlatList, each viewer briefly renders at zero height before native measurement finishes. With many items (e.g. a chat screen) this collapses and re-expands the whole list. Pass contentId and containerWidth to fix this:

const [containerWidth, setContainerWidth] = useState<number | null>(null);

<FlatList
  onLayout={(e) => setContainerWidth(e.nativeEvent.layout.width)}
  data={messages}
  renderItem={({ item }) => (
    <NativeProseViewer
      contentJSON={item.content}
      contentId={item.id}
      containerWidth={containerWidth ?? undefined}
    />
  )}
/>;

With both props set, the viewer measures content height synchronously via native text layout before rendering, and caches the result. Recycled items reuse the cached height on remount.

Call clearHeightCache() when leaving the screen to free cached heights:

import { clearHeightCache } from '@apollohg/react-native-prose-editor';

useEffect(() => {
  return () => clearHeightCache();
}, []);

NativeProseViewerLinkPressEvent

interface NativeProseViewerLinkPressEvent {
  href: string;
  text: string;
}
Field Type Meaning
href string URL carried by the tapped rendered link mark.
text string Visible text the user tapped on.

Link Tap Example

<NativeProseViewer
  contentJSON={documentJson}
  onPressLink={({ href, text }) => {
    analytics.track('viewer_link_tap', { href, text });
    openInAppBrowser(href);
  }}
/>

NativeProseViewerMentionPressEvent

interface NativeProseViewerMentionPressEvent {
  docPos: number;
  label: string;
  attrs: Record<string, unknown>;
}
interface NativeProseViewerMentionRenderContext {
  docPos: number;
  label: string;
  attrs: Record<string, unknown>;
}
Field Type Meaning
docPos number Document position of the pressed mention node.
label string Mention label resolved for the pressed node.
attrs Record<string, unknown> Mention attrs from the original document JSON — IDs, user metadata, or any custom payload you stored on the node.

Mention Press Example

<NativeProseViewer
  contentJSON={documentJson}
  onPressMention={({ attrs, label }) => {
    openProfile({
      id: String(attrs.id ?? ''),
      label,
    });
  }}
/>;

Mention Rendering Overrides

<NativeProseViewer
  contentJSON={documentJson}
  mentionPrefix={({ attrs }) => (attrs.kind === 'user' ? '@' : '#')}
  resolveMentionTheme={({ attrs }) =>
    attrs.id === 'vip-1'
      ? {
          textColor: '#fff7ed',
          backgroundColor: '#c2410c',
          fontWeight: '700',
        }
      : undefined
  }
/>

Per-mention overrides merge over the base theme.mentions tokens, so you can style specific nodes without duplicating the shared tokens.

Related Docs

Clone this wiki locally