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π‘.
- Use
dangerouslySetInnerHTML
π¨ - 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.
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.
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)