<img>タグのまま? Next.js開発者が抱える画像最適化の「面倒くさい」問題
Next.jsでの開発中、画像の扱いに悩んでいませんか? パフォーマンスの重要性は理解しつつも、従来の`<img>`タグをそのまま使ってしまっているケースは少なくありません。
Next.jsには強力な画像最適化コンポーネントである`next/image`が用意されていますが、その導入にはいくつかの心理的ハードルが存在します。
「とりあえず<img>」が引き起こすパフォーマンスの懸念
<img>タグをそのまま使用すると、以下のような問題が発生しがちです。
- 巨大な生画像の読み込みによる、ページの表示速度(特にLCP)の悪化。
- ブラウザがサポートしているにもかかわらず、WebPなどの次世代フォーマットを提供できない。
- レスポンシブ対応のために、手動で複数の画像サイズ(srcset)を管理する必要がある。
- 画像の読み込み遅延(Lazy Loading)を自前で実装する必要がある。
これらの問題は、Core Web Vitalsのスコアに直接影響し、結果としてユーザー体験(UX)やSEO評価の低下につながる可能性があります。
next/imageへの移行を妨げる「3つの誤解」
多くの開発者が`next/image`の利用をためらう背景には、以下のような誤解や疑問があります。
- 「設定が面倒そう」:
next.config.jsの記述や、多くの`props`(プロパティ)を覚えるのが大変だと感じてしまう。 - 「画像の置き場所が分からない」:
publicディレクトリに置くべきか、src/assetsなどからimportすべきか、明確な基準が分からない。 - 「自動化の範囲が不明確」: 「どこまで自動でやってくれるのか?」が具体的に分からず、導入メリットを確信できない。
本記事では、これらの疑問を解消し、next/imageがいかに強力で、かつ簡単に開発者の負担を軽減してくれるかを解説します。
手動での画像最適化が招く「工数」と「UX低下」という悪循環
next/imageを使わずに手動で画像最適化を行おうとすると、開発プロセスは一気に複雑化します。
Core Web Vitals(特にLCP)への深刻な影響
LCP (Largest Contentful Paint) は、ビューポート内で最も大きなコンテンツ(多くの場合、画像や動画)が読み込まれる速度を示す指標です。<img>タグで巨大な画像をそのまま配信すると、LCPの値は著しく悪化します。
また、画像のサイズ(width, height)が指定されていないと、画像読み込み後にレイアウトがガタつくCLS (Cumulative Layout Shift) が発生し、ユーザーにストレスを与えてしまいます。
Core Web Vitalsは、Googleが提唱するUXの質を測定するための核となる指標群です。LCP、CLS、FID(INP)が含まれ、これらは検索順位にも影響を与える要因とされています。
デバイスごとに画像を用意する非効率な運用
スマートフォン、タブレット、PCなど、異なるデバイス解像度(ビューポート)に対応するため、<img>タグのsrcset属性を手動で管理することを想像してみてください。
- デザインカンプから画像を書き出す。
@1x,@2x,@3x… または320w,640w,1024w… など、複数の解像度を手動で(またはスクリプトで)生成する。- それらをすべてリポジトリにコミットし、`srcset`に記述する。
この作業は非常に手間がかかるだけでなく、画像の差し替えが発生するたびに同様の作業が必要となり、メンテナンスコストが膨れ上がります。
解決策はnext/image! 驚くべき「画像最適化の自動化」機能
next/imageコンポーネントは、これらの問題を「自動的」に解決するために設計されています。開発者は、最適化のロジックを意識することなく、コンポーネントを使うだけでその恩恵を受けられます。
1. 次世代フォーマット(WebP/AVIF)への自動変換
開発者が用意する元画像がJPEGやPNGであっても、next/imageはブラウザのサポート状況(Acceptヘッダー)に応じて、自動的にWebPやAVIFといった次世代フォーマットに変換して配信します。
これにより、画質を維持したままファイルサイズを大幅に削減(一般的にJPEGより25〜35%削減)でき、読み込み速度が向上します。
2. デバイスサイズに応じた自動リサイズ(`srcset`の動的生成)
手動での`srcset`管理は不要です。`next/image`は、デバイスのビューポートや`sizes`プロパティに基づき、必要な画像サイズをオンデマンドで自動生成し、適切な`srcset`をHTMLに出力します。
開発者は基本的に1つの高解像度な元画像を用意するだけで、あとはNext.jsが最適な画像を配信してくれます。
3. ビューポート外画像の自動遅延読み込み(Lazy Loading)
デフォルトで、next/imageは遅延読み込み(loading=”lazy”)が有効になっています。これは、画像がビューポート(画面表示領域)に近づくまで読み込みを開始しない技術です。
ページの初期読み込み速度(First Contentful Paint)を高速化し、不要なデータ通信を削減できます。(※LCPになる可能性のある画像には priority={true} を指定することが推奨されます)
4. CLS(レイアウトシフト)の自動防止
next/imageは、widthとheightプロパティを必須(または`fill`プロパティの使用)とすることで、画像の表示領域を事前に確保します。これにより、画像読み込みによるレイアウトのガタつき(CLS)を自動的に防ぎます。
開発者の疑問に答える! publicとsrc/assetsの明確な使い分け基準
next/imageの利点は分かりましたが、最も混乱しやすいのが「画像をどこに置くか」です。publicディレクトリとsrc(またはapp)ディレクトリ内のassetsフォルダ、どちらを使うべきでしょうか。
結論:原則としてsrc(assets)からの「静的インポート」を推奨
アプリケーションのコンポーネント内で使用するロゴ、アイコン、記事のサムネイルなど、ビルド時に存在が確定している画像は、src/assets(またはapp/assetsなど)のディレクトリに配置し、importして使用することを強く推奨します。
src/app/page.tsx で静的インポートする例:
import Image from 'next/image';
import profileImage from '@/assets/images/profile.png'; // (1) src/assetsから画像をimport
export default function Home() {
return (
<main>
<h1>My Profile</h1>
{/* (2) importした変数を src プロパティに渡す */}
<Image
src={profileImage}
alt="プロフィール画像"
placeholder="blur" // オプション: ブラーアップ効果
/>
</main>
);
}
静的インポートがもたらす3大メリット
1. ビルド時の存在チェック
画像ファイルが存在しない場合、`import`時にエラーが発生するため、リンク切れの画像をデプロイしてしまうミスを防げます。
2. widthと`height`の自動設定
静的にインポートされた画像(`.jpg`, .png, .webpなど)は、Next.jsが自動的に`width`と`height`を検出し、コンポーネントに設定します。開発者が手動で指定する必要がありません。
3. 強力なキャッシュ(コンテンツハッシュ)
インポートされた画像は、ビルド時にコンテンツハッシュ(例: _next/image?url=%2F_next%2Fstatic%2Fmedia%2Fprofile.a1b2c3d4.png…)が付与されます。画像が更新されない限りURLが変わらないため、ブラウザやCDNで強力にキャッシュされます。
ではpublicディレクトリはいつ使うのか?
publicディレクトリは、Next.jsのビルドプロセスから「除外」され、そのままルート(`/`)に配置されるファイル群です。`next/image`で`public`内の画像を使う場合、パスは文字列(例: /images/hero.jpg)で指定します。
publicが適しているケース
favicon.icoやrobots.txt,sitemap.xml: これらは`src`からのインポート対象外であり、publicに配置する必要があります。- CMSや外部DBからパスが提供される画像: ビルド時に存在が確定できない、動的に変わる画像パス(例:
/uploads/image.jpg)を参照する場合。ただし、このケースでは`src`に文字列を渡すため、`width`と`height`を手動で指定する必要があります。 - (非推奨)静的インポートのメリットが不要な、ごく一部の静的アセット。
外部ドメイン画像の最適化と設定(next.config.js)
CMSや画像配信サービス(例: microCMS, Cloudinary)など、外部ドメインにある画像も`next/image`で最適化できます。ただし、セキュリティのため、許可するドメインをnext.config.js(またはnext.config.mjs)に明示的に設定する必要があります。
next.config.mjs で外部ドメインを許可する例:
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'images.microcms-assets.io',
port: '',
pathname: '/assets/**',
},
],
},
};
export default nextConfig;
この設定により、指定したドメインの画像もNext.jsの画像最適化サーバーを経由し、WebP変換やリサイズの恩恵を受けられるようになります。
next/imageで実現する未来:開発体験(DX)とユーザー体験(UX)の同時向上
next/imageは、単なる<img>タグの代替ではありません。画像最適化という複雑で面倒なタスクをフレームワークレベルで自動化し、開発者(DX)とユーザー(UX)の両方に大きなメリットをもたらす強力な機能です。
まずは1枚の画像から`next/image`に置き換えてみよう
これまで<img>タグを使っていた箇所を、まずは1つでも`next/image`に置き換えてみてください。特に、`src`からの静的インポートを試すことで、width/height指定の手間がなくなる快適さと、Lighthouseスコアの改善をすぐに体感できるはずです。
publicと`src`の使い分けをマスターし、Next.jsのポテンシャルを最大限に引き出しましょう。

