Skip to content

Toolbar Setup

Jayden Smith edited this page Sep 7, 2026 · 9 revisions

Toolbar Setup

Start with the editor's built-in toolbar. Use EditorToolbar directly only when its layout must live outside the editor wrapper.

Except for the standalone component below, snippets assume documentHandle is the long-lived handle created in Getting Started.

Built-in toolbar

showToolbar defaults to true; toolbarItems defaults to DEFAULT_EDITOR_TOOLBAR_ITEMS.

import {
  DEFAULT_EDITOR_TOOLBAR_ITEMS,
  RichTextEditor,
} from '@apollohg/react-native-rich-text-editor';

<RichTextEditor
  documentHandle={documentHandle}
  showToolbar
  toolbarItems={DEFAULT_EDITOR_TOOLBAR_ITEMS}
/>;

The default list supplies bold, italic, underline, strike, blockquote, bullet and ordered lists, list indent/outdent, line break, horizontal rule, undo, and redo. A button only enables when the active schema and selection allow its operation.

toolbarPlacement="keyboard" is the default: it is a native iOS keyboard accessory or Android above-keyboard toolbar. toolbarPlacement="inline" renders a React toolbar below the editor and remains visible with the keyboard closed.

<RichTextEditor
  documentHandle={documentHandle}
  toolbarPlacement="inline"
  containerStyle={{ gap: 8 }}
/>

For a toolbar within a ScrollView, FlatList, or other keyboard-aware parent, preserve toolbar taps while the keyboard is open. Composer-style screens normally use keyboardShouldPersistTaps="always" on that parent.

External focus-preserving controls

Use focusPreservingRefs for controls outside the editor that should not dismiss its keyboard or selection, such as send and attachment buttons.

import { useRef } from 'react';
import { Pressable, Text, View } from 'react-native';

const sendButtonRef = useRef<View>(null);

<>
  <RichTextEditor
    documentHandle={documentHandle}
    focusPreservingRefs={sendButtonRef}
  />
  <Pressable ref={sendButtonRef} onPress={sendMessage}>
    <Text>Send</Text>
  </Pressable>
</>;

Pass a readonly array of refs when several controls should preserve focus. Explicit calls to blur() still dismiss the editor.

Custom item list

Items use schema names. A custom mark button requires that mark in the handle's schema; node buttons require the matching node. The link and image items also require host callbacks, described in Links and Images.

import {
  RichTextEditor,
  type EditorToolbarItem,
} from '@apollohg/react-native-rich-text-editor';

const toolbarItems: readonly EditorToolbarItem[] = [
  { type: 'mark', mark: 'bold', label: 'Bold', icon: { type: 'default', id: 'bold' } },
  { type: 'heading', level: 2, label: 'Heading 2', icon: { type: 'default', id: 'h2' } },
  { type: 'list', listType: 'bullet_list', label: 'Bullets', icon: { type: 'default', id: 'bulletList' } },
  { type: 'separator' },
  { type: 'node', nodeType: 'horizontal_rule', label: 'Rule', icon: { type: 'default', id: 'horizontalRule' } },
];

<RichTextEditor documentHandle={documentHandle} toolbarItems={toolbarItems} />;

Use placement: "start" | "scroll" | "end" on an actionable item, group, or separator to control where it sits in the toolbar. An action item routes to onToolbarAction(key) and is for application behavior rather than a document command.

Every leaf item and group can set buttonStyle to override its icon size, colors, state backgrounds, or corner radius. Per-item fields override theme.toolbar for that button on the React, Android, and iOS toolbars:

const toolbarItems: readonly EditorToolbarItem[] = [
  {
    type: 'action',
    key: 'publish',
    label: 'Publish',
    icon: { type: 'glyph', text: '✓' },
    buttonStyle: {
      iconSize: 22,
      color: '#ffffff',
      backgroundColor: '#2563eb',
      disabledBackgroundColor: '#94a3b8',
      borderRadius: 10,
    },
  },
];

Grouped controls

Groups reduce a long toolbar to one slot. Their children must be actionable items: groups cannot contain another group or a separator. presentation defaults to "expand"; "menu" opens the children in a menu instead.

On iOS, menu groups open as a vertical menu anchored to the group button. Tapping the same button closes it; tapping another menu group switches directly to that menu.

const toolbarItems: readonly EditorToolbarItem[] = [
  {
    type: 'group',
    key: 'headings',
    label: 'Headings',
    icon: { type: 'glyph', text: 'H' },
    presentation: 'menu',
    items: [
      { type: 'heading', level: 1, label: 'Heading 1', icon: { type: 'default', id: 'h1' } },
      { type: 'heading', level: 2, label: 'Heading 2', icon: { type: 'default', id: 'h2' } },
    ],
  },
];

Link and image items

The package deliberately does not provide a URL modal, media-library permission flow, upload client, or image picker. Add the item, render your own UI in the request callback, then call the supplied context method.

const toolbarItems: readonly EditorToolbarItem[] = [
  { type: 'link', label: 'Link', icon: { type: 'default', id: 'link' } },
  { type: 'image', label: 'Image', icon: { type: 'default', id: 'image' } },
];

<RichTextEditor
  documentHandle={documentHandle}
  toolbarItems={toolbarItems}
  onRequestLink={(context) => context.setLink('https://example.com')}
  onRequestImage={(context) =>
    context.insertImage('https://cdn.example.com/photo.jpg', { alt: 'Example photo' })
  }
/>;

See Links and Images for a request-context walkthrough, schema requirements, image policy, and a real host-flow pattern.

Standalone toolbar

Use EditorToolbar when you own the layout. It receives editor state through callbacks and sends commands to the mounted editor ref.

import { useRef, useState } from 'react';
import {
  EditorToolbar,
  RichTextEditor,
  type HistoryState,
  type NativeEditorDocumentHandle,
  type RichTextEditorRef,
  type ReadonlyActiveState,
} from '@apollohg/react-native-rich-text-editor';

const emptyActiveState: ReadonlyActiveState = {
  marks: {}, markAttrs: {}, nodes: {}, commands: {}, allowedMarks: [], insertableNodes: [],
};

export function StandaloneToolbarEditor({
  documentHandle,
}: {
  documentHandle: NativeEditorDocumentHandle;
}) {
  const editorRef = useRef<RichTextEditorRef>(null);
  const [activeState, setActiveState] = useState<ReadonlyActiveState>(emptyActiveState);
  const [historyState, setHistoryState] = useState<HistoryState>({ canUndo: false, canRedo: false });

  return (
    <>
      <RichTextEditor
        ref={editorRef}
        documentHandle={documentHandle}
        showToolbar={false}
        onActiveStateChange={setActiveState}
        onHistoryStateChange={setHistoryState}
      />
      <EditorToolbar
        activeState={activeState}
        historyState={historyState}
        onToggleBold={() => editorRef.current?.toggleMark('bold')}
        onToggleItalic={() => editorRef.current?.toggleMark('italic')}
        onToggleUnderline={() => editorRef.current?.toggleMark('underline')}
        onToggleStrike={() => editorRef.current?.toggleMark('strike')}
        onToggleBlockquote={() => editorRef.current?.toggleBlockquote()}
        onUndo={() => editorRef.current?.undo()}
        onRedo={() => editorRef.current?.redo()}
        onToggleMark={(mark) => editorRef.current?.toggleMark(mark)}
        onToggleHeading={(level) => editorRef.current?.toggleHeading(level)}
        onToggleListType={(listType) => editorRef.current?.toggleList(listType)}
        onInsertNodeType={(nodeType) => editorRef.current?.insertNode(nodeType)}
        onRunCommand={(command) => {
          if (command === 'indentList') editorRef.current?.indentListItem();
          if (command === 'outdentList') editorRef.current?.outdentListItem();
          if (command === 'undo') editorRef.current?.undo();
          if (command === 'redo') editorRef.current?.redo();
        }}
      />
    </>
  );
}

Standalone EditorToolbar preserves the associated editor's focus by default. Set preserveEditorFocus={false} only when it is not associated with an editor or you intentionally manage focus yourself. The generic handlers above take precedence over the specific built-in handlers for matching items.

For the complete item union, icon descriptors, and handler precedence, see EditorToolbar Reference.

Clone this wiki locally