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)