Skip to content

Chat: render markdown in messages, matching the website #3468

Description

@feruzm

Mobile chat renders message bodies as plain text. ThreadMessageItem.tsx puts the body in a bare <Text> via renderTextWithBoldMentions, which does linkify, emoji and bold @mentions and nothing else. Web renders the same messages as markdown, so formatting a message on desktop and reading it on mobile shows raw ** and backticks.

What web does

features/chat/hooks/use-message-rendering.tsx runs simpleMarkdownToHTML from @ecency/render-helper, sanitizes with DOMPurify, and renders Ecency post links through a dedicated card component.

Scope

Use the same simpleMarkdownToHTML so both clients share one definition of what chat markdown means, and render the result with react-native-render-html. Both are already dependencies (@ecency/render-helper ^2.5.23, react-native-render-html ^6.3.4), so no new packages.

Deliberately simpleMarkdownToHTML and not the full post renderer in postParser.tsx: chat is a lighter subset and should not diverge from web.

Must not regress

The existing chat rendering does several things the markdown path has to keep:

  • linkify, including the Ecency profile link shortening in setLinkText
  • emoji substitution via emojifyMessage
  • bold @mentions with the tap-to-open-profile behaviour
  • image extraction in parseMessageContent (images are pulled out of the body and rendered separately)
  • the tap targets and long-press actions on the message row

Worth checking during implementation

  • Angle brackets get eaten. Once a body goes through an HTML renderer, literal <user> in a message is parsed as an unknown tag and disappears. The moderation bot posts help text containing !ban <user> [30d], so this is a live case, not hypothetical.
  • Sanitization. react-native-render-html will not execute scripts, but the allowed tag and attribute set should still be constrained rather than left at the default.
  • List performance. Chat threads are long and virtualized; per-message HTML parsing is heavier than a <Text>. Worth measuring on a long channel before and after, and memoizing per message id.

Related: the moderation commands in ecency/vision-web#1380 currently reply in plain text specifically because this renderer does not exist yet.

Activity

  1. feruzm commented on Aug 11, 2026

    @feruzm
    MemberAuthor

    Scope narrowed after investigating what simpleMarkdownToHTML actually does.

    It is full Remarkable with html: true, not a light subset. Against real chat content it turns - first point into a list, # introduceyourself into an H1 and > quoting you into a blockquote. Matching that on mobile means rendering arbitrary HTML through react-native-render-html inside a virtualized list while reattaching mentions, links, emoji and image extraction — a much larger and riskier change than this issue implied, in a screen people use constantly.

    Agreed to ship the inline subset first: bold, italic, strikethrough and inline code as nested <Text>, with the existing link/mention/emoji/image path untouched. Block-level parity remains open and can be decided after this is seen on a device.

    Also found while scoping: <user> in the moderation bot help text was being silently eaten on web, because Remarkable parsed it as an HTML tag and the sanitizer dropped it. Fixed at the source in ecency/mm-spam-monitor#2 rather than in each client.

    PR: #3480

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions