最適化
Next.js
Image・Font・Metadata・Bundle
Image と Font の最適化
next/image・next/font でCore Web Vitalsを改善
import Image from 'next/image';
import { Inter, Noto_Sans_JP } from 'next/font/google';
import localFont from 'next/font/local';
// === next/image ===
// ✅ 自動的に WebP/AVIF に変換
// ✅ 遅延読み込み
// ✅ CLS 防止(サイズ予約)
// ✅ レスポンシブ対応
function Hero() {
return (
<>
{/* 固定サイズ */}
<Image src="/hero.jpg" alt="Hero" width={1200} height={630} priority />
{/* fill(親要素のサイズに合わせる)*/}
<div className="relative h-64">
<Image src="/bg.jpg" alt="Background" fill className="object-cover" />
</div>
{/* 外部URL(next.config.js で許可が必要)*/}
<Image src="https://cdn.example.com/img.jpg" alt="" width={400} height={300} />
</>
);
}
// === next/font(レイアウトシフトなし・プライバシー保護)===
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter',
display: 'swap',
});
const notoSansJP = Noto_Sans_JP({
weight: ['400', '700'],
subsets: ['latin'],
variable: '--font-noto',
});
const myFont = localFont({
src: './fonts/MyFont.woff2',
variable: '--font-my',
});
export default function Layout({ children }) {
return (
<html className={`${inter.variable} ${notoSansJP.variable}`}>
<body>{children}</body>
</html>
);
}Metadata と SEO
静的・動的メタデータ、OGP、sitemap
import type { Metadata } from 'next';
// 静的メタデータ
export const metadata: Metadata = {
title: {
default: 'MyApp',
template: '%s | MyApp', // 子ページのタイトルに付与
},
description: 'アプリの説明',
keywords: ['Next.js', 'React'],
authors: [{ name: 'Author' }],
openGraph: {
type: 'website',
url: 'https://myapp.com',
title: 'MyApp',
description: 'アプリの説明',
images: [{ url: 'https://myapp.com/og.png', width: 1200, height: 630 }],
},
twitter: {
card: 'summary_large_image',
creator: '@handle',
},
robots: {
index: true,
follow: true,
},
};
// sitemap.ts
import type { MetadataRoute } from 'next';
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const posts = await getAllPosts();
return [
{ url: 'https://myapp.com', lastModified: new Date(), changeFrequency: 'daily', priority: 1 },
...posts.map(p => ({
url: `https://myapp.com/blog/${p.slug}`,
lastModified: p.updatedAt,
changeFrequency: 'weekly' as const,
priority: 0.8,
})),
];
}