DATA-WORLD-BLOG

Metadata | Next.js_知識をつけねばならん

👤horomi
要約
項目説明
Config-based Metadatalayout.jspage.jsファイル内で、static metadata objectまたは動的な[generateMetadata function](https://beta.nextjs.org/docs/api-reference/metadata#generatemetadata)をエクスポートして、metadataを定義する方法。
File-based Metadataroute segmentに特別なファイルを静的または動的に追加することでmetadataを定義する方法。
[metadata object](https://beta.nextjs.org/docs/api-reference/metadata#metadata-object)静的metadataを定義するには、layout.jsまたは静的page.jsファイルから[Metadata object](https://beta.nextjs.org/docs/api-reference/metadata#metadata-fields)をエクスポートします。
[generateMetadata function](https://beta.nextjs.org/docs/api-reference/metadata#generatemetadata-function)動的metadataは、現在のルートパラメータ、外部データ、または親セグメントのmetadataなどの動的情報に依存する場合、[Metadata object](https://beta.nextjs.org/docs/api-reference/metadata#metadata-fields)を返すgenerateMetadata関数をエクスポートすることで設定できます。
ParametersgenerateMetadata関数は、以下のパラメータを受け取ります。props - 現在のルートのパラメータを含むオブジェクト。parent - 親ルートセグメントから解決されたmetadataのPromise。
ReturnsgenerateMetadataは、1つ以上のmetadataフィールドを含む[Metadata object](https://beta.nextjs.org/docs/api-reference/metadata#metadata-fields)を返す必要があります。
Default Fieldsrouteがmetadataを定義しない場合でも、常に追加される2つのデフォルトのmetaタグがあります。
Orderingmetadataは、最終的なpage.jsセグメントに最も近いセグメントからルートセグメントまで順に評価されます。
Merging同じルート内の複数のセグメントからエクスポートされたmetadataオブジェクトは、浅くマージされて、ルートの最終的なmetadata出力を形成します。
Overwriting fields複数のセグメントからエクスポートされたmetadataのキーが重複する場合、そのキーは順序に基づいて上書きされます。
Inheriting fields子ルートセグメントで定義されたmetadataは、親ルートセグメントから自動的に継承されます。
メモ
やること
  • 導入中のDocument-head.tsxのコードとの関連を整理する
  • metadataの知識を入れる


公式ブログを読んで感じたこと
  • 変更コードの提示が少なすぎて全体的なつながりをイメージできない
    • →自分の知識が不足している証拠

AIにコードを生成させて感じたこと
  • 結論を出すのがNotionAI
    • 会話ができないので不快なズレを指示するのがストレスになってくる
  • 過程を作るのがChatGPT
    • 会話をしながら曖昧にしてきた知識の復習もできる
    • 最新情報をうまく取り入れられない傾向がある
  • 最新情報を意図的に食わせられるNotionAIをつかってChatGPTて積み重ねた過程を取り込めば正しいコードを生成できるような気もするが….微妙にうまくいかない
    • →普通に自分が知識付けたほうが早そうな気がしてきた

ポジティブに捉える
  • アップデートにビビるが、それは公式ドキュメントを読み込むいい機会だと捉えることにする
調べたことば
  • 用語が分からない:黄色の文字
  • 英単語が分からない:緑色の文字


導入中のmetadata

https://github.com/otoyo/easy-notion-blog/pull/174

import {
  NEXT_PUBLIC_URL,
  NEXT_PUBLIC_SITE_TITLE,
  NEXT_PUBLIC_SITE_DESCRIPTION,
} from '../app/server-constants'

const DocumentHead = ({ title = '', description = '', path = '' }) => {
  const elements = path.split('/')
  const isSlugPath =
    elements[0] === '' && elements[1] === 'blog' && elements.length === 3
  const isRootPath = path === '' || path === '/'

  let ogImageContent = ''
  if (!ogImageContent && NEXT_PUBLIC_URL) {
    if (isSlugPath) {
      ogImageContent = new URL(
        `/api/og-image?slug=${elements[2]}`,
        NEXT_PUBLIC_URL
      ).toString()
    } else {
      ogImageContent = new URL('/DWB-default.jpg', NEXT_PUBLIC_URL).toString()
    }
  }

  return (
    <>
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <meta name="robots" content="max-image-preview:large" />
      <meta charSet="utf-8" />
      <title>
        {title
          ? `${title} - ${NEXT_PUBLIC_SITE_TITLE}`
          : NEXT_PUBLIC_SITE_TITLE}
      </title>
      {NEXT_PUBLIC_URL ? (
        <link
          rel="canonical"
          href={new URL(path, NEXT_PUBLIC_URL).toString()}
        />
      ) : null}
      <meta
        itemProp="name"
        content={
          title
            ? `${title} - ${NEXT_PUBLIC_SITE_TITLE}`
            : NEXT_PUBLIC_SITE_TITLE
        }
      />
      {ogImageContent ? (
        <meta itemProp="image" content={ogImageContent} />
      ) : null}
      <meta
        name="description"
        content={description ? description : NEXT_PUBLIC_SITE_DESCRIPTION}
      />
      {NEXT_PUBLIC_URL ? (
        <meta
          property="og:url"
          content={new URL(path, NEXT_PUBLIC_URL).toString()}
        />
      ) : null}
      <meta
        property="og:title"
        content={title ? title : NEXT_PUBLIC_SITE_TITLE}
      />
      <meta
        property="og:description"
        content={description ? description : NEXT_PUBLIC_SITE_DESCRIPTION}
      />
      <meta property="og:site_name" content={NEXT_PUBLIC_SITE_TITLE} />
      <meta
        property="og:type"
        content={isRootPath ? 'website' : isSlugPath ? 'article' : 'blog'}
      />
      {ogImageContent ? (
        <meta property="og:image" content={ogImageContent} />
      ) : null}
      <meta name="twitter:card" content="summary_large_image" />
      {ogImageContent ? (
        <meta name="twitter:image" content={ogImageContent} />
      ) : null}
      <meta
        name="twitter:title"
        content={
          title
            ? `${title} - ${NEXT_PUBLIC_SITE_TITLE}`
            : NEXT_PUBLIC_SITE_TITLE
        }
      />
      <meta
        name="twitter:description"
        content={description ? description : NEXT_PUBLIC_SITE_DESCRIPTION}
      />
    </>
  )
}

export default DocumentHead


関連

公式ドキュメント

The Metadata API is used to define application metadata that improves SEO and web sharability.

There are two ways to define Metadata:

Config-based Metadata
// either Static metadata
export const metadata = {
  title: '...',
};

// or Dynamic metadata
export async function generateMetadata({ params }) {
  return {
    title: '...',
  };
}

Good to know

  • The metadata object and generateMetadata function exports are only supported in Server Components.
  • You cannot export both the metadata object and generateMetadata function from the same route segment.
metadata object

To define static metadata, export a Metadata object from a layout.js or static page.js file.

layout.tsx|page.tsx

import { Metadata } from 'next';

export const metadata: Metadata = {
  title: '...',
  description: '...',
};

export default function Page() {}

See the Metadata object reference for a complete list of supported fields.

generateMetadata function

Dynamic metadata depends on dynamic information, such as the current route parameters, external data, or metadata in parent segments, can be set by exporting a generateMetadata function that returns a Metadata object.

動的メタデータは、現在のルートパラメーター、外部データ、または親セグメントの「metadata」などの動的情報に依存し、generateMetadata 関数をエクスポートして、Metadata オブジェクトを返すように設定することができます。

app/products/[id]/page.tsx

import { Metadata, ResolvingMetadata } from 'next';

type Props = {
  params: { id: string };
  searchParams: { [key: string]: string | string[] | undefined };
};

export async function generateMetadata(
  { params, searchParams }: Props,
  parent?: ResolvingMetadata,
): Promise<Metadata> {
  // read route params
  const id = params.id;

  // fetch data
  const product = await fetch(`https://.../${id}`).then((res) => res.json());

  // optionally access and extend (rather than replace) parent metadata
  const previousImages = (await parent).openGraph?.images || [];

  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  };
}

export default function Page({ params, searchParams }: Props) {}
Parameters

generateMetadata function accepts the following parameters:

  • props - An object containing the parameters of the current route:

    • params - An object containing the dynamic route parameters object from the root segment down to the segment generateMetadata is called from.

    • searchParams - An object containing the current URL's search params.

  • parent - A promise of the resolved metadata from parent route segments.

Returns

generateMetadata should return a Metadata object containing one or more metadata fields.

Good to know

  • If metadata doesn't depend on runtime information, it should be defined using the static metadata object rather than generateMetadata.
    • runtime:プログラムの実行時、すなわちコンピューターのメモリにプログラムが読み込まれて実行される時間のことです。メタデータが「runtime情報」に依存する場合、その情報は実行時にのみ利用可能であるため、メタデータを定義する際に「generateMetadata」関数を使う必要があります。メタデータが「runtime情報」に依存しない場合、静的な「metadata」オブジェクトを使用して定義することができます。
  • When rendering a route, Next.js will automatically deduplicate fetch requests for the same data across generateMetadata, generateStaticParams, Layouts, Pages, and Server Components. React cache can be used if fetch is unavailable.

    route renderingは、Next.jsでページを作成するときに使用されるプロセスです。これは、ブラウザに表示されるHTMLコンテンツを生成するために、各ページに対して1回実行されます。

    route rendering中には、サーバーとクライアントの両方で実行される2つのフェーズがあります。

    • サーバーサイドレンダリング (SSR) - サーバーでレンダリングされ、HTMLを生成するために必要なデータが含まれたページが作成されます。
    • クライアントサイドレンダリング (CSR) - ダウンロードされたHTMLがクライアントによって処理され、動的なコンポーネントがレンダリングされます。

    route renderingは、generateMetadatagenerateStaticParams、レイアウト、ページ、およびサーバーコンポーネント間で同じデータに対する複数のfetchリクエストを自動的に削除するため、最適化されています。Reactのcacheを使って、fetchが利用できない場合、手動でキャッシュすることができます

  • searchParams are only available in page.js segments.

    searchParams は、現在のURLの search params を含むオブジェクトです。例えば、https://example.com?foo=bar&baz=qux のようなURLの場合、 searchParams オブジェクトは { "foo": "bar", "baz": "qux" } となります。searchParams は、 page.js セグメントでのみ利用可能です。

  • The redirect() and notFound() Next.js methods can also be used inside generateMetadata.

    redirect()メソッドは、クライアント側でのリダイレクトをトリガーするために使用されます。このメソッドを使用すると、サーバーサイドで実行された後、ブラウザーにリダイレクトを処理するために、Next.jsが新しいページにクライアント側で遷移します。リダイレクトが必要な場合に、サーバーサイドで処理されるため、SEOの観点からも優れています。

    リダイレクトは、クライアントがページにアクセスしたときに、別のURLに自動的に転送する機能です。これは、ページのURLを変更した場合や、ユーザーが古いリンクをクリックした場合に便利です。リダイレクトを実行するには、redirect()メソッドを使用します。redirect()メソッドを使うと、サーバー側でリダイレクトを処理し、ブラウザーに新しいページに移動するよう指示します。リダイレクトはSEOの観点からも重要で、ユーザーが正しいページにアクセスできるようにします。

Behavior
Default Fields

There are two default meta tags that are always added even if a route doesn't define metadata:

  • The meta charset tag sets the character encoding for the website.
  • The meta viewport tag sets the viewport width and scale for the website to adjust for different devices.
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
Ordering

Metadata is evaluated in order, starting from the root segment down to the segment closest to the final page.js segment. For example:

メタデータは、ルートセグメントから最終的な page.js セグメントに最も近いセグメントまで順番に評価されます。例えば:

  1. app/layout.tsx (Root Layout)
  2. app/blog/layout.tsx (Nested Blog Layout)
  3. app/blog/[slug]/page.tsx (Blog Page)
Merging

Following the evaluation order, Metadata objects exported from multiple segments in the same route are shallowly merged together to form the final metadata output of a route. Duplicate keys are replaced based on their ordering.

同じルート内の複数のセグメントからエクスポートされたMetadataオブジェクトは、評価順序に従って浅くマージされ、ルートの最終的なメタデータ出力を形成します。重複するキーは、その順序に基づいて置換されます。

  • 同じルート内:同じフォルダにあるページファイルを指します。たとえば、pages/blog/index.jspages/blog/[slug].jsは同じルート内にあると考えられます。metadataオブジェクトやgenerateMetadata関数が定義された場合、それらは、同じルート内のすべてのページに適用されます。
  • セグメント:次の様な Next.js アプリケーションのURLの一部を指します。
    <https://example.com/app/products/[id]>

    上記のURLの場合、 appproducts[id] はそれぞれセグメントになります。[id] は、動的なURLセグメントで、異なる id 値によって異なるページを表します。

    セグメントの定義は、 pages ディレクトリー内のフォルダーやファイル名によって決定されます。たとえば、 pages/app.js ファイルの場合、 app セグメントが定義されます。同様に、 pages/app/products/[id].js ファイルの場合、 appproducts セグメントが定義され、 [id] は動的なURLセグメントとして定義されます。

This means metadata with nested fields such as openGraph and robots that are defined in an earlier segment are overwritten by the last segment to define them.

これは、以前に定義された openGraphrobots のような入れ子になったフィールドを持つメタデータが、最後に定義するセグメントによって 上書き されることを意味します。

Overwriting fields

app/layout.js

export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
};

app/blog/page.js

export const metadata = {
  title: 'Blog',
  openGraph: {
    title: 'Blog',
  },
};

// Output:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />

In the example above:

  • title from app/layout.js is replaced by title in app/blog/page.js.
  • All openGraph fields from app/blog/page.js are replaced in app/blog/page.js because app/blog/page.js sets openGraph metadata. Note the absence of openGraph.description.

If you'd like to share some nested fields between segments while overwriting others, you can pull them out into a separate variable:

セグメント間で一部のフィールドを上書きしながら他のフィールドを共有したい場合は、それらを別の変数に抜き出すことができます:

app/shared-metadata.js

export const openGraphImage = { images: ['http://...'] };

app/page.js

import { openGraphImage } from './shared-metadata';

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'Home'
  },
};

app/about/page.js

import { openGraphImage } from '../shared-metadata';

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'About',
  },
};

In the example above, the OG image is shared between app/layout.js and app/about/page.js while the titles are different.

Inheriting fields

app/layout.js

export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
};

app/about/page.js

export const metadata = {
  title: 'About',
};

// Output:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />

Notes

  • title from app/layout.js is replaced by title in app/about/page.js.
  • All openGraph fields from app/layout.js are inherited in app/about/page.js because app/about/page.js doesn't set openGraph metadata.

「app/layout.js」のすべての「openGraph」フィールドは、「app/about/page.js」で「openGraph」メタデータが設定されていないため、「app/about/page.js」で継承されます。

🤔
OG:重複箇所は上書き・不足箇所は継承される
Metadata Fields
title

The title attribute is used to set the title of the document. It can be defined as a simple string or an optional template object.

String
export const metadata = {
  title: 'Next.js',
};
Output
<title>Next.js</title>
Template object

app/layout.tsx

export const metadata = {
  title: {
    template: '...',
    default: '...',
    absolute: '...'
  },
}
Template

title.template can be used to add a prefix or a suffix to title's defined in child route segments.

title.templateは、ルートセグメントで定義されたtitle接頭辞または接尾辞を追加するために使用できます。

%sの「s」はstringを意味します。

また、Acme | %sは、%sが文字列で置き換えられる部分を表します。接頭辞が必要な場合は、この部分に置き換える文字列を指定することができます。

🤔
%sの位置によって接頭辞にも接尾辞にもなるってことか

app/layout.tsx

export const metadata = {
  title: {
    template: '%s | Acme',
  },
}

app/about/page.tsx

export const metadata = {
  title: 'About',
}

// Output: <title>About | Acme</title>

Good to know

  • title.template applies to child route segments and not the segment it is defined in. This means:

    title.templateは、ルートセグメントに適用され、定義されたセグメント自体には適用されません。つまり、以下の点に注意する必要があります。

    • title.template defined in layout.js will not apply to a title defined in a page.js of the same segment.
      • layout.jsで定義されたtitle.templateは、同じセグメント内のpage.jsで定義されたtitleには適用されません。
    • title.template defined in page.js has no effect because a page is always the terminating segment.
      • page.jsで定義されたtitle.templateは効果を持ちません。ページは常に終端セグメントであるため、特定のルートのメタデータを決定する際に評価される最後のセグメントです。

      「terminate」とは、ルートの最後のセグメントを指します。最後のセグメントは常にページであり、特定のルートのメタデータを決定する際に評価される最後のセグメントです。ページが常に終端セグメントであるため、page.jsで定義されたtitle.templateは効果を持ちません。

      🤔
      page.jsでtitle.templateを定義して効果を持たせるにはどうしたらいいんだ?

      page.jsでtitle.templateを適用するには、そのセグメント内でtitleを定義する必要があります。

      import { Metadata } from 'next';
      
      export const metadata: Metadata = {
        title: 'This is the default title',
      };
      
      export default function Page() {
        return (
          <>
            <h1>My Page</h1>
          </>
        );
      }
      

      ここで、titleを動的に定義する場合、generateMetadata関数をエクスポートして以下のようにします。

      import { Metadata } from 'next';
      
      type Props = {
        params: { id: string };
      };
      
      export async function generateMetadata({ params }: Props): Promise<Metadata> {
        // read route params
        const id = params.id;
      
        return {
          title: `My Page ${id}`,
        };
      }
      
      export default function Page() {
        return (
          <>
            <h1>My Page</h1>
          </>
        );
      }
      

      ここで、title.templateを使用してMy Page {id}のようなテンプレートを定義し、generateMetadata関数でidの値に応じて動的にtitleを生成することもできます。

      😲
      要は、子ファイルでやる場合は親で固定部分を定義して差し替え部分を%sにしておく。
      ファイルに直接titleをいれると差し替え部分が適用される。
      その部分の一部を動的に変更したい場合にはパラメータを入れればOK
  • title.template has no effect if a route has not defined a title or title.default.

    🤔
    OGの時のように不足箇所だからといって補充されることはないってことか
Default

title.default can be used to provide a fallback title to child route segments that don't define a title.

app/layout.tsx

export const metadata = {
  title: {
    default: 'Acme',
  },
}

app/about/page.tsx

export const metadata = {
}

// Output: <title>Acme</title>
🤔
補充させたい要素はdefault設定にすればいい
Absolute

title.absolute can be used to provide a title that ignores title.template set in parent segments.

🤔
! important みたいな感じだな

app/layout.tsx

export const metadata = {
  title: {
    template: '%s | Acme',
  },
}

app/about/page.tsx

export const metadata = {
  title: {
    absolute: 'About',
  },
}

// Output: <title>About</title>

Good to know

  • layout.js

    • title (string) and title.default define the default title for child segments (that do not define their own title). It will augment title.template from the closest parent segment if it exists.
    • title.absolute defines the default title for child segments. It ignores title.template from parent segments.
    • title.template defines a new title template for child segments.
  • page.js

    • If a page does not define its own title the closest parents resolved title will be used.
    • title (string) defines the routes title. It will augment title.template from the closest parent segment if it exists.
    • title.absolute defines the route title. It ignores title.template from parent segments.
    • title.template has no effect in page.js because a page is always the terminating segment of a route.
description
export const metadata = {
  description: 'The React Framework for the Web',
};
Output
<meta name="description" content="The React Framework for the Web">
Basic Fields
export const metadata = {
  generator: 'Next.js',
  applicationName: 'Next.js',
  referrer: 'origin-when-cross-origin',
  keywords: ['Next.js', 'React', 'JavaScript'],
  authors: [{ name: 'Seb' }, { name: 'Josh', url: 'https://nextjs.org' }],
  colorScheme: 'dark',
  creator: 'Jiachi Liu',
  publisher: 'Sebastian Markbåge',
  formatDetection: {
    email: false,
    address: false,
    telephone: false,
  },
};
Output
<meta name="application-name" content="Next.js">
<meta name="author" content="Seb"/>
<link rel="author" href="https://nextjs.org"/>
<meta name="author" content="Josh"/>
<meta name="generator" content="Next.js">
<meta name="keywords" content="Next.js,React,JavaScript">
<meta name="referrer" content="origin-when-cross-origin">
<meta name="color-scheme" content="dark">
<meta name="creator" content="Jiachi Liu">
<meta name="publisher" content="Sebastian Markbåge">
<meta name="format-detection" content="telephone=no, address=no, email=no">
metadataBase

metadataBase is a convenience option to set a base URL prefix for metadata fields that require a fully qualified URL.

metadataBaseは、完全修飾URLが必要な metadata フィールドのベースURLプレフィックスを設定するための便利なオプションです。

  • metadataBase allows URL-based metadata fields defined in the current route segment and below to use a relative path instead of an otherwise required absolute URL.

    metadataBaseは、現在のルートセグメント以下で定義されたURLベースのmetadataフィールドが、必要な場合には絶対URLではなく相対パスを使用できるようにします。

  • The field's relative path will be composed with metadataBase to form a fully qualified URL.

    フィールドの相対パスはmetadataBase結合され、完全修飾URLが形成されます。

  • If not configured, metadataBase is automatically populated with a default value.

    設定されていない場合、metadataBaseデフォルト値自動的に埋められます

export const metadata = {
  metadataBase: new URL('https://acme.com'),
  alternates: {
    canonical: '/',
    languages: {
      'en-US': '/en-US',
      'de-DE': '/de-DE',
    },
  },
  openGraph: {
    images: '/og-image.png',
  },
};
Output
<link rel="canonical" href="https://acme.com">
<link rel="alternate" hreflang="en-US" href="https://nextjs.org/en-US">
<link rel="alternate" hreflang="de-DE" href="https://nextjs.org/de-DE">
<meta property="og:image" content="https://acme.com/og-image.png">

Good to know

  • metadataBase is typically set in root app/layout.js to apply to URL-based metadata fields across all routes.
  • All URL-based metadata fields that require absolute URLs can be configured with a metadataBase option.
  • metadataBase can contain a sub domain e.g. https://app.acme.com or base path e.g. https://acme.com/start/from/here
  • If a metadata field provides an absolute URL, metadataBase will be ignored.
  • Using a relative path in a URL-based metadata field without configuring a metadataBase will cause a build error.
  • Next.js will normalize duplicate slashes between metadataBase (e.g. https://acme.com/) and a relative field (e.g. /path) to a single slash (e.g. https://acme.com/path)

    Next.jsは、metadataBase(例:https://acme.com/)と相対フィールド(例:/path)の間の重複したスラッシュを正規化し、単一のスラッシュ(例:https://acme.com/path)にします。

    metadataBaseを使用する場合、metadataフィールドで設定されたURLが絶対URLでなくても、相対パスを使用できます。フィールドの相対パスは、metadataBaseと共に完全修飾URLを形成します。

    つまり、metadataBasehttps://acme.com/で、相対パスが/pathの場合、最終的なURLはhttps://acme.com/pathに正規化されます。つまり、metadataBaseとフィールドの間にあるスラッシュが重複している場合、Next.jsはこれらのスラッシュを1つのスラッシュに正規化します。

    💁‍♀️
    /blog/tags/pageのようなURLで具体例を示します。

    metadataBasehttps://example.comの場合、/blog/tags/pagehttps://example.com/blog/tags/pageになります。
    しかし、metadataBasehttps://example.com/の場合、/blog/tags/pagehttps://example.com//blog/tags/pageになります。
    この場合、Next.jsは重複したスラッシュを1つのスラッシュに正規化し、https://example.com/blog/tags/pageになります。

    🤔
    つまり、metaBase末尾にスラッシュがあると生じるダブルスラッシュを解消してくれるってことか
Default value

If not configured, metadataBase has a default value

  • When VERCEL_URL is detected: https://${process.env.VERCEL_URL}
  • If there's no environment variable VERCEL_URL is set, will always fallback to http://localhost:${process.env.PORT || 3000}.
  • When overriding the default, we recommend using environment variables to compute the URL. This allows configuring a URL for local development, staging, and production environments.

    デフォルト値を上書きする場合、URLを計算するために環境変数を使用することをお勧めします。これにより、ローカル開発、ステージング、本番環境でURLを設定できます。

🤔
今までドメインでNEXT_PUBLIC_URLで設定していた部分を自動割り振りURLでもやれるってことか
const NEXT_PUBLIC_URL = process.env.NEXT_PUBLIC_URL
server-constants.jsより
{NEXT_PUBLIC_URL ? (
        <link
          rel="canonical"
          href={new URL(path, NEXT_PUBLIC_URL).toString()}
        />
      ) : null}
document-head.tsxより
URL Composition

URL composition favors developer intent over default directory traversal semantics.

URLの構成は、デフォルトのディレクトリトラバーサルの意味合いよりも、開発者の意図優先します。

ディレクトリトラバーサル (directory traversal) :Webアプリケーションでよく起こる脆弱性の一種で、攻撃者がWebサーバーのファイルシステム内のファイルにアクセスできるようになる攻撃です。攻撃者は、Webアプリケーションがサーバー上のファイルを操作するために使用するファイルパスを調べ、ファイルパスを操作して、許可されていないファイルにアクセスします。攻撃者は、ファイルを読み取る、書き換える、削除するなど、悪意のある操作を行うことができます。

  • Trailing slashes between metadataBase and metadata fields are normalized.

    「metadataBase」と「metadata」フィールド間の末尾のスラッシュは正規化されます。

  • An "absolute" path in a metadata field (that typically would replace the whole URL path) is treated as a "relative" path (starting from the end of metadataBase).

    「絶対パス」と「相対パス」の違いは、URLの構成方法によるものです。

    • 「絶対パス」とは、URLの最初からのパスを指定します。例:https://example.com/path/to/page
    • 「相対パス」とは、現在のURLからの相対的な位置を指定します。例:./path/to/page(同じ階層)、../path/to/page(一つ上の階層)

For example, given the following metadataBase:

app/api/layout.tsx

export const metadata = {
  metadataBase: new URL('https://acme.com/api'),
};

Any metadata fields that inherit the above metadataBase and set their own value will be resolved as follows:

metadata fieldResolved URL
/https://acme.com/api
./https://acme.com/api
paymentshttps://acme.com/api/payments
/paymentshttps://acme.com/api/payments
./paymentshttps://acme.com/api/payments
../paymentshttps://acme.com/payments
https://beta.acme.com/api/paymentshttps://beta.acme.com/api/payments
openGraph
export const metadata = {
  openGraph: {
    title: 'Next.js',
    description: 'The React Framework for the Web',
    url: 'https://nextjs.org',
    siteName: 'Next.js',
    images: [
      {
        url: 'https://nextjs.org/og.png',
        width: 800,
        height: 600,
      },
      {
        url: 'https://nextjs.org/og-alt.png',
        width: 1800,
        height: 1600,
        alt: 'My custom alt',
      },
    ],
    locale: 'en-US',
    type: 'website',
  },
};
Output
<meta property="og:title" content="Next.js" />
<meta property="og:description" content="The React Framework for the Web" />
<meta property="og:url" content="https://nextjs.org/" />
<meta property="og:site_name" content="Next.js" />
<meta property="og:locale" content="en-US" />
<meta property="og:image:url" content="https://nextjs.org/og.png" />
<meta property="og:image:width" content="800" />
<meta property="og:image:height" content="600" />
<meta property="og:image:url" content="https://nextjs.org/og-alt.png" />
<meta property="og:image:width" content="1800" />
<meta property="og:image:height" content="1600" />
<meta property="og:image:alt" content="My custom alt" />
<meta property="og:type" content="website" />
export const metadata = {
  openGraph: {
    title: 'Next.js',
    description: 'The React Framework for the Web',
    type: 'article',

    authors: ['Seb', 'Josh'],
  },
};
Output
<meta property="og:title" content="Next.js" />
<meta property="og:description" content="The React Framework for the Web" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2023-01-01T00:00:00.000Z" />
<meta property="article:author" content="Seb" />
<meta property="article:author" content="Josh" />

Good to know

  • It may be more convenient to use the file-based Metadata API for Open Graph images. Rather than having to sync the config export with actual files, the file-based API will automatically generate the correct metadata for you.

    Open Graph画像には、ファイルベースのMetadata APIを使用する方が便利かもしれません。設定エクスポートと実際のファイルを同期する必要がなく、ファイルベースのAPIは自動的に正しいメタデータを生成します。

robots
export const metadata = {
  robots: {
    index: false,
    follow: true,
    nocache: true,
    googleBot: {
      index: true,
      follow: false,
      noimageindex: true,
      'max-video-preview': -1,
      'max-image-preview': 'large',
      'max-snippet': -1,
    },
  },
};
icons

Note: We recommend using the file-based Metadata API for icons where possible. Rather than having to sync the config export with actual files, the file-based API will automatically generate the correct metadata for you.

iconsを使用すると、アプリケーションのアイコンをブラウザに表示することができます。これにより、ユーザーがWebサイトまたはWebアプリケーションをブックマークするときに、ブックマークにアイコンが表示されます。iconsオブジェクトには、アイコンの各サイズに対する画像のパスを指定できます。例えば、以下のようになります:

export const metadata = {
  icons: [
    { src: '/favicon-16x16.png', sizes: '16x16', type: 'image/png' },
    { src: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' },
    { src: '/favicon-48x48.png', sizes: '48x48', type: 'image/png' },
  ],
};

icon オブジェクトには、以下のプロパティを指定できます。

  • src:アイコン画像のパス
  • sizes:アイコン画像のサイズ。WIDTHxHEIGHTの形式で指定します。
  • type:アイコン画像のMIMEタイプ。省略可能です。

また、metadataオブジェクトにはappleTouchIconプロパティがあり、iOSのホーム画面に追加する場合に使用されるアイコンを指定することができます。

export const metadata = {
  appleTouchIcon: {
    src: '/apple-touch-icon.png',
    sizes: '180x180',
  },
};
export const metadata = {
  icons: {
    icon: '/icon.png',
    shortcut: '/shortcut-icon.png',
    apple: '/apple-icon.png',
    other: {
      rel: 'apple-touch-icon-precomposed',
      url: '/apple-touch-icon-precomposed.png',
    },
  },
};
Output
<link rel="shortcut icon" href="/shortcut-icon.png" />
<link rel="icon" href="/icon.png" />
<link rel="apple-touch-icon" href="/apple-icon.png" />
<link
  rel="apple-touch-icon-precomposed"
  href="/apple-touch-icon-precomposed.png"
/>

export const metadata = {
  icons: {
    icon: [{ url: '/icon.png' }, new URL('/icon.png', 'https://example.com')],
    shortcut: ['/shortcut-icon.png'],
    apple: [
      { url: '/apple-icon.png' },
      { url: '/apple-icon-x3.png', sizes: '180x180', type: 'image/png' },
    ],
    other: [
      {
        rel: 'apple-touch-icon-precomposed',
        url: '/apple-touch-icon-precomposed.png',
      },
    ],
  },
};
Output
<link rel="shortcut icon" href="/shortcut-icon.png" />
<link rel="icon" href="/icon.png" />
<link rel="apple-touch-icon" href="/apple-icon.png" />
<link
  rel="apple-touch-icon-precomposed"
  href="/apple-touch-icon-precomposed.png"
/>
<link rel="icon" href="https://example.com/icon.png" />
<link
  rel="apple-touch-icon"
  href="/apple-icon-x3.png"
  sizes="180x180"
  type="image/png"
/>

Note: The msapplication-* meta tags are no longer supported in Chromium builds of Microsoft Edge, and thus no longer needed.

themeColor

Learn more about theme-color.

Simple theme color

export const metadata = {
  themeColor: 'black',
};
Output
<meta name="theme-color" content="black" />

With media attribute

export const metadata = {
  themeColor: [
    { media: '(prefers-color-scheme: light)', color: 'cyan' },
    { media: '(prefers-color-scheme: dark)', color: 'black' },
  ],
};
Output
<meta name="theme-color" media="(prefers-color-scheme: light)" content="cyan" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="black" />

metadatathemeColorを設定すると、ブラウザのアドレスバーの色や、Androidのホーム画面に追加したときのアイコンの背景色が変化します。

manifest

The manifest.json file is the only file that every extension using WebExtension APIs must contain.

export const metadata = {
  manifest: 'https://nextjs.org/manifest.json',
};
twitter

Learn more about the Twitter Card markup reference.

export const metadata = {
  twitter: {
    card: 'summary_large_image',
    title: 'Next.js',
    description: 'The React Framework for the Web',
    siteId: '1467726470533754880',
    creator: '@nextjs',
    creatorId: '1467726470533754880',
    images: ['https://nextjs.org/og.png'],
  },
};
Output
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site:id" content="1467726470533754880" />
<meta name="twitter:creator" content="@nextjs" />
<meta name="twitter:creator:id" content="1467726470533754880" />
<meta name="twitter:title" content="Next.js" />
<meta name="twitter:description" content="The React Framework for the Web" />
<meta name="twitter:image" content="https://nextjs.org/og.png" />
export const metadata = {
  twitter: {
    card: 'app',
    title: 'Next.js',
    description: 'The React Framework for the Web',
    siteId: '1467726470533754880',
    creator: '@nextjs',
    creatorId: '1467726470533754880',
    images: {
      url: 'https://nextjs.org/og.png',
      alt: 'Next.js Logo',
    },
    app: {
      name: 'twitter_app',
      id: {
        iphone: 'twitter_app://iphone',
        ipad: 'twitter_app://ipad',
        googleplay: 'twitter_app://googleplay',
      },
      url: {
        iphone: 'https://iphone_url',
        ipad: 'https://ipad_url',
      },
    },
  },
};
Output
<meta name="twitter:site:id" content="1467726470533754880" />
<meta name="twitter:creator" content="@nextjs" />
<meta name="twitter:creator:id" content="1467726470533754880" />
<meta name="twitter:title" content="Next.js" />
<meta name="twitter:description" content="The React Framework for the Web" />
<meta name="twitter:card" content="app" />
<meta name="twitter:image" content="https://nextjs.org/og.png" />
<meta name="twitter:image:alt" content="Next.js Logo" />
<meta name="twitter:app:name:iphone" content="twitter_app" />
<meta name="twitter:app:id:iphone" content="twitter_app://iphone" />
<meta name="twitter:app:id:ipad" content="twitter_app://ipad" />
<meta name="twitter:app:id:googleplay" content="twitter_app://googleplay" />
<meta name="twitter:app:url:iphone" content="https://iphone_url" />
<meta name="twitter:app:url:ipad" content="https://ipad_url" />
<meta name="twitter:app:name:ipad" content="twitter_app" />
<meta name="twitter:app:name:googleplay" content="twitter_app" />
viewport
// Note: This is the same values as the default value set
// You likely don't need to change this, but it's included for completeness
export const metadata = {
  viewport: {
    width: 'device-width',
    initialScale: 1,
    maximumScale: 1,
  },
};
Output
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

metadataオブジェクトには、viewportプロパティを設定することができます。これにより、モバイルデバイスでのWebサイトの表示をカスタマイズすることができます。

widthプロパティにはdevice-widthを指定しています。これは、表示領域の幅をデバイスの画面幅に自動的に合わせることを意味します。initialScalemaximumScaleプロパティは、初期拡大率と最大拡大率を指定します。

viewportプロパティには、他にもいくつかの設定があります。たとえば、以下のように設定することができます。

export const metadata = {
  viewport: {
    width: 'device-width',
    initialScale: 1,
    maximumScale: 5,
    minimumScale: 1,
    userScalable: true,
  },
};

minimumScaleプロパティを設定することで、最小拡大率を指定することができます。userScalableプロパティをfalseにすることで、ユーザーが拡大縮小できないようにすることもできます。

verification
export const metadata = {
  verification: {
    google: 'google',
    yandex: 'yandex',
    yahoo: 'yahoo',
    other: {
      me: ['my-email', 'my-link'],
    },
  },
};
Output
<meta name="google-site-verification" content="google">
<meta name="y_key" content="yahoo">
<meta name="yandex-verification" content="yandex">
<meta name="me" content="my-email">
<meta name="me" content="my-link">

metadataオブジェクトには、verificationプロパティを設定することができます。これにより、GoogleやYahooなどのサービスの所有権を確認するためのメタタグを自動的に挿入することができます。

appleWebApp
export const metadata = {
  itunes: {
    appId: 'myAppStoreID',
    appArgument: 'myAppArgument',
  },
  appleWebApp: {
    title: 'Apple Web App',
    statusBarStyle: 'black-translucent',
    startupImage: [
      '/assets/startup/apple-touch-startup-image-768x1004.png',
      {
        url: '/assets/startup/apple-touch-startup-image-1536x2008.png',
        media: '(device-width: 768px) and (device-height: 1024px)',
      },
    ],
  },
};
Output
<meta name="apple-itunes-app" content="app-id=myAppStoreID, app-argument=myAppArgument">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="Apple Web App">
<link href="/assets/startup/apple-touch-startup-image-768x1004.png" rel="apple-touch-startup-image">
<link href="/assets/startup/apple-touch-startup-image-1536x2008.png" media="(device-width: 768px) and (device-height: 1024px)" rel="apple-touch-startup-image">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

metadataオブジェクトには、appleWebAppプロパティを設定することができます。これにより、iOSのホーム画面に追加してWebアプリケーションとして使用することができます。

appleWebAppプロパティには、以下のプロパティを指定できます。

export const metadata = {
  appleWebApp: {
    title: 'My App',
    statusBarStyle: 'default',
    startupImage: '/assets/startup.png',
  },
};
  • title:ホーム画面に追加されたときに表示されるアプリ名
  • statusBarStyle:ホーム画面に追加されたときのステータスバーのスタイル。defaultblackblack-translucentのいずれかを指定します。
  • startupImage:起動時に表示される画像。stringまたはobjectの配列を指定します。objectには以下のプロパティを指定できます。
    • url:画像のURL
    • media:画像が表示される条件を指定するmediaクエリ。省略可能です。

また、metadataオブジェクトにはitunesプロパティがあり、App StoreでのアプリのIDや引数を指定することができます。

export const metadata = {
  itunes: {
    appId: 'myAppStoreID',
    appArgument: 'myAppArgument',
  },
};
Output
<meta name="apple-itunes-app" content="app-id=myAppStoreID, app-argument=myAppArgument">
alternates
export const metadata = {
  alternates: {
    canonical: 'https://nextjs.org',
    languages: {
      'en-US': 'https://nextjs.org/en-US',
      'de-DE': 'https://nextjs.org/de-DE',
    },
    media: {
      'only screen and (max-width: 600px)': 'https://nextjs.org/mobile',
    },
    types: {
      'application/rss+xml': 'https://nextjs.org/rss',
    },
  },
};
Output
<link rel="canonical" href="https://nextjs.org">
<link rel="alternate" hreflang="en-US" href="https://nextjs.org/en-US">
<link rel="alternate" hreflang="de-DE" href="https://nextjs.org/de-DE">
<link rel="alternate" media="only screen and (max-width: 600px)" href="https://nextjs.org/mobile">
<link rel="alternate" type="application/rss+xml" href="https://nextjs.org/rss">

alternatesプロパティを使用すると、Webサイトの「リンクの関係性」を指定することができます。alternatesプロパティには、以下のプロパティを指定できます。

  • canonical:WebサイトのカノニカルURLを指定します。
  • languages:Webサイトの言語バージョンを指定します。{言語コード: URL}の形式で指定します。
  • media:メディアクエリに基づいて、異なるバージョンのWebサイトを提供する場合に使用します。
  • types:Webサイトの別のバージョンを提供するために使用される別のMIMEタイプを指定します。
appLinks
export const metadata = {
  appLinks: {
    ios: {
      url: 'https://nextjs.org/ios',
      app_store_id: 'app_store_id',
    },
    android: {
      package: 'com.example.android/package',
      app_name: 'app_name_android',
    },
    web: {
      url: 'https://nextjs.org/web',
      should_fallback: true,
    },
  },
};
Output
<meta property="al:ios:url" content="https://nextjs.org/ios">
<meta property="al:ios:app_store_id" content="app_store_id">
<meta property="al:android:package" content="com.example.android/package">
<meta property="al:android:app_name" content="app_name_android">
<meta property="al:web:url" content="https://nextjs.org/web">
<meta property="al:web:should_fallback" content="true">
archives

Describes a collection of records, documents, or other materials of historical interest (source).

export const metadata = {
  archives: ['https://nextjs.org/13'],
};
Output
<link rel="archives" href="https://nextjs.org/13">
assets
export const metadata = {
  assets: ['https://nextjs.org/assets'],
};
Output
<link rel="assets" href="https://nextjs.org/assets">

metadataassetsプロパティを設定することで、関連するファイルやリソースを指定することができます。これにより、Webサイトの構造を明確にすることができます。

たとえば、以下のように設定することができます。

export const metadata = {
  assets: ['/styles.css', '/script.js'],
};

この場合、/styles.css/script.jsがWebサイトに関連するファイルであることを示します。ブラウザはこれらのファイルを事前にダウンロードし、Webサイトの読み込みを高速化することができます。

bookmarks
export const metadata = {
  bookmarks: ['https://nextjs.org/13'],
};
Output
<link rel="bookmarks" href="https://nextjs.org/13">

bookmarksプロパティを使用すると、Webブラウザーのブックマーク機能でWebページをブックマークするときに使用されるデフォルトのURLを指定することができます。

category
export const metadata = {
  category: 'technology',
};
Output
<meta name="category" content="technology">
other

All metadata options should be covered using the built-in support. However, there may be custom metadata tags specific to your site, or brand new metadata tags just released. You can use the other option to render any custom metadata tag.

すべてのメタデータオプションは、組み込みサポートを使用してカバーする必要があります。ただし、サイト固有のカスタムメタデータタグまたは新しくリリースされたメタデータタグがある場合は、 その他 オプションを使用してカスタムメタデータタグをレンダリングできます。

export const metadata = {
  other: {
    custom: 'meta',
  },
};
Output
<meta name="custom" content="meta">
Unsupported Metadata

The following metadata types do not currently have built-in support. However, they can still be rendered in the layout or page itself.

MetadataRecommendation
<meta http-equiv="...">Use appropriate HTTP Headers via redirect(), Middleware, Security Headers
<base>Render the tag in the layout or page itself.
<noscript>Render the tag in the layout or page itself.
<style>Learn more about styling in Next.js.
<script>Learn more about using scripts.
<link rel="stylesheet" />import stylesheets directly in the layout or page itself.
<link rel="preload />Use ReactDOM preload method
<link rel="preconnect" />Use ReactDOM preconnect method
<link rel="dns-prefetch" />Use ReactDOM prefetchDNS method
🤔
scriptはよく使いたくなるから要注意だ
Resource hints

The <link> element has a number of rel keywords that can be used to hint to the browser that a external resource is likely to be needed. The browser uses this information to apply preloading optimizations depending on the keyword.

<link>要素には、ブラウザに外部リソースが必要であることを示唆するために使用できるいくつかのrelキーワードがあります。ブラウザは、キーワードに応じてプリロード最適化を適用するためにこの情報を使用します。

While the Metadata API doesn't directly support these hints, you can use new ReactDOM methods to safely insert them into the <head> of the document.

Metadata APIはこれらのヒントを直接サポートしていませんが、新しい ReactDOM メソッドを使用して、これらのヒントをドキュメントの <head> に安全に挿入することができます。

📍

app/preload-resources.tsx

'use client';

import ReactDOM from 'react-dom';

export function PreloadResources() {
  ReactDOM.preload('...', { as: '...' });
  ReactDOM.preconnect('...', { crossOrigin: '...' });
  ReactDOM.prefetchDNS('...');

  return null;
}
<link rel="preload">

Start loading a resource early in the page rendering (browser) lifecycle. MDN Docs.

ReactDOM.preload(href: string, options: { as: string })
<link rel="preconnect">

Preemptively initiate a connection to an origin. MDN Docs.

ReactDOM.preload(href: string, options?: { crossOrigin?: string })
<link rel="dns-prefetch">

Attempt to resolve a domain name before resources get requested. MDN Docs.

ReactDOM.prefetchDNS(href: string)

Good to know

  • These methods are currently only supported in Client components.
    • Note: Client Components are still Server Side Rendered on initial page load.
  • Next.js in-built features such as next/font, next/image and next/script automatically handle relevant resource hints.
  • React 18.3 does not yet include type definitions for ReactDOM.preload, ReactDOM.preconnect, and ReactDOM.preconnectDNS. You can use // @ts-ignore as a temporary solution to avoid type errors.
Types

You can add type safety to your metadata by using the Metadata type. If you are using the built-in TypeScript plugin in your IDE, you do not need to manually add the type, but you can still explicitly add it if you want.

metadata object
import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'Next.js',
};
generateMetadata function
Regular function
import type { Metadata } from 'next';

export function generateMetadata(): Metadata {
  return {
    title: 'Next.js',
  };
}
Async function
import type { Metadata } from 'next';

export async function generateMetadata(): Promise<Metadata> {
  return {
    title: 'Next.js',
  };
}
With segment props
import type { Metadata } from 'next';

type Props = {
  params: { id: string };
  searchParams: { [key: string]: string | string[] | undefined };
};

export function generateMetadata({ params, searchParams }: Props): Metadata {
  return {
    title: 'Next.js',
  };
}

export default function Page({ params, searchParams }: Props) {}
With parent metadata
import type { Metadata, ResolvingMetadata } from 'next';

export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata,
): Promise<Metadata> {
  return {
    title: 'Next.js',
  };
}
JavaScript Projects

For JavaScript projects, you can use JSDoc to add type safety.

/** @type {import("next").Metadata} */
export const metadata = {
  title: 'Next.js',
};
File-based Metadata

File-based metadata can be defined by adding special metadata files to route segments.

Each file convention can be defined using a static file e.g. app/opengraph-image.jpg, or a dynamic variant that uses code to generate the file e.g. app/opengraph-image.js.

Once a file is defined, Next.js will automatically serve the file (with hashes in production for caching) and update the relevant head elements with the correct metadata, such as the asset's URL, file type, and image size.

Good to know

  • File-based metadata has the higher priority and will override any config-based metadata. e.g. app/opengraph-image.jpg will override a metadata.opengraph.image export in app/layout.js.
App Icons

Add an icon file to the root segment of the app directory to set an icon for your application.

Static icon

Add a favicon.ico, icon.(ico|jpg|jpeg|png|svg), or apple-icon.(jpg|jpeg|png|svg) file to the root segment.

Generate an icon

Add a icon.(js|ts|tsx) or apple-icon.(js|ts|tsx) file that default exports a function that returns Blob | ArrayBuffer | TypedArray | DataView | ReadableStream | Response.

The easiest way to generate an image is to use the ImageReponse API from next/server.

You can optionally configure the icon's image metadata by exporting sizes and contentType variables from the file.

import { ImageResponse } from 'next/server';

export const size = {
  width: 32,
  height: 32,
};
export const contentType = 'image/png';
export const runtime = 'edge';

export default function icon() {
  return new ImageResponse(
    (
      <div
        style={{
          fontSize: 24,
          background: 'black',
          width: '100%',
          height: '100%',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          color: 'white',
        }}
      >
        A
      </div>
    ),
    size,
  );
}

Good to know

  • You can set multiple icons for your application by adding a number suffix to the file name. For example, icon1.(jpg|tsx), icon2.(jpg|tsx), etc. Numbered files will sort lexically.
  • sizes="any" is added to favicon.ico output to avoid a browser bug where an .ico icon is favored over .svg.
  • You cannot dynamically generate a favicon file.
Open Graph Images

Add an opengraph-image or twitter-image file to a route segment to set an Open Graph or Twitter image for that segment.

Static Images

Add an opengraph-image.(jpg|jpeg|png|gif) or twitter-image.(jpg|jpeg|png|gif) file to any route segment.

Generate a dynamic image

Add an opengraph-image.(js|ts|tsx) or twitter-image.(js|ts|tsx) file that default exports a function that returns Blob | ArrayBuffer | TypedArray | DataView | ReadableStream | Response.

The easiest way to generate an image is to use the ImageReponse API from next/server.

You can optionally configure the image's metadata by exporting alt, size, and contentType variables from the file.

app/about/opengraph-image.tsx

import { ImageResponse } from 'next/server';

export const alt = 'About Acme';
export const size = {
  width: 1200,
  height: 630,
};
export const contentType = 'image/png';

export default function og() {
  return new ImageResponse(
    (
      <div
        style={{
          fontSize: 128,
          background: 'white',
          width: '100%',
          height: '100%',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        About Acme
      </div>
    ),
    size,
  );
}

Good to know

  • You can set multiple Open Graph images for a route segment by adding a number suffix to the file name. For example, opengraph-image1.(jpg|tsx), opengraph-image2.(jpg|tsx), etc. Numbered files will sort lexically within their given segment.
Robots file

Add or generate a robots.txt file that matches the Robots Exclusion Standard in the root of app directory to tell search engine crawlers which URLs they can access on your site.

Static robots.txt

app/robots.txt

User-Agent: *
Allow: /
Disallow: /private/

Sitemap: https://acme.com/sitemap.xml
Generate a Robots file

Add a robots.js or robots.ts file that returns a Robots object.

app/robots.ts

import { MetadataRoute } from 'next';

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: '*',
      allow: '/',
      disallow: '/private/',
    },
    sitemap: 'https://acme.com/sitemap.xml',
  };
}
Robots object
type Robots = {
  rules:
    | {
        userAgent?: string | string[];
        allow?: string | string[];
        disallow?: string | string[];
        crawlDelay?: number;
      }
    | Array<{
        userAgent: string | string[];
        allow?: string | string[];
        disallow?: string | string[];
        crawlDelay?: number;
      }>;
  sitemap?: string | string[];
  host?: string;
};
Sitemap

Add or generate a sitemap.xml file that matches the Sitemaps XML format in the root of app directory to help search engine crawlers crawl your site more efficiently.

Static sitemap.xml

app/sitemap.xml

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://acme.com</loc>
    <lastmod>2023-04-06T15:02:24.021Z</lastmod>
  </url>
  <url>
    <loc>https://acme.com/about</loc>
    <lastmod>2023-04-06T15:02:24.021Z</lastmod>
  </url>
  <url>
    <loc>https://acme.com/blog</loc>
    <lastmod>2023-04-06T15:02:24.021Z</lastmod>
  </url></urlset>
Generate a Sitemap

Add a sitemap.js or sitemap.ts file that returns Sitemap.

app/sitemap.ts

import { MetadataRoute } from 'next';

export default function sitemap(): MetadataRoute.Sitemap {
  return [
    {
      url: 'https://acme.com',
      lastModified: new Date(),
    },
    {
      url: 'https://acme.com/about',
      lastModified: new Date(),
    },
    {
      url: 'https://acme.com/blog',
      lastModified: new Date(),
    },
  ];
}
Sitemap Return Type
type Sitemap = Array<{
  url: string;
  lastModified?: string | Date;
}>;

Good to know

  • In the future we will support multiple sitemaps and sitemap indexes.
Next Steps