meta

metaエクスポートを使用すると、アプリケーションのすべてのルートにメタデータHTMLタグを追加できます。これらのタグは、検索エンジン最適化(SEO)や、特定の動作を決定するためのブラウザディレクティブなど、重要な役割を果たします。また、ソーシャルメディアサイトがアプリのリッチプレビューを表示するためにも使用できます。

meta関数は、MetaDescriptorオブジェクトの配列を返す必要があります。これらのオブジェクトはHTMLタグと1対1に対応します。そのため、次のmeta関数:

export const meta: MetaFunction = () => {
  return [
    { title: "Very cool app | Remix" },
    {
      property: "og:title",
      content: "Very cool app",
    },
    {
      name: "description",
      content: "This app is the best",
    },
  ];
};

は、次のHTMLを生成します。

<title>Very cool app | Remix</title>
<meta property="og:title" content="Very cool app" />;
<meta name="description" content="This app is the best" />

デフォルトでは、メタ記述子はほとんどの場合、<meta>タグをレンダリングします。2つの例外は次のとおりです。

  • { title }<title>タグをレンダリングします。
  • { "script:ld+json" }<script type="application/ld+json">タグをレンダリングし、その値はシリアライズ可能なオブジェクトである必要があります。オブジェクトは文字列化され、タグに挿入されます。
export const meta: MetaFunction = () => {
  return [
    {
      "script:ld+json": {
        "@context": "https://schema.org",
        "@type": "Organization",
        name: "Remix",
        url: "https://remix.run",
      },
    },
  ];
};

メタ記述子は、tagNameプロパティを"link"に設定することで、<link>タグをレンダリングすることもできます。これは、canonicalURLなどのSEOに関連する<link>タグに役立ちます。スタイルシートやファビコンなどのアセットリンクについては、linksエクスポートを使用する必要があります。

export const meta: MetaFunction = () => {
  return [
    {
      tagName: "link",
      rel: "canonical",
      href: "https://remix.run",
    },
  ];
};

meta関数の引数

location

これは現在のルーターLocationオブジェクトです。これは、特定のパスまたはクエリパラメータのルートのタグを生成するのに役立ちます。

export const meta: MetaFunction = ({ location }) => {
  const searchQuery = new URLSearchParams(
    location.search
  ).get("q");
  return [{ title: `Search results for "${searchQuery}"` }];
};

matches

これは、現在のルートのマッチングの配列です。多くの情報にアクセスできますが、特に親のマッチングからメタデータとデータにアクセスできます。

matchesのインターフェースはuseMatchesの戻り値に似ていますが、各マッチングにはそのmeta関数の出力が含まれます。これは、ルート階層全体でメタデータをマージするのに役立ちます。

data

これは、ルートのloaderからのデータです。

export async function loader({
  params,
}: LoaderFunctionArgs) {
  return json({
    task: await getTask(params.projectId, params.taskId),
  });
}
 
export const meta: MetaFunction<typeof loader> = ({
  data,
}) => {
  return [{ title: data.task.name }];
};

params

ルートのURLパラメータです。ルーティングガイドの動的セグメントを参照してください。

error

エラー境界をトリガーするスローされたエラーは、meta関数に渡されます。これは、エラーページのメタデータを生成するのに役立ちます。

export const meta: MetaFunction = ({ error }) => {
  return [{ title: error ? "oops!" : "Actual title" }];
};

親ルートのローダーからデータにアクセスする

現在のルートのデータに加えて、多くの場合、ルート階層の上位のルートのデータにアクセスしたいことがあります。matchesでルートIDを検索して、データにアクセスできます。

app/routes/project.$pid.tasks.$tid.tsx
import type { loader as projectDetailsLoader } from "./project.$pid";
 
export async function loader({
  params,
}: LoaderFunctionArgs) {
  return json({ task: await getTask(params.tid) });
}
 
export const meta: MetaFunction<
  typeof loader,
  { "routes/project.$pid": typeof projectDetailsLoader }
> = ({ data, matches }) => {
  const project = matches.find(
    (match) => match.id === "routes/project.$pid"
  ).data.project;
  const task = data.task;
  return [{ title: `${project.name}: ${task.name}` }];
};

metaとネストされたルートに関する注意点

複数のネストされたルートが同時にレンダリングされるため、最終的にレンダリングされるメタタグを決定するために、マージする必要があります。Remixでは、明らかなデフォルトがないため、このマージを完全に制御できます。

Remixは、メタエクスポートを持つ最後のマッチングルートを使用します。これにより、titleをオーバーライドしたり、親ルートが追加したog:imageのようなものを削除したり、親のすべてを保持して子ルートに新しいメタを追加したりできます。

これは、初心者にとってはかなり難しい場合があります。

/projects/123のようなルートを考えてみましょう。このルートには、app/root.tsxapp/routes/projects.tsxapp/routes/projects.$id.tsxという3つのマッチングルートがある可能性があります。これら3つのルートはすべて、メタ記述子をエクスポートしている可能性があります。

app/root.tsx
export const meta: MetaFunction = () => {
  return [
    {
      name: "viewport",
      content: "width=device-width,initial-scale=1",
    },
    { title: "New Remix App" },
  ];
};
app/routes/projects.tsx
export const meta: MetaFunction = () => {
  return [{ title: "Projects" }];
};
app/routes/projects.$id.tsx
export const meta: MetaFunction<typeof loader> = ({
  data,
}) => {
  return [{ title: data.project.name }];
};

このコードでは、/projects/projects/123ではviewportメタタグが失われます。これは、最後のメタのみが使用され、コードが親とマージされないためです。

グローバルmeta

ほとんどのアプリケーションでは、viewportcharSetのようなグローバルメタを使用します。metaエクスポートではなく、ルートルート内の通常の<meta>タグを使用することをお勧めします。これにより、マージする必要がなくなり、処理が簡単になります。

app/root.tsx
import {
  Links,
  Meta,
  Outlet,
  Scripts,
} from "@remix-run/react";
 
export default function App() {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />
        <Meta />
        <Links />
      </head>
      <body>
        <Outlet />
        <Scripts />
      </body>
    </html>
  );
}

親ルートでのmetaの回避

親ルートからオーバーライドする必要があるmetaをエクスポートしないことで、マージの問題を回避することもできます。親ルートにmetaを定義するのではなく、インデックスルートを使用します。これにより、タイトルなどの複雑なマージロジックを回避できます。そうでなければ、親のタイトル記述子を検索して、子のタイトルに置き換える必要があります。インデックスルートを使用すれば、オーバーライドする必要がなくなり、はるかに簡単になります。

metaとのマージ

通常は、親がすでに定義しているmetametaを追加するだけです。スプレッド演算子とmatches引数を使用して、親metaをマージできます。

export const meta: MetaFunction = ({ matches }) => {
  const parentMeta = matches.flatMap(
    (match) => match.meta ?? []
  );
  return [...parentMeta, { title: "Projects" }];
};

ただし、titleのようなものをオーバーライドすることはできません。これは単に追加するだけです。継承されたルートのメタにtitleタグが含まれている場合は、Array.prototype.filterを使用してオーバーライドできます。

export const meta: MetaFunction = ({ matches }) => {
  const parentMeta = matches
    .flatMap((match) => match.meta ?? [])
    .filter((meta) => !("title" in meta));
  return [...parentMeta, { title: "Projects" }];
};

metaマージヘルパー

グローバルメタまたはインデックスルートを使用してマージの問題を回避できない場合は、親メタを簡単にオーバーライドして追加できるヘルパーをアプリケーションに置くことができます。