⚱️

【基本を解説】Next.jsの画像最適化システム「next/image」

2022/10/18に公開

はじめに

今回の記事では、Next.jsにおける画像最適化システムであるnext/imageの基本を解説する。Next.js ver10ではnext/imageから提供されるコンポーネントを使うだけで、簡単に画像を最適化できるようになる。今回の記事では、サンプルコードを使いながらnext/imageへの理解を深める。

本記事の読者の対象は以下の通り。

  • Next.jsの基礎知識を理解しているプログラマー
  • next/imageに対する理解を深めたいプログラマー

ちなみに、本記事で使用しているサンプルコードはすべてNext.jsの公式ドキュメントから引用している。

基本的な使い方

原則、<Image />タグを使って実装する。

src

主に以下のものを含められる。

  • 静的な画像ファイル(サイズを変更できない画像)
  • パス文字列

外部のURLで文字列を表示する場合、next.config.jsremotePatternsに外部のURLを追加しなければならない。

width

layoutあるいはsize属性に応じて、レンダリング幅または元の幅をpx(ピクセル)単位で表現できる。

  • layout="intrinsic"あるいはlayout="fixed"を用いる場合:widthはpx単位でレンダリング幅を表すゆえ、画像の表示サイズに影響する。
  • layout="responsive"あるいはlayout="fill"を用いる場合:widthはpx単位で元の幅を表すので、アスペクト比にのみ影響する。

静的な画像ファイルとlayout="fill"を使った画像を除いて、widthは必須である。

height

layoutあるいはsize属性に応じて、レンダリングの高さまたは元の高さをpx(ピクセル)単位で表現できる。

  • layout="intrinsic"あるいはlayout="fixed"を用いる場合:widthはpx単位でレンダリングの高さを表すゆえ、画像の表示サイズに影響する。
  • layout="responsive"あるいはlayout="fill"を用いる場合:widthはpx単位で元の高さを表すので、アスペクト比にのみ影響する。

静的な画像ファイルとlayout="fill"を使った画像を除いて、heightは必須である。

プロパティの意味

<Image />コンポーネントは必要なプロパティ以外にも多くのプロパティを持つ。最も一般的に使われているプロパティは以下の通り。

  • intrinsic:コンテナの幅に応じて縮小し、画像サイズに合わせて拡大する。内在する場合、画像は小さいビューであれば縮小されるものの、大きいビューであれば元の寸法が維持される。
  • fixed:幅と高さにちょうど合わせたサイズ。修正された場合、元のimg属性と同様にビューが変化しても画像の寸法が変化しないようになる。
  • responsive:コンテナの幅に合わせて拡大縮小する。レスポンシブデザインを作る上では必要不可欠必ず親要素のスタイルシート(CSSあるいはSCSS)にてdisplay: blockが表記されていることを確認すること
  • fill:画像を画面いっぱいに拡大する。画像は幅と高さの両方を親要素の寸法に伸ばす。しかし、親要素が相対的であることが条件である。必ず親要素のスタイルシートにてposition: relativeがあることを確認すること。

loader

URLを解決するために使用されるカスタム要素である。Imageコンポーネントをpropとしてloaderを設定すると、next.config.jsimagesセクションで定義されたデフォルトのloaderが継承される。loaderは以下のパラメータを与えて、画像のURL文字列を返す関数である

  • src
  • width
  • quality

以下にnext/imageを使ったサンプルコードを示す。

import Image from 'next/image'

const myLoader = ({ src, width, quality }) => {
  return `https://example.com/${src}?w=${width}&q=${quality || 75}`
}

const MyImage = (props) => {
  return (
    <Image
      loader={myLoader}
      src="me.png"
      alt="Picture of the author"
      width={500}
      height={500}
    />
  )
}

sizes

sizesはNext.jsで画像の幅に関する情報を提供する文字列を示す。sizesの値はlayout="responsive"layout="fill"を使っている画像のパフォーマンスに大きく影響する。layout="intrinsic"あるいはlayout="fixed"を使っている画像では適用されない。

sizesプロパティは画像のパフォーマンスに関連する2つの重要な役割を果たす。

  1. sizeの値を使って、next/imageの自動生成されたソースセットからダウンロードする画像のサイズを決定する役割。sizesを使うと、画像が実際にはフルスクリーンよりも小さくなることをブラウザに伝達できる。値を指定しない場合は、デフォルト値である100vw(フルスクリーン幅)が適用される
  2. sizeの値を解析して、自動的に作成されるソースセットの内側にある値をトリミングする際に使う役割。

例えば、スマートフォンでは画像をフル幅で表示し、タブレットでは2列レイアウト、デスクトップディスプレイでは3列レイアウトとする場合は以下のようなsizesプロパティを記述する。

import Image from 'next/image'

const Example = () => (
  <div className="grid-element">
    <Image
      src="/example.png"
      layout="fill"
      sizes="(max-width: 768px) 100vw,
              (max-width: 1200px) 50vw,
              33vw"
    />
  </div>
)

上記のソースコードは、Next.jsでレスポンシブデザインを制作する上では非常に重要なので十分に理解しておくこと。

priority

trueの場合、画像が優先度の高いものであると認識される。そのため、優先度が高いものはLazy Load(画像の遅延読み込み。ページをの表示速度を向上させるための具体的なテクニック)が自動で無効化される。デフォルトはfalse

style

基礎となる画像要素にCSSを適用できるプロパティ。すべてのレイアウトモードは画像要素に独自のスタイルを適用する。これらの自動的なスタイル(CSSのプログラム)は、styleよりも優先度が高いことには注意が必要だ。

さらに、必要なwidthheightの小道具は自分で作ったCSSプログラムと相互作用する可能性があることには十分に留意すること。画像の幅を変更するためにスタイルを使用する場合、height="auto"スタイルを同様に設定しなければならない。さもないと画像がゆがむ。

lazyRoot

スクロール可能な親要素を意味するReactのRef属性。デフォルトはnullRefはDOM要素あるいは、RefをベースにしたDOM要素に転送するReactコンポーネントを指し示していなければならない。

// DOM要素を指定している場合
import Image from 'next/image'
import React from 'react'

const Example = () => {
  const lazyRoot = React.useRef(null)

  return (
    <div ref={lazyRoot} style={{ overflowX: 'scroll', width: '500px' }}>
      <Image lazyRoot={lazyRoot} src="/one.jpg" width="500" height="500" />
      <Image lazyRoot={lazyRoot} src="/two.jpg" width="500" height="500" />
    </div>
  )
}
// Reactコンポーネントを指定している場合
import Image from 'next/image'
import React from 'react'

const Container = React.forwardRef((props, ref) => {
  return (
    <div ref={ref} style={{ overflowX: 'scroll', width: '500px' }}>
      {props.children}
    </div>
  )
})

const Example = () => {
  const lazyRoot = React.useRef(null)

  return (
    <Container ref={lazyRoot}>
      <Image lazyRoot={lazyRoot} src="/one.jpg" width="500" height="500" />
      <Image lazyRoot={lazyRoot} src="/two.jpg" width="500" height="500" />
    </Container>
  )
}

unoptimized

trueの場合、ソース画像は品質、サイズやフォーマットを変更しないでそのまま適用される。要は、next/imageに画像最適化してほしくない場合にこれを設定しよう。デフォルトはfalsenext.config.jsを以下のような設定で更新することでこのプロパティをすべての画像に適用できる。

next.config.js
module.exports = {
  images: {
    unoptimized: true,
  },
}

知っておいて損はないテクニック

remotePatternsの設定

悪意のあるユーザからアプリケーションを保護するために、Next.jsでは外部画像を利用するための設定ができる。これを使うことで、Next.jsから提供される画像は自分のアカウントの外部の画像のみになる。next.config.jsremotePatternsプロパティを使って以下のように設定できる。

next.config.js
module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'example.com',
        port: '',
        pathname: '/account123/**',
      },
    ],
  },
}

すべてのドメインからの画像を取得することを許可する場合は以下のように実装する。

next.config.js
module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: '**.example.com',
      },
    ],
  },
}

静的ファイル(画像)のインポートを無効化する方法

デフォルトの動作では、

import icon from './icon.png'

上記のように静的ファイルをインポートしてそれをsrcプロパティに渡せる。(Next.jsはReactをベースに開発されているので)場合によっては、インポートの際に異なる動作を期待する他のプラグインと競合する故に、この機能を無効化できる。静的画像のインポートを無効化するにはnext.config.jsの中で行う。

next.config.js
module.exports = {
  images: {
    disableStaticImages: true,
  },
}

例えば、画像数が100を超えるような場合は、あらかじめ全画像をインポートするのは得策ではない。(画像を使う際に逐一import文を書かなければならないため)

参考サイト

https://nextjs.org/docs/api-reference/next/image

https://zenn.dev/saitoeku3/articles/read-next-image

https://www.trusty-systems.com/blog/detail.php?id=400

https://webbibouroku.com/Blog/Article/react-public-folder

GitHubで編集を提案

Discussion