👯‍♀️

Next.jsで画像の出しわけってどうやるの?getImagePropsの使い方を見てみよう!

に公開

はじめに

Next.js では Image コンポーネントを使用することが推奨されていますが、デバイス毎の画像出し分けをどのように実装すれば良いか知らない人も少なくないはず…🤔

レスポンシブデザインでデスクトップとモバイルで異なる画像を表示したい場合、ついつい JavaScript で画面幅を判定してしまいがちですが、実はこれは最適ではありません!

今回は、Next.js の getImageProps を使った効率的な画像出し分け方法を紹介します🚀

問題:よくある非効率な実装

JavaScript による条件分岐

import { useMediaQuery } from 'react-responsive'

export const HeroImage = () => {
  const isMobile = useMediaQuery('(max-width: 766px)')

  return isMobile ? (
    <Image src="/sp.jpg" alt="hero" width={375} height={812} />
  ) : (
    <Image src="/pc.jpg" alt="hero" width={1200} height={800} />
  )
}

display: none による切り替え

export const HeroImage = () => {
  return (
    <>
      <Image 
        src="/pc.jpg" 
        alt="hero" 
        className="block md:hidden"
        width={1200} 
        height={800} 
      />
      <Image 
        src="/sp.jpg" 
        alt="hero" 
        className="hidden md:block"
        width={375} 
        height={812} 
      />
    </>
  )
}

なぜこれらの方法が非効率なのか?

JavaScript 判定の問題点

  1. クライアントサイドでの判定: 初期レンダリング時に正しく判定できない
  2. レイアウトシフト: 画面サイズ判定後に画像が切り替わる
  3. 余計な JavaScript: メディアクエリで解決できる問題をJSで処理している

display: none の問題点

  1. 両方の画像を読み込み: 非表示でも画像のダウンロードが発生
  2. パフォーマンス低下: 不要な通信でページ表示が遅くなる
  3. メモリ消費: 使わない画像もメモリに保持されてしまう

解決方法:getImageProps + picture要素

Next.js 14以降で利用できる getImageProps を使用した最適な実装方法です💘

import { getImageProps } from "next/image"

export const ResponsiveHeroImage = () => {
  const common = {
    alt: "hero",
    className: "w-full h-auto object-cover",
  }

  // デスクトップ用画像の設定
  const {
    props: { srcSet: desktop },
  } = getImageProps({
    ...common,
    width: 1200,
    height: 800,
    quality: 90,
    src: "/images/hero-desktop.jpg",
  })

  // モバイル用画像の設定
  const {
    props: { srcSet: mobile, ...rest },
  } = getImageProps({
    ...common,
    width: 375,
    height: 812,
    quality: 80,
    src: "/images/hero-mobile.jpg",
  })

  return (
    <picture>
      <source media="(min-width: 768px)" srcSet={desktop} />
      <source media="(max-width: 767px)" srcSet={mobile} />
      <img {...rest} alt="hero" />
    </picture>
  )
}

実装の詳細解説

getImageProps の活用

getImageProps は Next.js の Image コンポーネントの機能を関数として利用できます👯‍♀️

const {
  props: { srcSet: desktop },
} = getImageProps({
  width: 1200,
  height: 800,
  quality: 90,
  src: "/images/hero-desktop.jpg",
  alt: "hero"
})

// 生成される srcSet
// "/_next/image?url=%2Fimages%2Fhero-desktop.jpg&w=1200&q=90 1x, /_next/image?url=%2Fimages%2Fhero-desktop.jpg&w=2400&q=90 2x"

picture要素による条件分岐

<picture>
  <source media="(min-width: 768px)" srcSet={desktop} />
  <source media="(max-width: 767px)" srcSet={mobile} />
  <img {...rest} alt="hero" />
</picture>

ブラウザが自動的に適切な画像を選択して読み込みます🤖

実用的な使用例

背景画像での活用

import { getImageProps } from "next/image"

export const BackgroundImage = () => {
  const common = {
    alt: "background",
    className: "absolute inset-0 w-full h-full object-cover blur-sm",
  }

  const {
    props: { srcSet: desktop },
  } = getImageProps({
    ...common,
    width: 1920,
    height: 1080,
    quality: 85,
    src: "/images/bg-desktop.jpg",
  })

  const {
    props: { srcSet: mobile, ...rest },
  } = getImageProps({
    ...common,
    width: 768,
    height: 1024,
    quality: 75,
    src: "/images/bg-mobile.jpg",
  })

  return (
    <picture>
      <source media="(min-width: 768px)" srcSet={desktop} />
      <source media="(max-width: 767px)" srcSet={mobile} />
      <img {...rest} alt="background" />
    </picture>
  )
}

タブレット対応の3パターン分岐

export const MultiDeviceImage = () => {
  const common = {
    alt: "content",
    className: "w-full h-auto",
  }

  const {
    props: { srcSet: desktop },
  } = getImageProps({
    ...common,
    width: 1200,
    height: 800,
    src: "/images/content-desktop.jpg",
  })

  const {
    props: { srcSet: tablet },
  } = getImageProps({
    ...common,
    width: 768,
    height: 1024,
    src: "/images/content-tablet.jpg",
  })

  const {
    props: { srcSet: mobile, ...rest },
  } = getImageProps({
    ...common,
    width: 375,
    height: 812,
    src: "/images/content-mobile.jpg",
  })

  return (
    <picture>
      <source media="(min-width: 1024px)" srcSet={desktop} />
      <source media="(min-width: 768px)" srcSet={tablet} />
      <source media="(max-width: 767px)" srcSet={mobile} />
      <img {...rest} alt="content" />
    </picture>
  )
}

メリット

パフォーマンス向上

  • 必要な画像のみ読み込み: 条件に合致した画像だけダウンロード
  • サーバーサイドレンダリング対応: 初期レンダリングから正しい画像
  • レイアウトシフト回避: JavaScript判定による遅延がない

実装上のメリット

  • Next.js の画像最適化が自動適用: ファイルサイズの自動圧縮による高速化
  • コンポーネントの再利用性: 共通の設定を使い回しやすい構造
  • メンテナンス性: picture要素による明確な条件分岐で可読性が高い

SEO・アクセシビリティ

  • 共通プロパティの一元管理: commonオブジェクトでalt属性やclassNameを一箇所定義するだけで全デバイスに適用
  • クロールしやすい構造: picture要素は検索エンジンが理解しやすい
  • プログレッシブエンハンスメント: JavaScriptが無効でも動作

注意点

Next.js バージョン

getImageProps は Next.js 14以降で利用可能です🤞

{
  "dependencies": {
    "next": "^14.0.0"
  }
}

画像ファイルの準備

各端末用に最適化された画像を事前に用意する必要があります🎁

public/
├── images/
│   ├── hero-desktop.jpg  (1200x800)
│   ├── hero-mobile.jpg   (375x812)
│   ├── bg-desktop.jpg    (1920x1080)
│   └── bg-mobile.jpg     (768x1024)

まとめ

Next.js の getImageProps と picture要素を組み合わせることで、効率的な端末別画像出し分けが実現できます🎯

この方法により:

  • パフォーマンス向上: 必要な画像のみ読み込み
  • SEO対応: サーバーサイドレンダリングで最適化
  • 開発効率: Next.jsの画像最適化機能をフル活用

JavaScriptによる判定や display: none による切り替えから卒業して、より効率的な画像配信を実現しましょう!

レスポンシブな画像対応でお悩みの方の参考になれば嬉しく思います✨!!!

Discussion