head.js

A head file allows you to configure the <head> tag for a route segment.

app/post/[slug]/head.tsx
async function getPost(slug) {
  const res = await fetch('...');
  return res.json();
}

export default async function Head({ params }) {
  const post = await getPost(params.slug);

  return (
    <>
      <title>{post.title}</title>
    </>
  )
}

Note: head.js does not return a <head> tag, but a fragment.

Only certain tags are allowed to be returned from the Head export:

  • <title>
  • <meta>
  • <link> (only if it has the attribute precedence)
  • <script> (only if it has the attribute async)

The nearest parent head.js file will be the one that applies to the route segment when it's returned. For example, if you had the following files:

  • app/head.js
  • app/dashboard/head.js
  • app/dashboard/billing/head.js

Then accessing the following route segments will have the indicated <head> from the head.js file:

  • / will use app/head.js
  • /dashboard will use app/dashboard/head.js
  • /dashboard/billing will use app/dashboard/billing/head.js

You can import other components that include the allowed <head> tags so you may include defaults with each head.js file that you create for each route.

Unlike next/head, you can use arbitrary React components and data fetching inside of these to create compositions. They can also be async functions.

A few exceptions:

  • You can't use synchronous inline <script> in here. Use next/script if you need this or async scripts. Alternatively place them in <head> in your root layout.
  • You can't use <noscript> in here. If you need it, place it in <head> in your root layout.
  • If you want to manually place <link rel="stylesheet" /> it needs a precedence field which is a new React-specific attribute <link rel="stylesheet" precedence="default" />.(<style> will be supported soon.)

Note: We'll have an improved API for this in the future.

On a technical level, head.tsx is not special. React now just lets you render base, title, meta, link, script (async) and style tags anywhere in the document and it automatically hoists them to the head.

The only thing that's special about head.tsx is that it blocks the page from rendering until its data is done. That ensures that they flush in the <head> instead of streaming in later in the document which could happen if you put it inside a <Suspense> boundary of the page. That's why we need a different API than next/head in the first place.