GioJSdocs
On this page

metadata

Describe the document head of a page or layout - title, description, Open Graph, canonical URL and more - as a static object.

app/layout.tsx
import type { Metadata } from '@gio.js/core';

export const metadata: Metadata = {
  metadataBase: 'https://acme.example',
  title: { default: 'Acme', template: '%s | Acme' },
  description: 'The Acme store.',
  openGraph: { siteName: 'Acme', images: '/og.png' },
};
app/pricing/page.tsx
import type { Metadata } from '@gio.js/core';

export const metadata: Metadata = {
  title: 'Pricing',                                  // <title>Pricing | Acme</title>
  alternates: { canonical: '/pricing' },             // https://acme.example/pricing
};

metadata is read from layout.tsx, page.tsx, not-found.tsx and error.tsx. For values that depend on the request or on data, export generateMetadata instead (or as well).

Reference

Fields

Every field is optional. null removes a value a layout above set.

FieldTypeDefaultDescription
titlestring | { default?, template?, absolute? }-<title>. A string gets the nearest template set above this segment (%s is replaced). default is the title for segments below that set none, template applies to titles set below (null drops it), and absolute ignores every template.
descriptionstring-<meta name="description">, and the default og:description.
metadataBasestring | URL-The base for relative URLs in openGraph, twitter, alternates and authors. Its path is kept. Falls back to GIO_SITE_URL; only http and https bases are used.
keywordsstring | string[]-<meta name="keywords">, a list joined with , .
authors{ name?, url? } | Array-<meta name="author"> per name, <link rel="author"> per URL.
robotsstring | RobotsMetadata-<meta name="robots">: a string as is, or index, follow, noarchive, nosnippet, noimageindex, nocache, max-snippet, max-image-preview, max-video-preview. googleBot renders <meta name="googlebot">.
alternates{ canonical?, languages? }-<link rel="canonical">, and one <link rel="alternate" hreflang> per entry of languages (a locale or x-default to a URL).
openGraphOpenGraphMetadata-og:title and og:description (default: the resolved title and description), og:url, og:site_name, og:locale, og:type, and per image og:image with :type, :width, :height, :alt.
twitterTwitterMetadata-twitter:card (summary, summary_large_image, app, player), :site, :creator, :title, :description, :image with :alt.
iconsstring | URL | IconDescriptor | Array | { icon?, apple?, shortcut? }-<link rel="icon">, apple-touch-icon or shortcut icon, with type, sizes, media, color; a descriptor's rel overrides the group's.
manifeststring | URL-<link rel="manifest"> (app/manifest.ts serves /manifest.webmanifest).
themeColorstring | { color, media? } | Array-<meta name="theme-color">, one per entry, with media for light and dark.
otherRecord<string, string | number | Array>-Extra <meta name content> tags, one per value (site verification codes, ...).

How segments merge

  • The root layout comes first, then each nested layout down to the page. The merge is shallow: the deepest segment that sets a top-level field wins it whole. A page that sets openGraph replaces the layout's entire openGraph object - share common values through a variable.
  • undefined (a field left out) inherits; null removes the inherited value.
  • A layout's title.template never applies to the layout's own title, only to titles set below it.
  • In one module, what generateMetadata returns is merged over the static metadata field by field.

Behavior

  • The tags render into the <head> on the server, streamed pages included, and are replaced on every client-side navigation. Every value is rendered by React, so it is HTML-escaped.
  • A metadata title supersedes a <title> a component renders: the server HTML keeps only the metadata one, and development mode warns.
  • A URL that should be absolute but stays relative (no metadataBase and no GIO_SITE_URL) logs a warning once per route. The request's Host header is never used as a base: on a cached page a client-chosen host would reach every visitor.
  • icons and manifest URLs are not resolved against metadataBase: the browser resolves them against the page.
  • The static metadata export is plain data: a value that is not an object is ignored.

Examples

Keep a page out of search results

app/account/page.tsx
import type { Metadata } from '@gio.js/core';

export const metadata: Metadata = {
  title: 'Your account',
  robots: { index: false, follow: true },   // <meta name="robots" content="noindex, follow">
};

export default function Account() {
  return <h1>Your account</h1>;
}

Alternate languages

app/pricing/page.tsx
import type { Metadata } from '@gio.js/core';

export const metadata: Metadata = {
  title: 'Pricing',
  alternates: {
    canonical: '/pricing',
    languages: { de: '/de/pricing', 'x-default': '/pricing' },
  },
};

export default function Pricing() {
  return <h1>Pricing</h1>;
}

A not-found page that is not indexed

app/not-found.tsx
import type { Metadata } from '@gio.js/core';

export const metadata: Metadata = { title: 'Not found', robots: 'noindex' };

export default function NotFound() {
  return <h1>This page does not exist</h1>;
}

Good to know

  • Hand-written head tags in the root layout other than <title> (charset, viewport, a description) are not deduplicated: declare each tag in one place.
  • There is no viewport export, no file-based icons (app/icon.png) and no generated Open Graph images (opengraph-image.tsx). Put icons in public/ and reference them from icons.
  • For JSON-LD structured data, render <JsonLd> in the page.

Version history

VersionChanges
v0.1.0-beta.8Introduced.