為什麼要用 next/image 而不是原生

Next.js 的 元件(來自 next/image)比原生 HTML 多做了這些事:

  1. 自動格式轉換:根據瀏覽器支援,自動把圖片轉為 WebP 或 AVIF 格式(WebP 比 JPEG 小 25-35%,AVIF 更小但瀏覽器支援稍差,Next.js 會自己挑)
  2. 自動調整尺寸:根據裝置的實際像素密度(Retina, 2x, 3x)提供適當大小的圖片
  3. Lazy Loading 預設開啟:視窗外的圖片不會立即載入
  4. 防止 CLS:要求提供寬高,預留圖片空間,避免頁面載入時的版面跳動
  5. 內建 Blur 預覽:可以在圖片載入前顯示模糊的佔位圖(placeholder="blur")

這些都是 Core Web Vitals 的直接改善點,幾乎不需要額外設定就能在發布後看到效能提升。

基本使用語法

import Image from 'next/image'

// 本地圖片(Next.js 會自動取得寬高)
import heroImg from '@/public/images/hero.jpg'

<Image
  src={heroImg}
  alt="首頁 Hero 圖片"
  placeholder="blur" // 自動產生 blurDataURL
/>

// 遠端圖片(需要指定寬高)
<Image
  src="https://example.com/photo.jpg"
  alt="描述文字"
  width={800}
  height={600}
  sizes="(max-width: 768px) 100vw, 800px"
/>

容易搞混的一點widthheight 填的是原始圖片的寬高,作用是讓 Next.js 算出長寬比來預先佔位,不是這張圖在頁面上顯示的大小。實際顯示尺寸由 CSS(className)控制。

最重要的參數:priority 和 sizes

priority:Hero 圖片必須設定

預設是 lazy loading。但 Hero 圖片或頁面上第一個大型可見圖片,不應該 lazy load——這樣做反而會讓 LCP(最大內容繪製)指標變差。

<Image
  src={heroImg}
  alt="品牌形象圖"
  priority // 告訴 Next.js 這張圖要優先載入,加入 preload
  fill
  className="object-cover"
/>

經驗法則:第一屏(Above the Fold) 內可見的圖片都加 priority,其他圖片讓 lazy loading 處理。

反過來說,不要因為「加了比較快」就整頁圖片都掛 priority。全部都優先載入,等於全部都不優先,而且會完全失去 lazy loading 的效益。

sizes:告訴瀏覽器不同螢幕寬度下的圖片顯示尺寸

sizes 是最常被忽略但非常重要的參數。它告訴瀏覽器在下載圖片之前,這張圖在不同螢幕寬度下會佔多大空間,讓瀏覽器選擇最合適的圖片解析度。

// 全寬圖片(100% 螢幕寬)
<Image
  src={bannerImg}
  alt="橫幅圖片"
  fill
  sizes="100vw"
/>

// 兩欄 grid 佈局(桌機佔一半,手機全寬)
<Image
  src={cardImg}
  alt="卡片圖片"
  width={600}
  height={400}
  sizes="(max-width: 768px) 100vw, 50vw"
/>

// 三欄 grid(桌機佔三分之一,平板佔一半,手機全寬)
<Image
  src={portfolioImg}
  alt="作品集圖片"
  width={400}
  height={300}
  sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
/>

沒有設定 sizes 時,Next.js 預設假設圖片是 100vw(全螢幕寬),會讓手機也下載桌機尺寸的圖片,浪費頻寬降低速度。

fill 模式:讓圖片填滿父容器

當你不知道圖片的確切尺寸,或需要圖片填滿容器(如 Hero 背景圖、作品集封面)時,使用 fill 模式:

<div className="relative w-full h-[400px]">
  <Image
    src={coverImg}
    alt="封面圖"
    fill
    className="object-cover object-center"
    sizes="100vw"
  />
</div>

父容器必須設定 position: relative(Tailwind 的 relative),圖片才能正確定位。object-cover 讓圖片按比例縮放填滿容器而不變形。

placeholder:載入前的模糊佔位

placeholder="blur" 會在圖片完全載入前先顯示一張模糊版本,避免畫面突然跳出一張圖:

  • 本地圖片public/ 目錄、用 import 引入):Next.js 自動產生 blurDataURL,什麼都不用做。
  • 外部圖片:必須自己提供 blurDataURL,內容是一段極小的 base64 圖片 URI,否則會報錯。

遠端圖片的網域白名單設定

src 是外部 URL 時,需要在 next.config.ts 設定允許的網域,否則 Next.js 會拒絕優化這些圖片(防止 SSRF 攻擊):

// next.config.ts
const nextConfig: NextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'your-cdn.com',
        pathname: '/images/**',
      },
      {
        protocol: 'https',
        hostname: '**.supabase.co', // 萬用字元支援子網域
      },
    ],
  },
}

CMS 媒體庫、圖庫服務或自架 CDN 都適用同一套設定方式。

原始圖片還是要先壓縮

會幫你優化,但它不是萬能的:原始檔太大,建構或即時轉檔的成本一樣會反映在伺服器與首次載入上。

Hero 主圖用 2000px 寬左右的 JPEG 當原始檔很合理,但沒有必要丟一張 8000px 的高畫質輸出上去。上傳前先用 Squoosh、ImageOptim 或 TinyPNG 壓過一輪,再交給 Next.js 處理。

alt text:SEO 和無障礙都需要

每張有意義的圖片都需要描述性的 alt 文字:

// 不好的做法
<Image src={logo} alt="logo" />
<Image src={photo} alt="image" />

// 好的做法
<Image src={logo} alt="SimpleCreative 簡創數位工作室" />
<Image src={teamPhoto} alt="SimpleCreative 設計師在工作室進行設計稿審閱" />

Google 會讀 alt 來理解圖片內容,螢幕閱讀器也靠它描述畫面,所以不要留空或填「圖片1」這種沒有資訊的字串。

純裝飾性的圖片(背景圖案、抽象裝飾)則相反,應該使用空 alt:alt="",讓輔助技術直接略過。

常見問題排解

問題:圖片載入後版面跳動(CLS 高)

原因:沒有提供 width 和 height(或沒有使用 fill 模式的父容器)

解法:明確提供 width/height 或使用父容器 + fill 模式

問題:Hero 圖片出現在 LCP 問題列表

原因:Hero 圖片沒有加 priority,被 lazy loading 延遲

解法:加上 priority prop

問題:行動裝置下載了過大的圖片

原因:沒有設定 sizes prop

解法:根據實際佈局設定正確的 sizes

問題:外部圖片加了 placeholder="blur" 就報錯

原因:遠端圖片沒有自動的 blurDataURL

解法:自行提供 blurDataURL,或這張圖不要用 blur placeholder

正確使用 Next.js Image 元件,通常是最快能看到 PageSpeed Insights 分數提升的單一改動。