Environment: server.

We use renderPage() in order to server-side render our pages.

If we pre-render all our pages then we can omit using renderPage(). (We still need renderPage() for development, but we can use Vite's development server as vite-plugin-ssr automatically embeds renderPage() into it.)

// server.js

// In this example we use Express.js but we could use any other server framework
import express from 'express'
import { renderPage } from 'vite-plugin-ssr/server'

const isProduction = process.env.NODE_ENV === 'production'
const root = `${__dirname}/..`


async function startServer() {
  const app = express()

  if (isProduction) {
  } else {
    // Instantiate Vite's development server and integrate its middleware to our server.
    // ⚠️ We should instantiate it *only* in development. (It isn't needed in production
    // and would unnecessarily bloat our server in production.)
    const vite = await import('vite')
    const viteDevMiddleware = (await vite.createServer({
      server: { middlewareMode: true }

  app.get('*', async (req, res, next) => {
    const pageContextInit = { urlOriginal: req.originalUrl }
    const pageContext = await renderPage(pageContextInit)
    if (pageContext.httpResponse === null) return next()
    const { body, statusCode, contentType } = pageContext.httpResponse

  const port = 3000
  console.log(`Server running at http://localhost:${port}`)
  • pageContext.httpResponse.statusCode is either 200, 404, or 500.
  • pageContext.httpResponse.contentType is either text/html;charset=utf-8 or application/json. (The Content-Type is application/json when Client Routing fetches the pageContext of the newly navigated page.)
  • pageContext.httpResponse.body is the HTML string returned by the render() hook with additional <script> and <style> tags automatically injected by vite-plugin-ssr.
  • pageContext.httpResponse is null if:
    • An error occurs while rendering the page and you didn't create an file.
    • An error occurs while rendering
    • The URL is skipped: vite-plugin-ssr doesn't render certain URLs. (E.g. pageContext.urlOriginal === '/favicon.ico' because browsers automatically make favicon requests.)

renderPage() doesn't depend on Node.js and we can use renderPage() (and therefore embed vite-plugin-ssr) anywhere:

  • Any server environment (Express.js, HatTip, Deno, Fastify, Vite's development server, Node.js's HTTP server, ...)
  • Any deployment provider (AWS, Cloudflare Workers, Vercel, ...)



We recommend the following.

In development:

  • Don't set process.env.NODE_ENV, or set it to one of the following values: ['development', 'dev', '', undefined].
  • Instantiate the Vite dev server, and add its middleware to your server as shown in the example code snippet above.

In production (and staging):

  • Set process.env.NODE_ENV to a value different than ['development', 'dev', '', undefined], for example 'production' or 'staging'.
  • Don't instantiate the Vite dev server: it isn't needed in production and would unnecessarily bloat your server in production.

process.env.NODE_ENV is a convention to tell libraries whether your app is running in production or development:

While the convention was introduced by Node.js, it's used by libraries regardless whether you use Node.js or not. That's why we recommend to set process.env.NODE_ENV in Edge environments as well.

Vite-plugin-ssr shows a warning if you don't respect the recommendation:

[vite-plugin-ssr][Warning] Vite's development server was instantiated while the environment
is set to be a production environment by `process.env.NODE_ENV === 'production'` which is
[vite-plugin-ssr][Warning] Vite's development server wasn't instantiated while the environment
is set to be a development environment by `process.env.NODE_ENV === undefined` which is