Next.js v13.3
Next.js v13.3 がリリース。
ファイルベース Metadata API
Next.js v13.2 で追加されたコンフィグベースの Metadata API に加えて、ファイル規則によってメタデータをカスタマイズ出来るようになりました。この機能は、App Router でのみ利用できます。
次のように利用します。
app
├── favicon.(ico|jpg|png|svg)
├── sitemap.(xml|js|jsx)
├── robots.(txt|js|jsx)
├── manifest.(json|js|jsx)
├── layout.js
├── page.js
└── about
├── opengraph-image.(jpg|png|svg)
├── twitter-image.(jpg|png|svg)
└── page.js配置された favicon、sitemap、 robots、manifest、opengraph-image、twitter-image は自動で head に反映されます。
動的なファイル生成
JS や JSX をサポートしている sitemap や robots、manifest では動的な生成が可能です。
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];
}動的な OGP 及び Twitter 画像生成
ファイルベース Metadata API における opengraph-image や twitter-image も動的な生成が可能です。拡張を js や jsx にして次のようにします。
// /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() {
// 中身であるsatoriと同様の記述が出来ます。
return new ImageResponse(
(
<div
style={{
fontSize: 128,
background: "white",
width: "100%",
height: "100%",
display: "flex",
textAlign: "center",
alignItems: "center",
justifyContent: "center",
}}
>
Hello world!
</div>
),
{
width: 1200,
height: 600,
}
);
}App Router の静的エクスポート
App Router を完全に静的なファイルとして出力できるようになりました。
**
* @type {import('next').NextConfig}
*/
const nextConfig = {
output: 'export',
};
module.exports = nextConfig;静的エクスポートの場合、サーバーコンポーネントや動的な画像生成などは、ビルド中に実行されます。ただし、サーバーが必要な headers()や cookies()などは実行出来ません。
また、この変更により next export コマンドは不要になりました。
Interception Routes
Interception routes は URL を変化させつつ、現在のレイアウトに新しいページを表示します。Interception routes では、相対パスの../を表す(..)や app からの相対パスを表す(…)を利用します。
次は Vercel が用意したサンプルアプリケーションの構成です。
app
├── @modal
│ └── (..)photos
│ └── [id]
│ └── page.tsx
├── page.tsx
├── layout.tsx
└── photos
└── [id]
└── page.tsxルートページには画像一覧があり、画像をクリックすると/app/@modal/(..)photos/[id]/page.tsx で定義された画像を子要素に持つモーダルが表示されますが、このとき URL は../photos/[id]になります。この状態でページをリロードすると、表示されるのは/app/photos/[id]/page.tsx の内容になります。
Parallel Routes
Parallel routes は、同じレイアウトの中に複数のページを表示します。Interception Routes で紹介したサンプルアプリケーションでは、この機能によってルートページに画像一覧とモーダルを同時に表示しています。
Parallel routes は、Web components の slot をイメージすると良いと思います。ディレクトリ名の先頭@を付けて、名前付きのスロットとして定義して利用します。サンプルアプリケーションでは@modal がこれに相当します。
app
├── @modal
│ └── (..)photos
│ └── [id]
│ └── page.tsx
├── page.tsx
├── layout.tsx
└── photos
└── [id]
└── page.tsx名前付きのスロットは同じ階層にある layout.js の props から利用します。サンプルアプリケーションでは app/layout.js が相当します。
export default function Layout(props) {
return (
<html>
<body>
<GithubCorner />
{props.children}
{props.modal}
</body>
</html>
);
}