data()
hook
Environment: server (configurable).
The data()
hook is used for fetching data. It's usually used together with useData()
.
See Guides > Data Fetching for an introduction about
data()
and fetching data in general.
For a lower-level hook with more control, see API >
onBeforeRender()
hook.
ORM/SQL
By default (it's configurable) the data()
hook always runs the server-side, which means 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('https://star-wars.brillout.com/api/films.json')
let { movies } = await response.json()
/* Or with an ORM:
let movies = await Movie.findAll() */
/* Or with SQL:
let movies = await sql.run('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 = movies.map(({ title, release_date }) => ({ title, release_date }))
return {
movies
}
}
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(`https://star-wars.brillout.com/api/films/${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 Integration > Authentication > Login flow.
Alternatively, you can use throw render()
and throw redirect()
inside of a guard()
hook, see Integration > Authentication.
Environment
By default, the data()
hook always runs on the server-side.
By using .client.js
(or .shared.js
),
you can tell Vike to only (or also) load and execute data()
on the client-side.
Alternatively, instead of using
.shared.js
and.client.js
, you can modify themeta.env
setting of thedata()
hook in a global fashion, removing the need to add.shared.js
/.client.js
to each of your+data.js
files.
Client-side only
By using .client.js
,
you can tell Vike to load and execute data()
only on the client-side (never on the server-side).
For pre-rendered pages (SSG), if you want to fetch dynamic data provided by some server (Java/PHP/JavaScript/...), then make sure to always call
data()
on the client-side (i.e.+data.client.js
).Note that, for pre-rendered pages, the data that is fetched from the "server-side" is the static JSON file
dist/client/some-page/index.pageContext.json
that was generated at build-time upon pre-rendering the page.
Server- and client-side
By using .shared.js
,
you can tell Vike to load and execute data()
also on the client-side:
data()
runs on the server-side for the first page the user visits.data()
runs on the client-side for subsequent page navigations.
In general, we recommend running
data()
only on the server-side because it's easier to write code that runs in only one environment.That said, if you want to minimize requests made to your server, then it can make sense to run
data()
also on the client-side. See:
Examples
React + JavaScript:
- /boilerplates/boilerplate-react/pages/star-wars/index/+data.js
- /boilerplates/boilerplate-react/pages/star-wars/index/+Page.jsx (
useData()
usage) - /boilerplates/boilerplate-react/pages/star-wars/@id/+data.js
- /boilerplates/boilerplate-react/pages/star-wars/@id/+Page.jsx (
useData()
usage)
React + TypeScript:
- /boilerplates/boilerplate-react-ts/pages/star-wars/index/+data.ts
- /boilerplates/boilerplate-react-ts/pages/star-wars/index/+Page.tsx (
useData()
usage) - /boilerplates/boilerplate-react-ts/pages/star-wars/@id/+data.ts
- /boilerplates/boilerplate-react-ts/pages/star-wars/@id/+Page.tsx (
useData()
usage)
Vue + JavaScript:
- /boilerplates/boilerplate-vue/pages/star-wars/index/+data.js
- /boilerplates/boilerplate-vue/pages/star-wars/index/+Page.vue (
useData()
usage) - /boilerplates/boilerplate-vue/pages/star-wars/@id/+data.js
- /boilerplates/boilerplate-vue/pages/star-wars/@id/+Page.vue (
useData()
usage)
Vue + TypeScript:
- /boilerplates/boilerplate-vue-ts/pages/star-wars/index/+data.ts
- /boilerplates/boilerplate-vue-ts/pages/star-wars/index/+Page.vue (
useData()
usage) - /boilerplates/boilerplate-vue-ts/pages/star-wars/@id/+data.ts
- /boilerplates/boilerplate-vue-ts/pages/star-wars/@id/+Page.vue (
useData()
usage)
TypeScript
See API > useData()
> TypeScript.
Without vike-{react,vue,solid}
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 implementuseData()
yourself as shown at API >useData()
> Withoutvike-{react,vue,solid}
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 pageContext.data
const { Page, data } = pageContext
const pageHtml = await renderToHtml(
// Pass pageContext.data to the <Page> component
createElement(Page, data)
)
/* JSX:
const pageHtml = await renderToHtml(<Page {...data} />)
*/
return escapeInject`<html>
<div id='view-root'>
${dangerouslySkipEscape(pageHtml)}
</div>
</html>`
}
// /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 pageContext.data to the <Page> component
createElement(Page, data),
document.getElementById('view-root')
)
/* JSX:
await hydrateDom(<Page {...data} />, document.getElementById('view-root'))
*/
}
// /pages/movies/+Page.js
// Environment: browser and server
export { Page }
// In the onRenderHtml() and onRenderClient() hooks above,
// pageContext.data is passed to the <Page> component.
function Page(data) {
const { movies } = data
// ...
}