data() hook

The data() hook is used for fetching data. See Guides > Data Fetching for an introduction about data() and about data fetching in general.

It's commonly used together with useData().

For a lower-level hook with more control, see API > onBeforeRender() hook.


React + JavaScript:

React + TypeScript:

Vue + JavaScript:

Vue + TypeScript:


pageContext provides contextual information.

// /pages/product/+data.js

export async function data(pageContext) {
  // pageContext properties commonly used in data()

pageContext.user is a custom pageContext property, see:


See API > useData() > TypeScript.

Error handling

If an error is thrown by data(), then Vike renders your error page and there is usually nothing for you to do (beyond defining an error page /pages/_error/+Page.js).

But if you want a more precise error handling (such as showing an insightful error message to the user instead of some generic "Something went wrong"), then use throw render() and/or throw redirect().

// /pages/movies/+data.js
// Environment: server

export { data }

import fetch from 'node-fetch'
import { render, redirect } from 'vike/abort'

async function data(pageContext) {
  const { id } = pageContext.routeParams
  const response = await fetch(`${id}.json`)

  if (response.status === 404) {
    // Tell the user what went wrong
    throw render(404, `Movie with ID ${id} doesn't exist.`)
    /* Or redirect the user:
    throw redirect('/movie/add') */
    /* Or render the movie submission form while preserving the URL:
    throw render('/movie/add') */

  // ...

throw render('/movie/add') is a technique explained at Guides > Authentication > Login flow.

Alternatively, you can use throw render() and throw redirect() inside of a guard() hook, see Guides > Authentication.


By default, the data() hook always runs the server-side. Thus you can directly use ORM/SQL database queries:

// /pages/movies/+data.js

export { data }

// Note how we use `node-fetch`; this file is only run on the server-side, thus we don't need
// to use an isomorphic (aka universal) implementation such as `cross-fetch`.
import fetch from 'node-fetch'

async function data(pageContext) {
  const response = await fetch('')
  let { movies } = await response.json()
  /* Or with an ORM:
  let movies = await Movie.findAll() */
  /* Or with SQL:
  let movies = await'SELECT * FROM movies;') */

  // `movies` is serialized and passed to the client. Therefore, we pick only the
  // data the client needs in order to minimize what is sent over the network.
  movies ={ title, release_date }) => ({ title, release_date }))

  return {


By default, data() always runs on the server-side.

But, by using meta, you can tell Vike to run data() also on the client-side: Vike calls data() on the server-side for the first page the user visits and then, for subsequent page navigations, Vike calls data() on the client-side.

See API > meta > Example: modify data() env.

In general, we recommend running data() only on the server-side because it's usually easier to write code that only runs on the server-side. That said, if you want to minimize requests made to your server, then it makes sense to run data() on the client-side. Keep in mind that you'll have to make sure that your data() hook is isomorphic: it needs to be able to run on the server-side as well as on the client-side.

You can also set data()'s meta config env to { client: true, server: false}: Vike will then always call data() on the client-side and never on the server-side.

The data() hook sets the value of (While useData() exposes the value of

This means that, beyond using useData(), you can also access the data in other hooks over

Without useData()

The data() hook is usually used together with the component hook useData() which is provided by the UI framework Vike extension (vike-react/vike-vue/vike-solid).

In general, for improved DX, we recommend using data() together with a useData() implementation.

In case you don't use vike-react/vike-vue/vike-solid, you can implement useData() yourself as shown at API > useData() > Without Vike extension

That said, you can also use data() without useData():

// /renderer/+onRenderHtml.js
// Environment: server

export { onRenderHtml }

import { escapeInject, dangerouslySkipEscape } from 'vike/server'
import { renderToHtml, createElement } from 'some-ui-framework'

async function onRenderHtml(pageContext) {
  // The data is available at
  const { Page, data } = pageContext
  const pageHtml = await renderToHtml(
    // Pass to the <Page> component
    createElement(Page, data)
  /* JSX:
  const pageHtml = await renderToHtml(<Page {} />)

  return escapeInject`<html>
    <div id='view-root'>
// /renderer/+onRenderClient.js
// Environment: browser

export { onRenderClient }

import { hydrateDom, createElement } from 'some-ui-framework'

async function onRenderClient(pageContext) {
  const { Page, data } = pageContext
  await hydrateDom(
    // Pass to the <Page> component
    createElement(Page, data),
  /* JSX:
  await hydrateDom(<Page {} />, document.getElementById('view-root'))
// /pages/movies/+Page.js
// Environment: browser and server

export { Page }

// In the onRenderHtml() and onRenderClient() hooks above,
// is passed to the <Page> component.
function Page(data) {
  const { movies } = data
  // ...

See also