Skip to content
Mobile
Skill

/rich-text

Software Mansion's best practices for rich text in React Native using react-native-enriched and react-native-enriched-markdown. Use when building rich text editors, formatted text inputs, Markdown renderers, or any feature requiring inline styling, mentions, links, structured

From plugin
software-mansion-labs-skills
26523 skills
Install
$ npx -y skills add software-mansion-labs/skills --skill rich-text --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/rich-text

Context preview

The summary Claude sees to decide when to auto-load this skill.

Software Mansion's best practices for rich text in React Native using react-native-enriched and react-native-enriched-markdown. Use when building rich text editors, formatted text inputs, Markdown renderers, or any feature requiring inline styling, mentions, links, structured

SKILL.md

rich-text.SKILL.md
name: rich-text
description: "Software Mansion's best practices for rich text in React Native using react-native-enriched and react-native-enriched-markdown. Use when building rich text editors, formatted text inputs, Markdown renderers, or any feature requiring inline styling, mentions, links, structured text editing, or Markdown display. Trigger on: 'rich text editor', 'rich text input', 'text editor', 'react-native-enriched', 'react-native-enriched-markdown', 'EnrichedTextInput', 'EnrichedMarkdownText', 'formatted text input', 'WYSIWYG', 'mentions input', 'text formatting toolbar', 'markdown renderer', 'markdown display', 'render markdown', 'display markdown natively', 'LaTeX math', 'GFM tables', or any request to build rich text editing or Markdown rendering in React Native."

Rich Text in React Native

Software Mansion's production patterns for rich text editing and Markdown rendering in React Native.

There are two libraries that cover rich text use cases:

| Library | Component | Purpose | |---------|-----------|---------| | `react-native-enriched` | `EnrichedTextInput` | Rich text **editing** (input) | | `react-native-enriched-markdown` | `EnrichedMarkdownText` | Markdown **rendering** (display) |

Both libraries require the React Native New Architecture (Fabric) and support iOS and Android.

Choosing the right library

  • **User needs to type/edit rich text** (bold, italic, mentions, links, inline images): use `react-native-enriched`
  • **App needs to display Markdown content** (chat messages, documentation, AI responses): use `react-native-enriched-markdown`
  • **Both editing and display**: use both libraries together

react-native-enriched (Editor)

`EnrichedTextInput` is a native, uncontrolled rich text input. It directly interacts with platform-specific components for performance, meaning it does not use React state for its value.

npm install react-native-enriched

Basic usage

import { EnrichedTextInput } from 'react-native-enriched';
import type {
  EnrichedTextInputInstance,
  OnChangeStateEvent,
} from 'react-native-enriched';
import { useState, useRef } from 'react';
import { View, Button, StyleSheet } from 'react-native';

export default function RichEditor() {
  const ref = useRef<EnrichedTextInputInstance>(null);
  const [stylesState, setStylesState] = useState<OnChangeStateEvent | null>();

  return (
    <View style={styles.container}>
      <EnrichedTextInput
        ref={ref}
        onChangeState={(e) => setStylesState(e.nativeEvent)}
        style={styles.input}
      />
      <Button
        title={stylesState?.bold.isActive ? 'Unbold' : 'Bold'}
        color={stylesState?.bold.isActive ? 'green' : 'gray'}
        onPress={() => ref.current?.toggleBold()}
      />
    </View>
  );
}

Key concepts

**Toggling styles via ref**: All formatting is applied imperatively through the ref. Call `ref.current?.toggleBold()`, `ref.current?.toggleItalic()`, etc.

**Style detection via onChangeState**: The `onChangeState` callback fires whenever the style state changes (e.g., cursor moves into bold text). Each style reports three properties:

  • `isActive`: The style is applied at the current selection (highlight the toolbar button)
  • `isBlocking`: The style is blocked by another active style (disable the toolbar button)
  • `isConflicting`: The style conflicts with another active style (toggling it removes the conflicting style)

**Inline vs paragraph styles**:

  • Inline styles (bold, italic, underline, strikethrough, inline code) apply to the exact character range selected. With no selection, they apply to the next characters typed.
  • Paragraph styles (headings, codeblock, blockquote, lists) apply to entire paragraphs (text between newlines). If the selection spans multiple paragraphs, all are affected.

**HTML output**: Get HTML via `ref.current?.getHTML()` (on-demand, returns a Promise) or the `onChangeHtml` callback (continuous, has performance cost for large documents). Prefer `getHTML()` when you only need HTML at save time.

**Setting content**: Use `defaultValue` prop for initial HTML content, or `ref.current?.setValue(html)` to update imperatively.

Supported styles

The `OnChangeStateEvent` key column shows the exact property name on the event object returned by `onChangeState`. Use these keys when reading style state (e.g. `stylesState.strikeThrough.isActive`). Note that casing varies (e.g. `strikeThrough` with capital T, `inlineCode` with capital C).

| Style | Toggle method | `OnChangeStateEvent` key | Type | |-------|--------------|--------------------------|------| | Bold | `toggleBold()` | `bold` | Inline | | Italic | `toggleItalic()` | `italic` | Inline | | Underline | `toggleUnderline()` | `underline` | Inline | | Strikethrough | `toggleStrikeThrough()` | `strikeThrough` | Inline | | Inline code | `toggleInlineCode()` | `inlineCode` | Inline | | H1 | `toggleH1()` | `h1` | Paragraph | | H2 | `toggleH2()` | `h2` | Paragraph | | H3 | `toggleH3()` | `h3` | Paragraph | | H4 | `toggleH4()` | `h4` | Paragraph | | H5 | `toggleH5()` | `h5` | Paragraph | | H6 | `toggleH6()` | `h6` | Paragraph | | Code block | `toggleCodeBlock()` | `codeBlock` | Paragraph | | Block quote | `toggleBlockQuote()` | `blockQuote` | Paragraph | | Ordered list | `toggleOrderedList()` | `orderedList` | Paragraph | | Unordered list | `toggleUnorderedList()` | `unorderedList` | Paragraph | | Checkbox list | `toggleCheckboxList(checked)` | `checkboxList` | Paragraph |

Links

Links are detected automatically (customizable via `linkRegex` prop) or applied manually:

// Set link on selected text
ref.current?.setLink(selection.start, selection.end, selectedText, url);

// Remove link
ref.current?.removeLink(start, end);

Use `onChangeSelection` to get selection position and `onLinkDetected` to detect when the cursor is near a link.

Mentions

Mentions support custom indicators (default: `@`). Set custom indicators via the `mentionIndi

Read more
Ships withsoftware-mansion-labs-skills

Software Mansion's set of skills for AI-assisted React Native development.

Get the whole plugin