Head

Kind: cumulative.
Environment: server.
Implemented by: vike-react/vike-vue/vike-solid.

You need vike-react/vike-vue/vike-solid to be able to use this setting.

The +Head setting allows you to add <head> tags to your pages.

See Guides > <head> meta tags for a general introduction and for other ways to add <head> tags.

As explained below, it's only used when rendering the HTML of the first page the user visits. Consequently, it usually cannot be used for setting the <title> tag.

// /pages/+Head.jsx
// /pages/+Head.vue
// Environment: server
 
import previewImage from './previewImage.jpg'
import favicon from './favicon.png'
import iconMobile from './iconMobile.png'
 
export function Head() {
  return <>
    {/* Adding a script tag */}
    <script type="text/javascript" src="https://example.com/some-script.js"></script>
 
    {/* Icon shown in the browser tab (aka favicon) */
    <link rel="icon" href={favicon}>
 
    {/* Icon shown on mobile homescreens (PWA) */
    <link rel="apple-touch-icon" href={iconMobile}>
 
    {/* Image shown when sharing on social sites (Twitter, WhatsApp, ...) */}
    <meta property="og:image" content={previewImage}>
  </>
}

Using data

You can set <head> tags based on fetched data (or pageContext) by using:

<Head> inside components

You can use the <Head> component inside your components:

// Product.jsx
 
import { Head } from 'vike-react/Head' // or vike-{vue,solid}
 
function Product({ data }) {
  return <>
    <Head>
      {/* Image shown when sharing on social sites (Twitter, WhatsApp, ...) */}
      <meta property="og:image" content={data.product.image}>
    </Head>
    <h1>{data.product.name}</h1>
    <p>{data.product.description}</p>
  </>
}

Here data comes from the props passed from the parent component, but it can also come from a data-fetching component hook such as const { data } = useSuspenseQuery() when using vike-react-query.

It also works inside .vue files (when using vike-vue).

useConfig() inside +data

You can use the useConfig() universal hook inside your +data hook.

// pages/product/@id/+data.jsx
 
import { useConfig } from 'vike-react/useConfig' // or vike-{vue,solid}
 
export async function data() {
  const config = useConfig()
  const data = await fetchSomeData()
  config({
    Head: <>
      {/* Image shown when sharing on social sites (Twitter, WhatsApp, ...) */}
      <meta property="og:image" content={data.product.image}>
    </>
  })
}

For Vue you can use the following:

import { h } from 'vue'
config({
  Head: h('meta', {
    property: 'og:image',
    content: data.product.image
  })
})

useData()/usePageContext() inside +Head

The value defined by +Head is a component and thus you can use useData() and usePageContext() as usual:

// pages/product/@id/+Head.js
 
import { useData } from 'vike-react/useData' // or vike-{vue,solid}
 
export function Head() {
  const data = useData()
  return <>
    {/* Image shown when sharing on social sites (Twitter, WhatsApp, ...) */}
    <meta property="og:image" content={data.product.image}>
  </>
}

Only HTML

Only renders for the first page's HTML

The <Head> component is only used when rendering the HTML of the first page the user visits: the tags set by <Head> aren't updated upon client-side page navigation.

Limitation

The most notable limitation is that the +Head setting cannot be used to set the <title> value, because the title isn't updated when navigating to a page with a different tilte.

See example below for a more detailed explanation.

Instead use the +title setting.

For use cases where the +Head setting cannot be used, Vike offers tailored settings that update upon client-side navigation.

A small limitation

This may seem like a major limitation but it actually isn't: you can use the +Head setting for the vast majority of use cases.

You can use +Head for setting <head> tags are read by HTML crawlers:

  • Tags for social sites (Twitter, Instagram, ...) such as <meta property="og:image"> (the preview image upon URL sharing).

    Social site bots navigate your website only by using HTML requests: they don't execute client-side JavaScript and don't do client-side navigation.

  • Tags for SEO such as <meta name="description">.

    While Google can do client-side navigation, it still discovers <head> tags by using its HTML crawler.

You can use +Head for setting <head> tags that are global (they have the same value for all pages):

  • Favicon.

    Assuming all your pages share the same favicon (<link rel="icon">), there isn't any need to update the favicon upon client-side navigation.

  • PWA settings.

    PWA settings are global and there isn't any need to update them upon client-side navigation.

  • <script>

    Assuming the script applies to all your pages.

Example

The following example showcases that using +Head for setting <title> doesn't work, while it does work for setting <meta name="description">.

// /pages/index/+Head.jsx
// Environment: server
 
function Head() {
  return <>
    <title>AwesomeRockets</title>
    <meta name="description" content="The rocket company.">
  </>
}
// /pages/about/+Head.jsx
// Environment: server
 
function Head() {
  return <>
    <title>About us</title>
    <meta name="description" content="We deliver payload to space.">
  </>
}

If the first URL the user visits is / then the rendered HTML is:

<!DOCTYPE html>
<html>
  <head>
    <title>AwesomeRockets</title>
    <meta name="description" content="The rocket company.">
  </head>
</html>

If the user then clicks on a link <a href="/about">About us</a>, then Vike does client-side navigation and the page's title isn't updated: the browser sill shows Welcome even though the URL is now /about. That's because the HTML isn't used upon client-side navigation (DOM manipulations are made instead) while +Head is only used when generating HTML.

The <Head> component is only loaded on the server-side and only used when rendering HTML of the first page by design.

This isn't an issue for <meta name="description"> tag because it's meant for search engines bots which crawl your website using HTML.

Cumulative

The +Head setting is cumulative. For example:

// /pages/+Head.js
// Environment: server
 
import favicon from './favicon.png'
 
export const Head = () =>
  // This favicon applies to all pages
  <link rel="icon" href={favicon}>
// /pages/about-us/+Head.js
// Environment: server
 
import previewImage from './previewImage.jpg'
 
export const Head = () =>
  // Both the favicon above and this tag applies to /pages/about-us/+Page.js
  <meta property="og:image" content={previewImage}>

To apply different +Head settings to different pages:

// /pages/(marketing)/+Head.js
// Environment: server
 
import favicon from './favicon.png'
 
// Applies to all marketing pages
export const Head = () => <link rel="icon" href={favicon}>
// /pages/admin/+Head.js
// Environment: server
 
import favicon from './favicon.png'
 
// Applies to all admin pages
export const Head = () => <link rel="icon" href={favicon}>

See: API > Config > Inheritance

If you have a need for overriding, then add a comment at: #1692 - Add override and default options for cumulative configs

How to inject raw HTML?

You can inject any arbitrary HTML string to the page's <head>. Examples using:

⚠️

Be aware and cautious about the security risk called XSS injections.

React

You can use React's dangerouslySetInnerHTML to add raw HTML, for example:

import React from 'react'
import { Head } from 'vike-react/Head'
 
function Image({ src, author }) {
  return (
    <>
      <img src={src} />
      <Head>
        <script
          type="application/ld+json"
          dangerouslySetInnerHTML={{
            __html: JSON.stringify({
              '@context': 'https://schema.org/',
              contentUrl: { src },
              creator: {
                '@type': 'Person',
                name: author
              }
            })
          }}
        ></script>
      </Head>
    </>
  )
}

Vue

You can use innerHTML to add raw HTML, for example:

<template>
  <img :src v-bind="otherAttrs" />
</template>
 
<script setup>
import { useAttrs, h } from 'vue'
import { useConfig } from 'vike-vue/useConfig'
 
const { src, author, ...otherAttrs } = useAttrs()
 
const config = useConfig()
config({
  Head: h('script', {
    type: 'application/ld+json',
    innerHTML: JSON.stringify({
      '@context': 'https://schema.org/',
      contentUrl: { src },
      creator: {
        '@type': 'Person',
        name: author
      }
    })
  })
})
</script>

Solid

You can use innerHTML to add raw HTML, for example:

import { Head } from "vike-solid/Head"
 
function Image({ src, author }) {
  return (
    <>
      <img src={src} />
      <Head>
        <script
          type="application/ld+json"
          innerHTML={JSON.stringify({
            "@context": "https://schema.org/",
            contentUrl: { src },
            creator: {
              "@type": "Person",
              name: author
            }
          })}
        ></script>
      </Head>
    </>
  )
}

See also