Add an optional safeData prop to MsgMessage. When it is set, call message.format(...) with the resolved data object and insert the formatted string into the enclosing <span> with dangerouslySetInnerHTML, so HTML tags in the message render as markup.
data and safeData may be used together. When both are passed, HTML-escape string values from data, merge the result with safeData (safeData wins on key conflicts), and pass that object to format.
Today MsgMessage always renders formatted text as children of the span, so tags in the message are escaped.
User Stories
-
As a msg-react user, I want to pass safeData so a message that contains HTML (for example <strong>) renders as markup inside the existing <span class="msg">, with lang and dir still taken from the message attributes.
- Call
message.format(resolvedData, options) (same options as data).
- Set
dangerouslySetInnerHTML={{ __html: formatted }} on the span instead of rendering the string as children.
- Presence of
safeData (including {}) selects HTML rendering.
-
As a msg-react user, I want to pass data and safeData together so untrusted interpolations stay text-safe while trusted HTML interpolations (and markup in the message) still render.
- HTML-escape string values in
data (&, <, >, ", '). Leave non-string values (numbers, dates, etc.) unchanged so plural/select formatting still works.
- Merge
{ ...escapedData, ...safeData } so safeData wins on duplicate keys.
- Pass the merged object to
message.format(merged, options).
- Render with
dangerouslySetInnerHTML (because safeData is set).
- TypeScript should allow both props. Do not throw if both are passed at runtime.
- Omit both → current behavior (
message.toString(), text children).
data alone → current behavior (message.format(data, options), text children; React escapes the text).
safeData alone → message.format(safeData, options), HTML via dangerouslySetInnerHTML.
-
As a msg-react user, I want docs to state that safeData is for trusted HTML only (XSS-sensitive): the message string and safeData values must already be safe. This component does not sanitize safeData or the message source. String values in data are HTML-escaped when safeData is also passed.
-
As a msg-react maintainer, I want tests covering HTML rendering, merge + escape behavior (including safeData winning on conflicts and numbers left unescaped), options forwarded with safeData, and an empty span when the message is missing, without changing existing data-only tests.
Out of scope
- Sanitizing or escaping HTML inside
safeData / the message source.
- Changing how
data alone renders (still text children).
- Rich React children /
formatToParts markup.
Implementation notes
- Both
data and safeData are optional Record<string, any>.
- Presence of
safeData (including {}) selects HTML rendering.
- Keep the same
<span className="msg" lang dir> wrapper.
- Document
safeData in README next to data, including merge/escape behavior.
Meet Definition of done in project/rules.md (tests, type-check, build, coverage > 90%).
Process
Follow the Standard track in project/process.md (new public API), using project/rules.md and project/info.md. Intermediate commits use phase prefixes and Refs #<n>.
Add an optional
safeDataprop toMsgMessage. When it is set, callmessage.format(...)with the resolved data object and insert the formatted string into the enclosing<span>withdangerouslySetInnerHTML, so HTML tags in the message render as markup.dataandsafeDatamay be used together. When both are passed, HTML-escape string values fromdata, merge the result withsafeData(safeDatawins on key conflicts), and pass that object toformat.Today
MsgMessagealways renders formatted text as children of the span, so tags in the message are escaped.User Stories
As a msg-react user, I want to pass
safeDataso a message that contains HTML (for example<strong>) renders as markup inside the existing<span class="msg">, withlanganddirstill taken from the message attributes.message.format(resolvedData, options)(sameoptionsasdata).dangerouslySetInnerHTML={{ __html: formatted }}on the span instead of rendering the string as children.safeData(including{}) selects HTML rendering.As a msg-react user, I want to pass
dataandsafeDatatogether so untrusted interpolations stay text-safe while trusted HTML interpolations (and markup in the message) still render.data(&,<,>,",'). Leave non-string values (numbers, dates, etc.) unchanged so plural/select formatting still works.{ ...escapedData, ...safeData }sosafeDatawins on duplicate keys.message.format(merged, options).dangerouslySetInnerHTML(becausesafeDatais set).message.toString(), text children).dataalone → current behavior (message.format(data, options), text children; React escapes the text).safeDataalone →message.format(safeData, options), HTML viadangerouslySetInnerHTML.As a msg-react user, I want docs to state that
safeDatais for trusted HTML only (XSS-sensitive): the message string andsafeDatavalues must already be safe. This component does not sanitizesafeDataor the message source. String values indataare HTML-escaped whensafeDatais also passed.As a msg-react maintainer, I want tests covering HTML rendering, merge + escape behavior (including
safeDatawinning on conflicts and numbers left unescaped),optionsforwarded withsafeData, and an empty span when the message is missing, without changing existingdata-only tests.Out of scope
safeData/ the message source.dataalone renders (still text children).formatToPartsmarkup.Implementation notes
dataandsafeDataare optionalRecord<string, any>.safeData(including{}) selects HTML rendering.<span className="msg" lang dir>wrapper.safeDatain README next todata, including merge/escape behavior.Meet Definition of done in
project/rules.md(tests, type-check, build, coverage > 90%).Process
Follow the Standard track in
project/process.md(new public API), usingproject/rules.mdandproject/info.md. Intermediate commits use phase prefixes andRefs #<n>.