Edit this page

Pre-rendering (SSG)

What is pre-rendering?

Pre-rendering means to render the HTML of pages at build-time (when running $ vike build).

Without pre-rendering, the HTML of a page is rendered at request-time (when the user goes to that page).

If you pre-render all your pages, you don't need a production server: your app consists only of a static assets (HTML, JS, CSS, images, ...) that can be deployed to a static host (GitHub Pages, Cloudflare Pages, Netlify, ...).

If you don't pre-render, you need a production server to dynamically render your pages' HTML at request-time. (Deploying a Node.js server to AWS, Cloudflare Workers, Vercel...)

Tools that pre-render pages are also known as "SSG" (Static-Site Generators).

Should I pre-render?

In a nutshell: pre-render your pages whenever you can.

Because pre-rendering removes the need for a production server and makes deployment very easy and very cheap (usually free). It's also significantly more performant as the HTML isn't re-generated on every HTTP request.

But pre-rendering cannot be used for websites with content that changes very frequently. For example Hacker News or Reddit: users are constantly creating new posts, which would require pre-rendering to run again each time — pre-rendering the entire website (millions of pages) every millisecond isn't possible.

In theory, it's possible to re-render only the subset of pages that are affected by new content, but it isn't practical (it has been tried before) and we recommend against this practice.

Technically, the bottom line is: how frequently does the HTML of your pages change? See:

Pre-rendering can be used for websites with content that changes only occasionally. For example, the content of https://vike.dev changes only when a maintainer updates the documentation — the entire website https://vike.dev can then be pre-rendered again every time there is change.

This website https://vike.dev uses pre-rendering and is deployed to GitHub Pages — a static host that is completely free, easy to use, and performant.

Get started

To enable pre-rendering:

// pages/+config.js
 
export default {
  prerender: true
}
// pages/+config.ts
 
import type { Config } from 'vike/types'
 
export default {
  prerender: true
} satisfies Config

Your pages' HTML are now rendered at build-time (when you run $ vike build) instead of request-time.

The generated .html files are written to dist/client/.

CLI instead of server

With pre-rendering, you don't need a JavaScript server — you can use $ vike dev and $ vike preview instead. (You do, however, need a JavaScript server if you skip pre-rendering some pages.)

Parameterized routes

For a page with a parameterized route (e.g. /movie/@id), you must use the onBeforePrerenderStart() hook to provide the list of URLs to be pre-rendered.

If, instead of having to provide a list of URLs, you want parameterized routes to be resolved dynamically on the client-side, then see the workarounds at Feature Request #1476 — Pre-render dynamic routes (static host deployment).

Configuration

Common +prerender options:

// pages/+config.js
 
export default {
  prerender: {
    noExtraDir: true, // Don't create a new directory for each HTML file
    parallel: false // Disable concurrency — pre-render pages sequentially (one by one)
    // ...
  }
}
// pages/+config.ts
 
import type { Config } from 'vike/types'
 
export default {
  prerender: {
    noExtraDir: true, // Don't create a new directory for each HTML file
    parallel: false // Disable concurrency — pre-render pages sequentially (one by one)
    // ...
  }
} satisfies Config

See API > +prerender for the list of all options.

You can also:

Partial

By default, all pages are pre-rendered, but you can also pre-render only some pages.

Opt-out

You can set prerender: false to skip a page from pre-rendering:

// /pages/+config.js
 
export default {
  // Pre-render all pages by default
  prerender: true
}
// /pages/+config.ts
 
import type { Config } from 'vike/types'
 
export default {
  // Pre-render all pages by default
  prerender: true
} satisfies Config
// /pages/news/+config.js
 
export default {
  // Skip pre-rendering for /pages/news/+Page.js
  prerender: false
}
// /pages/news/+config.ts
 
import type { Config } from 'vike/types'
 
export default {
  // Skip pre-rendering for /pages/news/+Page.ts
  prerender: false
} satisfies Config

For example, a news page that displays the latest news fetched from a database should be rendered at request-time (not at build-time).

See also: Should I pre-render?

You can also disable pre-rendering for a group of pages:

// /pages/products/+prerender.js
 
// Applies to all /pages/products/**/+Page.js
export const prerender = false
// /pages/products/+prerender.ts
 
// Applies to all /pages/products/**/+Page.ts
export const prerender = false

See also: API > Config Files > Inheritance

Opt-in instead of opt-out

You can make pre-rendering opt-in instead of opt-out:

// /pages/+config.js
 
export default {
  // By default, pages aren't pre-rendered
  prerender: false
}
// /pages/+config.ts
 
import type { Config } from 'vike/types'
 
export default {
  // By default, pages aren't pre-rendered
  prerender: false
} satisfies Config
// /pages/(marketing)/+prerender.js
 
// Applies to all marketing pages /pages/(marketing)/**/+Page.js
export const prerender = true
// /pages/(marketing)/+prerender.ts
 
// Applies to all marketing pages /pages/(marketing)/**/+Page.ts
export const prerender = true

The directory (marketing) is ignored by Filesystem Routing, see Guides > Routing > Groups.

Dynamic condition

You can dynamically disable/enable pre-rendering:

// /pages/+prerender.js
 
export const prerender = someCondition() ? false : true
// /pages/+prerender.ts
 
export const prerender = someCondition() ? false : true

For example, to disable pre-rendering when previewing CMS changes:

// /pages/+prerender.js
 
export const prerender = process.env.MODE !== 'preview'
// /pages/+prerender.ts
 
export const prerender = process.env.MODE !== 'preview'

Note that onBeforePrerenderStart() hooks are always called, even if prerender is false. If you want to define onBeforePrerenderStart() conditionally:

// /pages/movie/+prerender.js
 
export { prerender }
 
import { someCondition } from './someCondition'
 
const prerender = someCondition()
// /pages/movie/+prerender.ts
 
export { prerender }
 
import { someCondition } from './someCondition'
 
const prerender = someCondition()
// /pages/movie/+onBeforePrerenderStart.js
 
export { onBeforePrerenderStart }
 
import { someCondition } from './someCondition'
 
const onBeforePrerenderStart = someCondition()
  ? async () => {
      // ...
    }
  : null
// /pages/movie/+onBeforePrerenderStart.ts
 
export { onBeforePrerenderStart }
 
import { someCondition } from './someCondition'
 
const onBeforePrerenderStart = someCondition()
  ? async () => {
      // ...
    }
  : null

SSG vs SSR

The only difference between SSG and SSR is when the HTML is rendered:

  • SSG: the HTML of the page is rendered at build-time (when calling $ vike build).
  • SSR: the HTML of the page is rendered at request-time (when the user goes to the page).

The client-side code of the page is always loaded and executed in the user's browser at request-time (regardless of SSG and SSR).

If you pre-render all your pages, there's no server — all "server-side code" runs at build-time. Calling it "server-side code" is technically a misnomer, but we keep the term for simplicity. One way to think about pre-rendering is that it essentially means "pre-rendered SSR (Server-Side Rendering)".

Essentially, and technically, SSG means SSR + pre-rendering:

// pages/+config.js
 
// SSG
export default {
  prerender: true,
  ssr: true // (optional: `ssr` is true by default)
}
// pages/+config.ts
 
import type { Config } from 'vike/types'
 
// SSG
export default {
  prerender: true,
  ssr: true // (optional: `ssr` is true by default)
} satisfies Config

See also:

Can I use SSG + SPA?

Yes, you can have SPA pages while others are SSG.

You usually still pre-render all your pages, including SPA pages. (See explanation below.)

For example:

pages/+config.js
pages/(marketing)/+config.js
pages/(marketing)/index/+Page.js # SSR
pages/(marketing)/about/+Page.js # SSR
pages/admin-panel/+config.js
pages/admin-panel/index/+Page.js # SPA
pages/+config.ts
pages/(marketing)/+config.ts
pages/(marketing)/index/+Page.ts # SSR
pages/(marketing)/about/+Page.ts # SSR
pages/admin-panel/+config.ts
pages/admin-panel/index/+Page.ts # SPA
// pages/+config.js
 
export default {
  // Pre-render *all* pages (including SPA pages)
  prerender: true
}
// pages/+config.ts
 
import type { Config } from 'vike/types'
 
export default {
  // Pre-render *all* pages (including SPA pages)
  prerender: true
} satisfies Config
// pages/admin-panel/about/+Page.js
 
export default {
  // SPA
  ssr: false
}
// pages/admin-panel/about/+Page.ts
 
import type { Config } from 'vike/types'
 
export default {
  // SPA
  ssr: false
} satisfies Config
// pages/(marketing)/about/+Page.js
 
export default {
  // SSG
  ssr: true // (optional: `ssr` is true by default)
}
// pages/(marketing)/about/+Page.ts
 
import type { Config } from 'vike/types'
 
export default {
  // SSG
  ssr: true // (optional: `ssr` is true by default)
} satisfies Config

SPA means the page is rendered only on the client-side (see What is SSR and SPA?). In other words, no SSR:

// pages/+config.js
 
// SPA
export default {
  ssr: false
}
// pages/+config.ts
 
import type { Config } from 'vike/types'
 
// SPA
export default {
  ssr: false
} satisfies Config

But when the user visits an SPA page, the browser still needs an HTML response to kick off client-side rendering. This HTML is just an empty shell (it doesn't contain the content of the page). If you want to avoid the need for a production server, you can pre-render this empty shell:

// pages/+config.js
 
// SPA
export default {
  prerender: true,
  ssr: false
}
// pages/+config.ts
 
import type { Config } from 'vike/types'
 
// SPA
export default {
  prerender: true,
  ssr: false
} satisfies Config

That's why the common practice is to set ssr: false together with prerender: true.

In a nutshell: the only difference is that the content of your SSG pages is included in the HTML, while the content of your SPA pages isn't.

Even though the content of SPA pages is missing, meta information (title, description, social image, ...) is still included in the pre-rendered HTML. See Guides > <head> tags > SPA.

Can I use SSG + SSR?

Yes, you can have SSR pages while others are SSG.

SSG is basically "pre-rendered SSR", see SSG vs SSR.

For example:

pages/+config.js
pages/user/@id/+Page.js # SSR
pages/(marketing)/+config.js
pages/(marketing)/index/+Page.js # SSG
pages/(marketing)/about/+Page.js # SSG
pages/+config.ts
pages/user/@id/+Page.ts # SSR
pages/(marketing)/+config.ts
pages/(marketing)/index/+Page.ts # SSG
pages/(marketing)/about/+Page.ts # SSG
// pages/+config.js
 
// SSR
export default {
  ssr: true // (optional: `ssr` is true by default)
}
// pages/+config.ts
 
import type { Config } from 'vike/types'
 
// SSR
export default {
  ssr: true // (optional: `ssr` is true by default)
} satisfies Config
// pages/(marketing)/+config.js
 
// SSG
export default {
  prerender: true,
  ssr: true // (optional: `ssr` is inherited by pages/+config.js)
}
// pages/(marketing)/+config.ts
 
import type { Config } from 'vike/types'
 
// SSG
export default {
  prerender: true,
  ssr: true // (optional: `ssr` is inherited by pages/+config.ts)
} satisfies Config

See also:

See also

Pre-rendering options: