DEV Community

Yukiya Nakagawa
Yukiya Nakagawa

Posted on

Render HTML-marked text Received from Server

Sometimes we need to render HTML-marked strings from server, with React. However, React can usually only render UI written in JSX or createElement.

So, let's use unusual methods. I have two ideas๐Ÿ’ก.

  1. Use dangerouslySetInnerHTML ๐Ÿšจ
  2. Use RegExp and Split

Use dangerouslySetInnerHTML ๐Ÿšจ

This is easy but VERY DANGEROUS method (as the documentation says).

const marked = "This sentense has <b>a bold text</b> and <b>another one</b>."; return <div dangerouslySetInnerHTML={{ __html: marked }} />; 

Okey, run it.

result of dangerous method

Great!๐Ÿ˜ˆ

But, but, this method carries a significant risk๐Ÿงจ against XSS attacks.

Use RegExp and Split

As a safe way, split the string and re-markup it.

Here is the sample.

const BoldableText = ({ text }) => { // shortest match for marked text with <b> tag const re1 = /<b>(.+?)<\/b>/g; // for removing tags included in the string matched by re1 const re2 = /<b>(.+)<\/b>/; // strings to re-markup with JSX const matched = text .match(re1) // ["<b>a bold text<b>", "<b>another one</b>"] .map(s => s.match(re2)[1]); // ["a bold text", "another one"] // split strings to re-markup const texts = text.split(re1); // ["This sentense has ", "a bold text", " and", ...] const markedJsx = texts.map((s, index) => { if (index === 0 || index === texts.length - 1) { // first and last item is not the target to re-markup // because "<b>foo bar</b> buz..." generates ["", "foo bar", " buz"...] return s; } if (matched.includes(s)) { return <b key={s}>{s}</b>; // re-markup!! } return s; }); return markedJsx; }; 

And use it as below.

const marked = "This sentense has <b>a bold text</b> and <b>another one</b>."; return <BoldableText text={marked} />; 

Okey, run it.

result of split method

I did it!!!๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰

Memo

  • Do I really have to use two regular expressions?
  • How about nested tags?
  • rehype-react is more easy way? (thx @_sunnyone)

Top comments (0)