A cross-platform rich text editor for React Native and Expo applications
Features β’ Installation β’ Quick Start β’ API β’ Examples β’ Contributing
- π οΈ Highly Customizable - Adaptive toolbar with multiple density options
- π Cross-Platform - Works seamlessly on iOS and Android
- βΏ Accessible - WCAG compliant with proper ARIA labels
- π― Multiple Toolbar Styles - Basic, custom styled, and responsive adaptive toolbars
- π Format Toggle - Smart formatting state management
- π Rich Formatting - Bold, italic, underline, strikethrough, lists, links, and more
# npm
npm install expo-rte
# yarn
yarn add expo-rte
# pnpm
pnpm add expo-rteFor Expo development builds or bare React Native apps, you'll need to rebuild your app after installing:
# For Expo development builds
npx expo run:ios
npx expo run:android
# For bare React Native
npx react-native run-ios
npx react-native run-androidNote: This library requires a development build and will not work with Expo Go.
import React, { useRef } from 'react';
import { View } from 'react-native';
import { RichTextEditor, RichTextEditorRef } from 'expo-rte';
export default function App() {
const editorRef = useRef<RichTextEditorRef>(null);
const handleContentChange = ({ nativeEvent }) => {
console.log('Content:', nativeEvent.content);
};
return (
<View style={{ flex: 1, padding: 20 }}>
<RichTextEditor
ref={editorRef}
content="<p>Start typing...</p>"
placeholder="Enter your text here..."
onChange={handleContentChange}
style={{ height: 300 }}
/>
</View>
);
}import React, { useRef } from 'react';
import { View } from 'react-native';
import { RichTextEditor, RichTextEditorRef, ToolbarConfig } from 'expo-rte';
const customToolbarConfig: ToolbarConfig = {
adaptive: true,
groupButtons: true,
density: 'comfortable',
scrollable: true,
showLabels: true,
buttons: [
{ type: 'bold', icon: 'B', label: 'Bold', group: 'format' },
{ type: 'italic', icon: 'I', label: 'Italic', group: 'format' },
{ type: 'underline', icon: 'U', label: 'Underline', group: 'format' },
{ type: 'bullet', icon: 'β’', label: 'Bullet List', group: 'list' },
{ type: 'numbered', icon: '1.', label: 'Numbered List', group: 'list' },
{ type: 'link', icon: 'π', label: 'Link', value: 'https://example.com', group: 'insert' },
{ type: 'undo', icon: 'βΆ', label: 'Undo', group: 'action' },
{ type: 'redo', icon: 'β·', label: 'Redo', group: 'action' },
],
};
export default function AdvancedEditor() {
const editorRef = useRef<RichTextEditorRef>(null);
return (
<View style={{ flex: 1, padding: 20 }}>
<RichTextEditor
ref={editorRef}
content="<p><strong>Rich</strong> <em>text</em> editor with <u>custom</u> toolbar!</p>"
toolbarConfig={customToolbarConfig}
style={{ height: 400 }}
/>
</View>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
content |
string |
"" |
Initial HTML content |
placeholder |
string |
"" |
Placeholder text |
editable |
boolean |
true |
Whether the editor is editable |
showToolbar |
boolean |
true |
Whether to show the toolbar |
toolbarConfig |
ToolbarConfig |
undefined |
Custom toolbar configuration |
customToolbar |
ReactNode |
undefined |
Completely custom toolbar component |
style |
ViewStyle |
undefined |
Container style |
onChange |
function |
undefined |
Content change callback |
interface RichTextEditorRef {
setContent: (content: string) => Promise<void>;
getContent: () => Promise<string>;
format: (type: FormatType, value?: any) => Promise<void>;
undo: () => Promise<void>;
redo: () => Promise<void>;
}interface ToolbarConfig {
buttons?: ToolbarButton[]; // Custom button array
style?: ViewStyle; // Toolbar container styling
buttonStyle?: ViewStyle; // Individual button styling
buttonTextStyle?: TextStyle; // Button text styling
scrollable?: boolean; // Enable horizontal scrolling
showLabels?: boolean; // Show button labels
groupButtons?: boolean; // Group buttons by category
adaptive?: boolean; // Auto-adapt to screen size
compactMode?: boolean; // Use compact layout
maxButtons?: number; // Max buttons for adaptive mode
density?: 'comfortable' | 'compact' | 'dense'; // Button density
}interface ToolbarButton {
type: FormatType; // Button action type
icon: string | ReactNode; // Button icon (text or component)
label?: string; // Optional label text
value?: any; // Optional value (e.g., for links)
group?: 'format' | 'list' | 'action' | 'insert'; // Button grouping
}type FormatType =
| 'bold'
| 'italic'
| 'underline'
| 'strikethrough'
| 'bullet'
| 'numbered'
| 'link'
| 'undo'
| 'redo';const responsiveConfig: ToolbarConfig = {
adaptive: true,
density: 'comfortable',
scrollable: true,
groupButtons: true,
maxButtons: 8,
buttons: [
{ type: 'bold', icon: 'B', label: 'Bold', group: 'format' },
{ type: 'italic', icon: 'I', label: 'Italic', group: 'format' },
{ type: 'underline', icon: 'U', label: 'Underline', group: 'format' },
{ type: 'bullet', icon: 'β’', label: 'Bullets', group: 'list' },
{ type: 'numbered', icon: '1.', label: 'Numbers', group: 'list' },
{ type: 'undo', icon: 'βΆ', label: 'Undo', group: 'action' },
{ type: 'redo', icon: 'β·', label: 'Redo', group: 'action' },
],
};const customConfig: ToolbarConfig = {
buttons: [
{ type: 'bold', icon: 'π', label: 'Bold' },
{ type: 'italic', icon: 'πΌ', label: 'Italic' },
{ type: 'link', icon: 'π', label: 'Link', value: 'https://example.com' },
],
buttonStyle: {
backgroundColor: '#007AFF',
borderRadius: 8,
paddingHorizontal: 12,
},
buttonTextStyle: {
color: '#ffffff',
fontWeight: '600',
},
showLabels: true,
density: 'comfortable',
};For comprehensive examples and real-world usage patterns, see our Examples Documentation:
- Basic Usage - Simple editor setup and content handling
- Custom Toolbar - Minimal, professional, and completely custom toolbars
- Responsive Design - Adaptive toolbars and device-specific configurations
- Event Handling - Content tracking, global listeners, and real-time updates
- Content Management - Save/load functionality and content templates
- Styling - Dark themes and custom brand colors
- Integration Examples - Form integration and modal editors
import React, { useRef } from 'react';
import { View } from 'react-native';
import { RichTextEditor, RichTextEditorRef, ToolbarConfig } from 'expo-rte';
const customConfig: ToolbarConfig = {
adaptive: true,
density: 'comfortable',
buttons: [
{ type: 'bold', icon: 'B', label: 'Bold' },
{ type: 'italic', icon: 'I', label: 'Italic' },
{ type: 'undo', icon: 'βΆ', label: 'Undo' },
],
};
export default function MyEditor() {
const editorRef = useRef<RichTextEditorRef>(null);
return (
<View style={{ flex: 1, padding: 20 }}>
<RichTextEditor
ref={editorRef}
toolbarConfig={customConfig}
placeholder="Start writing..."
style={{ height: 300 }}
/>
</View>
);
}The editor automatically adapts to different screen sizes:
- π± Small screens (<400px): Compact buttons, essential formatting only
- π» Regular screens (400-768px): Standard button sizes, full toolbar
- π Tablets (768px+): Larger touch targets, more spacing
const responsiveConfig: ToolbarConfig = {
adaptive: true,
density: 'comfortable', // 'comfortable' | 'compact' | 'dense'
maxButtons: 6, // Limit buttons on small screens
scrollable: true, // Enable scrolling when needed
};// Set rich content
await editorRef.current?.setContent(`
<h1>My Document</h1>
<p>This is <strong>bold</strong> and <em>italic</em> text.</p>
<ul>
<li>Bullet point 1</li>
<li>Bullet point 2</li>
</ul>
`);
// Get HTML content
const htmlContent = await editorRef.current?.getContent();// Programmatic undo/redo
await editorRef.current?.undo();
await editorRef.current?.redo();import { MaterialIcons } from '@expo/vector-icons';
const customButtons: ToolbarButton[] = [
{
type: 'bold',
icon: <MaterialIcons name="format-bold" size={20} color="#333" />,
label: 'Bold'
},
// ... more buttons with custom icons
];const customStyle = {
container: {
borderRadius: 12,
backgroundColor: '#f5f5f5',
},
toolbar: {
backgroundColor: '#ffffff',
borderBottomColor: '#e0e0e0',
},
button: {
backgroundColor: '#007AFF',
borderRadius: 8,
},
buttonText: {
color: '#ffffff',
fontWeight: '600',
}
};
<RichTextEditor
style={customStyle.container}
toolbarConfig={{
style: customStyle.toolbar,
buttonStyle: customStyle.button,
buttonTextStyle: customStyle.buttonText,
}}
/>- Editor not appearing: Ensure you're using a development build, not Expo Go
- Formatting not working: Check that the toolbar is enabled and buttons are configured
- Content not updating: Verify the
onChangecallback is properly connected - Performance issues: Consider using
adaptive: truefor better performance on lower-end devices
// Enable debug logging
<RichTextEditor
onChange={({ nativeEvent }) => {
console.log('Content changed:', nativeEvent.content);
}}
/>We welcome contributions! Please see our Contributing Guide for details.
# Clone the repository
git clone https://github.com/mdadul/expo-rte.git
cd expo-rte
# Install dependencies
npm install
# Run the example app
cd example
npm install
npx expo run:ios # or npx expo run:androidnpm testnpm run buildMIT License - see the LICENSE file for details.
Made with β€οΈ by Emdadul Islam