Next.js
ã§WebP
ã䜿çšããŠç»åæé©åãå®çŸããæ¹æ³
ç§ã¯Next.js
ãštailwindcss
ã䜿çšããããã³ããšã³ãéçºãè¡ã£ãŠããåå¿è
ã§ãããããŸã§PNG
ãã©ãŒããããäž»ã«äœ¿çšããŠããŸããããWebP
ã®ååšãç¥ãããã®åªããç¹æ§ã«ã€ããŠãã£ãšç解ããããšèããããã«ãªããŸãããããã§ãWebP
ã«ã€ããŠãããã調ã¹ãäžã§åŸãç¥èãå®è·µæ¹æ³ããä»åã®èšäºã§ãŸãšããŠã玹ä»ããŸãã
ç®æ¬¡
- ã¯ããã«
- åºæ¬çãªäœ¿çšäŸ
- è€æ°ãµã€ãºã®WebPç»åã®çæã«ã€ããŠ
- ãã©ãã«ã·ã¥ãŒãã£ã³ã°
- çºå±çãªäœ¿çšäŸ
- ãŸãšã
ã¯ããã«
WebP
ïŒãŠã§ãããŒïŒã¯ãGoogleãéçºããæ°ããç»åãã©ãŒãããã§ãåŸæ¥ã®JPEG
ãPNG
ãšæ¯èŒããŠå€§å¹
ãªãã¡ã€ã«ãµã€ãºã®åæžãå®çŸããªãããé«å質ãªç»å衚瀺ãå¯èœã«ããŸããããã«ãããWebãµã€ãã®èªã¿èŸŒã¿é床ãåäžããããŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ã倧å¹
ã«æ¹åããããšãå¯èœã«ãªããŸãã
ããããäžéšã®å€ããã©ãŠã¶ã§ã¯WebP
圢åŒããµããŒãããŠããªããããäºææ§ã®ç¢ºä¿ã課é¡ãšãªã£ãŠããŸãããŸããéçºè
ã«ãšã£ãŠWebP
ç»åã®çæã管çãæ°ããªè² æ
ãšãªãå¯èœæ§ããããŸãã
Next.js
ã®Image
ã³ã³ããŒãã³ãã䜿çšããããšã§ããããã®èª²é¡ãå¹ççã«è§£æ±ºããæé©ãªç»å衚瀺ãå®çŸã§ããŸããä»åã¯ãNext.js
ã§ã®WebP
ç»åã®å®è£
æ¹æ³ãšãImage
ã³ã³ããŒãã³ãã掻çšããç»åã®æé©åææ³ã«ã€ããŠãå
·äœçãªã³ãŒãäŸã亀ããªãã解説ããŸãã
åºæ¬çãªäœ¿çšäŸ
Next.js
ã®Image
ã³ã³ããŒãã³ãã®åºæ¬å®è£
import Image from 'next/image'
export default function Hero() {
return (
// æé©åãããç»åã³ã³ããŒãã³ãã衚瀺
<Image
// ç»åã®ãœãŒã¹ãã¹
src="/hero-image.jpg"
// ç»åã®ä»£æ¿ããã¹ã
alt="Hero image"
// ç»åã®å¹
width={1200}
// ç»åã®é«ã
height={600}
// åªå
çã«èªã¿èŸŒãããã«èšå®
priority
// ç»é¢ãµã€ãºã«å¿ããç»åãµã€ãºã®æå®
sizes="(max-width: 768px) 100vw, 800px"
/>
)
}
ãã®ã³ãŒãã§ã¯ãNext.js
ãèªåçã«ç»åãWebP
圢åŒã«å€æãããã©ãŠã¶ããµããŒãããŠããªãå Žåã¯å
ã®åœ¢åŒã«ãã©ãŒã«ããã¯ããŸãã
Image
ã³ã³ããŒãã³ãã®ããããã£
-
src
: 衚瀺ããç»åã®ãã¹ -
alt
: ç»åã衚瀺ãããªãå Žåã«è¡šç€ºããã代æ¿ããã¹ã -
width
: ç»åã®è¡šç€ºå¹ -
height
: ç»åã®è¡šç€ºé«ã -
priority
: ç»åã®åªå 床ãæå®
ïŒããŒãžè¡šç€ºæã«åªå çã«èªã¿èŸŒãå¿ èŠãããç»åã«èšå®ïŒ -
placeholder
: ç»åã®èªã¿èŸŒã¿äžã«è¡šç€ºãããã¬ãŒã¹ãã«ããŒãæå® -
quality
: ç»åã®å質ãæå®
ïŒããã©ã«ãã¯75ïŒ -
sizes
: ã¬ã¹ãã³ã·ããã¶ã€ã³ã«ãããç»å衚瀺ãµã€ãºãæå®
ïŒç»é¢å¹ ã«å¿ããŠç°ãªããµã€ãºã®ç»åãæäŸïŒ
placeholder
å±æ§ã«ã€ããŠ
placeholder
å±æ§ã¯ãç»åã®èªã¿èŸŒã¿äžã«è¡šç€ºãããã¬ãŒã¹ãã«ããŒãæå®ãããã®ã§ããããã䜿ãããšã§ããŠãŒã¶ãŒã¯ç»åãå®å
šã«èªã¿èŸŒãŸãããŸã§ã®éãããŒãã£ã³ã°ç¶æ
ãèŠèŠçã«èªèã§ããããã«ãªããŸããNext.js
ã®Image
ã³ã³ããŒãã³ãã§ã¯ãempty
ïŒç©ºïŒãŸãã¯blur
ïŒãŒããïŒã®ãããããæå®ã§ããŸãã
-
empty
: ãã¬ãŒã¹ãã«ããŒã衚瀺ããŸããç»åãèªã¿èŸŒãŸãããŸã§äœã衚瀺ããªããããã·ã³ãã«ãªããŒãã£ã³ã°äœéšãæäŸããŸãã
ïŒç»åèªã¿èŸŒã¿äžã¯ç©ºã®ã¹ããŒã¹ãè¡šç€ºïŒ -
blur
: ç»åã®ãŒãããããŒãžã§ã³ããã¬ãŒã¹ãã«ããŒãšããŠè¡šç€ºããŸãblurDataURL
ããããã£ãšçµã¿åãããŠãäºåã«çæãããbase64圢åŒã®ãŒããç»åãèšå®ããå¿ èŠããããŸãã
ããŒãã£ã³ã°äžã«èŠèŠçãªæããããäžããããšã§ããŠãŒã¶ãŒäœéšãåäžãããŸãã
ïŒéçã«ã€ã³ããŒãããç»åã®å Žåã¯ãèªåçã«blurDataURL
ãçæãããïŒ
base64ããŒã¿ãšã¯ïŒ
base64ãšã¯ãç»åãªã©ã®ããŒã¿ãããã¹ã圢åŒã«å€æããæ¹æ³ã®äžã€ã§ããéåžžãç»åã¯ãã€ããªããŒã¿ãšããã³ã³ãã¥ãŒã¿ãç解ã§ãã圢åŒã§ä¿åãããŠããŸããããããWebããŒãžã§çŽæ¥ãã€ããªããŒã¿ãæ±ãã®ã¯é£ããã®ã§ãbase64ã䜿ã£ãŠããã¹ã圢åŒã«å€æããŸãã
ãã®ããã¹ã圢åŒã«å€æãããããŒã¿ã¯ãURLã®äžã«çŽæ¥åã蟌ãããšãã§ããŸããblurDataURL
ã§ã¯ããã®base64圢åŒã®å°ããªãŒãããç»åã®ããŒã¿ãèšå®ããããšã§ãç»åèªã¿èŸŒã¿äžã«ãŒãã衚瀺ãå®çŸããŠããŸãã
ãã£ãããbase64ããŒã¿ã¯ãç»åããŒã¿ãæåã§è¡šãããã®ããšç解ããã°å€§äžå€«ã§ãã
base64ããŒã¿ã®äœææ¹æ³
base64ããŒã¿ãäœæããæ¹æ³ã¯ããã€ããããŸããããªã³ã©ã€ã³ããŒã«ã䜿çšããã®ãæãç°¡åã§ãã
ç»åãã¢ããããŒãããçæãããbase64ã³ãŒããã³ããŒããŠäœ¿çšããŸãã
è€æ°ãµã€ãºã®WebP
ç»åã®çæã«ã€ããŠ
Image
ã³ã³ããŒãã³ãã¯ãsizes
ããããã£ã«å¿ããŠãèªåçã«è€æ°ãµã€ãºã®WebP
ç»åãçæããŸãã
äŸãã°ãsizes="(max-width: 768px) 100vw, 500px"
ãšæå®ããå Žåã768px以äžã®ç»é¢å¹
ã§ã¯ç»é¢å¹
ãã£ã±ãã®ãµã€ãºã§ã769px以äžã®ç»é¢å¹
ã§ã¯500pxã®ãµã€ãºã§ç»åã衚瀺ãããŸãã
sizes
ããããã£ã®è©³çŽ°
sizes
ããããã£ã¯ãsrcset
å±æ§ãšçµã¿åãããŠãã¬ã¹ãã³ã·ããªç»åã®è¡šç€ºãå¶åŸ¡ããŸããããã¯ãã¡ãã£ã¢ã¯ãšãªãšå¯Ÿå¿ããç»åãµã€ãºãæå®ããæååã§ãã
-
(max-width: 768px) 100vw
: ç»é¢å¹ ã768px以äžã®å Žåãç»åã®å¹ ã¯ãã¥ãŒããŒãã®å¹ ïŒ100vwïŒã«ãªãããã«æå® -
500px
: ç»é¢å¹ ã769px以äžã®å Žåãç»åã®å¹ ã¯500pxã«ãªãããã«æå®
sizes
ããããã£ã䜿ãããšã§ãç°ãªãç»é¢ãµã€ãºã«å¯ŸããŠæé©ãªç»åãµã€ãºããã©ãŠã¶ã«éžæãããããšãã§ããŸããImage
ã³ã³ããŒãã³ãã¯ããã®èšå®ã«åºã¥ããèªåã§é©åãªãµã€ãºã®ç»åãäœæããsrcset
å±æ§ã«ã»ããããŠãããŸãã
srcset
ããããã£ã®è©³çŽ°
srcset
å±æ§ã®åºæ¬æ§é ãšäœ¿çšæ¹æ³
srcset
ããããã£ã¯ãHTMLã®<img>
ã¿ã°ã®å±æ§ã®äžã€ã§ãã¬ã¹ãã³ã·ããªç»å衚瀺ãå®çŸããããã«ããã©ãŠã¶ã«è€æ°ã®ç»åãœãŒã¹ãšããããã®ãµã€ãºãæ瀺ãããã®ã§ãããã©ãŠã¶ã¯ããã®srcset
å±æ§ãšsizes
å±æ§ã®æ
å ±ãåºã«ãæé©ãªç»åãèªåçã«éžæããŠè¡šç€ºããŸããNext.js
ã®Image
ã³ã³ããŒãã³ãã§ã¯ããã®srcset
å±æ§ãèªåã§çæããŠãããŸãã
srcset
å±æ§ã¯ã以äžã®ãããªåœ¢åŒã§èšè¿°ããŸãã
<img
src="image.jpg"
srcset="image-320w.jpg 320w, image-480w.jpg 480w, image-800w.jpg 800w"
sizes="(max-width: 320px) 280px, (max-width: 480px) 440px, 800px"
alt="Description of the image"
/>
äžèšã®äŸã§ã¯ãsrcset
å±æ§ã®å€ã¯ã«ã³ãã§åºåãããè€æ°ã®ç»åãœãŒã¹ãšå¹
èšè¿°åã®ãã¢ã§ãã
-
image-320w.jpg 320w
: å¹ ã320pxã®ç»åimage-320w.jpgãæå®ããŸãã -
image-480w.jpg 480w
: å¹ ã480pxã®ç»åimage-480w.jpgãæå®ããŸãã -
image-800w.jpg 800w
: å¹ ã800pxã®ç»åimage-800w.jpgãæå®ããŸãã
ãã©ãŠã¶ã¯ãsizes
å±æ§ã§æå®ãããæ¡ä»¶ïŒã¡ãã£ã¢ã¯ãšãªãªã©ïŒãšsrcset
å±æ§ã§æå®ãããç»åãœãŒã¹ã®ãªã¹ããç §ããåãããŠãæé©ãªç»åãéžæããŸãã
Next.js
ã§ã®srcset
å±æ§ã®èªåçæ
Next.js
ã®Image
ã³ã³ããŒãã³ãã§ã¯ãsizes
ããããã£ãèšå®ãããšããã®srcset
å±æ§ãèªåã§çæããŠãããŸããããã«ãããéçºè
ã¯è€éãªèšå®ãããããšãªããã¬ã¹ãã³ã·ããªç»å衚瀺ãç°¡åã«å®è£
ã§ããŸãã
import Image from 'next/image';
const MyImage = () => {
return (
<div className="relative w-full h-[600px]">
<Image
src="/my-image.jpg"
alt="My image"
width={800}
height={600}
// ã¬ã¹ãã³ã·ããªãµã€ãºæå®
sizes="(max-width: 320px) 280px, (max-width: 480px) 440px, 800px"
className="object-contain"
/>
</div>
);
}
object-fit
ããããã£ã«é¢ããèšå®ã説æ
-
object-contain
: ç»åã¯å ã®çžŠæšªæ¯ãç¶æãããŸãŸã芪èŠçŽ å ã«åãŸãããã«æ¡å€§çž®å°ããã
ïŒç»åã芪èŠçŽ ãå®å šã«èŠãããã§ã¯ãªããäœçœãã§ããå ŽåããããŸããç»åãæªãŸãªãããã«æ³šæããå Žåã«äœ¿çšïŒ -
object-cover
: ç»åã®çžŠæšªæ¯ãç¶æãããŸãŸã芪èŠçŽ ãå®å šã«èŠãããã«æ¡å€§çž®å°ãã
ïŒç»åã®äžéšããåãåãããå ŽåããããŸããããŒããŒç»åãªã©ãèŠçŽ ãå®å šã«èŠãããå Žåã«äœ¿çšïŒ -
object-fill
: ç»åã芪èŠçŽ ã«åãããŠå€åœ¢ããã
ïŒç»åã®çžŠæšªæ¯ãç¶æãããªããããæªãã§è¡šç€ºãããããšããããŸããèŠçŽ ãã£ã±ãã«è¡šç€ºãããå Žåã«äœ¿çšïŒ -
object-none
: ç»åãæ¡å€§çž®å°ãããå ã®ãµã€ãºã§è¡šç€ºãã
ïŒç»åã芪èŠçŽ ããã¯ã¿åºãå ŽåããããŸããå ã®ãµã€ãºãç¶æãããå Žåã«äœ¿çšïŒ -
object-scale-down
: ç»åãçž®å°ããŠã object-none ãŸã㯠object-contain ã®ã©ã¡ããå°ããæ¹ã®ãµã€ãºã§è¡šç€ºãã
ïŒç»åã倧ããããå Žåã«çž®å°ããã®ã¿ïŒ
ãã©ãã«ã·ã¥ãŒãã£ã³ã°
1. ç»åã衚瀺ãããªãå Žåã®å¯ŸåŠ
`onError`ããããã£ã䜿çšããŠãç»åèªã¿èŸŒã¿å€±ææã®ä»£æ¿è¡šç€ºãèšå®ã§ããŸãã
```typescript
// ç»å衚瀺ã³ã³ããŒãã³ã
const ImageWithFallback = () => {
// ãšã©ãŒç¶æ
ã管çãã useState
const [isError, setIsError] = useState(false)
return (
// äžé
æŒç®åã§ãšã©ãŒç¶æ
ã«å¿ããŠè¡šç€ºããç»åãåãæ¿ã
!isError ? (
// ãšã©ãŒããªãå Žåã®ã¡ã€ã³ç»å
<Image
src="/main-image.jpg"
alt="ã¡ã€ã³ç»å"
width={500}
height={300}
// ãšã©ãŒçºçæã«ã¹ããŒãæŽæ°ãããã³ãã©ãŒ
onError={() => setIsError(true)}
/>
) : (
// ãšã©ãŒæã®ä»£æ¿ç»åã衚瀺
<Image
src="/fallback-image.jpg"
alt="代æ¿ç»å"
width={500}
height={300}
/>
)
)
}
```
2. ãã¡ã€ã³ãšã©ãŒ
å€éšç»åã䜿çšããå Žåã¯ã`next.config.js`ã§ãã¡ã€ã³ãç»é²ããå¿
èŠããããŸãã
```typescript
// next.config.js
module.exports = {
images: {
// ç»åãèªã¿èŸŒãèš±å¯ããããã¡ã€ã³ãæå®
domains: ['example.com'],
// ç»åãã©ãŒãããã®èšå®
formats: ['image/webp']
}
}
```
3. ãµã€ãºãšã©ãŒ
芪èŠçŽ ã§è¡šç€ºãšãªã¢ã®ãµã€ãºã決ãïŒ`relative w-full h-80`ïŒããã®äžã«ç»åãåºãïŒ`fill`ïŒãç»åã®åœ¢ã厩ããã«ãšãªã¢ãåããïŒ`className="object-cover"`ïŒèšå®ãããŸããããã§ç»åããã¯ã¿åºããã«è¡šç€ºãããŸãã
```typescript
// 芪èŠçŽ ã«åãããŠãµã€ãºèª¿æŽãããç»åã³ã³ããŒãã³ã
const ImageWithSize = () => {
return (
// ç»åãå
ã芪èŠçŽ ã«relativeããžã·ã§ã³ãšãµã€ãºãèšå®
<div className="relative w-full h-80">
// 芪èŠçŽ ãã£ã±ãã«åºããç»åãé
眮
<Image
src="/my-image.jpg"
alt="ç»å"
// 芪èŠçŽ ã«åãããŠãµã€ãºã調æŽïŒèŠªèŠçŽ ã«ã¯`relative`ãå¿
èŠïŒ
fill
className="object-cover"
/>
</div>
)
}
```
çºå±çãªäœ¿çšäŸ
è€æ°ã®ç»åãåãæ¿ããŠè¡šç€º
// ç»ååãæ¿ãã³ã³ããŒãã³ã
const ImageSwitcher = () => {
// çŸåšã®ç»åã€ã³ããã¯ã¹ã管çããã¹ããŒã
const [currentImageIndex, setCurrentImageIndex] = useState(0);
// 衚瀺ããç»åã®ãªã¹ã
const images = [
'/image1.jpg',
'/image2.jpg',
'/image3.jpg',
];
// 次ã®ç»åã衚瀺ããé¢æ°
const nextImage = () => {
// ã€ã³ããã¯ã¹ãæŽæ°ïŒãªã¹ãã®æåŸãªãæåã«æ»ãïŒ
setCurrentImageIndex((currentImageIndex + 1) % images.length);
};
return (
<div>
{/* ã¯ãªãã¯ã§ç»åãåãæ¿ãããã¿ã³ */}
<button onClick={nextImage}>次ã®ç»å</button>
{/* çŸåšã®ç»åã衚瀺 */}
<div className="relative w-full h-[300px]">
<Image
// 衚瀺ããç»åã®ãã¹ããªã¹ãããååŸ
src={images[currentImageIndex]}
alt={`Image ${currentImageIndex + 1}`}
fill
className="object-contain"
/>
</div>
</div>
);
};
export default ImageSwitcher;
ããŒãã£ã³ã°ç¶æ ã詳现ã«å¶åŸ¡
// ããŒãã£ã³ã°å¶åŸ¡ã³ã³ããŒãã³ã
const LoadingImage = () => {
// ããŒãã£ã³ã°ç¶æ
ã管çããã¹ããŒã
const [isLoading, setIsLoading] = useState(true);
// ç»åã®èªã¿èŸŒã¿å®äºæã«å®è¡ãããé¢æ°
const handleLoadingComplete = () => {
// ããŒãã£ã³ã°ç¶æ
ãfalseã«èšå®ããããŒãã£ã³ã°ã¢ãã¡ãŒã·ã§ã³ãçµäºããã
setIsLoading(false);
};
return (
<div>
{/* ããŒãã£ã³ã°äžã¯ã¡ãã»ãŒãžã衚瀺 */}
{isLoading && <p>Now Loading...</p>}
{/* ããŒãã£ã³ã°å®äºåŸã«ç»åã衚瀺 */}
<Image
src="/loading-image.jpg"
alt="ããŒãã£ã³ã°ç»å"
width={500}
height={300}
// ããŒãã£ã³ã°å®äºæã«å®è¡ããé¢æ°ãæå®
onLoadingComplete={handleLoadingComplete}
/>
</div>
);
};
export default LoadingImage;
onLoadingComplete
ããããã£ã䜿ãããšã§ãç»åã®èªã¿èŸŒã¿å®äºæã«ç¹å®ã®åŠçãå®è¡ã§ããŸãã
ïŒç»åã®èªã¿èŸŒã¿ãå®äºããéã«åŒã³åºããã ã³ãŒã«ããã¯é¢æ° ãèšå®ããããã®ã€ãã³ããã³ãã©ãŒããããã£ïŒ
ãŸãšã
Next.js
ã®Image
ã³ã³ããŒãã³ãã䜿çšããWebP
ç»åã®æé©åã¯ãWebãµã€ãã®ããã©ãŒãã³ã¹åäžã«å€§ããè²¢ç®ããŸããèªåçãªåœ¢åŒå€æãã¬ã¹ãã³ã·ã察å¿ãé
延èªã¿èŸŒã¿ãªã©ã®æ©èœã«ãããéçºè
ã¯è€éãªç»åæé©åãç°¡åã«å®è£
ã§ããŸããç¹ã«ã¢ãã€ã«ãŠãŒã¶ãŒã«ãšã£ãŠãããŒãžããŒãæéã®ççž®ãšåž¯åå¹
ã®ç¯çŽã¯éèŠãªå©ç¹ãšãªããŸãã
ããèšäºã®å 容ã«ééããããã°ãã³ã¡ã³ãã§ãææããã ããŸããšå¹žãã§ãããŸããããè¯ãæ¹æ³ã代æ¿æ段ããåç¥ã®æ¹ãããã£ããããŸãããããã²å ±æããŠããã ããã°ãšåããŸããäŸãã°ãCDNã掻çšããç»åé ä¿¡ããã£ãã·ã¥æŠç¥ããããªãããã©ãŒãã³ã¹æé©åã®ãã¯ããã¯ãªã©ãçæ§ã®ç¥èŠããã¹ããã©ã¯ãã£ã¹ããèããããã ããã°å¹žãã§ãã