To integrate Vike with a server, we recommend the vike-server extension — it supports popular servers (Hono, Express, ...) and deployments (Cloudflare, Vercel, ...).

Version history: CHANGELOG.md
Examples: examples/
Source code: GitHub > vikejs/vike-server

This page contains the entire documentation for vike-server.

Alternatively, instead of using a server, you can pre-render your pages and deploy to a static host.

If you want more control over server integration, see Server (manual integration).

Add vike-server to an existing Vike app

To add vike-server to an existing Vike app: install the vike-server npm package (e.g. $ npm install vike-server) then extend your existing +config.js file (or create one) with vike-server:

// +config.js
 
import vikeServer from 'vike-server/config'
 
export const config = {
  extends: [vikeServer], 
  // Points to your server entry
  server: 'server/index.js'
}

Update your production script in package.json:

// package.json
 
"scripts": {
  "dev": "vike dev",
  "build": "vike build",
  "prod": "NODE_ENV=production node dist/server/index.js"
}

Create (or update) your server entry:

// server/index.js
 
import { Hono } from 'hono'
import { apply } from 'vike-server/hono'
import { serve } from 'vike-server/hono/serve'
 
function startServer() {
  const app = new Hono()
  apply(app)
  return serve(app, {
    // options
    port: 3000,
    hostname: 'localhost'
  })
}
 
export default startServer()

Where:

  • apply() installs the middleware of Vike and Vike extensions.
  • serve() attaches your server to Node.js, Cloudflare, Netlify, Vercel, Deno, Bun, ...

    See serve() options.

    serve() enables you to define a single server entry while being able to target multiple environments (e.g. Node.js and Cloudflare have different server attachment styles).

Note that:

  • Vike is automatically added to your server — no need to manually integrate renderPage().
  • Some Vike extensions may also automatically add server middlewares.
  • Static files are automatically served.

Deployment

In production run $ NODE_ENV=production node dist/server/index.js (or Bun/Deno).

We're currently working on out-of-the-box support for Cloudflare and Vercel (ETA the next couple of weeks). In the meantime, see:

serve() options

serve(app, {
  // [Required] Server port. It's ignored in Cloudflare and Vercel Edge (there
  // isn't any server in serverless deployment).
  port: 3000,
 
  // Called once the server is accepting connections
  onReady() {
    console.log('Server is ready.')
  },
 
  // Called once the server is created
  onServer(server) {
    // `server` type depends on your runtime:
    //   Node.js:  Server ('node:http') by default. It's an HTTPS or HTTP2 server
                   if the `createServer` option was provided (see below).
    //   Deno:     return of Deno.Serve (experimental support)
    //   Bun:      return of Bun.Serve  (experimental support)
 
    // `server` is `undefined` in Cloudflare and Vercel Edge (there
    // isn't any server in serverless deployment)
  },
 
 
  // ⚠️  The following two options are available only when running on Node.js
 
  // [Node.js] Can be one of:
  //     import { createServer } from 'node:http'
  //     import { createServer } from 'node:https'
  //     import { createSecureServer as createServer } from 'node:http2'
  createServer,
 
  // [Node.js] Options forwarded to `createServer()`
  serverOptions: {
    // For example SSL/TLS key and certificate for HTTPS
    key: fs.readFileSync('/etc/letsencrypt/live/example.com/privkey.pem'),
    cert: fs.readFileSync('/etc/letsencrypt/live/example.com/fullchain.pem'),
    // ... other createServer() options ...
  },
 
 
  // 👉 Other options are passed down as-is to the target environment
 
  // For example, you can define all @hono/node-serve options here, such as:
  fetch,
  overrideGlobalObjects: false,
 
  // ... any options of your target environment ...
})

Custom pageContext

To define custom pageContext properties based on the HTTP request, you can use +onCreatePageContext.server.js with pageContext.runtime, for example:

// pages/+onCreatePageContext.server.js
// Environment: server
 
// This hook is called upon new incoming HTTP requests
export async function onCreatePageContext(pageContext) {
  pageContext.user = pageContext.runtime.req.user
}

See also: Integration > Authentication > With vike-server.

The +onCreatePageContext.server.js hook above isn't really needed: you can simply access pageContext.runtime.req.user instead — it's just a convenience to set pageContext.user as an alias for pageContext.runtime.req.user

Standalone

With standalone: true, the build output directory (dist/) contains everything needed for deployment. This means that, in production, only the dist/ directory is required (you can remove node_modules/ and skip $ npm install).

⚠️

This feature is experimental and may break upon any version update.

If the production code built with standalone: true fails to run with errors like ENOENT: no such file or directory, then disable standalone mode or replace the npm package throwing the error with another npm package. (The issue is that some npm package have dependencies that aren't explicit and, consequently, cannot be statically analyzed and cannot be included in the standalone bundle.)

// +config.js
 
import vikeServer from 'vike-server/config'
 
export const config = {
  // ...
  extends: [vikeServer],
  server: {
    entry: 'server/index.js',
    standalone: true
  }
}

Options:

export const config = {
  // ...
  extends: [vikeServer],
  server: {
    entry: 'server/index.js',
    standalone: {
      esbuild: {
        minify: true,
        // ... or any other esbuild option
      }
    }
  }
}

Instead of using standalone: true, we recommend tools such as pnpm deploy --prod. This provides better control over packed files and ensures greater compatibility.

HMR

If you change a server file, the server code is automatically updated: the next HTTP response will be generated by the latest code. No more full server reload required.

This is experimental and doesn't always work: vike-server sometimes still triggers a full server reload.

If HMR isn't what you want (for example if you modify the database connection) you can manually trigger a full server reload by pressing r + enter.

Compression

In production, vike-server compresses all Vike responses.

You can disable it:

apply(app, {
  compress: false
})

HTTPS

If you want to use HTTPS in development, pass createServer and the HTTPS certificates to serve():

import { createServer } from 'node:https'
/* Or with HTTP2:
import { createSecureServer as createServer } from 'node:http2'
*/
 
serve(app, {
  createServer,
 
  // [Node.js] Options forwarded to `createServer()`
  serverOptions: {
    // For example SSL/TLS key and certificate for HTTPS
    key: fs.readFileSync('/etc/letsencrypt/live/example.com/privkey.pem'),
    cert: fs.readFileSync('/etc/letsencrypt/live/example.com/fullchain.pem')
    // ... other createServer() options ...
  },
})

See also