Next.jsでPublicディレクトリに配置した画像を配列としてmapで表示する

いちいちコード書くのは面倒くさいと思った

経緯

個人の趣味のブログサイトでイラスト公開のページを作成した際、Next.js<Image>で画像を表示していました。

ただ<article>のレイアウトをコンポーネント化したのに、画像が増える都度にPropsでパスを渡しながらコンポーネントを増やしていくのは、まだ運用していないにも関わらずちょっと面倒だなと思った次第です。

というかせっかく記事はNotionをCMSとして更新できるので、画像の更新もコードいじりたくないと。

案1:NotionAPIで取得

記事内の画像は普通にNotionAPIで取得しているので、ここの画像もNotionAPIで取得するということも考えました。

ただ、APIで取得した画像の仕様として1時間でリンク切れになってしまうのを画像置場で使うのは。。。

また<Image>と違って自動最適化に対応しないので却下。

案2:S3などに置く

そもそも趣味でちょっとブログ書きたかっただけで、メインは画像ではないのでそこまでのコストはかけたくないので却下

案3:publicディレクトリに置く

Next.jsPublicディレクトリに画像を置いておけば簡単に画像にアクセスでき、かつ<Image>は自動で最適化や遅延ロードにも対応しているので非常に使い勝手がよいです。

調べたらglobというワイルドカードを利用してパスを取得できるライブラリがあったので、それでいけるのではと。

配列で取得できるのでmapで表示していくのにも都合がよい。

ということで案3を採用

Next.jsでglob使用の注意点

そもそもglobnode.jsのライブラリなのでフロント側のコードに書いたとしても下記エラーで動かないです。

Module not found: Error: Cannot resolve 'fs'

サーバーサイドで動かさないといけないので、getStaticProps配下でパスを取得して、ページにはpropsで渡す必要があります。

下記・・・Aだけで指定フォルダを検査してくれます。拡張子も指定できるので今回はjpgpngのファイルだけを指定します。関数に渡すパスは絶対パスでも相対パスでもOKです

これで指定したファイルのパスが配列で取得できます。

export const getStaticProps = async () => {
    const glob = require('glob');

    const files = glob.sync( "./public/*.{jpg,png}"); //・・・A

    const fileNames = files.map((file)=>{ return file.split("/").pop()}) //・・・B
   
  const comments = commentsArray
    return {
      props: {
        comments:comments,
        posts: fileNames,
      },
      revalidate: 1,
    };
  };

欲しいのはファイル名

パスの取得ができましたが、Next.jsはpublicディレクトリを読む際に”/ファイル名”で読めるので、取得したパスからファイル名だけ切り出します。

あとは切り出したファイル名の配列をPropsで渡すだけです。

全体

コードはこんな感じです。

import { memo } from "react";
import ArticleImg from "../components/articleImg";
import styles from "./illustration.module.css";
import { commentsArray } from "../lib/comment";

const illustration = memo(({comments, posts}) =>{
    return (
        <>
        <main className={styles.container}>
        {posts.map((fileName,index)=>{return <ArticleImg key={index} text={comments[index]} imgUrl={`/${fileName}`} /> })
           }
        </main>
        </>)
})
export default illustration

export const getStaticProps = async () => {
    const glob = require('glob');
    const files = glob.sync( "./public/*.{jpg,png}");
    const fileNames = files.map((file)=>{ return file.split("/").pop()})
    const comments = commentsArray
    return {
      props: {
        comments:comments,
        posts: fileNames,
      },
      revalidate: 1,
    };
  };

ちなみに配列の順番はファイルの読み込み順になるので、コメントとずれないようにファイル名を変更してフォルダに入れておく必要があります。

それくらいは許容範囲(^^)

意外と使い勝手が良かった

そんなこんなで調査の時間含めると4〜5時間くらい掛かりましたが、ssgのお陰でページ遷移早いし、別のページにgridでサムネイル画像並べるのもpublicディレクトリ配下でフォルダ分ければロジックそのまま使い回せるのでしばらくは便利に使えるかも。

参考にした記事

glob公式Readme

javascript(nodejs)でフルパスからファイル名のみを取得する方法

【Node.js】 ワイルドカードを使用したファイル一覧取得

Next.js でのエラー Module not found: Can't resolve 'fs' の対処方法

← Go home
;