nextjs-image-art-direction
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNext.js Image: Art Direction
Next.js 图片:艺术指导
Art direction means showing completely different images based on viewport size — not just resizing the same image. Common use cases include homepage carousels with different assets for mobile vs desktop, switching from landscape (desktop) to portrait (mobile), or showing cropped vs full compositions.
艺术指导指的是根据视口尺寸展示完全不同的图片——而不仅仅是调整同一张图片的大小。常见使用场景包括首页轮播图在移动端和桌面端使用不同资源、从横版(桌面端)切换为竖版(移动端),或者展示裁剪版与完整构图的图片。
Art Direction vs Responsive Images
艺术指导 vs 响应式图片
| Approach | Purpose | Implementation |
|---|---|---|
| Art Direction | Different image content/composition | |
| Responsive Images | Same image, different sizes | |
Use Art Direction When:
- Homepage carousels with different images for mobile and desktop (e.g., square images on mobile, wide banner on desktop)
- Mobile shows portrait crop, desktop shows landscape
- Different focal points for different screen sizes
- Completely different compositions are needed
- Content hierarchy changes between breakpoints
- Different image assets optimized for each viewport (e.g., mobile-optimized JPEGs vs desktop quality)
Use Responsive Images When:
- Same image works at all sizes
- Only the dimensions change
- Standard responsive behavior is sufficient
| 方案 | 用途 | 实现方式 |
|---|---|---|
| 艺术指导 | 不同图片内容/构图 | 带有多个 |
| 响应式图片 | 同一张图片,不同尺寸 | 搭配 |
适合使用艺术指导的场景:
- 首页轮播图在移动端和桌面端使用不同图片(例如移动端用方形图,桌面端用宽幅横幅)
- 移动端展示竖版裁剪图,桌面端展示横版图
- 不同屏幕尺寸使用不同焦点
- 需要完全不同的构图
- 内容层级在断点间发生变化
- 为每个视口优化不同的图片资源(例如移动端优化的JPEG vs 桌面端质量的图片)
适合使用响应式图片的场景:
- 同一张图片适配所有尺寸
- 仅尺寸发生变化
- 标准响应式行为足够满足需求
Implementation with getImageProps()
getImageProps()使用getImageProps()
实现
getImageProps()The function (stable since Next.js 14.1.0) generates the necessary props without calling React , making it ideal for art direction.
getImageProps()useState()getImageProps()useState()Step-by-Step Implementation
分步实现
jsx
import { getImageProps } from 'next/image'
export default function ArtDirectedImage() {
// Common props shared across all image versions
const common = {
alt: 'Mountain landscape',
sizes: '100vw'
}
// Desktop version (landscape, higher quality)
const {
props: { srcSet: desktop },
} = getImageProps({
...common,
src: '/hero-desktop.jpg',
width: 1440,
height: 875,
quality: 80,
})
// Mobile version (portrait, smaller dimensions)
const {
props: { srcSet: mobile, ...rest },
} = getImageProps({
...common,
src: '/hero-mobile.jpg',
width: 750,
height: 1334,
quality: 70,
})
return (
<picture>
{/* Desktop: min-width 1000px */}
<source media="(min-width: 1000px)" srcSet={desktop} />
{/* Mobile: min-width 500px */}
<source media="(min-width: 500px)" srcSet={mobile} />
{/* Fallback img element (rendered if no media query matches) */}
<img {...rest} style={{ width: '100%', height: 'auto' }} />
</picture>
)
}jsx
import { getImageProps } from 'next/image'
export default function ArtDirectedImage() {
// 所有图片版本共享的通用属性
const common = {
alt: 'Mountain landscape',
sizes: '100vw'
}
// 桌面端版本(横版,更高质量)
const {
props: { srcSet: desktop },
} = getImageProps({
...common,
src: '/hero-desktop.jpg',
width: 1440,
height: 875,
quality: 80,
})
// 移动端版本(竖版,更小尺寸)
const {
props: { srcSet: mobile, ...rest },
} = getImageProps({
...common,
src: '/hero-mobile.jpg',
width: 750,
height: 1334,
quality: 70,
})
return (
<picture>
{/* 桌面端:最小宽度1000px */}
<source media="(min-width: 1000px)" srcSet={desktop} />
{/* 移动端:最小宽度500px */}
<source media="(min-width: 500px)" srcSet={mobile} />
{/* 回退img元素(无媒体查询匹配时渲染) */}
<img {...rest} style={{ width: '100%', height: 'auto' }} />
</picture>
)
}Key Implementation Details
关键实现细节
Props to Vary by Breakpoint:
- : Different image file
src - /
width: Different dimensionsheight - : Different compression levels
quality
Common Props (Shared):
- : Accessibility text (must work for all versions)
alt - : Responsive size hints for browser
sizes
HTML Structure:
- wrapper element
<picture> - elements with
<source>attribute for each breakpointmedia - element last as fallback (required)
<img>
按断点调整的属性:
- : 不同的图片文件
src - /
width: 不同尺寸height - : 不同压缩级别
quality
通用属性(共享):
- : 无障碍文本(必须适配所有版本)
alt - : 给浏览器的响应式尺寸提示
sizes
HTML结构:
- 包裹元素
<picture> - 每个断点对应带有属性的
media元素<source> - 最后放置元素作为回退(必填)
<img>
Breakpoint Strategy
断点策略
Order matters! The browser uses the first matching . List from largest to smallest (desktop-first) or smallest to largest (mobile-first).
<source>顺序很重要!浏览器会使用第一个匹配的。可以按从大到小(桌面优先)或从小到大(移动优先)的顺序排列。
<source>Desktop-First (Largest to Smallest)
桌面优先(从大到小)
jsx
<picture>
<source media="(min-width: 1000px)" srcSet={desktop} />
<source media="(min-width: 500px)" srcSet={tablet} />
<img {...rest} style={{ width: '100%', height: 'auto' }} />
</picture>jsx
<picture>
<source media="(min-width: 1000px)" srcSet={desktop} />
<source media="(min-width: 500px)" srcSet={tablet} />
<img {...rest} style={{ width: '100%', height: 'auto' }} />
</picture>Mobile-First (Smallest to Largest)
移动优先(从小到大)
jsx
<picture>
<source media="(max-width: 499px)" srcSet={mobile} />
<source media="(max-width: 999px)" srcSet={tablet} />
<img {...rest} style={{ width: '100%', height: 'auto' }} />
</picture>jsx
<picture>
<source media="(max-width: 499px)" srcSet={mobile} />
<source media="(max-width: 999px)" srcSet={tablet} />
<img {...rest} style={{ width: '100%', height: 'auto' }} />
</picture>Common Pitfalls
常见陷阱
⚠️ Cannot Use preload
or loading="eager"
preloadloading="eager"⚠️ 不可使用preload
或loading="eager"
preloadloading="eager"These would cause all images to load immediately, defeating the purpose of art direction:
jsx
// BAD: Would load both desktop and mobile
getImageProps({
src: '/desktop.jpg',
preload: true, // Don't do this!
})
// BAD: Same problem
getImageProps({
src: '/desktop.jpg',
loading: 'eager', // Don't do this!
})Solution: Use if you need to prioritize the LCP image:
fetchPriority="high"jsx
const common = {
alt: 'Hero image',
fetchPriority: 'high', // Only load the matching image eagerly
}这会导致所有图片立即加载,违背艺术指导的初衷:
jsx
// 错误:会同时加载桌面端和移动端图片
getImageProps({
src: '/desktop.jpg',
preload: true, // 请勿这样做!
})
// 错误:同样的问题
getImageProps({
src: '/desktop.jpg',
loading: 'eager', // 请勿这样做!
})解决方案: 如果需要优先加载LCP(最大内容绘制)图片,请使用:
fetchPriority="high"jsx
const common = {
alt: 'Hero image',
fetchPriority: 'high', // 仅会优先加载匹配的图片
}⚠️ Alt Text Must Work for All Versions
⚠️ 替代文本必须适配所有版本
The text is shared across all image versions. Make sure it accurately describes all possible images:
altjsx
// BAD: Only describes desktop version
const common = { alt: 'Wide panoramic mountain landscape' }
// GOOD: Describes both versions
const common = { alt: 'Mountain landscape with snow-capped peaks' }altjsx
// 错误:仅描述了桌面端版本
const common = { alt: 'Wide panoramic mountain landscape' }
// 正确:描述了所有版本
const common = { alt: 'Mountain landscape with snow-capped peaks' }⚠️ Cannot Use placeholder
Prop
placeholder⚠️ 不可使用placeholder
属性
placeholdergetImageProps()placeholdergetImageProps()placeholder⚠️ Ensure Images Exist for All Breakpoints
⚠️ 确保所有断点的图片都存在
Missing images will cause broken image icons on certain devices. Always test on actual devices or browser dev tools with different viewport sizes.
缺失的图片会导致某些设备上显示损坏的图片图标。请务必在实际设备或浏览器开发者工具的不同视口尺寸下进行测试。
Complete Example: Hero Section
完整示例:英雄区域
jsx
import { getImageProps } from 'next/image'
export default function Hero() {
const common = {
alt: 'Team collaboration in modern office',
sizes: '100vw',
fetchPriority: 'high',
}
// Large desktop: Full office scene
const { props: { srcSet: desktop } } = getImageProps({
...common,
src: '/hero-office-wide.jpg',
width: 1920,
height: 1080,
quality: 85,
})
// Tablet: Focused team shot
const { props: { srcSet: tablet } } = getImageProps({
...common,
src: '/hero-team-focused.jpg',
width: 1024,
height: 768,
quality: 80,
})
// Mobile: Single person portrait
const { props: { srcSet: mobile, ...rest } } = getImageProps({
...common,
src: '/hero-person-portrait.jpg',
width: 750,
height: 1334,
quality: 75,
})
return (
<section className="relative">
<picture>
<source media="(min-width: 1200px)" srcSet={desktop} />
<source media="(min-width: 768px)" srcSet={tablet} />
<source media="(min-width: 500px)" srcSet={mobile} />
<img
{...rest}
className="w-full h-auto object-cover"
style={{ maxHeight: '80vh' }}
/>
</picture>
<div className="absolute inset-0 flex items-center justify-center">
<h1 className="text-white text-4xl font-bold drop-shadow-lg">
Welcome to Our Platform
</h1>
</div>
</section>
)
}jsx
import { getImageProps } from 'next/image'
export default function Hero() {
const common = {
alt: 'Team collaboration in modern office',
sizes: '100vw',
fetchPriority: 'high',
}
// 大桌面端:完整办公室场景
const { props: { srcSet: desktop } } = getImageProps({
...common,
src: '/hero-office-wide.jpg',
width: 1920,
height: 1080,
quality: 85,
})
// 平板端:聚焦团队的照片
const { props: { srcSet: tablet } } = getImageProps({
...common,
src: '/hero-team-focused.jpg',
width: 1024,
height: 768,
quality: 80,
})
// 移动端:单人肖像
const { props: { srcSet: mobile, ...rest } } = getImageProps({
...common,
src: '/hero-person-portrait.jpg',
width: 750,
height: 1334,
quality: 75,
})
return (
<section className="relative">
<picture>
<source media="(min-width: 1200px)" srcSet={desktop} />
<source media="(min-width: 768px)" srcSet={tablet} />
<source media="(min-width: 500px)" srcSet={mobile} />
<img
{...rest}
className="w-full h-auto object-cover"
style={{ maxHeight: '80vh' }}
/>
</picture>
<div className="absolute inset-0 flex items-center justify-center">
<h1 className="text-white text-4xl font-bold drop-shadow-lg">
Welcome to Our Platform
</h1>
</div>
</section>
)
}Advanced: CSS Background Images
进阶:CSS背景图片
You can use to optimize background images with :
getImageProps()image-set()jsx
import { getImageProps } from 'next/image'
function getBackgroundImage(srcSet = '') {
const imageSet = srcSet
.split(', ')
.map((str) => {
const [url, dpi] = str.split(' ')
return `url("${url}") ${dpi}`
})
.join(', ')
return `image-set(${imageSet})`
}
export default function HeroBackground() {
const {
props: { srcSet },
} = getImageProps({
alt: '',
width: 1920,
height: 1080,
src: '/hero-bg.jpg',
quality: 80,
})
const backgroundImage = getBackgroundImage(srcSet)
return (
<main
style={{
height: '100vh',
width: '100vw',
backgroundImage,
backgroundSize: 'cover',
backgroundPosition: 'center',
}}
>
<h1>Content Here</h1>
</main>
)
}你可以使用结合优化背景图片:
getImageProps()image-set()jsx
import { getImageProps } from 'next/image'
function getBackgroundImage(srcSet = '') {
const imageSet = srcSet
.split(', ')
.map((str) => {
const [url, dpi] = str.split(' ')
return `url("${url}") ${dpi}`
})
.join(', ')
return `image-set(${imageSet})`
}
export default function HeroBackground() {
const {
props: { srcSet },
} = getImageProps({
alt: '',
width: 1920,
height: 1080,
src: '/hero-bg.jpg',
quality: 80,
})
const backgroundImage = getBackgroundImage(srcSet)
return (
<main
style={{
height: '100vh',
width: '100vw',
backgroundImage,
backgroundSize: 'cover',
backgroundPosition: 'center',
}}
>
<h1>Content Here</h1>
</main>
)
}Quick Reference
快速参考
DO
建议做法
- Use for multiple image versions
getImageProps() - Share and
altacross all versionssizes - Order elements correctly (first match wins)
<source> - Use for LCP images (not
fetchPriority="high")preload - Test on actual devices or responsive mode in dev tools
- Ensure all image files exist for defined breakpoints
- 使用处理多版本图片
getImageProps() - 在所有版本间共享和
alt属性sizes - 正确排序元素(第一个匹配的会被使用)
<source> - 为LCP图片使用(而非
fetchPriority="high")preload - 在实际设备或开发者工具的响应式模式下测试
- 确保所有定义的断点对应的图片文件都存在
DON'T
禁止做法
- Use prop (loads all images)
preload - Use (loads all images)
loading="eager" - Use prop with
placeholdergetImageProps() - Write alt text that only describes one version
- Forget to include the final element
<img> - Use art direction when simple responsive images suffice
- 使用属性(会加载所有图片)
preload - 使用(会加载所有图片)
loading="eager" - 在中使用
getImageProps()属性placeholder - 编写仅描述一个版本的替代文本
- 忘记添加最终的元素
<img> - 在简单响应式图片足够的情况下使用艺术指导