Next.jsをWebサイトの制作方面で試しているのですが、SSRにSG(Static Generation)を持ち込んだ合理的な処理は素晴らしいなと思います。
ただ、SSG(Static Site Generator)として使おうとすると一気に厳しくなるあたり、Next.jsの立ち位置がよく現れていると思います。画像の扱いなんて特に…。
Next.jsの画像最適化であるnext/imageは、必要なものをオンデマンドで処理する非常に合理的なものです。そのため、内部に画像最適化のためのAPIを用意し、イメージコンポーネントはそこへ向けたURLで構成されたレスポンシブイメージのコードを出力しています。
ただ、こうした構成のため、APIを用意できないSSGとはなじまないことになります。
実際、next/imageを使った状態でSSGとして出力するnext exportを実行すると、こんなエラーメッセージ(https://nextjs.org/docs/messages/export-image-api)が表示されます。
Error: Image Optimization using Next.js' default loader is not compatible with `next export`.
Possible solutions:
- Use `next start` to run a server, which includes the Image Optimization API.
- Use any provider which supports Image Optimization (like Vercel).
- Configure a third-party loader in `next.config.js`.
- Use the `loader` prop for `next/image`.
ただ、
default loader is not compatible with `next export`.
そして、
Use the `loader` prop for `next/image`
とあります。next/imageのドキュメント(https://nextjs.org/docs/api-reference/next/image)の方にも、
The next/image component's default loader is not supported when using next export. However, other loader options will work.
とあります。つまり、APIを用意すれば、SSGでもnext/imageは使えるということのようです。
そこで、imgixが使えるmicroCMSが相性が良さそうなので、ちょっと試してみました。
const microCMSLoader = ({ src, width, quality }) => {
return `${src}?auto=format&fit=max&w=${width}`
}
という感じで、microCMS用のloaderを用意して、
<figure style={{ position: "relative", height: "clamp(150px,26vw,200px)", }}>
<Image
loader={microCMSLoader}
src={content.eyecatch.url}
alt=""
layout="fill"
objectFit="cover"
sizes="(min-width: 960px) 480px, 50vw"
/>
</figure>
こんな感じでloader propsに設定します。すると、microCMSのimgixで最適化を行うコードになります。
<figure style="position: relative; height: clamp(150px, 26vw, 200px);">
<span style="box-sizing: border-box; display: block; overflow: hidden; width: initial; height: initial; background: none; opacity: 1; border: 0px; margin: 0px; padding: 0px; position: absolute; inset: 0px;">
<img alt=""
sizes="(min-width: 960px) 480px, 50vw"
srcset="https://images.microcms-assets.io/…/everyday.jpg?auto=format&fit=max&w=384 384w, https://images.microcms-assets.io/…/everyday.jpg?auto=format&fit=max&w=640 640w, …略…, https://images.microcms-assets.io/…/everyday.jpg?auto=format&fit=max&w=3840 3840w"
src="https://images.microcms-assets.io/…/everyday.jpg?auto=format&fit=max&w=3840"
decoding="async" data-nimg="fill"
style="position: absolute; inset: 0px; box-sizing: border-box; padding: 0px; border: none; margin: auto; display: block; width: 0px; height: 0px; min-width: 100%; max-width: 100%; min-height: 100%; max-height: 100%; object-fit: cover; filter: none; background-size: cover; background-image: none; background-position: 0% 0%;">
<noscript></noscript>
</span>
</figure>
これなら、next exportでも問題ないのでは?
ということで、next exportでSSGにチャレンジすると、やっぱり、このメッセージが…。
Error: Image Optimization using Next.js' default loader is not compatible with `next export`.
Possible solutions:
- Use `next start` to run a server, which includes the Image Optimization API.
- Use any provider which supports Image Optimization (like Vercel).
- Configure a third-party loader in `next.config.js`.
- Use the `loader` prop for `next/image`.
原因がわからず、色々と検索していたらたどり着いたのがこちらのissueです。
Custom loaders are not recognized by next export
https://github.com/vercel/next.js/issues/21079
ようするに、loader propsに加えて、next.config.js に loader: 'custom' を追加しましょう(Next.js11以降)ということです。
そこでこんな感じに追加すると、問題なくnext exportが通るようになります。
const nextConfig = {
images: {
loader: 'custom',
domains: ['images.microcms-assets.io'],
},
}
解決策は4択じゃなかったんですね…(Built-in Loadersの説明ってそういうことだったんだと、ここで理解)。
Next.jsでは、「画像の最適化はオンデマンドで行う」というのが基本です。これはSSGの場合も同様で、GatsbyImageのように「あらかじめ画像を用意する」という形はあまりなじまないのでしょう。
next-optimized-images
https://github.com/cyrilwanner/next-optimized-images
などもありますが、決してお手軽とは言えないと思いますし、Static Imports との兼ね合いもあります。
Imgix、Cloudinary、Akamaiといった外部の画像最適化APIを利用するか、microCMSのような画像最適化APIを装備したHeadless CMSを選択するか、自前でAPIを用意するか。外部にAPIを用意した上でSSGでもnext/imageを使うというのが、Next.jsのスタイルのように思えます。Core Web Vitalsを考慮した設定も活かせますので。
もっとも、自分が試した範囲ではNetlifyでnext/image、ISG、ISR(ちょっと不安定?)が動きましたので、Next.jsの良さを消してまで無理にSSGにする必要ももうないのかなと思い始めています。
"next": "12.0.3",
"@netlify/plugin-nextjs": "^4.0.0-beta.11",