Install any skill in seconds. Free to start, no credit card required.
Get Started Free →This skill should be used when users need to implement or work with the TenTap rich text editor for React Native. It provides comprehensive guidance on installation, basic setup, advanced customization, API reference, and real-world examples for building rich text editing experiences in mobile applications.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 182% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 136% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 172% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 192% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 283% | 0% |
A typed, customizable, and extendable rich text editor for React Native built on Tiptap and Prosemirror. TenTap provides a bridge architecture that enables seamless communication between React Native and a web-based editor, offering powerful editing capabilities with native mobile performance.
TenTap Editor delivers comprehensive rich text editing capabilities through a WebView-based architecture that bridges React Native and Tiptap. It provides essential features including text formatting (bold, italic, underline, strikethrough), lists (bullet, ordered, task), headings, blockquotes, code blocks, images, links, colors, and highlights. The editor supports custom themes, dark mode, dynamic CSS injection, custom fonts, and full extensibility through custom bridge extensions.
Use TenTap when building mobile applications requiring rich text editing, including chat interfaces, email composers, content management systems, note-taking apps, social media platforms, document editors, or any application where users need to format text beyond plain input. It offers both simple plug-and-play usage for standard features and advanced customization for specialized requirements.
React Native:
bashyarn add @10play/tentap-editor react-native-webview cd ios && pod install
Expo:
bashnpx expo install @10play/tentap-editor react-native-webview
Note: Expo Go supports only basic usage. For advanced features, use Expo Dev Client.
The simplest implementation requires three components: useEditorBridge hook, RichText component, and Toolbar component.
tsximport { KeyboardAvoidingView, StyleSheet } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { RichText, Toolbar, useEditorBridge } from '@10play/tentap-editor'; export const BasicEditor = () => { const editor = useEditorBridge({ autofocus: true, avoidIosKeyboard: true, initialContent: '<p>Start editing!</p>', }); return ( <SafeAreaView style={styles.fullScreen}> <RichText editor={editor} /> <KeyboardAvoidingView behavior="padding" style={styles.keyboardAvoidingView} > <Toolbar editor={editor} /> </KeyboardAvoidingView> </SafeAreaView> ); }; const styles = StyleSheet.create({ fullScreen: { flex: 1 }, keyboardAvoidingView: { position: 'absolute', width: '100%', bottom: 0, }, });
This creates a fully functional rich text editor with standard formatting capabilities. See references/Basic.tsx for the complete working example.
TenTap uses a bridge pattern to communicate between React Native and the Tiptap editor running in a WebView:
This architecture enables powerful features while maintaining type safety and minimizing WebView-Native communication overhead.
Simple Usage: Pre-configured with TenTapStarterKit including all standard rich text features. Ideal for most use cases requiring standard formatting capabilities.
Advanced Usage: Custom bundling with Vite for full control over the web editor, custom Tiptap extensions, and specialized bridge implementations. Required when adding custom extensions beyond the pre-built bridges.
The primary hook for creating and configuring an EditorBridge instance.
Key Configuration Options:
bridgeExtensions: Array of BridgeExtensions (default: TenTapStarterKit)initialContent: HTML string or JSON object for initial editor contentautofocus: Auto-focus editor on mount (default: false)avoidIosKeyboard: Keep cursor visible above keyboard (default: false, works on both iOS and Android)dynamicHeight: WebView height matches content height (default: false)theme: Custom theme configuration for native componentseditable: Enable/disable editing (default: true)customSource: Custom HTML string for advanced setupsonChange: Callback fired on content changesDEV / DEV_SERVER_URL: Development mode configuration for advanced setupstsxconst editor = useEditorBridge({ autofocus: true, avoidIosKeyboard: true, initialContent: '<p>Hello, world!</p>', bridgeExtensions: [...TenTapStartKit, CustomBridge], onChange: () => { // Handle content changes (debounce recommended) }, });
See references/useEditorBridge.md for complete API documentation.
The EditorBridge interface provides comprehensive editor control through the following methods:
Content Management:
getHTML(): Promise returning HTML contentgetText(): Promise returning plain text contentgetJSON(): Promise returning JSON document structuresetContent(content): Set editor content from HTML or JSONFocus & Selection:
focus(pos?): Focus editor at optional positionblur(): Remove focus and close keyboardsetSelection(from, to): Set text selection rangeState Retrieval:
getEditorState(): Get current BridgeState snapshotwebviewRef: Reference to underlying WebViewDynamic Styling:
injectCSS(css, tag?): Inject or update CSS stylesheetsinjectJS(js): Execute JavaScript in WebViewFormatting Commands:
toggleBold(), toggleItalic(), toggleUnderline(), toggleStrikethrough()toggleHeading(level): Set heading level (1-6)toggleCode(): Toggle inline codetoggleBlockquote(): Toggle blockquotetoggleBulletList(), toggleOrderedList(), toggleTaskList()setColor(color), unsetColor(): Set/unset text colorsetHighlight(color), toggleHighlight(color), unsetHighlight(): Text highlightingsetLink(url): Insert/update linksetImage(src): Insert imageList Management:
lift(): Outdent list itemsink(): Indent list itemHistory:
undo(), redo(): Navigate historyPlaceholder:
setPlaceholder(text): Update placeholder text dynamicallySee references/EditorBridge.md for complete API reference with all 30+ methods.
useBridgeState: Subscribe to real-time editor state changes.
tsxconst editorState = useBridgeState(editor); // Access state properties editorState.isFocused; // Editor focus state editorState.isEmpty; // Whether editor is empty editorState.isBoldActive; // Bold formatting state editorState.canToggleBold; // Whether bold can be toggled editorState.headingLevel; // Current heading level
BridgeState includes 30+ properties tracking formatting states, capabilities, and editor status. See references/BridgeState.md for complete property listing.
useEditorContent: Efficiently retrieve editor content with built-in debouncing.
tsxconst htmlContent = useEditorContent(editor, { type: 'html' }); // Alternative content types const textContent = useEditorContent(editor, { type: 'text' }); const jsonContent = useEditorContent(editor, { type: 'json' }); // Custom debounce interval (default: 10ms) const content = useEditorContent(editor, { type: 'html', debounceInterval: 100, });
See references/useEditorContent.md for complete documentation.
The WebView component rendering the Tiptap editor.
Props:
editor: EditorBridge instance (required)exclusivelyUseCustomOnMessage: Override internal onMessage handler (default: true)Supports all standard React Native WebView props (not recommended unless necessary).
tsx<RichText editor={editor} />
Pre-built toolbar with context menus for headings and links, plus 20+ toolbar items.
Props:
editor: EditorBridge instance (required)hidden: Control toolbar visibilityitems: Array of ToolbarItem configurations (default: DEFAULT_TOOLBAR_ITEMS)shouldHideDisabledToolbarItems: Hide disabled items instead of graying out (default: false)Pre-built Toolbar Items:
tsx<Toolbar editor={editor} hidden={!isKeyboardVisible} items={DEFAULT_TOOLBAR_ITEMS} shouldHideDisabledToolbarItems={true} />
Create custom toolbar items with the ToolbarItem interface:
tsxinterface ToolbarItem { onPress: ({ editor, editorState }) => () => void; active: ({ editor, editorState }) => boolean; disabled: ({ editor, editorState }) => boolean; image: ({ editor, editorState }) => any; }
See references/Components.md for complete component documentation and references/CustomAndStaticToolbar/ for custom toolbar implementation examples.
TenTap provides 19 pre-built BridgeExtensions covering standard rich text features:
Use configureExtension to customize Tiptap extension settings:
tsxconst editor = useEditorBridge({ bridgeExtensions: [ ...TenTapStartKit, PlaceholderBridge.configureExtension({ placeholder: 'Type something amazing...', }), LinkBridge.configureExtension({ openOnClick: false, }), HeadingBridge.configureExtension({ levels: [1, 2, 3], }), ], });
Use extendExtension to modify the document schema:
tsxCoreBridge.extendExtension({ content: 'heading block+', });
This configures the document to require a heading as the first node.
See references/BridgeExtensions.md and references/configureExtensions.md for complete extension reference and configuration examples.
Apply custom themes to native components using the theme configuration:
tsxconst editor = useEditorBridge({ theme: { toolbar: { toolbarBody: { backgroundColor: '#474747', borderTopColor: '#C6C6C6B3', borderBottomColor: '#C6C6C6B3', }, }, webview: { backgroundColor: '#1C1C1E', }, webviewContainer: {}, }, });
Implement complete dark mode by combining native theme and web CSS:
tsximport { darkEditorTheme, darkEditorCss } from '@10play/tentap-editor'; const editor = useEditorBridge({ bridgeExtensions: [ ...TenTapStartKit, CoreBridge.configureCSS(darkEditorCss), ], theme: darkEditorTheme, });
See references/darkTheme.md and references/DarkEditor.tsx for complete dark mode implementation.
Override or extend default CSS for any bridge extension:
tsxconst customCodeCSS = ` code { background-color: #ffdede; border-radius: 0.25em; color: #cd4242; padding: 0.25em; } `; const editor = useEditorBridge({ bridgeExtensions: [ ...TenTapStartKit, CodeBridge.configureCSS(customCodeCSS), ], });
Update CSS at runtime using the injectCSS method:
tsx// Update bridge-specific CSS editor.injectCSS(newCSS, CodeBridge.name); // Add new stylesheet without overriding editor.injectCSS(customCSS, 'custom-tag');
Integrate custom fonts by converting to base64 and configuring via CSS:
tsximport { customFont } from './font'; const editor = useEditorBridge({ bridgeExtensions: [ ...TenTapStartKit, CoreBridge.configureCSS(customFont), ], });
See references/customCss.md and references/CustomCss.tsx for complete CSS and font customization examples.
For custom Tiptap extensions or specialized editor behavior, use advanced setup with custom bundling.
Advanced setup provides:
customSource proptsx// editor-web/AdvancedEditor.tsx import { EditorContent } from '@tiptap/react'; import { useTenTap, TenTapStartKit } from '@10play/tentap-editor'; import { CounterBridge } from '../CounterBridge'; export const AdvancedEditor = () => { const editor = useTenTap({ bridges: [...TenTapStartKit, CounterBridge], tiptapOptions: { extensions: [Document, Paragraph, Text], }, }); return <EditorContent editor={editor} />; }; // In React Native app import { editorHtml } from './editor-web/build/editorHtml'; const editor = useEditorBridge({ customSource: editorHtml, // ... other config });
Implement custom bridge extensions for specialized functionality:
tsximport { BridgeExtension } from '@10play/tentap-editor'; import { CharacterCount } from '@tiptap/extension-character-count'; export const CounterBridge = new BridgeExtension< typeof CharacterCount, { words: number; characters: number } >({ tiptapExtension: CharacterCount, extendEditorState: (editor) => ({ words: editor.storage.characterCount.words(), characters: editor.storage.characterCount.characters(), }), });
See references/advancedSetup.md and references/Advanced/ for complete advanced setup instructions and working examples.
When using React Navigation headers on iOS, configure KeyboardAvoidingView with proper offsets:
tsximport { useSafeAreaInsets } from 'react-native-safe-area-context'; import { Platform } from 'react-native'; const HEADER_HEIGHT = 38; const { top } = useSafeAreaInsets(); const keyboardVerticalOffset = HEADER_HEIGHT + top; <KeyboardAvoidingView behavior="padding" keyboardVerticalOffset={Platform.OS === 'ios' ? keyboardVerticalOffset : undefined} > <Toolbar editor={editor} /> </KeyboardAvoidingView> // Add paddingBottom to RichText container on iOS <View style={{ paddingBottom: Platform.OS === 'ios' ? HEADER_HEIGHT : 0 }}> <RichText editor={editor} /> </View>
See references/navHeader.md and references/NavigationHeader.tsx for complete iOS keyboard handling examples.
The avoidIosKeyboard option helps keep the cursor visible when the editor is full-screen:
tsxconst editor = useEditorBridge({ avoidIosKeyboard: true, // Works on both iOS and Android });
This automatically:
File: references/Basic.tsx Demonstrates the simplest implementation with useEditorBridge, RichText, Toolbar, and KeyboardAvoidingView for keyboard-aware editing.
Key Concepts: Hook-based initialization, component composition, iOS keyboard handling, initial content setup
File: references/DarkEditor.tsx Complete dark theme implementation combining darkEditorTheme native theme with darkEditorCss web styling, including conditional toolbar visibility based on keyboard and focus state.
Key Concepts: Theme configuration, CSS customization, state-driven UI, conditional rendering
File: references/ConfigureExtentions.tsx Shows how to configure PlaceholderBridge, LinkBridge, and DropCursorBridge with custom options, plus dynamic content manipulation and placeholder updates.
Key Concepts: Extension configuration, option customization, dynamic content updates
File: references/CustomCss.tsx Demonstrates CSS customization through bridge extensions, dynamic CSS injection using injectCSS, and targeting specific bridges with named tags.
Key Concepts: CSS configuration, dynamic styling, bridge-specific targeting
File: references/NavigationHeader.tsx Proper toolbar positioning with React Navigation headers using safe area insets and keyboard vertical offset calculation.
Key Concepts: Safe area handling, keyboard avoidance, platform-specific logic
Directory: references/CustomAndStaticToolbar/ Build custom email composer interface with static always-visible toolbar and context-aware conditional toolbar, including state machine pattern for toolbar navigation and disabled state handling.
Key Concepts: Custom toolbar components, state management, conditional rendering, memoization
File: references/EditorStickToKeyboardExample.tsx Chat-like interface with editor attached to keyboard, scrollable message list with WebView HTML rendering, and async content extraction.
Key Concepts: Keyboard avoidance, message rendering, content extraction, ScrollView management
File: references/Advanced/AdvancedRichText.tsx Advanced implementation with custom bridge extensions (CounterBridge), real-time word/character counting, bridge state consumption, and custom HTML source integration.
Key Concepts: Custom extensions, state management, computed values, custom source
File: references/Advanced/CounterBridge.ts Complete example of creating custom BridgeExtension integrating Tiptap's CharacterCount extension, including TypeScript module augmentation for extending BridgeState and EditorBridge interfaces.
Key Concepts: Extension creation, type augmentation, storage access, computed state
Directory: references/Advanced/editor-web/ Full advanced setup with Vite bundling, custom bridge integration, module aliasing for Tiptap compatibility, content injection timing workaround, and React 18 createRoot pattern.
Key Concepts: Custom bundling, Vite configuration, bridge composition, module resolution, development workflow
references/intro.md: Introduction, features, installation, basic usagereferences/mainConcepts.md: Bridge architecture, simple vs advanced usage patternsreferences/basic.md: Step-by-step basic editor tutorialreferences/customTheme.md: Native theme customization guidereferences/darkTheme.md: Dark mode implementation with theme + CSSreferences/customCss.md: CSS and font customization, dynamic injectionreferences/configureExtensions.md: Extension configuration and schema extensionreferences/navHeader.md: iOS keyboard handling with React Navigationreferences/advancedSetup.md: Complete advanced setup guide with Vitereferences/EditorBridge.md: Complete EditorBridge interface with 30+ methodsreferences/BridgeExtensions.md: All 19 built-in extensions and configurationsreferences/BridgeState.md: BridgeState properties and useBridgeState hookreferences/Components.md: RichText, Toolbar, and ToolbarItem componentsreferences/useEditorBridge.md: Hook configuration options and usagereferences/useEditorContent.md: Content retrieval hook with debouncingBasic Examples:
Basic.tsx - Simple editor implementationDarkEditor.tsx - Dark mode editorConfigureExtentions.tsx - Extension configurationCustomCss.tsx - CSS customizationNavigationHeader.tsx - React Navigation integrationEditorStickToKeyboardExample.tsx - Chat interface patternAdvanced Examples:
Advanced/AdvancedRichText.tsx - Advanced editor with custom extensionsAdvanced/CounterBridge.ts - Custom bridge extension implementationAdvanced/editor-web/ - Complete advanced setup with Viteindex.html - HTML templateAdvancedEditor.tsx - Web editor componentindex.tsx - Entrypoint with content injectiontsconfig.json - TypeScript configurationvite.config.ts - Vite bundler configurationCustom Toolbar Examples:
CustomAndStaticToolbar/CustomAndStaticToolbar.tsx - Email composer with custom toolbarsCustomAndStaticToolbar/CustomRichText.tsx - Custom RichText wrapperUtility Components:
Icon.tsx - Reusable SVG icon componentfont.ts - Custom font definition (base64)useEditorContent instead of direct getHTML() calls for better performanceavoidIosKeyboard option for better typing experience in full-screen editorsuseBridgeState for reactive UI updates based on editor stateonChange callback with debouncing for auto-save functionalityTenTapStartKit before custom extensions to avoid duplicationkeyboardVerticalOffset when using navigation headersCombine keyboard avoidance, message rendering, and content extraction for real-time chat applications. See references/EditorStickToKeyboardExample.tsx.
Implement custom toolbars, formatting controls, and conditional rendering for email composition. See references/CustomAndStaticToolbar/.
Integrate theme and CSS customization for complete dark mode implementation. See references/DarkEditor.tsx and references/darkTheme.md.
Extend editor functionality with custom bridge extensions and TypeScript augmentation. See references/Advanced/CounterBridge.ts and references/Advanced/AdvancedRichText.tsx.
Use useEditorContent with debouncing for efficient content synchronization.
tsxconst content = useEditorContent(editor, { type: 'html' }); useEffect(() => { if (content) { debouncedSave(content); } }, [content]);
Other measured skills in the registry, with their headline benchmark lift.