escapeInject

Environment: Node.js

The escapeInject string template tag sanitizes HTML (to prevent so-called XSS injections security breaches).

It is usually used in the render() hook defined in renderer/_default.page.server.js.

// renderer/_default.page.server.js
// Environment: Node.js

import { escapeInject, dangerouslySkipEscape } from 'vite-plugin-ssr'

export { render }

async function render() {
  const title = 'Hello<script src="https://devil.org/evil-code"></script>'
  const pageHtml = "<div>I'm already <b>sanitized</b>, e.g. by Vue/React</div>"

  // This HTML is safe thanks to the template tag `escapeInject` which sanitizes `title`
  return escapeInject`<!DOCTYPE html>
    <html>
      <head>
        <title>${title}</title>
      </head>
      <body>
        <div id="page-root">${dangerouslySkipEscape(pageHtml)}</div>
      </body>
    </html>`
}

All strings, e.g. title above, are automatically sanitized (technically speaking: HTML-escaped) so that we can safely include untrusted strings such as user-generated text.

The dangerouslySkipEscape(str) function injects the string str as-is without sanitizing. We should use dangerouslySkipEscape() with a lot of caution and only for HTML strings that are guaranteed to be already sanitized. We usually use it to include HTML generated by UI frameworks (React/Vue/...) as these always generate already-sanitized HTML. If we find ourself using dangerouslySkipEscape() in other situations, we should be extra careful as we run into the risk of creating a security breach.

HTML Fragments

We can assemble the overall HTML document from several pieces of HTML fragments. For example, if we want some HTML parts to be included only for certain pages:

// _default.page.server.js
// Environment: Node.js

import { escapeInject, dangerouslySkipEscape } from 'vite-plugin-ssr'
import { renderToHtml } from 'some-view-framework'

export { render }

async function render(pageContext) {
  // We only include the `<meta name="description">` tag if the page has a description.
  // (Pages define `pageContext.documentProps.description` in their `onBeforeRender()` hook.)
  const description = pageContext.documentProps?.description
  let descriptionTag = ''
  if( description ) {
    // Note how we use the `escapeInject` string template tag for an HTML fragment
    descriptionTag = escapeInject`<meta name="description" content="${description}">`
  }

  // We use the `escapeInject` template tag again for the overall HTML and we can use
  // our `descriptionTag` HTML fragment.
  return escapeInject`<html>
    <head>
      ${descriptionTag}
    </head>
    <body>
      <div id="root">
        ${dangerouslySkipEscape(await renderToHtml(pageContext.Page))}
      </div>
    </body>
  </html>`
}

Example: