-
Notifications
You must be signed in to change notification settings - Fork 2
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.
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.
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.
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,
},
},
];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' } },
],
},
];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.
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.
React Native Rich Text Editor · Documentation · Migration Guide