ข้ามไปยังเนื้อหา

Image และ Font

next/image resize, แปลงเป็น format ใหม่ และ lazy-load image ให้พร้อมจองพื้นที่ไว้ล่วงหน้า ส่วน next/font self-host font ตอน build — หน้าเว็บจึงโหลดเร็วและไม่ขยับ

Image คือตัวแทนแบบ drop-in ของ <img> ที่ optimize ให้ตอน request โดยสร้าง version ที่ resize ตามอุปกรณ์ ส่ง format ใหม่อย่าง WebP หรือ AVIF เมื่อ browser รองรับ และ lazy-load ทุกอย่างที่อยู่ใต้ fold ให้อัตโนมัติ

เมื่อคุณ import ไฟล์ในเครื่อง Next.js อ่านขนาดจริงตอน build ดังนั้น width กับ height จึงถูกเติมให้เอง

app/page.tsx
import Image from 'next/image'
import hero from './hero.png'
export default function Page() {
return <Image src={hero} alt="Product hero shot" />
}

image ที่ Next.js เดาขนาดไม่ได้ — เช่น remote URL — คุณ ต้อง ส่ง width กับ height ค่าสองตัวนี้จองกล่องขนาดเป๊ะไว้ก่อน pixel จะมาถึง ซึ่งคือสิ่งที่กัน Cumulative Layout Shift ใส่ priority ให้ image ตัวที่เป็น Largest Contentful Paint เพื่อให้ Next.js preload ภาพนั้นและข้าม lazy-load

// Remote image: width + height are required to reserve space
import Image from 'next/image'
export default function Avatar() {
return (
<Image
src="https://cdn.example.com/avatar.png"
alt="User avatar"
width={64}
height={64}
priority // preload the LCP image, skip lazy-loading
/>
)
}

เมื่อไม่รู้ขนาดจริง ๆ ใช้ fill ภายใน parent ที่ตั้ง position ไว้ แล้วปล่อยให้ CSS จัดการขนาดกล่องแทน

// Unknown dimensions: use fill inside a positioned parent
<div style={{ position: 'relative', width: 400, height: 300 }}>
<Image src={src} alt="" fill sizes="400px" style={{ objectFit: 'cover' }} />
</div>

การ optimize external URL ตามใจชอบเท่ากับเปิด open proxy Next.js จึงปฏิเสธจนกว่าคุณจะ allowlist host นั้น ประกาศ images.remotePatterns ใน next.config และระบุให้เจาะจงที่สุดเท่าที่ทำได้

next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'cdn.example.com', pathname: '/**' },
],
},
}
export default nextConfig

next/font ดาวน์โหลดไฟล์ font ตอน build แล้วเสิร์ฟจาก origin ของคุณเอง ไม่มี request ตอน runtime ไป Google และเพราะรู้ metric ของ font ล่วงหน้า จึงจองพื้นที่ได้ถูกต้องและได้ layout shift เป็นศูนย์

// app/layout.tsx — self-hosted, zero layout shift, no request to Google
import { Inter } from 'next/font/google'
import localFont from 'next/font/local'
const inter = Inter({ subsets: ['latin'], display: 'swap' })
const brand = localFont({ src: './brand.woff2' })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={inter.className}>
<body className={brand.className}>{children}</body>
</html>
)
}
flowchart LR
  Src[Source asset] --> Build[Build step]
  Build --> Opt["Resized, WebP/AVIF, self-hosted font"]
  Opt --> Reserve["width + height reserve space"]
  Reserve --> Page["No layout shift, fast LCP"]
From source asset to a stable, fast page
ทำไมต้องส่ง width และ height ให้ remote next/image
prop priority ทำหน้าที่อะไร
ต้องมีอะไรก่อน next/image จะ optimize remote URL
next/font ทำอะไรตอน build