Edit

+onRenderHtml() hook

Environment: server

The onRenderHtml() hook defines how pages are rendered to HTML.

The hooks onRenderHtml() and onRenderClient() are essentially the glue code between Vike and your UI framework (React/Vue/Solid/...).

If you use a UI framework Vike extension vike-react/vike-vue/vike-solid, then you don't need to (and shouldn't) define onRenderHtml() as it's already defined by vike-react/vike-vue/vike-solid.

// +onRenderHtml.js
// Environment: server
 
export { onRenderHtml }
 
import { escapeInject, dangerouslySkipEscape } from 'vike/server'
import { renderToHtml, createElement } from 'some-ui-framework'
 
async function onRenderHtml(pageContext) {
  const { Page, data } = pageContext
  const pageHtml = await renderToHtml(createElement(Page, data))
 
  const documentHtml = escapeInject`<!DOCTYPE html>
    <html>
      <head>
        <title>My SSR App</title>
      </head>
      <body>
        <div id="page-root">${dangerouslySkipEscape(pageHtml)}</div>
      </body>
    </html>`
 
  return {
    documentHtml,
    pageContext: {
      // We can define pageContext values here
    }
  }
}
// +onRenderHtml.ts
// Environment: server
 
export { onRenderHtml }
 
import type { PageContextServer } from 'vike/types'
import { escapeInject, dangerouslySkipEscape } from 'vike/server'
import { renderToHtml, createElement } from 'some-ui-framework'
 
async function onRenderHtml(pageContext: PageContextServer){
  const { Page, data } = pageContext
  const pageHtml = await renderToHtml(createElement(Page, data))
 
  const documentHtml = escapeInject`<!DOCTYPE html>
    <html>
      <head>
        <title>My SSR App</title>
      </head>
      <body>
        <div id="page-root">${dangerouslySkipEscape(pageHtml)}</div>
      </body>
    </html>`
 
  return {
    documentHtml,
    pageContext: {
      // We can define pageContext values here
    }
  }
}

Where pageContext.Page is the +Page value of the page that is being rendered.

Non-HTML content

Instead of returning documentHtml, the onRenderHtml() hook can set pageContext.content to render a page that isn't HTML, such as an RSS/Atom feed, a sitemap, a Markdown file, or an image.

// pages/feed/+route.js
 
export default '/feed.atom'
// pages/feed/+route.ts
 
export default '/feed.atom'
// pages/feed/+onRenderHtml.js
// Environment: server
 
export { onRenderHtml }
 
function onRenderHtml(pageContext) {
  pageContext.content = `<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>My blog</title>
</feed>`
}
// pages/feed/+onRenderHtml.ts
// Environment: server
 
export { onRenderHtml }
 
import type { PageContextServer } from 'vike/types'
 
function onRenderHtml(pageContext: PageContextServer) {
  pageContext.content = `<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>My blog</title>
</feed>`
}

The value of pageContext.content can be:

  • A string
  • A Uint8Array (binary content, such as images)
  • A ReadableStream<Uint8Array> (streamed to the client upon SSR)

The Content-Type HTTP response header is deduced from the URL's file extension (e.g. /feed.atom => application/atom+xml;charset=utf-8). You can override it by using API > +headersResponse. For URLs without a known file extension, the default is application/octet-stream.

Upon pre-rendering, the content is written to the file at the page's URL: /feed.atom => dist/client/feed.atom.

Add rel="external" to links pointing to non-HTML pages (e.g. <a href="/feed.atom" rel="external">), so that the browser fetches them instead of Vike's client-side router. See +clientRouting > Links intercepting.

See also