Edit this page

pageContext

The pageContext object provides contextual information about the current page.

// /pages/product/@id/+data.js
 
export async function data(pageContext) {
  // Common built-in properties
  pageContext.urlParsed.pathname // /product/42
  pageContext.routeParams.id // 42
  pageContext.headers // { cookie: 'user-id=1337', ... }
 
  // Common custom properties
  pageContext.user // { name: 'John', id: 1337 }
  pageContext.initialStoreState // { todoList: [{ id: 1718872184291, text: 'Buy milk' }] }
}

The +data hook is explained at Guides > Data Fetching.

You can access pageContext:

It includes:

When the user navigates to a new page, a completely new pageContext object is created and the previous pageContext becomes obsolete — that's why it's called pageContext, not appContext. See Lifecycle for more information.

See also: API > globalContext.

Built-in

Built-in properties.

The pageContext object also contains many internals (they are prefixed with _, e.g. pageContext._httpRequestId). You should use them only if strictly needed and, if you do, then let us know so that we can add official support for your use case (otherwise you'll expose yourself to breaking changes upon any version update).

pageContext.Page

The export { Page } or export default of the +Page.js file.

pageContext.data

The value returned by the data() hook, see also API > useData().

pageContext.routeParams

The route parameters. (E.g. pageContext.routeParams.movieId for a page with a Route String /movie/@movieId.)

pageContext.urlOriginal

The current URL.

On the server-side, pageContext.urlOriginal is the value you passed at the server middleware:

// Server middleware
app.get('*', async (req) => {
  const pageContextInit = {}
  // `pageContext.urlOriginal` is defined here
  pageContextInit.urlOriginal = req.url
  const pageContext = await renderPage(pageContextInit)
  /* ... */
})

On the client-side:

  • When using Client Routing, the value of pageContext.urlOriginal is the browser's current URL (window.location.href).
  • When using Server Routing, the value of pageContext.urlOriginal is undefined (unless you use passToClient).

pageContext.urlPathname

Alias for pageContext.urlParsed.pathname.

pageContext.urlParsed

URL information:

{
  pathname: string
  pathnameOriginal: string
  search: Record<string, string> // AKA query parameters
  searchAll: Record<string, string[]>
  searchOriginal: null | string
  hash: string
  hashOriginal: null | string
  href: string
  origin: null | string
  protocol: null | string
  hostname: null | string
  port: null | string
}

Example:

https://example.com/some-base-url/hello/s%C3%A9bastien?fruit=%C3%A2pple&fruit=orânge#%C3%A2ge
{
  // Without Base URL, and decodes escaped characters.
  pathname: '/hello/sébastien',
  // With Base URL, and doesn't decode escaped characters.
  pathnameOriginal: '/some-base-url/hello/s%C3%A9bastien',
  search: { fruit: 'orânge' },
  searchAll: { fruit: ['âpple', 'orânge'] },
  searchOriginal: '?fruit=%C3%A2pple&fruit=orânge',
  hash: 'âge',
  hashOriginal: '#%C3%A2ge'
  // Without Base URL, and doesn't decode escaped characters.
  href: 'https://example.com/hello/s%C3%A9bastien?fruit=%C3%A2pple&fruit=orânge#%C3%A2ge',
  origin: 'https://example.com',
  protocol: 'https://',
  hostname: 'example.com', // 'localhost' if http://localhost:3000
  port: null // 3000 if http://localhost:3000
}

pageContext.headers

The headers of the HTTP Request. As a string object (Record<string, string>) normalized by Vike, see HTTP Headers.

pageContext.headersOriginal

The headers of the HTTP Request. The original object provided by the server, see HTTP Headers.

pageContext.config

All runtime information about the page's configuration (the page's route, hooks, settings, ...).

pageContext.isHydration

Whether the page is rendered to HTML.

When using Client Routing, the value is true for the first page the user navigates to, and false for any subsequent navigation. When using Server Routing, the value is always true.

If the page doesn't throw an error then it's equivalent to pageContext.isHydration === !pageContext.isClientSideNavigation. If there is an error, the error page is rendered and both pageContext.isHydration pageContext.isClientSideNavigation are false.

pageContext.isClientSide

Whether the page is being rendered on the client-side, or server-side / pre-rendered.

Also useful for narrowing down the PageContext type to either PageContextClient or PageContextServer, see Narrowing down.

It's equivalent to pageContext.isClientSide === !import.meta.env.SSR.

Like import.meta.env.SSR, code in if/else blocks is eliminated when building production client/server bundles (using Rollup's tree-shaking).

// This import is also removed when building for the server-side
import someClientImport from '../../client'
// This import is also removed when building for the client-side
import someServerImport from '.././server'
 
if (pageContext.isClientSide) {
  // This code block is removed when building for the server-side
  someClientImport()
  // ... client code ...
} else {
  // This code block is removed when building for the client-side
  someServerImport()
  // ... server code ...
}

pageContext.isPrerendering

Whether the page is being pre-rendered. The value is always false in development.

pageContext.isBackwardNavigation

Whether the user is navigating back in history.

The value is true when the user clicks on his browser's backward navigation button, or when invoking history.back().

The isBackwardNavigation property is only defined when using Client Routing.

pageContext.previousPageContext

Upon client-side page navigation, you can use pageContext.previousPageContext to access the pageContext of the previous page. See Lifecycle.

pageContext.is404

If an error occurs, whether the error is a 404 Page Not Found or a 500 Internal Error, see API > Error Page.

pageContext.isClientSideNavigation

Whether the page was navigated by the client-side router.

In other words, when using Client Routing, the value is false for the first page the user visits, and true for any subsequent navigation. (When using Server Routing, the value is always false.)

pageContext.abortReason

Set by throw render() and used by the error page.

pageContext.abortStatusCode

Set by throw render() and used by the error page.

pageContext.errorWhileRendering

The first error (if there is any) that occurred while rendering the page, see Integration > Error Tracking.

pageContext.isBaseMissing

Whether the Base URL is missing in the URL of the HTTP request made to the SSR server, see Guides > Base URL > Setup.

pageContext.globalContext

Runtime information about your app, see API > globalContext.

pageContext.isPageContext

Always true, useful for distinguishing pageContext from other objects and narrowing down TypeScript unions, see Narrowing down.

Custom

You can define custom pageContext properties.

pageContext.myCustomProp = someValue // Add or modify

See the section TypeScript > Extend for how to define the type of pageContext.myCustomProp.

See also: API > globalContext > Custom.

You can create/modify pageContext properties at:

Hooks

You can use any Vike hook to define custom properties.

For example +onCreatePageContext:

// pages/+onCreatePageContext.js
 
export async function onCreatePageContext(pageContext) {
  pageContext.myCustomProp = someValue
}

See concrete use case at Integration > Authentication.

Or +data:

// pages/product/@id/+data.js
 
export async function data(pageContext) {
  const { data, queryDuration } = await fetchSomeData()
  pageContext.queryDuration = queryDuration
  return data
}

UI components

You can define custom properties inside your UI components, by using usePageContext():

// Inside a UI component
const pageContext = usePageContext()
pageContext.myCustomProp = someValue // Add or modify property

Custom properties can be defined at navigate({ pageContext: { someExtra: 'value' } }).

renderPage()

If you don't use vike-server, then you can define custom properties at renderPage():

// Your Vike server middleware integration
app.get('*', async (req) => {
  const pageContextInit = {
    // ***************************************
    // *** Built-in pageContext properties ***
    // ***************************************
    urlOriginal: req.url,
    headersOriginal: req.headers,
 
    // ***************************************
    // **** Custom pageContext properties ****
    // ***************************************
    // Common use case: make information about logged-in user available at pageContext.user
    user: req.user,
    // Or any other value:
    // pageContext.myCustomProp
    myCustomProp: someValue
  }
  const pageContext = await renderPage(pageContextInit)
  // ...
})

Setting pageContext.user is a common use case for integrating authentication tools, see Integration > Authentication.

FAQ

Can I mutate pageContext?

Yes, it's a common practice to change/add pageContext properties. See Custom.

pageContext.someProp = someValue

Can I use pageContext as a UI store?

Instead of using pageContext, we generally recommend using a proper UI state management tool such as React's useState(), Redux, Vue's ref(), Pinia, etc.

That said, there are use cases for using pageContext to store client-side state. For example to pass information from the previous page to the next during navigation.

See Lifecycle to understand whether using pageContext can make sense for your use case.

Can I check whether SSR is enabled?

On the server-side, you can tell whether SSR is enabled by checking whether pageContext.Page is set:

// +onAfterRenderHtml.js
 
export function onAfterRenderHtml(pageContext) {
  const isSSR = !!pageContext.Page
  if (isSSR) {
    // ...
  }
}

TypeScript

Basics

import type {
  // For code loaded in client and server
  PageContext,
  // For code loaded in client only
  PageContextClient,
  // For code loaded in server only
  PageContextServer
} from 'vike/types'

Narrowing down

You can use pageContext.isClientSide and pageContext.isPageContext to narrow down TypeScript unions.

import type { PageContext, GlobalContext } from 'vike/types'
 
function someFunction(someObject: PageContext | GlobalContext) {
  if (someObject.isPageContext) {
    // someObject is PageContext
  } else {
    // someObject is GlobalContext
  }
}
import type { PageContext } from 'vike/types'
 
function someFunction(pageContext: PageContext) {
  if (pageContext.isClientSide) {
    // pageContext is PageContextClient
  } else {
    // pageContext is PageContextServer
  }
}

Extend

To extend and/or refine PageContext/PageContextServer/PageContextClient, use the global interface Vike.PageContext:

declare global {
  namespace Vike {
    interface PageContext {
      // Type of pageContext.user
      user?: {
        name: string
        id: string
        isAdmin: boolean
      }
      // Refine type of pageContext.Page
      Page: () => React.JSX.Element
    }
  }
}
 
// If you define Vike.PageContext in a .d.ts file then
// make sure there is at least one export/import statement.
// Tell TypeScript this file isn't an ambient module:
export {}

To define properties only for the server-/client-side, use the interfaces Vike.PageContextServer and Vike.PageContextClient instead.

Server Routing

If you use Server Routing:

import type {
  // For code loaded in client and server
  PageContextWithServerRouting as PageContext,
  // For code loaded in client only
  PageContextClientWithServerRouting as PageContextClient,
  // For code loaded in server only
  PageContextServer
} from 'vike/types'

Lifecycle

The pageContext object is tied to the rendering process of a single page: whenever a new page is rendered, a new pageContext object is created. (If the current page is re-rendered, a new pageContext object is created as well.)

The lifecycle of the pageContext object is straightforward on the client and server. But for pre-rendered pages it can be surprising (some pageContext properties may seem "outdated").

Server

On the server-side, a new pageContext object is created whenever a page is rendered to HTML. The pageContext object is discarded after the HTML is sent to the client. (But pageContext properties sent to the client via passToClient are preserved on the client-side.)

Client

On the client side, a new pageContext object is created whenever a page is rendered. In other words:

If data is fetched on the server-side, then some pageContext properties are fetched from the server, see pageContext.json requests.

On the client-side, you can access the pageContext of the previous render by using pageContext.previousPageContext.

Pre-rendering

Upon pre-rendering a page, the pageContext object used for pre-rendering the page to HTML is preserved and saved twice at:

  • dist/client/${urlOfThePage}/index.pageContext.json (see pageContext.json requests)
  • dist/client/${urlOfThePage}/index.html (see <script id="vike_pageContext">)

If you inspect dist/client/, you'll find a index.pageContext.json file for each pre-rendered page.

Since a pre-rendered page is built ahead of time, its server-side pageContext may be outdated by the time a user visits the page.

Problem: pageContext is outdated upon hydration

Upon hydration, some pageContext properties may appear incorrect or "outdated".

For example, if a user visits /products?filter=computer then pageContext.urlParsed.search is empty and ?filter=computer is missing.

That's because Vike uses the server-side pageContext.urlOriginal that was used to pre-render the URL /products which didn't have ?filter=computer.

In theory, Vike could update pageContext.urlParsed on the client-side to include ?filter=computer, but this would cause a hydration mismatch. That's why, upon hydration, Vike intentionally keeps the client- and server-side pageContext aligned.

Workarounds

To workaround the issue you can either:

  • Re-render the page after hydration, by using reload() with onHydrationEnd().
    // pages/products/+onHydrationEnd.js
     
    import { reload } from 'vike/client/router'
     
    export async function onHydrationEnd() {
      if (window.location.href.includes('?filter')) await reload()
    }
  • Or, if the number of ?filter= values isn't too large, you can use onBeforePrerenderStart() to pre-render all filter values: /products?filter=computer, /products?filter=car, ...

    You can then also use parameterized route /products/@filter instead of ?filter= if you prefer.

See also