Skip to content

Single tilde ~ incorrectly rendered as strikethrough (no way to configure singleTilde: false) #545

Description

@cheyenne404

Problem

Streamdown 2.5.0 internally uses remark-gfm with a default empty config {}. This means remark-gfm's strikethrough extension runs with singleTilde: true by default, causing a single ~ character to be parsed as strikethrough instead of being rendered as a plain character.

In AI chat scenarios, model output frequently contains ~ as a range separator or path component (e.g. 25~30, ~500ms, C:\Users\~\Documents). These get incorrectly rendered as striethrough.

Reproduction

import { Streamdown } from streamdown;

<Streamdown mode=static>25~30, 31~35</Streamdown>

Expected: 25~30, 31~35 (plain text)

Actual: 25<del>30, 31</del>35 (strikethrough)

Cause

In node_modules/streamdown/dist/chunk-BO2N2NFS.js, the default remark plugins are:

var Ks = { gfm: [remarkGfm, {}], codeMeta: un };

remark-gfm's strikethrough extension defaults to singleTilde: true, which allows ~text~ to trigger strikethrough. Additionally, @streamdown/cjk further relaxes CJK boundary matching for strikethrough, making it even easier to accidentally trigger in Chinese text.

Expected Behavior

Streamdown should either:

  1. Provide a way to configure remark-gfm options (e.g. singleTilde: false), or
  2. Default singleTilde to false, consistent with GitHub's own behavior: GitHub only renders ~~text~~ as strikethrough; a single ~ is treated as a plain character.

Current Workaround

Currently, consumers cannot override this because Streamdown's remarkPlugins prop appends to the internal plugin list rather than replacing it. The internal remark-gfm instance with singleTilde: true always runs first.

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