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.


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": "",
  "@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 (
      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 "";

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}`}>
          __html: parsed,

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