🚀

Next.js 13.3まとめ

2023/04/07に公開
3

基本的には以下のNext.js 13.3のブログを翻訳してまとめたものになります。
https://nextjs.org/blog/next-13-3

TL;DR

  • ファイルベースのメタデータAPI: サイトマップ、robots、ファビコンなどの動的な生成
  • 動的なOG画像: ビルトインのJSXとHTML、CSSを使用したOG画像の生成
  • Appルーターの静的エクスポート対応: サーバーコンポーネントの静的サイト・SPA対応
  • 並行ルートと横取りルート: Appルーターの拡張的なルーティングの機能

また、Appルーターは次のマイナーリリースで安定版になる予定のようです。ついにですね。

アップデート詳細

ファイルベースのメタデータAPI

Next.js 13.2で登場した設定ベースのメタデータAPIに加えて、ファイルベースのメタデータAPIとして以下の新しいファイル規約に対応しました。

  • opengraph-image.(jpg|png|svg)
  • twitter-image.(jpg|png|svg)
  • favicon.ico
  • icon.(ico|jpg|png|svg)
  • sitemap.(xml|js|jsx|ts|tsx)
  • robots.(txt|js|jsx|ts|tsx)
  • manifest.(json|js|jsx|ts|tsx)

以下の例では、アプリケーション全体にファビコンを設定し、/aboutページにOG画像を追加で設定しています。
そして、Next.jsはキャッシュのためにハッシュ付きのファイルを本番環境で自動的に提供し、関連するhead要素をアセットのURL、ファイルタイプ、画像サイズなどの適切なメタデータ情報で更新します。

app
├── favicon.ico
├── layout.js
├── page.js
└── about
    ├── opengraph-image.jpg
    └── page.js

// Visiting "/"
<link rel="icon" href="<computedUrl>"/>

// Visiting "/about"
<link rel="icon" href="<computedUrl>"/>
<meta property="og:image" content="<computedUrl>" type="<computedType>" ... />

また、静的なファイルを追加するだけではなく、.js|.jsx|.ts|.tsxのファイルを使用して動的なファイルを追加することもできます。

以下の例では、外部のデータソースを使用して動的に生成されたページを持つサイトで動的にサイトマップを生成するために、sitemap.jsを追加し、動的なルートの配列を返しています。

app/sitemap.js
export default async function sitemap() {
  const res = await fetch('https://.../posts');
  const allPosts = await res.json();

  const posts = allPosts.map((post) => ({
    url: `https://acme.com/blog/${post.slug}`,
    lastModified: post.publishedAt,
  }));

  const routes = ['', '/about', '/blog'].map((route) => ({
    url: `https://acme.com${route}`,
    lastModified: new Date().toISOString(),
  }));

  return [...routes, ...posts];
}

設定ベースとファイルベースのメタデータAPIを組み合わせることで、静的・動的両方のメタデータに対応することができます。

https://beta.nextjs.org/docs/api-reference/metadata#file-based-metadata

動的なOG画像の生成

Next.jsではこれまでも@vercel/ogをインストールして、APIルートで@vercel/ogからImageResponseをインポートすることで動的なOG画像の生成ができました。

https://zenn.dev/monicle/articles/f02e4a12da960b

Next.js 13.3では@vercel/ogをインストールしなくても、ルートハンドラーやファイルベースのメタデータAPIを含むNext.js APIでnext/serverからImageResponseをインポートすることで動的なOG画像の生成ができるようになりました。

app/about/opengraph-image.tsx
import { ImageResponse } from 'next/server';

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

export default function og() {
  return new ImageResponse();
  // ...
}

https://beta.nextjs.org/docs/optimizing/image-generation

Appルーターの静的エクスポート対応

Appルーターが静的エクスポートに対応しました。

next.config.jsoutput: 'export'を指定してnext buildを実行すると、Next.jsはルートごとのHTMLファイルを生成します。厳格なSPAを個々のHTMLファイルに分割することで、Next.jsはクライアントサイドで不要なJavaScriptコードの読み込みを回避し、バンドルサイズを小さくしてページの読み込みを高速化できます。

next.config.js
/**
 * @type {import('next').NextConfig}
 */
const nextConfig = {
  output: 'export',
};

module.exports = nextConfig;

静的エクスポートは静的ルートハンドラーやOG画像、Reactサーバーコンポーネントを含むAppルーターの新機能と連携します。

例えば、サーバーコンポーネントは、従来の静的サイト生成と同様に、ビルド中に実行され、コンポーネントを最初のページの読み込みのための静的HTMLとルート間のクライアント遷移のための静的ペイロードにレンダリングします。

https://beta.nextjs.org/docs/configuring/static-export

並行ルートと横取りルート

並行ルートでは、独立して遷移される同じビューで1つ、または複数のページを同時にレンダリングすることができます。また、条件付きでページをレンダリングすることもできます。

並行ルートは名前付きの "スロット" を使用して作成し、スロットは@folderの規約で定義します。

dashboard
├── @user
│   └── page.js
├── @team
│   └── page.js
├── layout.js
└── page.js

そして、同じルートセグメントのレイアウトは引数としてスロットを受け取ります。

app/dashboard/layout.js
export default async function Layout({ children, user, team }) {
  const userType = getCurrentUserType();

  return (
    <>
      {userType === 'user' ? user : team}
      {children}
    </>
  );
}

上記の例では、明示的な並行ルートのスロットである@user@itemは任意のロジックに基づいて条件付きでレンダリングされます。ここでchildren@folderへ対応される必要がない暗黙のルートのスロットです。例えば、dashboard/page.jsdashboard/@children/page.jsと同等です。

横取りルートでは、ブラウザのURLを"マスキング"しながら現在のレイアウト内で新しいルートを読み込むことができます。これは、モーダルでフィードの写真を展開するとフィードがモーダルの背景に保持されるなど、現在のページのコンテキストを維持することが重要な場合に、便利です。

横取りルートは、相対パス(../)と同様に、(..)の規約で定義されます。また、(...)の規約を使用して、appディレクトリへの相対パスを作成することもできます。

feed
├── @modal
│   └── (..)photo
│       └── [id]
│           └── page.tsx
├── page.tsx
└── layout.tsx
photo
└── [id]
    └── page.tsx

上記の例では、ユーザーのプロフィールから写真をクリックすると、Instagramのようにクライアントサイドの遷移中にモーダルで写真が開かれます。しかし、ページをリロード、またはシェアした場合は通常のレイアウトで写真が読み込まれます。

https://twitter.com/ctnicholasdev/status/1644130884323536899

これは、モーダルのコンテンツをURLでシェアできるようにしたり、ページがリロードされた際にコンテキストが失われるのを防いだり、戻る・進むの遷移でモーダルを閉じたり開いたりするなどの、モーダルを作成する際のいくつかの課題を解決します。

https://beta.nextjs.org/docs/routing/parallel-routes

https://beta.nextjs.org/docs/routing/intercepting-routes

その他のいくつかの改善

  • next.config.jsのファストリフレッシュ: next.config.jsの変更を契機にローカルの開発サーバーを自動的に再起動します。
  • create-next-appのオプションにTailwind CSSを追加: npx create-next-app@latestで新プロジェクトを立ち上げる際、Tailwind CSSを任意で選択することができます。
  • ルーティングから特定のフォルダを除外: フォルダの先頭に_を付けると、ルーティングからそのフォルダと子セグメントを除外することができます。例えば、app/_dashboard/page.tsxはルーティングされません。
  • AppルーターにuseParamsを追加: 与えられたルートセグメントの動的パラメータを読み取るための新しいuseParamsクライアントコンポーネントフックが追加されました。詳しくはこちら。
  • Not Foundの取り扱いの改善: 予想されるnotFound()エラーを捕捉するだけでなく、ルートのapp/not-found.jsファイルはアプリケーション全体の一致しないURLを捕捉します。これは、アプリケーションで取り扱われていないURLを訪れたユーザーにapp/not-found.jsファイルでエクスポートされたUIを表示することを意味します。詳しくはこちら。
  • など

Discussion

Honey32Honey32

失礼します。

intercepting に関してですが、通信の傍受、サッカーでのパスをカットするプレーを指して使われるように、二者の「間」に割り込んで彼らが渡そうとしてるモノを「取る」ニュアンスがある単語だと思います。

↓の解説を考えに入れると、photo/[id]/page.tsx から photo/[id]というルートを 「横取り」してレンダリングされる、というニュアンスに思えるので、「遮断」だけでは不足していて「横取り」「割り込み」のようなニュアンスがある単語のほうが的確に挙動を表しているかと思います。

https://twitter.com/ctnicholasdev/status/1644130894733774851?s=20

Yutaro AdachiYutaro Adachi

ご指摘ありがとうございます。

Intercepting Routesの訳語についてはまさに悩んでいたので、納得のご指摘でした。

コメントとツイートを拝見した上で、単語の意味を調べ直して、「横取りルート」が挙動を表すもっともらしい訳語ではないかと思ったので、そちらで修正させていただきます。

Honey32Honey32

対応ありがとうございます!わずかでも協力できて光栄です!