-
Notifications
You must be signed in to change notification settings - Fork 2
Links and Images
Link and image buttons deliberately request host UI rather than presenting a package-owned modal or picker. Your app decides how URLs are validated, how media permission and upload work, and when an operation is cancelled. The request context then applies the result to the current editor selection.
The exported defaultSchema, prosemirrorSchema, and tiptapCompatibleSchema include the standard link mark and block image node. A custom schema must retain those exact names for the built-in link/image toolbar items and ref helpers to work. Add the standard image node with withImagesSchema(schema) or imageNodeSpec(); define a link mark with an href attribute for links.
The standard image attributes are src, alt, title, width, and height. A custom schema must accept the attributes it needs. See Schema Customization for the schema types and composition helpers.
Add a link toolbar item and display an application-owned URL editor from onRequestLink. The supplied LinkRequestContext describes the active link and exposes the write operations:
| Field | Meaning |
|---|---|
href?: string |
Current link target when the selection is linked. |
isActive: boolean |
Whether the link mark is active at the selection. |
selection: Selection |
Engine document selection when the request was made. |
setLink(href) |
Apply or update a link at the active editor selection. |
unsetLink() |
Remove the link at the active editor selection. |
import { useState } from 'react';
import {
RichTextEditor,
type NativeEditorDocumentHandle,
type LinkRequestContext,
} from '@apollohg/react-native-rich-text-editor';
export function LinkEditor({ documentHandle }: { documentHandle: NativeEditorDocumentHandle }) {
const [request, setRequest] = useState<LinkRequestContext | null>(null);
const [href, setHref] = useState('');
return (
<>
<RichTextEditor
documentHandle={documentHandle}
toolbarItems={[
{ type: 'link', label: 'Link', icon: { type: 'default', id: 'link' } },
]}
onRequestLink={(context) => {
setRequest(context);
setHref(context.href ?? 'https://');
}}
/>
<LinkModal
visible={request != null}
value={href}
onChangeText={setHref}
onCancel={() => setRequest(null)}
onSave={() => {
const nextHref = href.trim();
if (nextHref === '') request?.unsetLink();
else request?.setLink(nextHref);
setRequest(null);
}}
/>
</>
);
}LinkModal is an app component in this example. Validate and normalize URLs according to your product's policy before calling setLink.
Outside a request flow, RichTextEditorRef.setLink(href) and .unsetLink() apply or remove a link at the current selection. There is no separate ref getter for the active link: use LinkRequestContext.href to inspect it during a toolbar request, or getContentJson() to inspect the document snapshot.
An image toolbar item is enabled only when the current schema permits insertion and onRequestImage is supplied. The ImageRequestContext contains:
| Field | Meaning |
|---|---|
selection: Selection |
Engine document selection when the request was made. |
insertImage(src, attrs?) |
Insert a block image at the active editor selection. attrs may supply alt, title, width, and height. |
import {
RichTextEditor,
type NativeEditorDocumentHandle,
type ImageRequestContext,
} from '@apollohg/react-native-rich-text-editor';
async function uploadImage(): Promise<string | null> {
// Pick, permission-check, and upload in your app.
return 'https://cdn.example.com/hero.jpg';
}
export function ImageEditor({ documentHandle }: { documentHandle: NativeEditorDocumentHandle }) {
const handleRequestImage = async (context: ImageRequestContext) => {
const src = await uploadImage();
if (src != null) {
context.insertImage(src, {
alt: 'Article hero',
width: 1200,
height: 800,
});
}
};
return (
<RichTextEditor
documentHandle={documentHandle}
toolbarItems={[
{ type: 'image', label: 'Image', icon: { type: 'default', id: 'image' } },
]}
onRequestImage={handleRequestImage}
/>
);
}RichTextEditorRef.insertImage(src, attrs?) provides the same insertion path for an app button outside the toolbar. Use getContentJson() to inspect the current image nodes; native resize writes the resulting width and height back to the document.
Selected images expose native resize controls by default on iOS and Android. Set allowImageResizing={false} on the editor to keep them fixed; insertion and rendering still work.
Images in valid imported HTML or JSON use the same native image pipeline as inserted images. Keep the standard image node in the schema and supply a supported source; importing content does not bypass loader budgets. The standard node is a block image, so use schema-valid document structure rather than assuming arbitrary inline image markup will behave like a browser.
Both iOS and Android decode raster images and a self-contained SVG subset from remote HTTP(S) bytes or base64 data URLs. SVG bytes are detected by the decoder, so a URL does not need to end in .svg. Native decoders rasterize SVG to a bounded bitmap using maxDecodeDimensionPx; the editor does not embed a browser or run SVG scripts.
Supported SVG content includes common vector shapes and text, with local fragment references such as gradients and use. This is not full browser SVG support: external references, embedded image elements, scripts/event handlers, foreign HTML, XML declarations that load external content, reference cycles, and excessive complexity are rejected. Platform renderer support differs; for example, iOS rejects clipping that requires unsupported bitmap masks. Verify the actual assets you plan to ship on both platforms. Failed SVGs follow the same unavailable-image path as other decode failures.
Set the flat document theme.image style for image borders, padding, margins, corner radius, and resizeMode. A slot can use an array of overrides. See Styling and EditorTheme Reference; these styles do not change the persisted image source or intrinsic attributes.
Allowing base64 data URLs in supported HTML paths is off by default and controlled by the handle's engine policy, not an editor prop:
import { createNativeEditorDocumentHandle } from '@apollohg/react-native-rich-text-editor';
const documentHandle = createNativeEditorDocumentHandle({
initialization: { type: 'localEmpty' },
policy: { allowBase64Images: true },
});This policy applies when supported HTML paths parse or replace content, including localHtml initialization, controlled value, and setContent(html). It does not gate JSON ingestion (localJson, valueJSON, or setContentJson) or insertImage, which inserts a JSON command. Treat JSON and insertion sources as application-controlled input; do not infer that a data URL is rejected there because allowBase64Images is false. The policy also does not replace the rendering loader's resource limits.
imageLoadingPolicy is an editor prop that bounds native image work. Its defaults are 10 MiB source bytes, 10-second connect timeout, 20-second read timeout, 60-second request timeout, 2 concurrent requests, 64 pending requests, a 2048-pixel decode dimension, and 32 MiB of retained decoded pixels per editor or viewer. You can lower or raise a value only within the exported hard limits.
<RichTextEditor
documentHandle={documentHandle}
imageLoadingPolicy={{
maxSourceBytes: 2 * 1024 * 1024,
maxConcurrentRequests: 1,
maxDecodeDimensionPx: 1280,
maxDecodedBytes: 16 * 1024 * 1024,
}}
/>;Both platforms use bounded native loading and decoding for data URLs and remote HTTP(S) images. Invalid, oversized, timed-out, unsupported, or failed sources do not become usable image pixels. For portable production behavior, upload picked local media and insert a controlled HTTPS URL rather than relying on an arbitrary device-local URI.
For global document/resource limits and error handling, see Production Limits and Errors. For putting the buttons in the toolbar, see Toolbar Setup.
React Native Rich Text Editor · Documentation · Migration Guide