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)