// pages/movies/+data.jsxexport { data }import { useConfig } from 'vike-react/useConfig' // or vike-vue / vike-solidasync function data(pageContext) { const config = useConfig() const response = await fetch('https://star-wars.brillout.com/api/films.json') let { movies } = await response.json() config({ title: `${movies.length} Star Wars Movies` Head: <meta name="description" content={`List of all ${movies.length} Star Wars movies.`} /> }) return { movies }}
Make sure to call useConfig() before any await:
export async function data(pageContext) { const response = await fetch('https://star-wars.brillout.com/api/films.json') // ❌ Doesn't work: useConfig() has to be called before `await fetch()` const config = useConfig()}
Inside components
import { useConfig } from 'vike-react/useConfig' // or vike-vue / vike-solidfunction Movies() { // Fetch data const data = useSomeDataFetchingTool() // Set <head> tags const config = useConfig() config({ title: data.title, Head: <meta property="og:image" content={data.previewImage} /> }) // Render UI return ( <ul> { data.movies.map(({ title }) => ( <li>{title}</li> )) } </ul> )}
<Config> & <Head>
Inside components, you can also use <Config> and <Head>:
import { Config } from 'vike-react/Config' // or vike-vue / vike-solidimport { Head } from 'vike-react/Head' // or vike-vue / vike-solidfunction Movies() { // Fetch data const data = useSomeDataFetchingTool() // Render UI and <head> tags return ( <Config title={data.title} /> <Head> <meta property="og:image" content={data.previewImage} /> </Head> <ul>{ data.movies.map(({ title }) => ( <li>{title}</li> )) }</ul> )}
Both achieve the same:
<Head><something/></Head>
<Config Head={<something/>}/>
There in't a <Title> component because it's expected to be a string: e.g. <Title><span>hello</span></Title> doesn't make sense.
Example: React Query
The useConfig() hook is particularly relevant when using integrations that enables components to fetch data
such as vike-react-query and vike-react-apollo. Your components can then use the data they fetch to set <head> tags.
import { useConfig } from 'vike-react/useConfig'import { useSuspenseQuery } from '@tanstack/react-query'function Movies() { // Fetch data const query = useSuspenseQuery({ queryKey: ['movies'], queryFn: () => fetch('https://star-wars.brillout.com/api/films.json') }) const movies = query.data // Set <head> tags const config = useConfig() config({ title: `${movies.length} Star Wars Movies` // <title> Head: <meta name="description" content={`List of all ${movies.length} Star Wars movies.`} /> }) // Render UI return ( <ul>{ movies.map(({ title }) => ( <li>{title}</li> )) }</ul> )}
Or with <Config> and <Head>:
import { Config } from 'vike-react/Config'import { Head } from 'vike-react/Head'import { useSuspenseQuery } from '@tanstack/react-query'function Movies() { // Fetch data const query = useSuspenseQuery({ queryKey: ['movies'], queryFn: () => fetch('https://star-wars.brillout.com/api/films.json') }) const movies = query.data // Render UI and <head> tags return ( <Config title={`${movies.length} Star Wars Movies`} /> <Head> <meta name="description" content={`List of all ${movies.length} Star Wars movies.`} /> </Head> <ul>{ movies.map(({ title }) => ( <li>{title}</li> )) }</ul> )}
Serialization Error
You may get a serialization error while using useConfig().
Cannot serialize config set by useConfig()
The most common reason is because you're using useConfig() in a +data.js to modify a non-serializable config.
// pages/some-page/+data.js// pages/some-page/+data.shared.jsimport { useConfig } from 'vike-react/useConfig' // or vike-vue / vike-solidexport async function data (pageContext) { const config = useConfig() // ...}
The issue here is that defining the data() hook inside a +data.js file means that it's always called on the server-side, even upon client-side page navigation. Consequently, upon client-side page navigation, the data fetched on the server-side needs to be serialized and passed to the client-side.