{"_id":"@domenico-esposito/react-native-markdown-editor","_rev":"2-6945a03855db2b3aed2d453e464f153a","name":"@domenico-esposito/react-native-markdown-editor","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.1":{"name":"@domenico-esposito/react-native-markdown-editor","version":"0.1.1","keywords":["react-native","expo","react-native-markdown-editor","ReactNativeMarkdownEditor"],"author":{"url":"Deploynk","name":"Domenico Esposito","email":"mail@domenicoesposito.it"},"license":"MIT","_id":"@domenico-esposito/react-native-markdown-editor@0.1.1","maintainers":[{"name":"domenico-esposito","email":"mail@domenicoesposito.it"}],"homepage":"https://github.com/domenico-esposito/react-native-markdown-editor#readme","bugs":{"url":"https://github.com/domenico-esposito/react-native-markdown-editor/issues"},"dist":{"shasum":"d3063a420a6d7917b8623188ef3b020a84b87b7d","tarball":"https://registry.npmjs.org/@domenico-esposito/react-native-markdown-editor/-/react-native-markdown-editor-0.1.1.tgz","fileCount":76,"integrity":"sha512-JYaFBG/n3DhSj6DehWBHUf9Yi1VPBgbd70LgLL8Vk2gxbju/nwlcmZekvUF7t1HMNHTFnOhhs2ZYoIiiqYgCrg==","signatures":[{"sig":"MEQCIFaVaPGVW63c5ul0uHDZW8HqoWJCDgws+5hRcbGrm4kCAiBza1zJ5Q1H4elnQiJXEIEPubIsSTh7zVL4BTxznaTO4Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":420917},"main":"build/index.js","types":"build/index.d.ts","source":"src/index.ts","gitHead":"91569e35bab42f6fbda241a7ad407877dd37168c","scripts":{"lint":"expo-module lint","test":"expo-module test","build":"expo-module build","clean":"expo-module clean","prepare":"expo-module prepare","expo-module":"expo-module","prepublishOnly":"expo-module prepublishOnly"},"_npmUser":{"name":"domenico-esposito","email":"mail@domenicoesposito.it"},"repository":{"url":"git+https://github.com/domenico-esposito/react-native-markdown-editor.git","type":"git"},"_npmVersion":"10.9.2","description":"Markdown Editor for React Native","directories":{},"_nodeVersion":"22.15.1","_hasShrinkwrap":false,"devDependencies":{"expo":"^54.0.27","jest":"^30.2.0","ts-jest":"^29.4.6","@types/jest":"^30.0.0","@types/react":"~19.1.0","react-native":"0.81.5","expo-module-scripts":"^5.0.8"},"peerDependencies":{"expo":"*","react":"*","react-native":"*","react-native-safe-area-context":"*"},"_npmOperationalInternal":{"tmp":"tmp/react-native-markdown-editor_0.1.1_1771689929035_0.9792970724624246","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@domenico-esposito/react-native-markdown-editor","version":"0.2.0","description":"Markdown Editor for React Native","main":"build/index.js","source":"src/index.ts","types":"build/index.d.ts","scripts":{"build":"expo-module build","clean":"expo-module clean","lint":"expo-module lint","test":"expo-module test","prepare":"expo-module prepare","prepublishOnly":"expo-module prepublishOnly","expo-module":"expo-module"},"keywords":["react-native","expo","react-native-markdown-editor","ReactNativeMarkdownEditor"],"repository":{"type":"git","url":"git+https://github.com/domenico-esposito/react-native-markdown-editor.git"},"bugs":{"url":"https://github.com/domenico-esposito/react-native-markdown-editor/issues"},"author":{"name":"Domenico Esposito","email":"mail@domenicoesposito.it","url":"Deploynk"},"license":"MIT","homepage":"https://github.com/domenico-esposito/react-native-markdown-editor#readme","devDependencies":{"@changesets/cli":"^2.29.8","@types/jest":"^30.0.0","@types/react":"~19.1.0","expo":"^54.0.27","expo-module-scripts":"^5.0.8","jest":"^30.2.0","react-native":"0.81.5","ts-jest":"^29.4.6"},"peerDependencies":{"expo":"*","react":"*","react-native":"*","react-native-safe-area-context":"*"},"_id":"@domenico-esposito/react-native-markdown-editor@0.2.0","gitHead":"66f4fe812b813a7b75f944945dfc88a190c67f03","_nodeVersion":"22.15.1","_npmVersion":"10.9.2","dist":{"integrity":"sha512-SdsRafH/CEyyE7gZAdRymwUR8+vU4pl/FxpqGHefUsYzDPTLLvZPfqnkNj38ZaCcCj353KkCH6qYGaR6fzRkBw==","shasum":"f3435e24489e0efd39b041b63d5957a35cb3168a","tarball":"https://registry.npmjs.org/@domenico-esposito/react-native-markdown-editor/-/react-native-markdown-editor-0.2.0.tgz","fileCount":48,"unpackedSize":278996,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCP2JomqWHBLWi0A96IVbKCm1zcN/FcFqjzn17R9tZdgAIhAINgibqmLDOwoQjjvZx7h9K9IkKpMX4LesZqRwyFoAwY"}]},"_npmUser":{"name":"domenico-esposito","email":"mail@domenicoesposito.it"},"directories":{},"maintainers":[{"name":"domenico-esposito","email":"mail@domenicoesposito.it"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/react-native-markdown-editor_0.2.0_1771972375350_0.4407061428332868"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-21T16:05:28.971Z","modified":"2026-02-24T22:32:55.614Z","0.1.1":"2026-02-21T16:05:29.179Z","0.2.0":"2026-02-24T22:32:55.495Z"},"bugs":{"url":"https://github.com/domenico-esposito/react-native-markdown-editor/issues"},"author":{"name":"Domenico Esposito","email":"mail@domenicoesposito.it","url":"Deploynk"},"license":"MIT","homepage":"https://github.com/domenico-esposito/react-native-markdown-editor#readme","keywords":["react-native","expo","react-native-markdown-editor","ReactNativeMarkdownEditor"],"repository":{"type":"git","url":"git+https://github.com/domenico-esposito/react-native-markdown-editor.git"},"description":"Markdown Editor for React Native","maintainers":[{"name":"domenico-esposito","email":"mail@domenicoesposito.it"}],"readme":"# React Native Markdown Editor\n\nA fully native Markdown editor for React Native with real-time syntax highlighting.\n\n## Features\n\n- 📝 **Real-time Syntax Highlighting**: Text is parsed and styled as you type.\n- ⚡️ **Native Performance**: Uses native components (`TextInput` with _attributed strings_) on iOS and Android, and `contentEditable` with inline syntax highlighting on Web.\n- 🧩 **Customizable Components**: You can replace segment renderers (`bold`, `heading`, `code`, etc.) with your own components.\n- 🛠 **Built-in Toolbar**: Buttons for quick formatting (bold, italic, lists, etc.) and extensible.\n- 🖼 **Image Support**: Insert, edit, and remove images via toolbar and programmatic API.\n- 📄 **Read-Only Renderer**: `MarkdownRenderer` component to display markdown without editing.\n- 📱 **Cross-Platform**: Support for iOS, Android, and Web (Expo), with native syntax highlighting on all platforms.\n\n### Supported Markdown Features\n\n| Feature          | Syntax                          | Toolbar Action     |\n| ---------------- | ------------------------------- | ------------------ |\n| Bold             | `**text**`                      | `bold`             |\n| Italic           | `_text_`                        | `italic`           |\n| Strikethrough    | `~~text~~`                      | `strikethrough`    |\n| Inline Code      | `` `code` ``                    | `code`             |\n| Code Block       | ` ``` `                         | `codeBlock`        |\n| Heading 1        | `# `                            | `heading1`         |\n| Heading 2        | `## `                           | `heading2`         |\n| Heading 3        | `### `                          | `heading3`         |\n| Heading 4        | `#### `                         | `heading4`         |\n| Heading 5        | `##### `                        | `heading5`         |\n| Heading 6        | `###### `                       | `heading6`         |\n| Blockquote       | `> `                            | `quote`            |\n| Unordered List   | `- `                            | `unorderedList`    |\n| Ordered List     | `1. `                           | `orderedList`      |\n| Horizontal Rule  | `---`                           | `divider`          |\n| Image            | `![alt](url)`                   | `image`            |\n| Link             | `[text](url)`                   | —                  |\n\nAll features are enabled by default. You can selectively enable a subset via the `features` option (see [Limiting Enabled Features](#limiting-enabled-features)).\n\n## Installation\n\n```bash\nnpm install @domenico-esposito/react-native-markdown-editor\n```\n\n## Usage\n\n### Editor with Toolbar\n\n```tsx\nimport * as React from 'react';\nimport { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';\nimport { MarkdownTextInput, MarkdownToolbar, useMarkdownEditor } from 'react-native-markdown-editor';\n\nexport default function App() {\n\tconst [value, setValue] = React.useState('# Hello World\\n\\nType your markdown here...');\n\tconst editor = useMarkdownEditor({\n\t\tvalue,\n\t\tonChangeText: setValue,\n\t\t// Optional: limit enabled features (default: all)\n\t\t// features: ['bold', 'italic', 'heading1', 'heading2', 'quote'],\n\t});\n\n\treturn (\n\t\t<SafeAreaProvider>\n\t\t\t<SafeAreaView style={{ flex: 1 }}>\n\t\t\t\t<MarkdownToolbar editor={editor} />\n\t\t\t\t<MarkdownTextInput editor={editor} placeholder=\"Write markdown...\" />\n\t\t\t</SafeAreaView>\n\t\t</SafeAreaProvider>\n\t);\n}\n```\n\n### Read-Only Renderer\n\n```tsx\nimport { MarkdownRenderer } from 'react-native-markdown-editor';\n\nfunction Preview({ markdown }: { markdown: string }) {\n\treturn <MarkdownRenderer markdown={markdown} />;\n}\n```\n\n### Limiting Enabled Features\n\nBy passing `features` to the hook (or directly to `MarkdownRenderer`), only the corresponding features are highlighted/rendered; the rest of the markdown syntax appears as plain text.\n\n```tsx\n// Editor: only bold, italic, and heading1 enabled\nconst editor = useMarkdownEditor({\n  value,\n  onChangeText: setValue,\n  features: ['bold', 'italic', 'heading1'],\n});\n\n<MarkdownToolbar editor={editor} />        {/* shows only 3 buttons */}\n<MarkdownTextInput editor={editor} />      {/* highlights only bold/italic/h1 */}\n\n// Standalone renderer with the same restrictions\n<MarkdownRenderer\n  markdown={value}\n  features={['bold', 'italic', 'heading1']}\n/>\n```\n\n## API\n\n### `useMarkdownEditor`\n\nHook that manages shared state between `MarkdownTextInput` and `MarkdownToolbar`.\nReturns a `MarkdownEditorHandle` object to pass as the `editor` prop to both components.\n\n#### Options\n\n| Parameter           | Type                                                                           | Default      | Description                                                                                                  |\n| ------------------- | ------------------------------------------------------------------------------ | ------------ | ------------------------------------------------------------------------------------------------------------ |\n| `value`             | `string`                                                                       | **Required** | The current markdown text (controlled).                                                                      |\n| `onChangeText`      | `(nextValue: string) => void`                                                  | **Required** | Callback called when the text changes.                                                                       |\n| `onSelectionChange` | `(selection: MarkdownSelection) => void`                                       | -            | Optional callback for selection changes.                                                                     |\n| `onToolbarAction`   | `(action: MarkdownToolbarAction, result: MarkdownToolbarActionResult) => void` | -            | Callback after each toolbar action.                                                                          |\n| `features`          | `MarkdownToolbarAction[]`                                                      | All features | Enabled markdown features. Controls both the toolbar buttons and syntax highlighting in `MarkdownTextInput`. |\n\n#### Return Value (`MarkdownEditorHandle`)\n\n| Property              | Type                            | Description                                                                   |\n| --------------------- | ------------------------------- | ----------------------------------------------------------------------------- |\n| `features`            | `MarkdownToolbarAction[]`       | Features enabled in the hook.                                                 |\n| `value`               | `string`                        | Current text value.                                                           |\n| `selection`           | `MarkdownSelection`             | Current selection range.                                                      |\n| `activeInlineActions` | `MarkdownInlineToolbarAction[]` | Active inline actions (e.g. bold, italic).                                    |\n| `activeImageInfo`     | `MarkdownImageInfo \\| null`     | Info about the image under the cursor, `null` otherwise.                      |\n| `imageInfo`           | `MarkdownImageInfo \\| null`     | Image info set by the toolbar (modal state).                                  |\n| `openImageInfo`       | `() => void`                    | Sets `imageInfo` to the current active image.                                 |\n| `dismissImageInfo`    | `() => void`                    | Closes the image popup (`imageInfo = null`).                                  |\n| `deleteActiveImage`   | `() => void`                    | Removes the markdown image at the current position.                           |\n| `highlightedSegments` | `HighlightSegment[]`            | Highlighted segments for live preview.                                        |\n| `inputRef`            | `RefObject<TextInput>`          | Ref to the underlying `TextInput` (on web points to `<div contentEditable>`). |\n\n### `MarkdownTextInput`\n\nInput component with live highlighting. Requires `editor`.\n\n| Prop                | Type                   | Default      | Description                                                                                     |\n| ------------------- | ---------------------- | ------------ | ----------------------------------------------------------------------------------------------- |\n| `editor`            | `MarkdownEditorHandle` | **Required** | Handle returned by `useMarkdownEditor`.                                                         |\n| `segmentComponents` | `SegmentComponentMap`  | -            | Override renderers for highlighted segments. Each component **must** be a `Text`-based component (or return `Text`), since segments are rendered as children of `TextInput`, which only supports `Text` children on native platforms. |\n| `textInputStyle`    | `TextStyle`            | -            | Style for the inner `TextInput`.                                                                |\n| `style`             | `ViewStyle`            | -            | Style for the outer container.                                                                  |\n| `...rest`           | `TextInputProps`       | -            | All other `TextInput` props (except `value`, `onChangeText`, `onSelectionChange`, `multiline`). |\n\n### `MarkdownToolbar`\n\nMarkdown action toolbar. Can work with `editor` (recommended) or in manual mode.\n\n| Prop                      | Type                                      | Default      | Description                                                                                                   |\n| ------------------------- | ----------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------- |\n| `editor`                  | `MarkdownEditorHandle`                    | -            | Handle returned by `useMarkdownEditor`. When present, the toolbar uses `editor.features` as the feature list. |\n| `features`                | `MarkdownToolbarAction[]`                 | All features | List of features to show (used only in manual mode, without `editor`).                                        |\n| `activeInlineActions`     | `MarkdownInlineToolbarAction[]`           | -            | Active inline actions (manual mode only).                                                                     |\n| `onPressAction`           | `(action: MarkdownToolbarAction) => void` | -            | Action callback (manual mode only).                                                                           |\n| `style`                   | `ViewStyle`                               | -            | Style for the toolbar container.                                                                              |\n| `buttonStyle`             | `ViewStyle \\| (state) => ViewStyle`       | -            | Style applied to each button (static or function).                                                            |\n| `buttonTextStyle`         | `TextStyle \\| (state) => TextStyle`       | -            | Text style applied to each button (static or function).                                                       |\n| `activeButtonStyle`       | `ViewStyle`                               | -            | Additional style for active buttons.                                                                          |\n| `activeButtonTextStyle`   | `TextStyle`                               | -            | Additional text style for active buttons.                                                                     |\n| `inactiveButtonStyle`     | `ViewStyle`                               | -            | Additional style for inactive buttons.                                                                        |\n| `inactiveButtonTextStyle` | `TextStyle`                               | -            | Additional text style for inactive buttons.                                                                   |\n| `renderButton`            | `(params) => ReactNode`                   | -            | Custom renderer for each button.                                                                              |\n\n#### Custom Style Example\n\n```tsx\n<MarkdownToolbar\n\teditor={editor}\n\tfeatures={['bold', 'italic', 'heading1', 'heading2', 'quote', 'unorderedList']}\n\tbuttonStyle={(state) => ({\n\t\tbackgroundColor: state.active ? '#007AFF' : '#f0f0f0',\n\t\tborderRadius: 6,\n\t})}\n/>\n```\n\n### `MarkdownRenderer`\n\nRead-only component for rendering markdown into React Native components.\n\n| Prop         | Type                            | Default      | Description                                                                                                                       |\n| ------------ | ------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------- |\n| `markdown`   | `string`                        | **Required** | The markdown string to render.                                                                                                    |\n| `components` | `Partial<MarkdownComponentMap>` | -            | Component overrides for specific tags.                                                                                            |\n| `style`      | `ViewStyle`                     | -            | Style for the root container.                                                                                                     |\n| `features`   | `MarkdownToolbarAction[]`       | -            | Enabled markdown features. When provided, only the corresponding features are rendered; disabled syntax is treated as plain text. |\n\n#### Component Overrides\n\n```tsx\nimport { MarkdownRenderer } from 'react-native-markdown-editor';\nimport type { MarkdownComponentProps } from 'react-native-markdown-editor';\n\nconst CustomBold = ({ children }: MarkdownComponentProps) => <Text style={{ fontWeight: '900', color: 'red' }}>{children}</Text>;\n\n<MarkdownRenderer markdown=\"Some **bold** and *italic* text\" components={{ bold: CustomBold }} />;\n```\n\n### Utilities\n\n| Export                                        | Description                                                                       |\n| --------------------------------------------- | --------------------------------------------------------------------------------- |\n| `parseMarkdown(markdown, features?)`          | Parses markdown into an array of block nodes (`MarkdownBlockNode[]`).             |\n| `parseMarkdownInline(content, features?)`     | Parses inline content into inline nodes (`MarkdownInlineNode[]`).                 |\n| `highlightMarkdown(markdown, features?)`      | Converts markdown into semantic segments for live preview (`HighlightSegment[]`). |\n| `applyMarkdownToolbarAction(params)`          | Applies a toolbar action to the text and returns the result.                      |\n| `DEFAULT_MARKDOWN_FEATURES`                   | Default list of enabled features.                                                 |\n| `DEFAULT_SEGMENT_COMPONENTS`                  | Complete map of default segment components.                                       |\n| `getDefaultSegmentStyle(type, meta?)`         | Returns the default `TextStyle` for a segment type.                               |\n| `isMarkdownFeatureEnabled(features, feature)` | Checks if a markdown feature is enabled based on the features array.              |\n| `isHeadingLevelEnabled(features, level)`      | Checks if a specific heading level (1–6) is enabled.                              |\n\n## Architecture\n\n### Source Structure\n\n```\nsrc/\n├── index.ts                    # Public re-exports\n├── markdownCore.types.ts     # Shared types (AST nodes, component props, editor handle)\n│\n├── useMarkdownEditor.ts        # Hook: shared state between input and toolbar\n├── MarkdownTextInput.tsx        # Editor component with live preview\n├── MarkdownToolbar.tsx          # Action toolbar component\n│\n├── MarkdownRenderer.tsx         # Read-only component for markdown rendering\n├── markdownRendererDefaults.tsx  # Default components and styles for the renderer\n│\n├── markdownParser.ts            # Parser: markdown → AST (block + inline nodes)\n├── markdownHighlight.ts         # Highlighter: markdown → semantic segments (for TextInput)\n├── markdownHighlight.types.ts   # Types for the highlighting module\n│\n├── markdownToolbarActions.ts    # Transformation logic for toolbar actions\n├── markdownSyntaxUtils.ts       # Shared utilities: escape, token scanning, image parsing\n├── markdownSegment.types.ts     # Segment types for TextInput renderers\n└── markdownSegmentDefaults.tsx  # Default segment components for TextInput\n```\n\n### How It Works\n\nThis module uses an advanced technique to ensure real-time highlighting while keeping the editor editable and performant.\n\n#### \"Styled Children\" Rendering\n\nUnlike approaches based on WebView or complex native libraries, `react-native-markdown-editor` uses the standard React Native system:\n\n1. **Highlighting** (`markdownHighlight.ts`): The markdown text is parsed and split into segments with associated styles. Unlike the AST parser, this module produces flat segments optimized for rendering in the `TextInput`.\n2. **Attributed Text**: Instead of passing a flat string to the `TextInput`, segments are passed as `<Text>` children with individual styles.\n     ```jsx\n     <TextInput>\n     \t<Text style={styles.heading}># Title</Text>\n     \t<Text style={styles.body}>Normal text</Text>\n     \t<Text style={styles.bold}>**Bold**</Text>\n     </TextInput>\n     ```\n3. **Native**: React Native converts these children into native _Attributed Strings_ (on iOS and Android). The operating system handles mixed text rendering, selection, cursor, and input.\n\n#### Web: ContentEditable\n\nOn Web, `<TextInput>` is rendered as a `<textarea>` by the DOM, which does not support styled children. To maintain syntax highlighting in the browser as well, `MarkdownTextInput` uses a different approach:\n\n1. **ContentEditable**: A `<div contentEditable>` replaces the `TextInput`. Highlighted segments are converted into `<span>` elements with inline CSS (same colors, fonts, and sizes as native) and injected via `innerHTML`.\n2. **Cursor Preservation**: On each DOM update, the cursor position is saved as a text offset (via `TreeWalker`) and restored after the rebuild, ensuring a smooth typing experience.\n3. **Input and Paste**: Enter and Paste keys are intercepted to insert plain text (`\\n` and clipboard plain text), preventing the browser from inserting unwanted HTML elements.\n4. **Programmatic Selection**: Toolbar actions update the selection via `editor.selection`, which is applied to the `contentEditable` via `useEffect`.\n\n#### Parser vs Highlighter\n\nThe codebase contains two distinct analysis modules:\n\n| Module                 | Output                               | Usage                             |\n| ---------------------- | ------------------------------------ | --------------------------------- |\n| `markdownParser.ts`    | Tree AST (`MarkdownBlockNode[]`)     | `MarkdownRenderer` (read-only)    |\n| `markdownHighlight.ts` | Flat segments (`HighlightSegment[]`) | `MarkdownTextInput` (live editor) |\n\nBoth share low-level utilities in `markdownSyntaxUtils.ts` (escape, token scanning, image URL parsing).\n\n#### Input Handling\n\n- Uses synchronization techniques (`requestAnimationFrame`) to update the native buffer without conflicts.\n- On Android, applies specific workarounds to force cursor position after programmatic toolbar actions.\n","readmeFilename":"README.md"}