⚠️
The vite-plugin-ssr project has been renamed Vike.
  • If you are already using vite-plugin-ssr, then migrate to Vike.
  • For new projects, don't use vite-plugin-ssr but use Vike instead.
It follows the same philosophy: Vike itself (without extensions) is unopinionated and lets you integrate tools with architectural freedom.

Preload

What is preloading? Preloading denotes the practice of loading assets (JavaScript, CSS, images, etc.) before the browser discovers them in HTML/CSS/JavaScript code. That way we reduce the network round trips required before the browser starts discovering and loading all dependencies.

By default, vite-plugin-ssr automatically inject tags to our HTML, such as <script type="module" stc="script.js">, <link rel="stylesheet" type="text/css" href="style.css">, and <link rel="preload" href="font.ttf" as="font" type="font/ttf">. It does so using a preload strategy that works for most users, but we can use injectFilter() to implement a custom preload strategy.

To improve preloading performance, we can use Early Hints which vite-plugin-ssr automatically generates.

Early Hints

The Early Hints Header is the official successor of the now deprecated HTTP2/Push.

// server.js

import { renderPage } from 'vite-plugin-ssr/server'

app.get('*', async (req, res) => {
  const pageContext = await renderPage({ urlOriginal: req.originalUrl } )
  const { earlyHints } = pageContext.httpResponse
  // For exampe with Node.js 18:
  res.writeEarlyHints({ link: earlyHints.map((e) => e.earlyHintLink) })
})
type PageContext = {
  httpResponse: {
    earlyHints: {
      earlyHintLink: string, // Header Line for the Early Hint Header
      assetType: "image" | "script" | "font" | "style" | null
      mediaType: string // MIME type
      src: string // Asset's URL
      isEntry: boolean // true  ⇒ asset is an entry
                       // false ⇒ asset is a dependency of an entry
    }[]
  }
}

Examples: $ npm init vite-plugin-ssr@latest.

See also:

injectFilter()

If vite-plugin-ssr's default preload strategy doesn't work for us, we can customize which and where preload/asset tags are injected.

// /renderer/_default.page.server.js

export async function render(pageContext) {
  // ...

  const documentHtml = escapeInject`<!DOCTYPE html>
    <html>
      <body>
        <div id="page-view">${stream}</div>
      </body>
    </html>`

  const injectFilter = (assets) => {
    assets.forEach(asset => {
      // Preload images
      if (asset.assetType === 'image') {
        asset.inject = 'HTML_BEGIN'
      }
    })
  }

  return { documentHtml, injectFilter }
}

See API > injectFilter().