🦁

【Next.js】Imageコンポーネントの画像が表示されないときに疑うこと

2022/07/06に公開

Next.js で実装をしていてpublicに配置したはずの画像がうまく表示されず、一瞬困りました。
よく考えてみたら解決できたのでその方法をまとめます。

前提

開発環境、ディレクトリ構成などを以下に示します。

開発環境

ソフトウェア、アプリ バージョン 確認方法
docker Docker version 20.10.14 コマンドで確認
Node node:14.17.0 Dockerfileで確認
Next.js "next": "12.1.4" package.json
yarn "yarn": "1.22.5” yarn -v で確認
typescript "typescript": "4.6.3" package.json

ディレクトリ構成

Next.jsの pages(ディレクトリ) の構成は以下の通りです。

pages
      |- api
              |- coffeeshops.ts // コーヒーショップのリストを返却する(例題)

実装方針

Next.jsのapi routerからAPIをコールして、画像を表示します。
レスポンスのデータの中には画像ファイルのパスが含まれており、画像ファイルはAPIサーバから公開(配信)されています。

レスポンスとして以下のようなjsonを返却します。
説明をするとコーヒーショップのオブジェクトをcoffeeShopListの配列に保持しています。

export const coffeeShopList = [
  {
    id: 1,
    name: 'hogehoge Coffee',
    url: 'https://hogehoge.coffee/',
    country: { id: 0, name: 'Unknown' },
    price: { id:1, value: 800 },
    shopArea: { id:1, name: 'Fukuoka' },
    cardImage: {
      id: 1,
      src: '/hogehoge_1.png',
      alt: 'coffeeShopPng'
    }
  }
]

解決方法

解決のポイントはimportを使わず、直接画像のパスをオブジェクトの中にセットすることです
今回の実装ではcardImage.srcをNext.jsのImageコンポーネントのsrcに渡すようにしています。

直接、オブジェクトに /(画像ファイル名) という形で画像のファイルパスを渡してあげることで画像を表示することができます。

またここでimportを使って画像を参照しようとすると、ビルド時にうまくいきませんでした。
うまくいかないパターンについては、以降の項で説明します。

coffeeshop.ts
import type { NextApiRequest, NextApiResponse } from 'next';

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<CoffeeShop[]>
) {
    let result = coffeeShopList;
    res.status(200).json(result);
  }
}

export const coffeeShopList = [
  {
    id: 1,
    name: 'Timeless Coffee',
    url: 'https://hogehoge.coffee/',
    country: { id: 0, name: 'Unknown' },
    price: { id:1, value: 800 },
    shopArea: { id:1, name: 'Fukuoka' },
    cardImage: {
      id: 1,
      src: '/hogehoge_1.png',
      alt: 'coffeeShopPng'
    }
  }
]

そもそも公式にをちゃんと読めば解決しました。

https://nextjs-ja-translation-docs.vercel.app/docs/basic-features/static-file-serving

Next.js は、ルートディレクトリの public フォルダ下で、画像などの静的ファイルを配信できます。public 内のファイルは、ベース URL(/)から始まるコードで参照できます。

困ったときはリファレンスを読みましょう。

失敗: うまく表示されないパターン

画像をpublicに配置してimportを利用してオブジェクトに入れようとするとうまくいきません。

なぜなら '../../public/hogehoge.png' には確かに画像のパスとしては正しいです。しかし、APIと返却されるパスとして適切なのは /hogehoge.pngになります。

一方で hogeHogePngを/hogehoge.png に変更すると画像が見つからずビルドエラーになってしまいます。

coffeeshop.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import hogeHogePng from '../../public/hogehoge.png';

// 以下のように書き換えると画像が見つからずビルドエラーになる
// import hogeHogePng from '/hogehoge.png';


export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<CoffeeShop[]>
) {
		let result = coffeeShopList;
    res.status(200).json(result);
  }
}

export const coffeeShopList = [
  {
    id: 1,
    name: 'Timeless Coffee',
    url: 'https://hogehoge.coffee/',
    country: { id: 0, name: 'Unknown' },
    price: { id:1, value: 800 },
    shopArea: { id:1, name: 'Fukuoka' },
    cardImage: {
      id: 1,
      src: hogeHogePng,
      alt: 'coffeeShopPng'
    }
  }
]

importを使うと、あちら立てればこちらが立たぬ、という状況になってしまいます。なので静的な画像のパスをAPIとして返す場合はImageコンポーネントのsrcに指定されるべきファイルパスをレスポンスとして返す必要があります。

参考記事

https://qiita.com/uwattotaitai/items/f1615999ce65917e3595

宣伝

カフェインレスコーヒーをまとめたサイトを開発、運営しています。

https://caffeinelessmore.com/

Oriental Coffee Ventures

Discussion