為什麼要用 next/image 而不是原生 ![]()
Next.js 的 元件(來自 next/image)比原生 HTML 多做了這些事:
- 自動格式轉換:根據瀏覽器支援,自動把圖片轉為 WebP 或 AVIF 格式(WebP 比 JPEG 小 25-35%,AVIF 更小但瀏覽器支援稍差,Next.js 會自己挑)
- 自動調整尺寸:根據裝置的實際像素密度(Retina, 2x, 3x)提供適當大小的圖片
- Lazy Loading 預設開啟:視窗外的圖片不會立即載入
- 防止 CLS:要求提供寬高,預留圖片空間,避免頁面載入時的版面跳動
- 內建 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"
/>
容易搞混的一點:width 和 height 填的是原始圖片的寬高,作用是讓 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 分數提升的單一改動。
