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
everywhere:
- In any UI component (by using
usePageContext()
). - In any Vike hook, e.g.
+data
. - On both the server and client (by using
passToClient
).
It includes:
- Built-in properties, e.g.
pageContext.urlParsed
andpageContext.routeParams
. - Custom properties that you can add, for example
pageContext.user
.
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.
Built-in
Built-in properties:
-
pageContext.Page
: theexport { Page }
orexport default
of the+Page.js
file. -
pageContext.data
: the value returned by thedata()
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
isundefined
(unless you usepassToClient
).
- When using Client Routing, the value of
-
pageContext.urlPathname
: alias forpageContext.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
: See API >meta
. -
pageContext.isHydration
: Whether the page is rendered to HTML. When using Client Routing, the value istrue
for the first page the user navigates to, andfalse
for any subsequent navigation. (When using Server Routing, the value is alwaystrue
.) (If the page doesn't throw an error then it's equivalent to!pageContext.isClientSideNavigation
, otherwise the error page is rendered and thuspageContext.isHydration
isfalse
whereas!pageContext.isClientSideNavigation
istrue
.) -
pageContext.isClientSide
: Whether the page is being rendered on the client-side, or rendered on the server-side / pre-rendered. It's equivalent to:pageContext.isClientSide !== import.meta.env.SSR
.⚠️
We recommend using Vite's
import.meta.env.SSR
whenever possible instead. Because Vite eliminates any code defined insideimport.meta.env.SSR
blocks from client-side bundles, saving potentially a lot of KBs. This isn't the case withpageContext.isClientSide
.// This import is also removed if it's used only inside import.meta.env.SSR code blocks import someImport from './somewhere' if (import.meta.env.SSR) { // Vite removes this code block from client-side bundles. // ... server-side code ... }
-
pageContext.isPrerendering
: Whether the page is being pre-rendered. The value is alwaysfalse
in development. -
pageContext.isBackwardNavigation
: Whether the user is navigating back in history. The value istrue
when the user clicks on his browser's backward navigation button, or when invokinghistory.back()
. TheisBackwardNavigation
property only works with Client Routing. (The value is alwaysnull
when using Server Routing.) -
pageContext.previousPageContext
: Upon client-side page navigation, you can usepageContext.previousPageContext
to access thepageContext
of the previous page. See Lifecycle. -
pageContext.is404
: If an error occurs, whether the error is a404 Page Not Found
or a500 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 isfalse
for the first page the user visits, andtrue
for any subsequent navigation. (When using Server Routing, the value is alwaysfalse
.) -
pageContext.abortReason
: Set bythrow render()
and used by the error page. -
pageContext.abortStatusCode
: Set bythrow 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.
Custom
You can define custom pageContext
properties. (See TypeScript for how to define their types.)
-
At
renderPage()
:// Your Vike server middleware integration app.get('*', async (req) => { const pageContextInit = { 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.someCustomProp someCustomProp: 'some-value' } const pageContext = await renderPage(pageContextInit) // ... })
Setting
pageContext.user
is a common use case for integrating authentication tools, see Integration > Authentication > Integration. -
At any Vike hook.
// +someHook.js export function someHook(pageContext) { pageContext.someCustomProp = 'some-value' // Add or modify property }
-
At any UI component, by using
usePageContext()
.// Inside a UI component const pageContext = usePageContext() pageContext.someCustomProp = 'some-value' // Add or modify property
FAQ
Can I mutate pageContext
?
Yes, it's a common practice to change/add pageContext
properties at any time.
// Anywhere
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
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'
To extend and/or refine Vike's types 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 (it's `unknown` by default)
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 {}
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 of a page — whenever a page is (re-)rendered, a new pageContext
object is created.
The lifecycle of the pageContext
object is fairly straightforward and predictable 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:
- Upon hydrating (the first page the user visits).
- Upon client-side navigation.
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 usingpageContext.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
(seepageContext.json
requests)dist/client/${urlOfThePage}/index.html
(see<script id="vike_pageContext">
)
If you inspect
dist/client/
, you'll find aindex.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-sidepageContext
aligned.
Workarounds
To workaround the issue you can either:
- Re-render the page after hydration, by using
reload()
withonHydrationEnd()
.// 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 useonBeforePrerenderStart()
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.