Edit

+onHookCall() hook

Environment: server, client
type OnHookCall = (hook: Hook, pageContext: null | PageContext) => void
type Hook = {
  name: string
  filePath: string
  call: () => void | Promise<void>
}

Global
Cumulative
Provided by: vike
⚠️

It's in beta: breaking changes may occur with any version update.

Hook called whenever a Vike hook is called. Useful for instrumentation (e.g. Sentry, OpenTelemetry).

// pages/+onHookCall.js
// Environment: server & client
 
export const onHookCall = async (hook, pageContext) => {
  console.log('before', hook.name, hook.filePath)
  await hook.call()
  console.log('after', hook.name, hook.filePath)
}
// pages/+onHookCall.ts
// Environment: server & client
 
import type { Config } from 'vike/types'
 
export const onHookCall: Config['onHookCall'] = async (hook, pageContext) => {
  console.log('before', hook.name, hook.filePath)
  await hook.call()
  console.log('after', hook.name, hook.filePath)
}

The first parameter hook contains:

  • hook.name — Name of the hook being called (e.g. 'onRenderHtml', 'data', 'guard')
  • hook.filePath — File path where the hook is defined
  • hook.call() — Call the hook

The second parameter is pageContext or null.

You can access globalContext by using getGlobalContext().

You must always call hook.call() and do so before any await.

// ❌ This breaks sync hooks
export const onHookCall = async (hook, pageContext) => {
  await someAsyncOperation()
  await hook.call() // Too late for sync hooks!
}
// ❌ This breaks sync hooks
export const onHookCall: Config['onHookCall'] = async (hook, pageContext) => {
  await someAsyncOperation()
  await hook.call() // Too late for sync hooks!
}
// ✅ This works for both sync and async hooks
export const onHookCall = async (hook, pageContext) => {
  await hook.call() // Called before any await
  await someAsyncOperation()
}
// ✅ This works for both sync and async hooks
export const onHookCall: Config['onHookCall'] = async (hook, pageContext) => {
  await hook.call() // Called before any await
  await someAsyncOperation()
}

Use case: instrumentation

Instrument hooks with Sentry:

// pages/+onHookCall.js
// Environment: server & client
 
import * as Sentry from '@sentry/node'
 
export const onHookCall = async (hook, pageContext) => {
  await Sentry.startSpan(
    {
      op: 'vike.hook',
      name: hook.name,
      attributes: { filePath: hook.filePath }
    },
    async () => {
      await hook.call()
    }
  )
}
// pages/+onHookCall.ts
// Environment: server & client
 
import * as Sentry from '@sentry/node'
import type { Config } from 'vike/types'
 
export const onHookCall: Config['onHookCall'] = async (hook, pageContext) => {
  await Sentry.startSpan(
    {
      op: 'vike.hook',
      name: hook.name,
      attributes: { filePath: hook.filePath }
    },
    async () => {
      await hook.call()
    }
  )
}

Instrument hooks with OpenTelemetry:

// pages/+onHookCall.js
// Environment: server & client
 
import { trace } from '@opentelemetry/api'
 
const tracer = trace.getTracer('vike')
 
export const onHookCall = async (hook, pageContext) => {
  await tracer.startActiveSpan(hook.name, async (span) => {
    span.setAttribute('hook.filePath', hook.filePath)
    try {
      await hook.call()
    } finally {
      span.end()
    }
  })
}
// pages/+onHookCall.ts
// Environment: server & client
 
import { trace } from '@opentelemetry/api'
import type { Config } from 'vike/types'
 
const tracer = trace.getTracer('vike')
 
export const onHookCall: Config['onHookCall'] = async (hook, pageContext) => {
  await tracer.startActiveSpan(hook.name, async (span) => {
    span.setAttribute('hook.filePath', hook.filePath)
    try {
      await hook.call()
    } finally {
      span.end()
    }
  })
}

See also: Integration > Error Tracking

Use case: logging

Log all hook executions for debugging:

// pages/+onHookCall.js
// Environment: server & client
 
export const onHookCall = async (hook, pageContext) => {
  const start = performance.now()
  console.log(`[+${hook.name}] start`)
  try {
    await hook.call()
    console.log(`[+${hook.name}] done in ${(performance.now() - start).toFixed(2)}ms`)
  } catch (error) {
    console.error(`[+${hook.name}] error`, error)
    throw error
  }
}
// pages/+onHookCall.ts
// Environment: server & client
 
import type { Config } from 'vike/types'
 
export const onHookCall: Config['onHookCall'] = async (hook, pageContext) => {
  const start = performance.now()
  console.log(`[+${hook.name}] start`)
  try {
    await hook.call()
    console.log(`[+${hook.name}] done in ${(performance.now() - start).toFixed(2)}ms`)
  } catch (error) {
    console.error(`[+${hook.name}] error`, error)
    throw error
  }
}

See also