Lynx/Modules/Rich Text/RichTextInput
@sigx/lynx-richtext · Stable · Component library

RichTextInput#

The typed SignalX wrapper over the native sigx-richtext element — a generic attributed-text input that decodes native events into typed shapes (RichDoc, SelectionState) before they reach your handlers, and delivers the element handle through onElement so you can drive it with commands. iOS + Android only.

Import#

TSX
import { RichTextInput } from '@sigx/lynx-richtext';

The field renders an internal sigx-richtext element (backed by UITextView on iOS / EditText on Android) and knows nothing about markdown — it reads a flat RichDoc model back out of its own text storage after each edit. It renders nothing meaningful under pure web/SSR; develop against a simulator or device.

Basic usage#

Capture the element handle through onElement, then pass it to every RichTextMethods command. Commands are fire-and-forget — state flows back through onChange / onSelection, which are the editor's single source of truth:

TSX
import { RichTextInput, RichTextMethods, type RichTextHandle } from '@sigx/lynx-richtext';

function Composer() {
  let el: RichTextHandle = null;

  return (
    <RichTextInput
      placeholder="Write something…"
      minHeight={40}
      maxHeight={160}
      onElement={(handle) => { el = handle; }}
      onChange={(doc, isComposing) => {
        // doc is a decoded RichDoc; do not echo writes while isComposing is true.
      }}
      onSelection={(sel) => {
        // sel.activeFormats drives toolbar active state.
      }}
      onHeightChange={(height) => {
        // auto-grow: apply height to the surrounding layout.
      }}
    />
  );
}

// Issue commands separately; a null/undefined handle no-ops silently.
RichTextMethods.toggleFormat(el, 'bold');
RichTextMethods.setBlockType(el, 'heading', 2);
RichTextMethods.insertText(el, '🎉');

Driving a formatting toolbar#

onSelection delivers a SelectionState whose activeFormats and activeBlock reflect what covers the caret (or the typing attributes when collapsed). Mirror that into your toolbar and call back into RichTextMethods — you never track formatting yourself:

TSX
import { RichTextInput, RichTextMethods, type RichTextHandle, type SelectionState } from '@sigx/lynx-richtext';

function ToolbarComposer() {
  let el: RichTextHandle = null;
  let active: SelectionState['activeFormats'] = [];

  return (
    <view>
      <RichTextInput
        accentColor="#2563eb"
        minHeight={48}
        onElement={(handle) => { el = handle; }}
        onSelection={(sel) => { active = sel.activeFormats; }}
      />
      <view bindtap={() => RichTextMethods.toggleFormat(el, 'bold')}>
        <text>{active.includes('bold') ? 'Bold (on)' : 'Bold'}</text>
      </view>
      <view bindtap={() => RichTextMethods.setBlockType(el, 'bullet')}>
        <text>Bullet list</text>
      </view>
    </view>
  );
}

Programmatic content#

The value prop sets the initial document only — once the user has edited (or after any applied setDocument), it is ignored. Use RichTextMethods.setDocument for later replacement. A RichDoc passed to value is JSON-stringified for you; always pass an explicit editable boolean (editable={undefined} historically coerced to false on Android):

TSX
import { RichTextInput, RichTextMethods, emptyDoc, type RichDoc, type RichTextHandle } from '@sigx/lynx-richtext';

function DraftComposer(props: { initial: RichDoc }) {
  let el: RichTextHandle = null;
  let latest: RichDoc = props.initial;

  function send() {
    // Hand `latest` to your transport, then clear the field.
    RichTextMethods.setDocument(el, emptyDoc());
  }

  return (
    <view>
      <RichTextInput
        value={props.initial}
        editable={true}
        onElement={(handle) => { el = handle; }}
        onChange={(doc, isComposing) => {
          if (isComposing) return; // do not snapshot mid-composition
          latest = doc;
        }}
      />
      <view bindtap={send}>
        <text>Send</text>
      </view>
    </view>
  );
}

Props#

Every prop is optional. See RichTextInputProps for the full type.

PropTypeDescription
valueRichDoc | stringInitial document — a RichDoc or JSON string. Initial-only; use setDocument for later replacement. A RichDoc is JSON-stringified for you.
placeholderstringPlaceholder text shown when the field is empty.
editablebooleanWhether the field accepts edits. Defaults to true; pass an explicit boolean.
minHeightnumberMinimum frame height in px (auto-grow lower bound).
maxHeightnumberMaximum frame height in px; clamps the frame and enables internal scrolling past the ceiling.
fontSizenumberBase font size in px; headings scale from it.
textColorstringText color (hex #RGB / #RRGGBB / #RRGGBBAA, leading # optional).
accentColorstringCaret tint and link color (hex).
placeholderColorstringPlaceholder text color (hex).
confirmType'send' | 'search' | 'next' | 'go' | 'done'Keyboard return-key style.
autoFocusbooleanRaises the keyboard on mount.
classstringStandard styling class.
stylestring | Record<string, string | number>Standard inline style.

Events#

EventTypeDescription
onElement(el: RichTextHandle) => voidReceives the RichTextHandle for command calls.
onChange(doc: RichDoc, isComposing: boolean) => voidFired after every edit with a decoded RichDoc. Do not echo writes while isComposing is true.
onSelection(sel: SelectionState) => voidCaret/selection moved; drives toolbar active state and popup anchoring via SelectionState.
onHeightChange(height: number, lines: number) => voidIntrinsic content height changed (auto-grow).
onFocus() => voidFired when the field gains focus.
onBlur() => voidFired when the field loses focus.

See also#

  • API reference — every export with signatures, including RichTextMethods and the document/event types.
  • Usage — links, mention chips, building a RichDoc by hand, and the IME/echo contract.