Next.js の Image にスタイルを当てる

最近 Next.js を使用することが増えました。一年くらい前は勢いで言えば Gatsby.js のほうが強そうだと思っていましたが、近頃はまた Next.js の人気が高まってきたような印象を持っています。

弊社でも Next.js を積極的に使っていくことにしているのですが、 Next.js では画像を表示させる場合には <img> タグの代わりに <Image /> コンポーネントを使います。 <Image /> コンポーネントを使うことによって Next.js が画像をいい感じに最適化してくれるようです。具体的には自動でリサイズしてくれたり、 WebP フォーマットを使ってくれたり、しかも外部ドメインの画像であっても最適化してくれたりと良いことずくめです。

しかし、<Image /> にスタイルを当てたい場合に <img> タグと同じようにやってみてもうまくいかない場合があります。理由は、ブラウザの開発者ツールを見てみればわかりますが、 <Image /> コンポーネントが単に画像を最適化して <img /> タグを出力しているのではなくて、以下のようなHTML要素を出力するからです。

<div style="display:inline-block;max-width:100%;overflow:hidden;position:relative;box-sizing:border-box;margin:0">
  <div style="box-sizing:border-box;display:block;max-width:100%">
    <img style="max-width:100%;display:block" alt="" aria-hidden="true" role="presentation" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2ZXJzaW9uPSIxLjEiLz4=">
  </div>
  <img alt="森の画像" src="/_next/image?url=%2Fimages%2Fforest.png&w=640&q=75" decoding="async" style="visibility: visible; 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%;" srcset="/_next/image?url=%2Fimages%2Fforest.png&w=256&q=75 1x, /_next/image?url=%2Fimages%2Fforest.png&w=640&q=75 2x">
</div>

これは、 React 上のコードとしては以下のようなものです。

<Image
  src="/images/forest.png"
  alt="森の画像"
  width={200}
  height={200}
/>

<div> で囲われていたり <img /> が2つあったりと、普通にスタイルを定義するだけでは意味がなさそうということがわかります。実際、素直に <Image /> に className を設定しても position などはうまく反映されません。

しかし、レイアウト上 <Image /> のposition などを変えたい場合もあると思います。そのような場合、以下のようなコードを書いて対応しています。

MyComponent.tsx

import Image from 'next/image'
import styles from './MyComponent.module.css'

export default function MyComponent() {
  return (
    <>
      <div className={styles.image_wrapper}
        <Image
          src="path/to/image"
          alt="my image"
          width={200}
          height={100}
          className={styles.image}
        />
      </div>
    </>
  )
}

MyComponent.module.css

.image_wrapper {
  position: absolute;
  top: 0;
  left: 0;
}

.image {
  object-fit: cover;
}

<Image /> コンポーネントをさらに <div> でラップして、その <div> に position の設定をしています。この方法で画像の位置を調整できました。多少苦肉の策感はありますが、、、せっかくならばフレームワークの最適化の恩恵にあずかりたいので以上のようなコードで対応しています。

<Image /> や Next.js の画像最適化の詳細については下記を御覧ください。