Fresh logo

Rendering raw HTML

Text content in Fresh is always escaped, whether serverside rendered or rendered in islands. While this generally desired, it can create issues in certain situations.

Warning

The TL;DR is to use Preact’s dangerouslySetInnerHTML. As the name implies, it should not be used lightly.

Setting arbitrary HTML can be dangerous. Make sure you trust the source. Rendering user-supplied HTML to the DOM makes your site vulnerable to cross- site scripting. The markup must first be sanitizied, or better yet, something you trust.

Example: Rendering JSON-LD

Suppose we need to add some microdata markup to a page. The following will result in escaped characters, and will not work:

const json = `
{
  "@context": "http://schema.org",
  "@type": "PostalAddress",
  "streetAddress": "8888 University Drive",
  "addressLocality": "Burnaby",
  "addressRegion": "British Columbia"
}
`;

export default function JsonLd() {
  return <script type="application/ld+json">{json}</script>;
}

Instead, we can use dangerouslySetInnerHTML:

export default function JsonLd() {
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: json }}
    />
  );
}

Another example: Code highlighting

Syntax highlighters parse strings into HTML tags, allowing them to be individually styled with CSS. We can build a simple Preact syntax highlighter like so:

import Prism from "https://esm.sh/prismjs@1.29.0";

interface Props {
  code: string;
  lang: string;
}

export default function Code({ code, lang }: Props) {
  const parsed = Prism.highlight(code, Prism.languages[lang], lang);

  return (
    <pre data-lang={lang} className={`language-${lang}`}>
      <code
        dangerouslySetInnerHTML={{
          __html: parsed,
        }}
      />
    </pre>
  );
}

Of course, we will also have to add some CSS to make this look nice.