Skip to content
Twilight React Playground
ثيم رائدaren

head

functionAdvancedlive demo

Converts a HeadDescriptor (title, description, Open Graph, canonical, JSON-LD) into the meta, links, styles and scripts arrays a TanStack route returns.

import { head } from '@salla.sa/twilight-theme-engine/tanstack';

In plain words

The engine describes page tags as one plain object, a HeadDescriptor: { title, description, canonical, openGraph, twitter, jsonLd, … }. TanStack Router wants lists instead: a meta list, a links list, a scripts list.

head(descriptor) does that conversion. withHead() calls it for you, so use head directly when you build a descriptor yourself, in a route that has no route module.

Signature

function head(descriptor: HeadDescriptor): {
  meta: Record<string, unknown>[];
  links: Record<string, unknown>[];
  styles: Record<string, unknown>[];
  scripts: Record<string, unknown>[];
}

Try it live

A HeadDescriptor converted to the meta, links, styles and scripts arrays a TanStack route returns from head.Try this: turn on the Open Graph image: one image becomes two meta entries, og:image and og:image:width. The title is a meta entry too.
Storefront canvas · ar · RTL
head(descriptor): {…} 4 keys
meta: Array(2)
0: {…} 1 keys
title: "Summer offers"
1: {…} 2 keys
name: "keywords"
content: "offers, sale"
links: Array(1)
0: {…} 2 keys
rel: "canonical"
href: "https://example.com/ar/offers"
styles: []
scripts: []
Controls
canonical
alternateLanguages
openGraph.images
jsonLd
What a theme writes
import { createFileRoute } from '@tanstack/react-router';
import { head } from '@salla.sa/twilight-theme-engine/tanstack';

export const Route = createFileRoute('/{-$locale}/offers-landing')({
  head: () =>
    head({
      title: 'Summer offers',
      keywords: ['offers', 'sale'],
      canonical: 'https://example.com/ar/offers',
    }),
  component: OffersLanding,
});

Example

app/routes/gift-cards.tsx
// Declared in app/routes.ts: route('/gift-cards', 'gift-cards.tsx')
import { createFileRoute } from '@tanstack/react-router';
import { head } from '@salla.sa/twilight-theme-engine/tanstack';

export const Route = createFileRoute('/{-$locale}/gift-cards')({
  head: () =>
    head({
      title: 'Gift cards',
      description: 'Send a gift card by email in a minute.',
      openGraph: { type: 'website', title: 'Gift cards' },
      robots: 'index, follow',
    }),
  component: GiftCards,
});

function GiftCards() {
  return <h1>Gift cards</h1>;
}

How it behaves

  • title becomes a { title } entry in meta, which TanStack renders as <title>. description, keywords (an array is joined with ", ") and robots become named meta tags.

  • openGraph becomes og:* property tags: each image adds og:image, plus og:image:width, og:image:height and og:image:alt when given, and publishedTime / modifiedTime become article:published_time / article:modified_time. twitter becomes twitter:* name tags.

  • canonical becomes <link rel="canonical">, and each alternateLanguages item a <link rel="alternate"> with hrefLang.

  • jsonLd becomes one <script type="application/ld+json"> holding JSON.stringify(jsonLd). importMap entries become type="importmap" scripts, placed before your scripts. meta, links, styles and scripts are appended as given.

  • createRouter() installs this function as the engine's head adapter, so route modules and resolveHead() produce this shape.

Gotchas

  • JSON-LD is not escaped for HTML. In the server render TanStack writes a script's body as raw HTML, so a string in jsonLd containing </script> (in a product description, say) ends the element early. When the data is not yours, add the script through scripts with < escaped: { type: 'application/ld+json', children: JSON.stringify(data).replace(/</g, '\\u003c') }.

  • links items are passed through untouched, so a hreflang key keeps that spelling and React warns in development that the prop should be hrefLang. Put language links in alternateLanguages, which writes hrefLang.

Related

Source and docs