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.
| Field | Type | Default | Description |
|---|---|---|---|
title | string | { 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. |
description | string | - | <meta name="description">, and the default og:description. |
metadataBase | string | 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. |
keywords | string | string[] | - | <meta name="keywords">, a list joined with , . |
authors | { name?, url? } | Array | - | <meta name="author"> per name, <link rel="author"> per URL. |
robots | string | 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). |
openGraph | OpenGraphMetadata | - | 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. |
twitter | TwitterMetadata | - | twitter:card (summary, summary_large_image, app, player), :site, :creator, :title, :description, :image with :alt. |
icons | string | 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. |
manifest | string | URL | - | <link rel="manifest"> (app/manifest.ts serves /manifest.webmanifest). |
themeColor | string | { color, media? } | Array | - | <meta name="theme-color">, one per entry, with media for light and dark. |
other | Record<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
openGraphreplaces the layout's entireopenGraphobject - share common values through a variable. undefined(a field left out) inherits;nullremoves the inherited value.- A layout's
title.templatenever applies to the layout's own title, only to titles set below it. - In one module, what
generateMetadatareturns is merged over the staticmetadatafield 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
metadataBaseand noGIO_SITE_URL) logs a warning once per route. The request'sHostheader is never used as a base: on a cached page a client-chosen host would reach every visitor. iconsandmanifestURLs are not resolved againstmetadataBase: the browser resolves them against the page.- The static
metadataexport 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, adescription) are not deduplicated: declare each tag in one place. - There is no
viewportexport, no file-based icons (app/icon.png) and no generated Open Graph images (opengraph-image.tsx). Put icons inpublic/and reference them fromicons. - For JSON-LD structured data, render
<JsonLd>in the page.
Related
- Metadata & SEO - the guide
generateMetadatasitemap.ts,robots.ts,manifest.tslayout.tsx,not-found.tsx
Version history
| Version | Changes |
|---|---|
v0.1.0-beta.8 | Introduced. |