20251017

2025/10/17

Next.js

今更だがNext.jsの書籍「Next.js + ヘッドレスCMSではじめる!かんたんモダンWebサイト制作入門 高速で、安全で、運用しやすいサイトのつくりかた」(microCMS著)を購入したので一通りやってみる。

https://github.com/zndk-hys/my-next-project

next/image

本を読んで出てきたサンプルコードについて気になったので調べた。

挙動としては

<Image src="/img-mv.jpg" width={4000} height={1200} alt="" />

これが

<img
  alt=""
  loading="lazy"
  width="4000"
  height="1200"
  decoding="async"
  data-nimg="1"
  class="page_bgimg__c4h1Z"
  style="color:transparent"
  srcset="/_next/image?url=%2Fimg-mv.jpg&amp;w=3840&amp;q=75 1x"
  src="/_next/image?url=%2Fimg-mv.jpg&amp;w=3840&amp;q=75"
 >

こう変換される。

気になるのは srcsetsrc 属性。

srcset="/_next/image?url=%2Fimg-mv.jpg&amp;w=3840&amp;q=75 1x"
src="/_next/image?url=%2Fimg-mv.jpg&amp;w=3840&amp;q=75"

w パラメータが横幅を示していると思うがなぜ 3840 なのか。あとなぜ1xだけなのか。 ちなみに w を1000とかに変えてアクセスしてみると

"w" parameter (width) of 1000 is not allowed

というエラーが出る。

結論からいうと以下の2点が影響している模様。

  • sizes 属性が設定されてない。
  • Next.js で設定されているデフォルトのデバイス幅リストの最大が3840px。

まず sizes が指定されていない場合、Next.jsでは srcset に設定するリストは最小限(1xと2x)にしか作らない。 https://nextjs.org/docs/pages/api-reference/components/image#sizes

次にデバイス幅の設定は next.config.jsimages.deviceSizes オプションで設定できるが、デフォルトは [640, 750, 828, 1080, 1200, 1920, 2048, 3840] となっており、4000px(元画像の横幅)に近い 3840px が選ばれる。

結果的に3840pxの srcsetsrc が出力される、という感じらしい。先ほどのサンプルコードでは 1x しか作られなかったが、もし元画像が7680pxより大きければ2x用の指定も srcset に追加されていたかも。

(追記:8000pxの画像を用意して試したけど追加されなかった。そもそも images.deviceSizes に 7680 の指定がないのでその通りだ。 やる意味はなさそうだが sizes 指定がない状態で 2x の画像が作られる条件を満たすには、元画像はそのままで width に 1800 を指定すればいい。実際の画像サイズ以外を width に指定していいかどうかは謎。アスペクト比の算出のためだけなら問題なさそう。2x 用の画像が作られるということは width の指定だけでなく実画像の横幅もちゃんと取得しているらしい。)

なのでレスポンシブを考慮するのであれば基本的に Image コンポーネントにも sizes 属性を指定した方がよさそう。常に横幅いっぱいに表示される画像についても 100vw の指定を追加するだけで srcset が適切に生成される。

<img
  alt=""
  loading="lazy"
  width="4000"
  height="1200"
  decoding="async"
  data-nimg="1"
  class="page_bgimg__c4h1Z"
  style="color:transparent"
  sizes="100vw"
  srcset="/_next/image?url=%2Fimg-mv.jpg&amp;w=640&amp;q=75 640w, /_next/image?url=%2Fimg-mv.jpg&amp;w=750&amp;q=75 750w, /_next/image?url=%2Fimg-mv.jpg&amp;w=828&amp;q=75 828w, /_next/image?url=%2Fimg-mv.jpg&amp;w=1080&amp;q=75 1080w, /_next/image?url=%2Fimg-mv.jpg&amp;w=1200&amp;q=75 1200w, /_next/image?url=%2Fimg-mv.jpg&amp;w=1920&amp;q=75 1920w, /_next/image?url=%2Fimg-mv.jpg&amp;w=2048&amp;q=75 2048w, /_next/image?url=%2Fimg-mv.jpg&amp;w=3840&amp;q=75 3840w"
  src="/_next/image?url=%2Fimg-mv.jpg&amp;w=3840&amp;q=75"
>

images.deviceSizes のデフォルト値( [640, 750, 828, 1080, 1200, 1920, 2048, 3840] )それぞれに対して srcset が設定されているのがわかる。

ちなみにビルド時の画像サイズの判定処理には images.deviceSizes に加えて images.imageSizes も使われる模様。こちらは画像の横幅を指定する。

next/imageの仕組みとしては

  • 適切な画像サイズのファイルをあらかじめ用意する
  • 「適切な画像サイズ」はあらかじめ決められた中から選ばれる。( images.deviceSizesimages.imageSizes の指定)
  • どのサイズが適しているかを判断するにはオリジナルの画像の横幅と sizes が必要。
    • sizesvw で指定されていたら基準となるデバイス幅を元に実際のサイズを計算し、適切な画像サイズを選ぶ。
    • sizespx で指定されていたら直接 images.deviceSizesimages.imageSizes の中から適切な画像サイズを選ぶ

という感じ? まだ理解が足りてない気がする。

めちゃくちゃシンプルに言うと next/image は srcset 生成機?

  • オリジナルの画像ファイルの横幅
  • width
  • sizes
  • fill
  • images.deviceSizes
  • images.imageSizes

最低限これらの情報をもとに srcset の指定値を生成する。

画像を配信するURL( /_next/image?url=***&w=*** )は srcset に関係なく、images.deviceSizesimages.imageSizes に指定されている w であれば画像を返す。

キャッシュ機能も併せ持っているようなのでただの srcset 生成機というのは言い過ぎ。

CSS Modules

CSSファイルをモジュールとして扱うやり方。

import styles from './page.module.css';

export default function Page() {
  return (
    <h1 className={styles.title}>Hello</h1>
  );
}

CSSの中身は通常のCSS。

.title {
  font-size: 30px;
  font-weight: 700;
}

モジュール化することでクラス名の重複を防ぐことができる。

具体的にはビルド後のCSSではクラス名にハッシュが付与される。(JSX側の {styles.title} ではハッシュ付きのクラス名が使われる)

.page_title__po7na {
  font-size: 30px;
  font-weight: 700;
}

このハッシュはモジュールごとに異なるようになっているため、別の箇所のスタイル用に title クラスが使われていたとしても相互に影響を及ぼさなくなる。

CSS Modules ではクラス名にしかハッシュはつけない。IDや要素でのセレクタはそのまま出力されるため、グローバルなスタイル適用となってしまう。