developit / htm

Hyperscript Tagged Markup: JSX alternative using standard tagged templates, with compiler support.
Apache License 2.0
8.64k stars 169 forks source link

Cannot insert literal HTML because & is always escaped #226

Closed paulinemss closed 2 years ago

paulinemss commented 2 years ago

Hi,

An issue came up with HTML content that we receive from the server. We want to insert it into htm without escaping it. According to #137, we should be able to do as follows:

import htm from "htm";
import vhtml from "vhtml";

const html = htm.bind(vhtml);

const frag = "<p>Hello, World!</p>"
console.log(html`<div>${html([frag])}</div>`);   // output: "<div><p>Hello, World!</p></div>"

However, this doesn't work with HTML special characters - see example below (and in CodeSandbox). It looks like html([]) is still escaping & to &amp;, which breaks other special chars.

import htm from "htm";
import vhtml from "vhtml";

const html = htm.bind(vhtml);

const frag = "<p>Hello &lt; World</p>"           // "Hello < World"
console.log(html`<div>${html([frag])}</div>`);   // output: "<div><p>Hello &amp;lt; World</p></div>"

Could this be a bug? Or is above not the intended use case for inserting literal HTML? Is this perhaps not supported at all?

As an example use case, we might be getting highlighted search results (I &lt;3 <em>htm</em>) from the trusted server, which we want to insert into a htm template without escaping.

See example in CodeSandbox

developit commented 2 years ago

Hi @paulinemss!

This is a vhtml thing fwiw, not HTM. That said, you're close to the right solution! html() doesn't support bypassing sanitization of nested calls (I don't recall the reason). It does support the dangerouslySetInnerHTML prop similar to React though, which does what you're looking for:

const frag = "<p>We &lt;3 <em>htm</em></p>";

const rendered = html`<div dangerouslySetInnerHTML=${{ __html: frag }}></div>`;
paulinemss commented 2 years ago

Thanks a lot @developit!