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

head (Next.js)

functionAdvancedlive demo

Converts a HeadDescriptor into a Next.js App Router Metadata object; part of the engine's experimental, unfinished Next.js adapter.

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

In plain words

A Next.js page describes its <head> by exporting metadata, or a generateMetadata() function that returns one. head(descriptor) from /nextjs converts the engine's HeadDescriptor into that object.

Salla themes run on TanStack Start. The /nextjs subpath is a start on Next.js support, and most of it is not built yet (see the notes).

Signature

function head(descriptor: HeadDescriptor): {
  title?: string;
  description?: string;
  keywords?: string | string[];
  robots?: string;
  openGraph?: {
    siteName?; type?; title?; description?; url?; locale?;
    images?: Array<{ url: string; width?: number; height?: number; alt?: string }>;
    publishedTime?; modifiedTime?;
  };
  twitter?: { card; title?; description?; images?: string[]; site?; creator? };
  alternates: { canonical: string | undefined; languages: Record<string, string> | undefined };
}

Try it live

The same HeadDescriptor through both adapters: a Next.js Metadata object on the left, TanStack head arrays on the right.Try this: turn on jsonLd and meta: they appear on the right only. The Next.js adapter has nowhere to put them and drops them.
Storefront canvas · en · LTR
/nextjs head(): {…} 6 keys
title: "New arrivals"
description: "Fresh arrivals every week"
keywords: undefined
robots: undefined
openGraph: {…} 9 keys
siteName: undefined
type: undefined
title: "New arrivals"
description: "Fresh arrivals every week"
url: undefined
locale: undefined
images: Array(1)
0: {…} 1 keys
publishedTime: undefined
modifiedTime: undefined
alternates: {…} 2 keys
canonical: "https://example.com/ar/new"
languages: {…} 1 keys
en: "https://example.com/en/new"
/tanstack head(): {…} 4 keys
meta: Array(3)
0: {…} 1 keys
title: "New arrivals"
1: {…} 2 keys
name: "description"
content: "Fresh arrivals every week"
2: {…} 2 keys
property: "og:image"
content: "https://example.com/og.png"
links: Array(2)
0: {…} 2 keys
rel: "canonical"
href: "https://example.com/ar/new"
1: {…} 3 keys
rel: "alternate"
hrefLang: "en"
href: "https://example.com/en/new"
styles: []
scripts: []
Controls
twitter: { site: '@example' }
jsonLd
meta (theme-color)
What a theme writes
// app/new/page.tsx (Next.js App Router)
import { head } from '@salla.sa/twilight-theme-engine/nextjs';

export const metadata = head({
  title: 'New arrivals',
  description: 'Fresh arrivals every week',
  canonical: 'https://example.com/ar/new',
  alternateLanguages: [{ hreflang: 'en', href: 'https://example.com/en/new' }],
  openGraph: { images: 'https://example.com/og.png' },
});

Example

app/[locale]/gift-cards/page.tsx (Next.js)
import { head } from '@salla.sa/twilight-theme-engine/nextjs';

export const metadata = head({
  title: 'Gift cards',
  description: 'Send a gift card by email in a minute.',
  alternateLanguages: [{ hreflang: 'en', href: '/en/gift-cards' }],
});

export default function GiftCardsPage() {
  return <h1>Gift cards</h1>;
}

How it behaves

  • title, description, keywords and robots are copied. openGraph and twitter fall back to the top-level title and description, and twitter.card defaults to summary_large_image. canonical and alternateLanguages become alternates.canonical and alternates.languages ({ en: href }).

  • Status of /nextjs: head, attrs, mergeAttrs and NextJsLinkAdapter are exported. There is no Next.js router, middleware or root route (src/nextjs/router.tsx is commented out), TwilightProvider has no Next.js page-class sync, and the route modules' loaders and head functions read a twilight context that only the TanStack wiring fills.

Gotchas

  • Next.js metadata has no place for jsonLd, meta, links, styles, scripts or importMap, nor for openGraph.alternateLocale and twitter.url. They are dropped without a warning; render JSON-LD and scripts in the page instead.

  • The JSDoc of src/nextjs/index.ts and docs/03-routing-system.md import createRouter from /nextjs and call head(Product.head(…)) with one argument. /nextjs exports no createRouter, and a route module's head takes (ctx, data).

Related

Source and docs