The onBeforeRender() hook is for expert Vike users. If you're new to Vike, we recommend using the
data() hook instead, and/or Vike extensions for
automatically integrating data fetching tools (RPC with Telefunc/tRPC, REST with Tanstack Query, GraphQL with Apollo/Relay, etc).
The most notable use case for the onBeforeRender() hook is deep integration with data fetching tools.
For simple data fetching needs, use the data() hook instead. As a strategy to decide which one to use, always first try to use data() and only use onBeforeRender() as a fallback if data() doesn't work out.
Another common use case is to use onBeforeRender() as a global initializing hook. (As a temporary workaround until #962 - New hook onBoot() is implemented.)
onBeforeRender() VS data()
The central difference between the two hooks is that the value returned by data() always sets the value of pageContext.data, whereas onBeforeRender() can set multiple pageContext values.
Another difference is that the entire pageContext.data value is always sent to the client-side, whereas with onBeforeRender() you can (and have to) decide which values are sent to the client-side by using passToClient.
In a nutshell: while onBeforeRender() requires more manual work, it also gives you more control.
Environment
Like data(), the onBeforeRender() hook always runs on the server-side by default. By using .shared.js or .client.js you can tell Vike to run onBeforeRender() on the client-side instead, see API > data() hook > Environment.
onBeforeRender() + meta
Using onBeforeRender() together with custom hooks and settings (see meta) is a powerful technique enabling you to implement your own tailored DX.
A single global onBeforeRender() hook orchestrating the customm setting:
// /pages/+onBeforeRender.jsimport { runQuery } from 'some-sql-engine'export async function onBeforeRender(pageContext) { // The value exported by /pages/**/+sql.js is available at pageContext.config.sql const { sql } = pageContext.config const data = await runQuery(sql) // ...}
There can be only one onBeforeRender() hook per page. For example, if you define a global onBeforeRender() hook at /pages/+onBeforeRender.js as well as a page-specific one at /pages/star-wars/+onBeforeRender.js then the page-specific one overrides the global one. See API > Config > Inheritance.
If you want multiple onBeforeRender() hooks, then consider:
Creating custom hooks instead: you can then use one global onBeforeRender() hook that orchestrates many custom hooks.
Using data() hooks: you can then use one global onBeforeRender() while using page-specific data() hooks.
You can also suppress globally defined onBeforeRender() hooks on a per-page basis:
// /pages/+onBeforeRender.js// Some global onBeforeRender() hookexport default () => { // ...}
// /pages/some-page/+config.jsexport default { // Suppress the global onBeforeRender() hook onBeforeRender: null}
Advanced example
The following is an advanced example of using onBeforeRender() with meta in order to integrate data fetching tools. In particular, this approach can be used for advanced integration with GraphQL tools.
If you use a custom renderer instead of vike-react/vike-vue/vike-solid, then you can modify your onRenderHtml()/onRenderClient() hooks instead of doing the following.
// /pages/+config.js// Environment: configexport default { // Pass the GraphQL cache to the client-side passToClient: ['cache'], // Modify/create hooks meta: { onBeforeRender: { // Modify the onBeforeRender() hook to run on both the server- and client-side env: { client: true, server: true } }, // Create new hook onBeforeRenderHtml: { env: { server: true } }, // Create new hook onBeforeRenderClient: { env: { client: true } } }}
// /pages/+onBeforeRender.js// Environment: server or clientexport async function onBeforeRender(pageContext) { // When run on the server-side if (pageContext.config.onBeforeRenderHtml) { const { pageContext } = await onBeforeRenderHtml(pageContext) return { pageContext } } // When run on the client-side if (pageContext.config.onBeforeRenderClient) { await onBeforeRenderClient(pageContext) }}
// /pages/+onBeforeRenderHtml.jsx// Environment: serverimport { renderToHtml } from 'my-graphql-tool/server'export async function onBeforeRenderHtml(pageContext) { const { Page } = pageContext // `cache` contains the data fetched by GraphQL const { cache, html } = await renderToHtml(<Page />) return { pageContext: { pageHtml: html, cache } }}
// /pages/+onBeforeRenderClient.jsx// Environment: clientimport { hydrate } from 'my-ui-framework/client'import { CacheProvider } from 'my-graphql-tool/client'export function onBeforeRenderClient(pageContext) { const { Page, cache } = pageContext hydrate( // Re-use the `cache` data that was fetched on the server-side <CacheProvider cache={cache}> <Page /> </CacheProvider>, document.getElementById('root') )}
Don't omit ReturnType<OnBeforeRenderAsync> (don't write const onBeforeRender: OnBeforeRenderAsync = async (pageContext) => {), otherwise TypeScript won't strictly check the return type for unknown extra properties: see this TypeScript playground and issue.