Renderer API

fastify-vite has a modular renderer API that aims to allow you to use it with any framework, as long as you provide the right adapter. Currently there are two official renderer adapters, fastify-vite-vue (Vue 3+ support) and fastify-vite-react (React 17+ support). This is an overview of what constitutes a renderer adapter and currently serves as a specification, with the Vue and React adapters as official reference implementations.

Quoting the introductory example:

const fastify = require('fastify')()
const fastifyVite = require('fastify-vite')
const renderer = require('fastify-vite-vue')

fastify.register(fastifyVite, { renderer })

The renderer plugin option is an object expected to contain the options associted with the framework renderer, plus functions to create a route handler for SSR (getHandler) and to load the application's entry file (getEntry), both for dev and production environments.


Key exports expected from a renderer adapter and their roles
options —— Vite options for framework

These are used in development:

dev.getHandler() —— Returns Vite + Fastify route handler for SSR

dev.getEntry() —— Returns Vite's entry points for the app

These are used when NODE_ENV === 'production'

getHandler() —— Returns production Fastify route handler for SSR

getEntry() —— Returns production entry points for the app

In addition to providing the options and functions required to load and render the application from Fastify, a renderer adapter must include client and server modules. The server module must export a createRenderFunction() function, and the client module must export, at the very least, the isServer flag, useHydration() and hydrate().

Depending on the framework and idioms employed, other helpers might be added. For instance, the React Renderer includes a ContextProvider export, while the Vue Renderer includes a useGlobalProperties() export which is not preset in the React one.

Server Module
createRenderFunction() —— creates and returns a render function (for SSR). Both Vue and React are based on idioms that makes this abstraction possible, but supporting more frameworks may involve expanding this API.
Client Module
useHydration(config) —— returns a context object with all hydrated values from the server. It'll work seamlessly for SSR (first render) and client-side navigation (History API) with a special abstraction of route payloads and isomorphic data.

hydrate() —— collect server rendered values (hardcoded in window) into an appropriate application context object (see Client Hydration).

isServer —— akin to process.server in Nuxt and importa.meta.env.SSR in Vite but guaranteed to work in both CJS and ESM runtimes.

The naming of these helper modules might be confusing.

The server module is supposed to be used only in a server context, that is, anything exported from it should only be imported in code that's required to only run in a Node.js context.

The client module is everything pertaining to the client, and its exports should be isomorphic in nature, that is, it should be possible to import the helper client module both in SSR (Node.js) and client (browser) contexts. Perhaps a better name for this module would be universal, not client, and this might still change in future versions of fastify-vite.

You can see the full pattern employed by the Vue and React fastify-vite renderers in the vue-base and react-base examples. You'll see for instance createRenderFunction() for Vue imported here, and for React imported here. And the client imports for Vue here, and for React here.