経緯
参考図書
参考図書の学習において、ブログ記事でアイキャッチ画像に Plaiceholder を使った blur 表示 を導入していました。
local の画像も外部 URL の画像も対応させて、next/image の placeholder="blur" で表示する予定です。
しかし、いざブラウザで確認すると…
- blur は表示される
- でも通常画像に滑らかに切り替わらない
という現象に遭遇しました。
原因の特定
最初は getPlaiceholder で生成した blurDataURL が正しくないのかと思い、コンソールで確認しました。
しかし、base64 はちゃんと入っていました。
次に Image コンポーネント周りを見直して気づいたのがこちら:
<Image
src={eyecatch.url}
layout="responsive"
width={eyecatch.width}
height={eyecatch.height}
sizes="{min-width: 1152px} 1152px, 100px" // ←ここ
placeholder="blur"
blurDataURL={eyecatch.blurDataURL}
/>
sizes の指定を {} で囲んでいた ため、Next.js は文字列として認識せず、正しくレスポンシブサイズを計算できていませんでした。
結果、blur が残ったまま通常画像に切り替わらない状態になっていたようです。
解決方法
単純ですが、{} を外して文字列として渡すだけで解決しました。
<Image
src={eyecatch.url}
layout="responsive"
width={eyecatch.width}
height={eyecatch.height}
sizes="(min-width: 1152px) 1152px, 100px" // ←修正
placeholder="blur"
blurDataURL={eyecatch.blurDataURL}
/>
⇩全体コード
import Image from 'next/image';
import path from 'path';
import { getPlaiceholder } from 'plaiceholder';
import { eyecatchLocal } from 'lib/constants';
import { getPostBySlug } from 'lib/api';
import { extractText } from 'lib/extract-text';
export const getStaticProps = async () => {
const slug = 'micro';
const post = await getPostBySlug(slug);
const description = extractText(post.content);
const eyecatch = post.eyecatch ?? eyecatchLocal;
const imagePath = eyecatch.url.startsWith('/')
? path.join(process.cwd(), 'public', eyecatch.url.replace(/^\//, ''))
: eyecatch.url;
const { base64 } = await getPlaiceholder(imagePath);
eyecatch.blurDataURL = base64;
return {
props: {
title: post.title,
publish: post.publishDate,
content: post.content,
eyecatch: eyecatch,
categories: post.categories,
description: description,
},
};
};
const PostPage = ({ title, content, eyecatch, description }) => {
return (
<article>
<h1>{title}</h1>
{/* アイキャッチ画像 */}
<Image
src={eyecatch.url}
alt={title}
layout="responsive"
width={eyecatch.width}
height={eyecatch.height}
sizes="(min-width: 1152px) 1152px, 100px" // ← {} を外して文字列に
placeholder="blur"
blurDataURL={eyecatch.blurDataURL} // ← しっかり base64 が入っている
/>
<div dangerouslySetInnerHTML={{ __html: content }} />
</article>
);
};
export default PostPage;
まとめ
- Next.js の
Imageでsizesを渡すときは 文字列として渡す - {} で囲むとオブジェクト扱いになり、ブラウザで正しく解釈されない
- blur 画像から通常画像への切り替えが効かない場合は、まずここを疑うと早い