
Next.js 13のnext/imageは、Next.js 12では「next/future/image」と呼ばれていたものです。Next.js 12で使われていたnext/imageは、Next.js 13では「next/legacy/image」となっています。
※以後、区別しやすいように next/future/image、next/legacy/image と表記します。
Next.js 12から13へ移行する場合、next/imageに関しては2つの選択肢があります。
- next/imageからインポートしていたものを、next/legacy/imageからインポートする形に修正する。
- next/future/imageに合わせて、Imageコンポーネントまわりを修正する。
ここではnext/future/imageに合わせて、Imageコンポーネントを書き換える方法をまとめます。
next/future/imageでは、next/legacy/imageでレスポンシブイメージのために用意されていたものを自前で用意する形になっています。ある意味わかりやすくなりましたが、面倒になったとも言えます。
ただ、どちらにしてもレスポンシブイメージに対する理解は必要です。
※レスポンシブイメージについての基本的な考え方は書籍『作って学ぶ Next.js/React Webサイト構築』のChapter 5にまとめていますので、参考にしてください。
next/future/imageでの主な変更点
next/future/imageでの主な変更点は以下のとおりです。(※()内は書籍の当該ページです)
- 古いブラウザへのサポートが打ち切られました。
- レイアウトシフト対策(P.146)は<img />のwidthとheight属性で行われます。古いブラウザ用の対策は行われないため、<span>が付加されません。
- 遅延読み込み(P.148)は<img />のloading="lazy"で行われます。IntersectionObserverによる古いブラウザ用の対策は行われません。
- layout属性とobjectFit属性が削除されました。
- sizes属性とfill属性によって出力コードが変わるようになっています。
- 用意されなくなったCSSについては自前で用意する必要があります。
- ブラー画像のフェードインをtransition(P.159)で調整できなくなっています。
以上の変更点を踏まえた上で、next/future/imageへの置き換えを進めていきます。置き換えはlayout属性に応じて次のように行います。
next/future/imageへの置き換え
layout="responsive"(サイズを可変にする画像)の場合

layout="responsive" の場合は、layout属性を削除し、sizes属性を追加します。sizes属性の値はどのようなサイズで画像を表示するかに応じて指定します。
さらに、画像を可変にするCSSも用意されていないので、追加します。
// legacy<Image src="/rocket.jpg" alt="空飛ぶロケット" layout="responsive" width={1980} height={1150}/>
▼
// future<Image src="/rocket.jpg" alt="空飛ぶロケット" width={1980} height={1150} sizes="100vw" style={{ width: '100%', height: 'auto', }}/>
<!-- 生成コード --><img alt="空飛ぶロケット" sizes="100vw" srcset=" /_next/image?url=%2Frocket.jpg&w=640&q=75 640w, /_next/image?url=%2Frocket.jpg&w=750&q=75 750w, /_next/image?url=%2Frocket.jpg&w=828&q=75 828w, /_next/image?url=%2Frocket.jpg&w=1080&q=75 1080w, /_next/image?url=%2Frocket.jpg&w=1200&q=75 1200w, /_next/image?url=%2Frocket.jpg&w=1920&q=75 1920w, /_next/image?url=%2Frocket.jpg&w=2048&q=75 2048w, /_next/image?url=%2Frocket.jpg&w=3840&q=75 3840w " src="/_next/image?url=%2Frocket.jpg&w=3840&q=75" width="1980" height="1150" decoding="async" data-nimg="1" loading="lazy" style="color: transparent; height: auto; width: 100%;"/>
layout="fill" (特定のサイズで切り抜く画像)の場合

layout="fill" の場合は、layout属性とobjectFit属性を削除し、fill属性を追加します。さらに、CSSでobject-fitを指定します。
sizes属性は必要に応じて指定します(標準では100vwで処理されます)。
// legacy<figure style={{ position: 'relative', width: '100%', height: '150px' }}> <Image src="/rocket.jpg" alt="空飛ぶロケット" layout="fill" objectFit="cover" /></figure>
▼
// future<figure style={{ position: 'relative', width: '100%', height: '150px' }}> <Image src="/rocket.jpg" alt="空飛ぶロケット" fill style={{ objectFit: 'cover', }} /></figure>
<!-- 生成コード --><figure style="position: relative; width: 100%; height: 150px"> <img alt="空飛ぶロケット" sizes="100vw" srcset=" /_next/image?url=%2Frocket.jpg&w=640&q=75 640w, /_next/image?url=%2Frocket.jpg&w=750&q=75 750w, /_next/image?url=%2Frocket.jpg&w=828&q=75 828w, /_next/image?url=%2Frocket.jpg&w=1080&q=75 1080w, /_next/image?url=%2Frocket.jpg&w=1200&q=75 1200w, /_next/image?url=%2Frocket.jpg&w=1920&q=75 1920w, /_next/image?url=%2Frocket.jpg&w=2048&q=75 2048w, /_next/image?url=%2Frocket.jpg&w=3840&q=75 3840w " src="/_next/image?url=%2Frocket.jpg&w=3840&q=75" decoding="async" data-nimg="fill" loading="lazy" style=" position: absolute; height: 100%; width: 100%; left: 0; top: 0; right: 0; bottom: 0; object-fit: cover; color: transparent; " /></figure>
layout="fixed"(サイズを固定する画像)の場合

layout="fixed" の場合はlayout属性を削除するだけです。
// legacy<Image src="/rocket.jpg" alt=" 空飛ぶロケット" layout="fixed" width={1024} height={595}/>
▼
// future<Image src="/rocket.jpg" alt=" 空飛ぶロケット" width={1024} height={595}/>
<!-- 生成コード --><img alt="空飛ぶロケット" srcset=" /_next/image?url=%2Frocket.jpg&w=1080&q=75 1x, /_next/image?url=%2Frocket.jpg&w=2048&q=75 2x " src="/_next/image?url=%2Frocket.jpg&w=2048&q=75" width="1024" height="595" decoding="async" data-nimg="1" loading="lazy" style="color: transparent"/>
layout="intrinsic"(サイズを固定した上で縮小を可能にする画像)の場合

layout="intrinsic" の場合は、layout="fixed" のときと同じようにlayout属性を削除します。その上で、縮小を可能にするCSSを適用します。
// legacy<Image src="/rocket.jpg" alt=" 空飛ぶロケット" layout="intrinsic" width={1024} height={595}/>
▼
// future<Image src="/rocket.jpg" alt="空飛ぶロケット" width={1024} height={595} style={{ maxWidth: '100%', height: 'auto', }}/>
<!-- 生成コード --><img alt="空飛ぶロケット" srcset=" /_next/image?url=%2Frocket.jpg&w=1080&q=75 1x, /_next/image?url=%2Frocket.jpg&w=2048&q=75 2x " src="/_next/image?url=%2Frocket.jpg&w=2048&q=75" width="1024" height="595" decoding="async" data-nimg="1" loading="lazy" style="color: transparent; max-width: 100%; height: auto"/>
codemodを使った置き換え
上記のような置き換えを行う codemod も用意されています。
Migrate to the new Image Component (next-image-experimental) - Experimental
https://beta.nextjs.org/docs/upgrade-guide/codemods#migrate-to-the-new-image-component-next-image-experimental---experimental
この codemod は next/legacy/image からインポートする形にした上で使用する必要があります。さらに、「Dangerously(危険な)」と書かれているように、layout属性の削除やCSSの追加が行われるため、実行後は問題が出ないかどうかをしっかりと確認する必要があるでしょう。
※『作って学ぶ Next.js/React Webサイト構築』で作成したプロジェクトでは、このcodemodですべての画像の置き換えが可能でした。
外部サイトの画像を使うときの設定
next/imageで外部サイトの画像を使う場合、next.config.js のdomains(P.232)でドメインを設定していましたが、remotePatternsを使うとパスなどを含めたより詳細な設定ができるようになっています。
たとえば、microCMSの画像は「https://images.microcms-assets.io/assets/…/…/photo.jpg」となっていますので、* または ** で次のように指定できます。* は単一のセグメント、** は複数のセグメントを指定します。
module.exports = { images: { remotePatterns: [ { protocol: 'https', hostname: 'images.microcms-assets.io', port: '', pathname: '/assets/*/*/*', }, ], },}
module.exports = { images: { remotePatterns: [ { protocol: 'https', hostname: 'images.microcms-assets.io', port: '', pathname: '/assets/**', }, ], },}
まとめ
ここではlayout属性に応じて置き換える形で見てきましたが、Next.js 13 の next/image の一番のポイントは、
「Imageコンポーネントのsizes属性の有無より、出力されるsrcset属性の画像セットの構成が変わる」
ことです。画像のレイアウトに合わせて画像セットを切り替え、必要なCSSを適用することで、最適化されたレスポンシブイメージを柔軟に利用できるようになっていると言えるでしょう。