Skip to content

External Text Composition

Jayden Smith edited this page Sep 6, 2026 · 5 revisions

External Text Composition

External text composition lets a consumer display provisional text directly inside RichTextEditor, revise it in place, and either commit or cancel it.

The package does not provide the external text source. It provides the editor integration needed to present provisional text without treating each revision as a document edit.

Availability

The API is available through RichTextEditorRef on iOS and Android:

supportsExternalTextComposition(): boolean;

beginExternalTextComposition(
  options?: ExternalTextCompositionOptions
): Promise<ExternalTextCompositionSession>;

Check supportsExternalTextComposition() immediately before beginning a session. It returns false when the native view is unavailable, not bound, or does not include this capability.

Basic usage

Keep the returned session and pass provisional revisions to update(). Finish the session with either commit() or cancel().

const editor = editorRef.current;
if (!editor?.supportsExternalTextComposition()) {
  return;
}

const composition = await editor.beginExternalTextComposition({
  onEnd(event) {
    console.log(event.outcome, event.cause);
  },
});

await composition.update(provisionalText);
await composition.update(correctedProvisionalText);
await composition.commit(finalText);

cancel() is the terminal alternative to commit():

await composition.cancel();

Connect the session to the external text source and controls owned by the consumer. Keep promise rejections observable and clear stored session state from onEnd.

Session API

ExternalTextCompositionSession exposes:

Method Behavior
update(text) Replaces the currently displayed provisional text. It may be called repeatedly as the value changes.
commit(finalText) Commits the final text through the editor's normal typed-input path.
cancel() Removes the provisional text without committing it.

Only one external composition owns an editor at a time. Beginning a new session settles the current owner. Calls made after a session ends reject with EXTERNAL_COMPOSITION_ENDED.

End events

onEnd runs once when a session finishes:

type ExternalTextCompositionEndCause =
  | 'consumer'
  | 'interaction'
  | 'documentChange'
  | 'lifecycle';

type ExternalTextCompositionEndEvent = {
  outcome: 'committed' | 'cancelled';
  cause: ExternalTextCompositionEndCause;
  text: string;
  error?: NativeEditorErrorBase;
};
Cause Meaning
consumer The consumer committed or cancelled the session, or began a replacement session.
interaction Native editing, selection, clipboard, accessibility, or toolbar interaction settled the composition.
documentChange A controlled or imperative document change ended the composition. An authoritative reset discards provisional text instead of committing it into the replacement document.
lifecycle Rebinding, a read-only transition, destruction, or final native detachment cancelled the session.

Use onEnd as the synchronization point for consumer state. It can arrive before the promise returned by a native command settles; the public session object handles this ordering.

Selection and document behavior

The editor captures its current text selection when composition begins:

  • A collapsed caret inserts the committed text.
  • A text range is replaced by the committed text.
  • A node or all-document selection cannot begin an external composition.

Provisional update() calls affect only the native presentation. They do not mutate the document, create undo entries, publish collaboration updates, or invoke content callbacks. The empty-editor placeholder stays hidden while provisional text is visible.

A changed commit() produces one normal typed transaction and one undo boundary. An unchanged or fully filtered commit succeeds without a local content update. Normal document policy—including readOnly, maxLength, inputFilter, schema validation, and resource limits—still applies.

For ordinary document changes, the captured position is resolved against the current document before commit. An authoritative reset, including clearContent() or controlled JSON with valueJSONUpdateMode="reset", cancels provisional presentation and wins over pending native input. The consumer does not manage document revisions or relative positions.

Errors and recovery

beginExternalTextComposition() can reject when the editor is unavailable, not editable, unsupported, or does not have a text selection.

commit() can reject when policy or position reconciliation refuses the final value. The editor removes provisional presentation, restores the latest authorized render, and ends the session as cancelled with an error.

Observe both error surfaces:

  • onEnd contains the terminal outcome and optional typed error.
  • Underlying autonomous native failures continue through documentHandle.addErrorListener(...).

A reconciliation failure can report EXTERNAL_COMPOSITION_COMMIT_FAILED to the session while its underlying error, such as POSITION_EPOCH_INVALID, is delivered to the document-handle error listener. Start a new session only after handling the terminal event.

See Production Limits and Errors for typed error handling.

Related pages

Clone this wiki locally