react-native-enriched-markdown

repository·main·Indexed 21 days ago

https://github.com/software-mansion/react-native-enriched-markdown

A high-performance library for rendering and editing Markdown in React Native applications. It provides fully native text rendering and a rich text input component, supporting iOS, Android, macOS, and Web. The library also includes native implementations for Jetpack Compose (Android) and SwiftUI (iOS) for direct platform integration.

Tokens
31.2K
Snippets
111
Records
156
Agent score
73%

What's inside react-native-enriched-markdown

  1. Prerequisites for react-native-enriched-markdown

    main

    Before installing, ensure your environment meets these requirements:

    Native Platforms (iOS, Android, macOS)

    • React Native New Architecture (Fabric): This library requires the New Architecture to be enabled.
    • macOS: Requires react-native-macos version 0.81+.

    Web

    • react-native-web: Required for web rendering.
    • EnrichedMarkdownTextInput: Note that the text input component is native-only and is not supported on the web. Only EnrichedMarkdownText works on web.
  2. Web Support Overview for EnrichedMarkdownText

    main

    On the web, EnrichedMarkdownText uses react-native-web for primitives and md4c (compiled to WebAssembly) for parsing. The WASM binary is bundled in the npm package, so no additional build steps are required.

    The web renderer uses semantic HTML elements (like <p>, <h1>, <table>, etc.) to ensure high accessibility and SEO-friendly structures.

  3. Overview of EnrichedMarkdownText and EnrichedMarkdownTextInput

    main

    The library provides two primary components:

    EnrichedMarkdownText

    Used for high-performance, native Markdown rendering (no WebView).

    • Features: CommonMark & GFM compliant, LaTeX math support, Markdown streaming, RTL support, and fully customizable styles.
    • Web: Supported via react-native-web and md4c (Wasm).

    EnrichedMarkdownTextInput

    Used for rich text editing with Markdown output.

    • Features: Imperative API for toggling styles, native context menu for formatting, auto-link detection, and mention support.
    • Note: This component is not supported on Web.
  4. Mention behavior and best practices

    main

    Behavior Notes

    • Atomic Deletion: When a user backspaces into a mention, the entire mention is deleted as a single unit (similar to Slack's behavior).
    • Toolbar Usage: If you are triggering a mention from a toolbar button, call focus() on the input before calling startMention() to ensure the input is active.

    Best Practices

    • Debouncing: onChangeMention fires on every single keystroke. If your suggestion logic involves network requests, ensure you debounce the calls to avoid excessive API traffic.
    • URL Schemes: Use unique URL schemes (e.g., user:// or channel://) for your mention links. This makes it easy to distinguish them from standard https:// links using regex in linkVariants.
  5. How MarkdownTheme layering and styling works

    main

    Themes in Enriched Markdown use a layering system. When you apply a .markdownTheme to a view, it appends to the existing theme hierarchy (starting from MarkdownTheme.default). Later layers override only the specific properties they define, allowing for scoped styling.

    You can define a theme using a result-builder DSL and apply it globally to a subtree or locally to a specific EnrichedMarkdownText view.

    // 1. Define a theme
    let AppMarkdownTheme = MarkdownTheme {
      Paragraph()
        .font(.body)
        .foregroundStyle(Color.primary)
    
      Heading(1)
        .font(.largeTitle)
        .bold()
    }
    
    // 2. Apply to a subtree
    HomeScreen()
      .markdownTheme(AppMarkdownTheme)
    
    // 3. Or apply inline to a specific view
    EnrichedMarkdownText(content)
      .markdownTheme {
        Link().foregroundStyle(.red)
      }
  6. Understand Line Break Semantics

    main

    The renderer follows standard CommonMark semantics for newlines:

    • Blank line: Starts a new paragraph.
    • Single newline (soft break): Renders as a space. Consecutive lines flow together into one wrapped paragraph.
    • Hard break: Ends a line with two trailing spaces or a backslash (\) to force a line break within a paragraph.
    This line and
    this line render as one continuous sentence.
    
    This line with two spaces  
    and this line with a backslash\
    force a line break within the paragraph.
  7. How RTL (Right-to-Left) support works

    main

    The library resolves writing direction per paragraph on both Android and iOS. Each paragraph automatically determines its base direction from its first strong directional character (e.g., Arabic, Hebrew, or Persian). This allows a single Markdown document to mix LTR (English) and RTL (Arabic) content seamlessly without global configuration.

    Key Behaviors:

    • Automatic Alignment: RTL content right-aligns automatically, even within an LTR app.
    • Fallback: If a paragraph contains only neutral characters (digits, punctuation, etc.), it falls back to the view's resolved layout direction (Yoga direction).
    • Code Blocks: Always render as LTR, regardless of the document's direction.
    • Lists & Blockquotes: Bullets, numbers, checkboxes, and blockquote borders align with the direction of the paragraph they belong to.
  8. Understand Copy-as-HTML RTL behavior

    main

    When copying Markdown content to the clipboard, the generated HTML carries a single dir attribute on the <html> (or <table>) tag. This attribute is determined by the first paragraph of the document:

    • first-strong document starting with Arabic: <html dir="rtl">
    • first-strong document starting with English: <html dir="ltr">
    • 'auto' mode: <html dir="auto">
    • 'ltr' or 'rtl' mode: The forced direction is used.

    Caveat: External receivers (like Gmail or Word) use their own Bidi algorithms. Because the current HTML output does not include per-paragraph <p dir="..."> attributes, mixed-direction documents may not visually match the in-app rendering when pasted into other applications.

  9. Configure Table Streaming behavior with streamingConfig

    main

    When using flavor="github" with streaming content, tables (GFM) require special handling because they are block-level elements that cannot be rendered until the parser receives enough structure (a header row and a separator line).

    You can control this behavior using the streamingConfig prop on EnrichedMarkdownText.

    <EnrichedMarkdownText
      markdown={streamingMarkdown}
      flavor="github"
      streamingAnimation
      streamingConfig={{ tableMode: 'hidden' }}
    />
  10. Understand Style Inheritance in Enriched Markdown

    main

    The library uses a hierarchical styling model based on two types of inheritance:

    1. Block Style Inheritance: All block elements (paragraphs, headings, lists, blockquotes, code blocks) share a common base typography set. This base includes fontSize, fontFamily, fontWeight, color, marginTop, marginBottom, and lineHeight. Specific block types extend this base with unique properties (e.g., textAlign for headings or bulletColor for lists).

    2. Inline Style Inheritance: Inline elements (strong, emphasis, links, inline code) automatically inherit the typography properties of their parent block. They use the block's fontSize, fontFamily, fontWeight, and color as a foundation and then apply their own specific styles on top.

    Example Hierarchy:

    • h2 (fontSize: 24, color: blue)
      • strong (inherits fontSize 24, color blue + adds bold weight)
      • link (inherits fontSize 24 + adds link color and underline)