react-native-richify 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +383 -120
  2. package/lib/commonjs/components/RenderedOutput.js +168 -0
  3. package/lib/commonjs/components/RenderedOutput.js.map +1 -0
  4. package/lib/commonjs/components/RichTextInput.js +130 -15
  5. package/lib/commonjs/components/RichTextInput.js.map +1 -1
  6. package/lib/commonjs/components/Toolbar.js +41 -2
  7. package/lib/commonjs/components/Toolbar.js.map +1 -1
  8. package/lib/commonjs/constants/defaultStyles.js +51 -1
  9. package/lib/commonjs/constants/defaultStyles.js.map +1 -1
  10. package/lib/commonjs/hooks/useFormatting.js +44 -2
  11. package/lib/commonjs/hooks/useFormatting.js.map +1 -1
  12. package/lib/commonjs/hooks/useRichText.js +75 -6
  13. package/lib/commonjs/hooks/useRichText.js.map +1 -1
  14. package/lib/commonjs/utils/formatter.js +48 -9
  15. package/lib/commonjs/utils/formatter.js.map +1 -1
  16. package/lib/commonjs/utils/parser.js +1 -1
  17. package/lib/commonjs/utils/parser.js.map +1 -1
  18. package/lib/commonjs/utils/serializer.js +102 -6
  19. package/lib/commonjs/utils/serializer.js.map +1 -1
  20. package/lib/commonjs/utils/styleMapper.js +11 -0
  21. package/lib/commonjs/utils/styleMapper.js.map +1 -1
  22. package/lib/module/components/RenderedOutput.js +163 -0
  23. package/lib/module/components/RenderedOutput.js.map +1 -0
  24. package/lib/module/components/RichTextInput.js +131 -16
  25. package/lib/module/components/RichTextInput.js.map +1 -1
  26. package/lib/module/components/Toolbar.js +41 -2
  27. package/lib/module/components/Toolbar.js.map +1 -1
  28. package/lib/module/constants/defaultStyles.js +51 -1
  29. package/lib/module/constants/defaultStyles.js.map +1 -1
  30. package/lib/module/hooks/useFormatting.js +45 -3
  31. package/lib/module/hooks/useFormatting.js.map +1 -1
  32. package/lib/module/hooks/useRichText.js +75 -6
  33. package/lib/module/hooks/useRichText.js.map +1 -1
  34. package/lib/module/utils/formatter.js +46 -9
  35. package/lib/module/utils/formatter.js.map +1 -1
  36. package/lib/module/utils/parser.js +1 -1
  37. package/lib/module/utils/parser.js.map +1 -1
  38. package/lib/module/utils/serializer.js +102 -6
  39. package/lib/module/utils/serializer.js.map +1 -1
  40. package/lib/module/utils/styleMapper.js +11 -0
  41. package/lib/module/utils/styleMapper.js.map +1 -1
  42. package/lib/typescript/src/components/RenderedOutput.d.ts +9 -0
  43. package/lib/typescript/src/components/RenderedOutput.d.ts.map +1 -0
  44. package/lib/typescript/src/components/RichTextInput.d.ts.map +1 -1
  45. package/lib/typescript/src/components/Toolbar.d.ts.map +1 -1
  46. package/lib/typescript/src/constants/defaultStyles.d.ts +1 -0
  47. package/lib/typescript/src/constants/defaultStyles.d.ts.map +1 -1
  48. package/lib/typescript/src/hooks/useFormatting.d.ts +4 -1
  49. package/lib/typescript/src/hooks/useFormatting.d.ts.map +1 -1
  50. package/lib/typescript/src/hooks/useRichText.d.ts.map +1 -1
  51. package/lib/typescript/src/index.d.ts +1 -1
  52. package/lib/typescript/src/index.d.ts.map +1 -1
  53. package/lib/typescript/src/types/index.d.ts +94 -1
  54. package/lib/typescript/src/types/index.d.ts.map +1 -1
  55. package/lib/typescript/src/utils/formatter.d.ts +9 -1
  56. package/lib/typescript/src/utils/formatter.d.ts.map +1 -1
  57. package/lib/typescript/src/utils/parser.d.ts.map +1 -1
  58. package/lib/typescript/src/utils/serializer.d.ts.map +1 -1
  59. package/lib/typescript/src/utils/styleMapper.d.ts.map +1 -1
  60. package/package.json +1 -1
  61. package/src/components/RenderedOutput.tsx +231 -0
  62. package/src/components/RichTextInput.tsx +193 -19
  63. package/src/components/Toolbar.tsx +54 -2
  64. package/src/constants/defaultStyles.d.ts +2 -1
  65. package/src/constants/defaultStyles.ts +20 -0
  66. package/src/hooks/useFormatting.ts +101 -2
  67. package/src/hooks/useRichText.ts +101 -5
  68. package/src/index.d.ts +1 -1
  69. package/src/index.ts +4 -0
  70. package/src/types/index.d.ts +94 -1
  71. package/src/types/index.ts +104 -1
  72. package/src/utils/formatter.ts +60 -6
  73. package/src/utils/parser.ts +6 -1
  74. package/src/utils/serializer.ts +150 -8
  75. package/src/utils/styleMapper.ts +21 -0
package/README.md CHANGED
@@ -1,22 +1,22 @@
1
1
  # react-native-richify
2
2
 
3
- A production-grade, fully customizable React Native Rich Text Input using the **Overlay Technique** no WebView required.
3
+ A rich text input for React Native with a normal `TextInput` editing surface, a built-in formatting toolbar, and live Markdown or HTML output.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/react-native-richify.svg)](https://www.npmjs.com/package/react-native-richify)
6
6
  [![license](https://img.shields.io/npm/l/react-native-richify.svg)](https://github.com/soumya-99/react-native-richify/blob/main/LICENSE)
7
7
 
8
8
  ## Features
9
9
 
10
- - 🚀 **No WebView** — Pure React Native components (TextInput + Text overlay)
11
- - 📝 **Rich Formatting** Bold, italic, underline, strikethrough, code
12
- - 🎨 **Text & Background Colors** Full color customization
13
- - 📐 **Font Sizes & Headings** — H1, H2, H3 presets + custom sizes
14
- - 🔧 **Fully Customizable Toolbar** Default toolbar + render props for full control
15
- - 🎭 **Theming** Complete theme system with plug-and-play defaults
16
- - 💾 **Serialization** Export/import as JSON
17
- - 📦 **TypeScript First** Full type definitions out of the box
18
- - 🪝 **Headless Hook** `useRichText` for building custom UIs
19
- - 🌐 **Context API** — `RichTextProvider` for nested component access
10
+ - Native editing with no WebView
11
+ - Rich inline formatting: bold, italic, underline, strikethrough, code
12
+ - Line-level formatting: H1, H2, H3, bullet list, ordered list, left/center/right alignment
13
+ - Link support on selected text
14
+ - Live output panel with Markdown or HTML serialization
15
+ - Raw output view or rendered preview view
16
+ - Auto-growing input with configurable input and preview heights
17
+ - Custom toolbar items, custom toolbar rendering, and theme overrides
18
+ - JSON import/export through typed segment data
19
+ - Headless `useRichText` hook for fully custom editors
20
20
 
21
21
  ## Installation
22
22
 
@@ -26,7 +26,7 @@ npm install react-native-richify
26
26
  yarn add react-native-richify
27
27
  ```
28
28
 
29
- No native dependencies required. Works with Expo and bare React Native.
29
+ No native modules are required. The package works in Expo and bare React Native apps.
30
30
 
31
31
  ## Quick Start
32
32
 
@@ -39,165 +39,411 @@ export default function App() {
39
39
  return (
40
40
  <View style={{ flex: 1, padding: 16 }}>
41
41
  <RichTextInput
42
- placeholder="Start typing..."
42
+ placeholder="Write something..."
43
43
  showToolbar
44
+ showOutputPreview
45
+ defaultOutputFormat="markdown"
44
46
  onChangeText={(text) => console.log('Plain text:', text)}
45
- onChangeSegments={(segments) => console.log('Segments:', segments)}
47
+ onChangeOutput={(output, format) =>
48
+ console.log(`Serialized ${format}:`, output)
49
+ }
46
50
  />
47
51
  </View>
48
52
  );
49
53
  }
50
54
  ```
51
55
 
52
- That's it! You get a fully functional rich text editor with default styling.
56
+ This gives you:
53
57
 
54
- ## API Reference
58
+ - a visible auto-growing `TextInput`
59
+ - a horizontally scrollable toolbar
60
+ - a collapsible output panel that opens when content exists
61
+ - Markdown output by default, with HTML and rendered preview toggles in the toolbar
55
62
 
56
- ### `<RichTextInput />`
63
+ ## How Editing Works
57
64
 
58
- The main component. Drop it in and it works.
65
+ The editor stores content as `StyledSegment[]`, not HTML strings. The plain text you type is always the source of truth for editing, and formatting metadata is applied to matching text ranges or lines.
59
66
 
60
- | Prop | Type | Default | Description |
61
- |------|------|---------|-------------|
62
- | `initialSegments` | `StyledSegment[]` | `[]` | Initial content |
63
- | `onChangeSegments` | `(segments: StyledSegment[]) => void` | | Called when content changes |
64
- | `onChangeText` | `(text: string) => void` | — | Called with plain text |
65
- | `placeholder` | `string` | `"Start typing..."` | Placeholder text |
66
- | `editable` | `boolean` | `true` | Whether input is editable |
67
- | `maxLength` | `number` | — | Max character length |
68
- | `showToolbar` | `boolean` | `true` | Show formatting toolbar |
69
- | `toolbarPosition` | `'top' \| 'bottom'` | `'top'` | Toolbar position |
70
- | `toolbarItems` | `ToolbarItem[]` | Default items | Custom toolbar items |
71
- | `theme` | `RichTextTheme` | Default theme | Theme overrides |
72
- | `multiline` | `boolean` | `true` | Enable multiline |
73
- | `minHeight` | `number` | `120` | Minimum editor height |
74
- | `maxHeight` | `number` | | Maximum editor height |
75
- | `autoFocus` | `boolean` | `false` | Auto-focus on mount |
76
- | `renderToolbar` | `(props) => ReactElement` | — | Custom toolbar renderer |
77
- | `onReady` | `(actions: RichTextActions) => void` | — | Called with action methods |
67
+ Formatting rules:
68
+
69
+ - If no text is selected, pressing a style button affects the next characters you type.
70
+ - If text is selected, pressing a style button formats the selected range immediately.
71
+ - Headings, lists, and alignment are line-level controls.
72
+ - Every formatting button is toggleable except the output controls: `MD`, `HTML`, `Raw`, and `View`.
73
+
74
+ ## Built-in Toolbar
75
+
76
+ The default toolbar includes these controls:
77
+
78
+ | Button | Purpose |
79
+ | --- | --- |
80
+ | `B` | Toggle bold |
81
+ | `I` | Toggle italic |
82
+ | `U` | Toggle underline |
83
+ | `S` | Toggle strikethrough |
84
+ | `<>` | Toggle inline code |
85
+ | `H1`, `H2`, `H3` | Toggle heading level on the current line |
86
+ | `•≡` | Toggle bullet list on the current line |
87
+ | `1≡` | Toggle ordered list on the current line |
88
+ | `🔗` | Apply or clear a hyperlink on the current selection |
89
+ | `⇤`, `↔`, `⇥` | Toggle left, center, or right alignment on the current line |
90
+ | `MD`, `HTML` | Switch serialized output format |
91
+ | `Raw`, `View` | Switch between literal serialized output and rendered preview |
78
92
 
79
- ### `useRichText(options?)`
93
+ Notes:
80
94
 
81
- Headless hook for building custom UIs.
95
+ - Pressing the active heading again removes that heading.
96
+ - Pressing the active list or alignment button again clears it.
97
+ - The toolbar is horizontally scrollable by default.
98
+ - Image insertion is not part of the built-in toolbar right now.
99
+
100
+ ## Output Panel
101
+
102
+ The panel below the input can show either:
103
+
104
+ - literal serialized output (`Raw`)
105
+ - rendered rich output (`View`)
106
+
107
+ It can serialize as either:
108
+
109
+ - Markdown
110
+ - HTML
111
+
112
+ Useful props:
113
+
114
+ | Prop | Description |
115
+ | --- | --- |
116
+ | `showOutputPreview` | Show or hide the output panel entirely |
117
+ | `outputFormat` | Controlled output format |
118
+ | `defaultOutputFormat` | Initial output format for uncontrolled usage |
119
+ | `outputPreviewMode` | Controlled preview mode: `'literal'` or `'rendered'` |
120
+ | `defaultOutputPreviewMode` | Initial preview mode for uncontrolled usage |
121
+ | `maxOutputHeight` | Max height of the output panel before it scrolls |
122
+ | `onChangeOutput` | Called whenever serialized output changes |
123
+ | `onChangeOutputFormat` | Called when toolbar output format changes |
124
+ | `onChangeOutputPreviewMode` | Called when toolbar preview mode changes |
125
+
126
+ Example:
82
127
 
83
128
  ```tsx
84
- import { useRichText } from 'react-native-richify';
129
+ import React, { useState } from 'react';
130
+ import { RichTextInput, type OutputFormat } from 'react-native-richify';
85
131
 
86
- function MyEditor() {
87
- const { state, actions } = useRichText({
88
- onChangeText: (text) => console.log(text),
89
- });
132
+ export function ControlledOutputEditor() {
133
+ const [format, setFormat] = useState<OutputFormat>('html');
90
134
 
91
135
  return (
92
- <>
93
- <Button title="Bold" onPress={() => actions.toggleFormat('bold')} />
94
- <TextInput
95
- value={actions.getPlainText()}
96
- onChangeText={actions.handleTextChange}
97
- onSelectionChange={(e) =>
98
- actions.handleSelectionChange(e.nativeEvent.selection)
99
- }
100
- />
101
- </>
136
+ <RichTextInput
137
+ outputFormat={format}
138
+ onChangeOutputFormat={setFormat}
139
+ defaultOutputPreviewMode="rendered"
140
+ maxOutputHeight={220}
141
+ onChangeOutput={(output) => {
142
+ console.log(output);
143
+ }}
144
+ />
102
145
  );
103
146
  }
104
147
  ```
105
148
 
106
- ### `<RichTextProvider>` & `useRichTextContext()`
149
+ ## Links
150
+
151
+ Link formatting is selection-based. Select text, then press the link button.
152
+
153
+ There are two ways to use it:
107
154
 
108
- Share state across the component tree:
155
+ 1. Provide `onRequestLink` and show your own prompt, sheet, or modal.
156
+ 2. Rely on the built-in fallback, which only auto-links when the selected text already looks like a URL, domain, or email address.
157
+
158
+ Custom link flow:
109
159
 
110
160
  ```tsx
111
- import { RichTextProvider, useRichTextContext } from 'react-native-richify';
161
+ <RichTextInput
162
+ onRequestLink={({ selectedText, currentUrl, applyLink }) => {
163
+ console.log('Selected:', selectedText);
164
+ console.log('Existing URL:', currentUrl);
112
165
 
113
- function CustomToolbar() {
114
- const { state, actions } = useRichTextContext();
115
- return <Button title="B" onPress={() => actions.toggleFormat('bold')} />;
116
- }
166
+ // Replace this with your own modal, bottom sheet, or form.
167
+ applyLink('https://example.com');
168
+ }}
169
+ />
170
+ ```
117
171
 
118
- function App() {
119
- return (
120
- <RichTextProvider onChangeText={console.log}>
121
- <CustomToolbar />
122
- <RichTextInput showToolbar={false} />
123
- </RichTextProvider>
124
- );
125
- }
172
+ If the selection already has a link, pressing the link button clears it.
173
+
174
+ ## Sizing and TextInput Behavior
175
+
176
+ `RichTextInput` uses a normal visible `TextInput`.
177
+
178
+ - `minHeight` controls the minimum editor height.
179
+ - `maxHeight` limits the editor height before the input itself scrolls.
180
+ - `maxOutputHeight` limits the output panel height before the preview scrolls.
181
+ - `textInputProps` lets you pass through additional native `TextInput` props.
182
+
183
+ Example:
184
+
185
+ ```tsx
186
+ <RichTextInput
187
+ minHeight={140}
188
+ maxHeight={280}
189
+ maxOutputHeight={200}
190
+ textInputProps={{
191
+ autoCapitalize: 'sentences',
192
+ keyboardType: 'default',
193
+ }}
194
+ />
126
195
  ```
127
196
 
128
- ### Actions
197
+ ## API Overview
198
+
199
+ ### `<RichTextInput />`
200
+
201
+ Most common props:
129
202
 
130
- | Method | Description |
131
- |--------|-------------|
132
- | `toggleFormat(format)` | Toggle bold/italic/underline/strikethrough/code |
203
+ | Prop | Type | Default | Description |
204
+ | --- | --- | --- | --- |
205
+ | `initialSegments` | `StyledSegment[]` | empty content | Initial document value |
206
+ | `onChangeSegments` | `(segments) => void` | - | Called with the rich document model |
207
+ | `onChangeText` | `(text) => void` | - | Called with plain text |
208
+ | `placeholder` | `string` | `"Start typing..."` | Placeholder text |
209
+ | `showToolbar` | `boolean` | `true` | Show the built-in toolbar |
210
+ | `toolbarPosition` | `'top' \| 'bottom'` | `'top'` | Place toolbar above or below the editor |
211
+ | `toolbarItems` | `ToolbarItem[]` | default items | Replace the built-in toolbar item list |
212
+ | `theme` | `RichTextTheme` | default theme | Visual overrides |
213
+ | `showOutputPreview` | `boolean` | `true` | Enable the output panel |
214
+ | `multiline` | `boolean` | `true` | Standard `TextInput` multiline editing |
215
+ | `minHeight` | `number` | `120` | Minimum editor height |
216
+ | `maxHeight` | `number` | - | Maximum editor height before scrolling |
217
+ | `autoFocus` | `boolean` | `false` | Focus input on mount |
218
+ | `textInputProps` | `TextInputProps` subset | - | Additional native input props |
219
+ | `renderToolbar` | `(props) => ReactElement` | - | Render a completely custom toolbar |
220
+ | `onReady` | `(actions) => void` | - | Access editor actions outside the toolbar |
221
+
222
+ ### Actions from `onReady` or `useRichText`
223
+
224
+ | Action | Description |
225
+ | --- | --- |
226
+ | `toggleFormat(format)` | Toggle `bold`, `italic`, `underline`, `strikethrough`, or `code` |
227
+ | `setHeading(level)` | Toggle `h1`, `h2`, `h3`, or clear with `none` |
228
+ | `setListType(type)` | Toggle `bullet`, `ordered`, or clear with `none` |
229
+ | `setTextAlign(align)` | Toggle `left`, `center`, or `right` alignment |
230
+ | `setLink(url?)` | Apply or clear hyperlink formatting |
133
231
  | `setColor(color)` | Set text color |
134
232
  | `setBackgroundColor(color)` | Set highlight color |
135
233
  | `setFontSize(size)` | Set font size |
136
- | `setHeading(level)` | Set heading (h1/h2/h3/none) |
137
- | `handleTextChange(text)` | Process text input changes |
138
- | `handleSelectionChange(sel)` | Process selection changes |
139
- | `getPlainText()` | Get plain text content |
140
- | `exportJSON()` | Export segments as JSON |
141
- | `importJSON(segments)` | Import segments from JSON |
142
- | `clear()` | Clear all content |
234
+ | `handleTextChange(text)` | Sync plain text input changes |
235
+ | `handleSelectionChange(selection)` | Sync `TextInput` selection changes |
236
+ | `isFormatActive(format)` | Check active state for inline formats |
237
+ | `getSelectionStyle()` | Read the common style for the current selection |
238
+ | `getOutput(format?)` | Serialize current content as Markdown or HTML |
239
+ | `getPlainText()` | Get plain text only |
240
+ | `exportJSON()` | Export the current segment array |
241
+ | `importJSON(segments)` | Replace content from a saved segment array |
242
+ | `clear()` | Reset the editor |
243
+
244
+ ## Custom Toolbar
245
+
246
+ Use `toolbarItems` when you only want to replace the built-in buttons, or `renderToolbar` when you want full control.
247
+
248
+ ### `toolbarItems`
249
+
250
+ Supported built-in item fields:
251
+
252
+ - `format`
253
+ - `heading`
254
+ - `listType`
255
+ - `textAlign`
256
+ - `outputFormat`
257
+ - `outputPreviewMode`
258
+ - `actionType: 'link'`
259
+ - `onPress`
260
+ - `renderButton`
261
+
262
+ Example:
263
+
264
+ ```tsx
265
+ <RichTextInput
266
+ toolbarItems={[
267
+ { id: 'bold', label: 'B', format: 'bold' },
268
+ { id: 'h1', label: 'H1', heading: 'h1' },
269
+ { id: 'bullet', label: '•≡', listType: 'bullet' },
270
+ { id: 'center', label: '↔', textAlign: 'center' },
271
+ { id: 'html', label: 'HTML', outputFormat: 'html' },
272
+ { id: 'link', label: '🔗', actionType: 'link' },
273
+ ]}
274
+ />
275
+ ```
276
+
277
+ ### `renderToolbar`
278
+
279
+ Use `renderToolbar` if you need a custom layout or your own button components.
280
+
281
+ ```tsx
282
+ <RichTextInput
283
+ renderToolbar={({
284
+ actions,
285
+ outputFormat,
286
+ outputPreviewMode,
287
+ onOutputFormatChange,
288
+ onOutputPreviewModeChange,
289
+ }) => (
290
+ <>
291
+ <MyButton onPress={() => actions.toggleFormat('bold')} label="Bold" />
292
+ <MyButton onPress={() => actions.setHeading('h2')} label="Heading" />
293
+ <MyButton
294
+ onPress={() => onOutputFormatChange('html')}
295
+ label={outputFormat === 'html' ? 'HTML on' : 'HTML'}
296
+ />
297
+ <MyButton
298
+ onPress={() => onOutputPreviewModeChange('rendered')}
299
+ label={outputPreviewMode === 'rendered' ? 'Preview on' : 'Preview'}
300
+ />
301
+ </>
302
+ )}
303
+ />
304
+ ```
143
305
 
144
306
  ## Theming
145
307
 
146
- Customize every aspect of the editor:
308
+ The theme object lets you style the editor container, input, toolbar, output panel, and button states.
147
309
 
148
310
  ```tsx
149
311
  <RichTextInput
150
312
  theme={{
151
313
  colors: {
152
- primary: '#8B5CF6',
153
- background: '#1F2937',
154
- text: '#F9FAFB',
155
- placeholder: '#6B7280',
156
- toolbarBackground: '#374151',
157
- toolbarBorder: '#4B5563',
158
- cursor: '#8B5CF6',
314
+ primary: '#0F766E',
315
+ background: '#FFFFFF',
316
+ text: '#0F172A',
317
+ placeholder: '#94A3B8',
318
+ toolbarBackground: '#F8FAFC',
319
+ toolbarBorder: '#CBD5E1',
320
+ link: '#0EA5E9',
321
+ cursor: '#0F766E',
159
322
  },
160
323
  containerStyle: {
161
324
  borderRadius: 16,
162
- borderWidth: 2,
163
- borderColor: '#4B5563',
325
+ borderWidth: 1,
326
+ borderColor: '#CBD5E1',
327
+ },
328
+ outputContainerStyle: {
329
+ marginHorizontal: 12,
330
+ marginBottom: 12,
331
+ padding: 12,
332
+ borderRadius: 12,
333
+ backgroundColor: '#F8FAFC',
164
334
  },
165
335
  toolbarButtonActiveStyle: {
166
- backgroundColor: '#4C1D95',
336
+ backgroundColor: '#CCFBF1',
167
337
  },
168
338
  }}
169
339
  />
170
340
  ```
171
341
 
172
- ## Custom Toolbar
342
+ Useful theme keys:
343
+
344
+ - `containerStyle`
345
+ - `inputStyle`
346
+ - `baseTextStyle`
347
+ - `outputContainerStyle`
348
+ - `outputLabelStyle`
349
+ - `outputTextStyle`
350
+ - `renderedOutputStyle`
351
+ - `toolbarStyle`
352
+ - `toolbarButtonStyle`
353
+ - `toolbarButtonActiveStyle`
354
+ - `toolbarButtonTextStyle`
355
+ - `toolbarButtonActiveTextStyle`
356
+ - `codeStyle`
357
+ - `colors.primary`
358
+ - `colors.link`
359
+
360
+ ## Headless Hook
361
+
362
+ Use `useRichText` when you want to build your own editor UI.
173
363
 
174
364
  ```tsx
175
- <RichTextInput
176
- toolbarItems={[
177
- { id: 'bold', label: 'B', format: 'bold' },
178
- { id: 'italic', label: 'I', format: 'italic' },
179
- {
180
- id: 'custom',
181
- label: '🎨',
182
- onPress: () => showColorPicker(),
183
- },
184
- {
185
- id: 'render',
186
- label: 'Custom',
187
- renderButton: ({ active, onPress }) => (
188
- <MyCustomButton active={active} onPress={onPress} />
189
- ),
190
- },
191
- ]}
192
- />
365
+ import React from 'react';
366
+ import { TextInput, View, Button } from 'react-native';
367
+ import { useRichText } from 'react-native-richify';
368
+
369
+ export function HeadlessEditor() {
370
+ const { state, actions } = useRichText({
371
+ onChangeText: (text) => console.log(text),
372
+ });
373
+
374
+ return (
375
+ <View>
376
+ <Button title="Bold" onPress={() => actions.toggleFormat('bold')} />
377
+ <Button title="Bullet" onPress={() => actions.setListType('bullet')} />
378
+ <TextInput
379
+ multiline
380
+ value={actions.getPlainText()}
381
+ onChangeText={actions.handleTextChange}
382
+ onSelectionChange={(event) =>
383
+ actions.handleSelectionChange(event.nativeEvent.selection)
384
+ }
385
+ style={{ minHeight: 120, borderWidth: 1, padding: 12 }}
386
+ />
387
+ </View>
388
+ );
389
+ }
390
+ ```
391
+
392
+ ## Context API
393
+
394
+ `RichTextProvider` and `useRichTextContext()` are for custom UIs built on the hook.
395
+
396
+ Important: the shipped `<RichTextInput />` manages its own state. Wrapping it in `RichTextProvider` does not automatically bind it to external context state.
397
+
398
+ Use context when you want to split a custom editor into multiple components:
399
+
400
+ ```tsx
401
+ import React from 'react';
402
+ import { Button, TextInput, View } from 'react-native';
403
+ import {
404
+ RichTextProvider,
405
+ useRichTextContext,
406
+ } from 'react-native-richify';
407
+
408
+ function ToolbarRow() {
409
+ const { actions } = useRichTextContext();
410
+ return <Button title="Italic" onPress={() => actions.toggleFormat('italic')} />;
411
+ }
412
+
413
+ function EditorField() {
414
+ const { actions } = useRichTextContext();
415
+
416
+ return (
417
+ <TextInput
418
+ multiline
419
+ value={actions.getPlainText()}
420
+ onChangeText={actions.handleTextChange}
421
+ onSelectionChange={(event) =>
422
+ actions.handleSelectionChange(event.nativeEvent.selection)
423
+ }
424
+ style={{ minHeight: 120, borderWidth: 1, padding: 12 }}
425
+ />
426
+ );
427
+ }
428
+
429
+ export function SplitEditor() {
430
+ return (
431
+ <RichTextProvider>
432
+ <View>
433
+ <ToolbarRow />
434
+ <EditorField />
435
+ </View>
436
+ </RichTextProvider>
437
+ );
438
+ }
193
439
  ```
194
440
 
195
441
  ## Data Model
196
442
 
197
- Content is stored as an array of `StyledSegment` objects:
443
+ Content is stored as segments:
198
444
 
199
- ```typescript
200
- interface StyledSegment {
445
+ ```ts
446
+ type StyledSegment = {
201
447
  text: string;
202
448
  styles: {
203
449
  bold?: boolean;
@@ -209,23 +455,40 @@ interface StyledSegment {
209
455
  backgroundColor?: string;
210
456
  fontSize?: number;
211
457
  heading?: 'h1' | 'h2' | 'h3' | 'none';
458
+ listType?: 'bullet' | 'ordered' | 'none';
459
+ textAlign?: 'left' | 'center' | 'right';
460
+ link?: string;
212
461
  };
213
- }
462
+ };
214
463
  ```
215
464
 
216
- ## How It Works
465
+ This makes it easy to:
466
+
467
+ - store and restore content with `exportJSON()` and `importJSON()`
468
+ - inspect formatting state in your own UI
469
+ - serialize on demand to Markdown or HTML
470
+
471
+ Example:
472
+
473
+ ```tsx
474
+ const saved = actions.exportJSON();
475
+ actions.importJSON(saved);
476
+
477
+ const markdown = actions.getOutput('markdown');
478
+ const html = actions.getOutput('html');
479
+ ```
217
480
 
218
- The **Overlay Technique** layers two components:
481
+ ## Current Scope
219
482
 
220
- 1. **Bottom**: A `<Text>` component renders the styled/formatted text
221
- 2. **Top**: A transparent `<TextInput>` captures user input and selection
483
+ Stable built-in features documented above are the recommended way to use the library.
222
484
 
223
- Both layers use identical font metrics so text aligns perfectly. The user sees the formatted text from the bottom layer while typing into the invisible top layer.
485
+ - Rich text formatting, headings, lists, links, alignment, theming, and output serialization are supported.
486
+ - The built-in toolbar does not currently expose image insertion.
224
487
 
225
488
  ## Contributing
226
489
 
227
- See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
490
+ See [AGENTS.md](AGENTS.md) for contributor notes and repository workflow guidance.
228
491
 
229
492
  ## License
230
493
 
231
- MIT © react-native-richify contributors
494
+ MIT