Image Loading Strategies (lazy, eager, priority) in Next.js 16
Next.js course · Module 3: Styling and Visual Optimization
In Metropolis Quantum, the city's transportation system is organized according to different priorities. Emergency vehicles have the highest priority and always have the right of way. Public transport has medium priority, and private vehicles have the lowest. This intelligent traffic management ensures that the most important resources always reach their destination fastest, while less critical ones can wait.
Similarly in web applications, different images have different levels of importance. The hero image of the page should load immediately, while images placed lower on the page can be loaded only when the user scrolls to them. Next.js 16 offers different image loading strategies that allow you to customize how images are loaded based on their importance and position on the page.
Three Main Image Loading Strategies
In Next.js 16, we have three main image loading strategies available:
- Lazy - default strategy, images are loaded only when they approach the visible area (viewport)
- Eager - images are loaded immediately, regardless of their position on the page
- Priority - images are loaded with the highest priority, ideal for "above the fold" images. In Next.js 16 you turn this strategy on with the
preloadattribute, which replaced the deprecatedpriorityattribute
Each of these strategies has its use case and impact on page performance.
Lazy Loading
Lazy loading is the default strategy for the Image component in Next.js 16. It works according to a simple principle:
Load only what the user currently sees or will see soon.
How Does Lazy Loading Work?
- Next.js automatically delays loading images that are outside the visible area (viewport)
- Images are loaded only when the user scrolls the page and they approach the visible area
- Behind the scenes, the modern browser feature
loading="lazy"is used, with additional optimizations
Lazy Loading Usage Example:
1import Image from 'next/image';
2
3export default function QuantumGallery() {
4 return (
5 <div className="gallery">
6 {Array.from({ length: 20 }).map((_, index) => (
7 <div key={index} className="relative h-64 w-full">
8 <Image
9 src={`/images/quantum-city-${index + 1}.jpg`}
10 alt={`Metropolis Quantum - photo ${index + 1}`}
11 fill
12 style={{ objectFit: 'cover' }}
13 // No need to add loading="lazy" - it is the default value
14 />
15 </div>
16 ))}
17 </div>
18 );
19}Zalety lazy loading:
- Faster initial page loading - only critical images are loaded immediately
- Less data consumption - images that the user never reaches are not loaded
- Better performance - the browser can focus resources on elements that are currently important
Scenarios Where Lazy Loading is Worth Using:
- Long pages with many images
- Photo galleries
- Product lists
- Image carousels
- Infinite scroll pages
Eager Loading
The eager loading strategy is the opposite of lazy loading. Instead of waiting for images to approach the visible area, images are loaded immediately during page loading.
How to Set Eager Loading:
1import Image from 'next/image';
2
3export default function QuantumLogo() {
4 return (
5 <Image
6 src="/images/quantum-logo.png"
7 alt="Logo Metropolis Quantum"
8 width={200}
9 height={100}
10 loading="eager"
11 />
12 );
13}Zalety eager loading:
- Immediate availability - all images are available right after page loading
- No delays - the user does not have to wait for images to load while scrolling
- More predictable behavior - all resources are loaded in one cycle
Scenarios Where Eager Loading is Worth Using:
- Short pages with a small number of images
- Pages where all images are important for context
- Applications where smooth scrolling without delays is crucial
- Offline-first applications that want to load all resources at once
Priority Loading
Priority loading is a special strategy for the most important images on the page, especially those visible "above the fold" (in the upper part of the page, visible without scrolling).
How to Set Priority Loading:
1import Image from 'next/image';
2
3export default function QuantumHero() {
4 return (
5 <Image
6 src="/images/quantum-hero.jpg"
7 alt="Witaj w Metropolis Quantum"
8 width={1920}
9 height={1080}
10 preload
11 />
12 );
13}When you use the preload attribute (called priority in Next.js 15 and earlier):
- Next.js adds a
<link rel="preload">tag for this image to the<head> - The browser starts downloading the image right away, before it reaches it in the page content
- The largest element of the page (LCP) appears sooner
The old priority attribute still works in Next.js 16, but it is deprecated. The documentation also advises using preload only when the image is definitely the LCP element; in other cases choose loading="eager" or fetchPriority="high".
Zalety priority loading:
- Lower LCP (Largest Contentful Paint) - a key Core Web Vitals metric
- Faster display of the most important images - better perceived performance
- Core Web Vitals optimization - positive impact on SEO
Scenarios Where Priority is Worth Using:
- Main page hero images
- Logo marki
- Product images on detail pages
- User avatars in social applications
- Any images that constitute the main content of the page
Key Notes About the preload Attribute:
- Use
preloadsparingly - only for the most important images - The Next.js documentation recommends
preloadonly for the LCP (Largest Contentful Paint) image, not for several images that may become it depending on the screen width - Too many images with
preloadcan paradoxically decrease performance
Loading Strategy Comparison
| Strategy | Attribute | Use Cases | Performance Impact | |-----------|---------|------------------|-------------------| | Lazy | loading="lazy" (default) | Images below the fold, galleries, lists | Faster initial loading, data savings | | Eager | loading="eager" | Small pages, all images important | Slower initial loading, better responsiveness while scrolling | | Priority | preload (formerly priority) | "Above the fold" images, hero images, main content | Better LCP, faster display of key images |
Advanced Image Loading Strategies
Beyond the basic strategies, Next.js 16 allows for more advanced approaches to image loading:
1. Preloading Important Images with useRouter
Sometimes we want to preload images for a page the user will likely navigate to:
1'use client';
2
3import { useEffect } from 'react';
4import { useRouter } from 'next/navigation';
5import Image from 'next/image';
6
7export default function ProductCard({ product, isPriority }) {
8 const router = useRouter();
9
10 useEffect(() => {
11 // Preload product detail image on mouse hover
12 const preloadDetailImage = () => {
13 router.prefetch(`/products/${product.id}`);
14 };
15
16 const cardElement = document.getElementById(`product-card-${product.id}`);
17 if (cardElement) {
18 cardElement.addEventListener('mouseenter', preloadDetailImage);
19 return () => {
20 cardElement.removeEventListener('mouseenter', preloadDetailImage);
21 };
22 }
23 }, [product.id, router]);
24
25 return (
26 <div
27 id={`product-card-${product.id}`}
28 className="relative h-64 overflow-hidden rounded-lg"
29 onClick={() => router.push(`/products/${product.id}`)}
30 >
31 <Image
32 src={product.thumbnail}
33 alt={product.title}
34 fill
35 style={{ objectFit: 'cover' }}
36 preload={isPriority}
37 />
38 <div className="absolute bottom-0 w-full bg-black bg-opacity-60 p-4">
39 <h3 className="text-white font-bold">{product.title}</h3>
40 </div>
41 </div>
42 );
43}2. Dynamic Priority Switching Based on Visibility
We can dynamically assign priority to images that are currently visible:
1'use client';
2
3import { useState, useEffect, useRef } from 'react';
4import Image from 'next/image';
5
6export default function DynamicPriorityImage({ src, alt }) {
7 const [isPriority, setIsPriority] = useState(false);
8 const imageRef = useRef(null);
9
10 useEffect(() => {
11 const observer = new IntersectionObserver(
12 ([entry]) => {
13 if (entry.isIntersecting) {
14 setIsPriority(true);
15 observer.disconnect();
16 }
17 },
18 { threshold: 0.1 }
19 );
20
21 if (imageRef.current) {
22 observer.observe(imageRef.current);
23 }
24
25 return () => {
26 observer.disconnect();
27 };
28 }, []);
29
30 return (
31 <div ref={imageRef} className="relative h-64 w-full">
32 <Image
33 src={src}
34 alt={alt}
35 fill
36 style={{ objectFit: 'cover' }}
37 preload={isPriority}
38 />
39 </div>
40 );
41}3. Progressive Image Loading
You can combine strategies to first display a low-quality image and then replace it with a high-quality version:
1'use client';
2
3import { useState } from 'react';
4import Image from 'next/image';
5
6export default function ProgressiveImage() {
7 const [isLoaded, setIsLoaded] = useState(false);
8
9 return (
10 <div className="relative h-96 w-full">
11 {/* Niskokwalitywowy placeholder */}
12 <Image
13 src="/images/quantum-city-low.jpg"
14 alt="Metropolis Quantum"
15 fill
16 style={{
17 objectFit: 'cover',
18 opacity: isLoaded ? 0 : 1,
19 transition: 'opacity 0.5s ease-in-out'
20 }}
21 preload // Placeholder loaded immediately
22 />
23
24 {/* High quality image */}
25 <Image
26 src="/images/quantum-city-high.jpg"
27 alt="Metropolis Quantum"
28 fill
29 style={{
30 objectFit: 'cover',
31 opacity: isLoaded ? 1 : 0,
32 transition: 'opacity 0.5s ease-in-out'
33 }}
34 onLoad={() => setIsLoaded(true)}
35 />
36 </div>
37 );
38}Image Optimization for Different Devices
Loading strategies can be combined with techniques for adapting images to different devices:
1. Responsive Images with Different Sources
1import Image from 'next/image';
2
3export default function ResponsiveHero() {
4 return (
5 <>
6 {/* Image for mobile devices */}
7 <div className="md:hidden">
8 <Image
9 src="/images/hero-mobile.jpg"
10 alt="Metropolis Quantum"
11 width={640}
12 height={960}
13 preload // Priority for the main image
14 />
15 </div>
16
17 {/* Image for tablets */}
18 <div className="hidden md:block lg:hidden">
19 <Image
20 src="/images/hero-tablet.jpg"
21 alt="Metropolis Quantum"
22 width={1024}
23 height={768}
24 preload
25 />
26 </div>
27
28 {/* Image for desktops */}
29 <div className="hidden lg:block">
30 <Image
31 src="/images/hero-desktop.jpg"
32 alt="Metropolis Quantum"
33 width={1920}
34 height={1080}
35 preload
36 />
37 </div>
38 </>
39 );
40}2. Adaptive Loading Based on Connection
1'use client';
2
3import { useState, useEffect } from 'react';
4import Image from 'next/image';
5
6export default function AdaptiveLoadingImage() {
7 const [connectionType, setConnectionType] = useState('high');
8
9 useEffect(() => {
10 // Detect connection quality
11 const connection = navigator.connection ||
12 navigator.mozConnection ||
13 navigator.webkitConnection;
14
15 if (connection) {
16 if (connection.effectiveType === '4g' && !connection.saveData) {
17 setConnectionType('high');
18 } else {
19 setConnectionType('low');
20 }
21
22 // Listen for changes in connection quality
23 const updateConnectionStatus = () => {
24 if (connection.effectiveType === '4g' && !connection.saveData) {
25 setConnectionType('high');
26 } else {
27 setConnectionType('low');
28 }
29 };
30
31 connection.addEventListener('change', updateConnectionStatus);
32 return () => connection.removeEventListener('change', updateConnectionStatus);
33 }
34 }, []);
35
36 return (
37 <div className="relative h-64 w-full">
38 <Image
39 src={connectionType === 'high' ? '/images/high-quality.jpg' : '/images/low-quality.jpg'}
40 alt="Adaptacyjny obraz"
41 fill
42 style={{ objectFit: 'cover' }}
43 loading={connectionType === 'high' ? 'eager' : 'lazy'}
44 quality={connectionType === 'high' ? 90 : 50}
45 />
46 </div>
47 );
48}Remember that in Next.js 16 both quality values (90 and 50) must be on the images.qualities list in next.config. By default the list contains only 75, so without extending it both variants get quality 75.
Implementing Advanced Loading Strategies in a Real Application
Example: Product Page with Different Loading Strategies
1// app/products/[id]/page.tsx
2import Image from 'next/image';
3import { getProduct, getRelatedProducts } from '@/lib/api';
4import RelatedProducts from '@/components/RelatedProducts';
5
6export default async function ProductPage({ params }) {
7 const product = await getProduct((await params).id);
8 const relatedProducts = await getRelatedProducts((await params).id);
9
10 return (
11 <div className="container mx-auto px-4 py-8">
12 <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
13 {/* Main product image - priority loading */}
14 <div className="relative aspect-square">
15 <Image
16 src={product.mainImage}
17 alt={product.name}
18 fill
19 style={{ objectFit: 'contain' }}
20 preload
21 sizes="(max-width: 768px) 100vw, 50vw"
22 />
23 </div>
24
25 <div>
26 <h1 className="text-3xl font-bold mb-4">{product.name}</h1>
27 <p className="text-xl mb-4">{product.price}</p>
28 <div className="prose max-w-none">
29 <p>{product.description}</p>
30 </div>
31
32 <button className="mt-6 bg-blue-600 text-white px-6 py-3 rounded-md">
33 Dodaj do koszyka
34 </button>
35 </div>
36 </div>
37
38 {/* Additional photos gallery - lazy loading (default) */}
39 <div className="mt-12">
40 <h2 className="text-2xl font-bold mb-6">Galeria produktu</h2>
41 <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
42 {product.gallery.map((image, index) => (
43 <div key={index} className="relative aspect-square">
44 <Image
45 src={image.url}
46 alt={`${product.name} - photo ${index + 1}`}
47 fill
48 style={{ objectFit: 'cover' }}
49 sizes="(max-width: 768px) 50vw, 25vw"
50 // Default lazy loading
51 />
52 </div>
53 ))}
54 </div>
55 </div>
56
57 {/* Podobne produkty - eager loading dla pierwszego elementu */}
58 <div className="mt-12">
59 <h2 className="text-2xl font-bold mb-6">Podobne produkty</h2>
60 <RelatedProducts products={relatedProducts} />
61 </div>
62 </div>
63 );
64}1// components/RelatedProducts.tsx
2'use client';
3
4import Image from 'next/image';
5import Link from 'next/link';
6import { useRef, useEffect } from 'react';
7
8export default function RelatedProducts({ products }) {
9 const containerRef = useRef(null);
10
11 useEffect(() => {
12 // Preload images when hovering over the section
13 const preloadImages = () => {
14 products.forEach(product => {
15 const img = new Image();
16 img.src = product.image;
17 });
18 };
19
20 const container = containerRef.current;
21 if (container) {
22 container.addEventListener('mouseenter', preloadImages, { once: true });
23 return () => {
24 container.removeEventListener('mouseenter', preloadImages);
25 };
26 }
27 }, [products]);
28
29 return (
30 <div ref={containerRef} className="grid grid-cols-2 md:grid-cols-4 gap-4">
31 {products.map((product, index) => (
32 <Link key={product.id} href={`/products/${product.id}`}>
33 <div className="relative aspect-square rounded-lg overflow-hidden">
34 <Image
35 src={product.image}
36 alt={product.name}
37 fill
38 style={{ objectFit: 'cover' }}
39 // Eager loading tylko dla pierwszego produktu
40 loading={index === 0 ? 'eager' : 'lazy'}
41 sizes="(max-width: 768px) 50vw, 25vw"
42 />
43 <div className="absolute bottom-0 w-full bg-black bg-opacity-60 p-2">
44 <p className="text-white text-sm font-medium truncate">
45 {product.name}
46 </p>
47 </div>
48 </div>
49 </Link>
50 ))}
51 </div>
52 );
53}Impact of Loading Strategies on Core Web Vitals
Core Web Vitals is a set of Google metrics that measure user experience on websites. Choosing the right image loading strategy has a direct impact on these metrics:
1. LCP (Largest Contentful Paint)
LCP measures the loading time of the largest visible element in the viewport, which is often an image.
Impact of loading strategies:
- Priority loading for the main image can significantly improve LCP
- Eager loading for all images can worsen LCP by dispersing resources
- Lazy loading for images below the fold does not affect LCP but improves overall performance
2. CLS (Cumulative Layout Shift)
CLS measures page layout instability during loading.
Impact of loading strategies:
- All strategies can minimize CLS if images have specified dimensions
- Priority loading with
placeholder="blur"provides the best CLS as it reserves space immediately - Always specify
widthandheightor usefillwith a container of defined dimensions
3. FID (First Input Delay)
FID measures the time from the user's first interaction to the moment when the browser can respond to it.
Impact of loading strategies:
- Lazy loading can improve FID by reducing browser work during loading
- Eager loading can worsen FID by increasing main thread load
- Priority loading for key images can be a compromise
Measuring and Optimizing Loading Strategies
To ensure that the chosen loading strategies work optimally, it is worth measuring their impact:
1. Performance Measurement Tools
- Lighthouse - comprehensive performance analysis tool
- Chrome DevTools Performance - detailed resource loading analysis
- WebPageTest - performance testing under various conditions
- Core Web Vitals w Google Search Console
2. A/B Testing of Loading Strategies
1'use client';
2
3import { useState, useEffect } from 'react';
4import Image from 'next/image';
5
6export default function ABTestImage({ src, alt }) {
7 const [testGroup, setTestGroup] = useState('');
8
9 useEffect(() => {
10 // Assign user to group A or B
11 const group = Math.random() > 0.5 ? 'A' : 'B';
12 setTestGroup(group);
13
14 // Save dane telemetryczne
15 const trackImageLoad = (time) => {
16 // Sending data to the analytics system
17 console.log(`Image loaded in ${time}ms (Group ${group})`);
18 };
19
20 const startTime = performance.now();
21 const onLoadComplete = () => {
22 const loadTime = performance.now() - startTime;
23 trackImageLoad(loadTime);
24 };
25
26 // Listen for the image load event
27 const imageEl = document.getElementById('test-image');
28 if (imageEl) {
29 imageEl.addEventListener('load', onLoadComplete);
30 return () => {
31 imageEl.removeEventListener('load', onLoadComplete);
32 };
33 }
34 }, []);
35
36 if (!testGroup) return null;
37
38 return (
39 <div className="relative h-96 w-full">
40 <Image
41 id="test-image"
42 src={src}
43 alt={alt}
44 fill
45 style={{ objectFit: 'cover' }}
46 preload={testGroup === 'A'} // Group A uses preload, group B does not
47 />
48 </div>
49 );
50}Best Practices for Image Loading Strategies
Use
preloadonly for above-the-fold images - ideally for a single LCP image per pageUse lazy loading by default - this saves resources and speeds up initial page loading
Monitor Web Vitals metrics - regularly check how loading strategies affect performance
Dostosuj strategie do rodzaju strony:
- Product pages:
preloadfor the main product photo - Category pages:
lazyfor most images,eagerfor the first few - Homepage:
preloadfor hero image,lazyfor the rest
- Product pages:
Consider user devices:
- Mobile users often have slower connections - it is even more important to use
lazyand smaller images - Desktop users can benefit from
eagerfor a better scrolling experience
- Mobile users often have slower connections - it is even more important to use
Use placeholders for a better loading experience:
- For images with
preload- show abluroremptyplaceholder - For images with
lazy- useblurfor a smooth transition
- For images with
Scenarios for Different Strategies
1. E-commerce Homepage
1export default function Homepage() {
2 return (
3 <div>
4 {/* Hero section - priority */}
5 <div className="relative h-[80vh]">
6 <Image
7 src="/images/hero.jpg"
8 alt="Witaj w Quantum Shop"
9 fill
10 style={{ objectFit: 'cover' }}
11 preload
12 />
13 </div>
14
15 {/* Promotions - eager for the first two */}
16 <div className="grid grid-cols-2 md:grid-cols-4 gap-4 my-8">
17 {promotions.map((promo, index) => (
18 <div key={promo.id} className="relative aspect-square">
19 <Image
20 src={promo.image}
21 alt={promo.title}
22 fill
23 style={{ objectFit: 'cover' }}
24 loading={index < 2 ? 'eager' : 'lazy'}
25 />
26 </div>
27 ))}
28 </div>
29
30 {/* Product categories - lazy (default) */}
31 <div className="grid grid-cols-2 md:grid-cols-3 gap-6 my-12">
32 {categories.map(category => (
33 <div key={category.id} className="relative aspect-video">
34 <Image
35 src={category.image}
36 alt={category.name}
37 fill
38 style={{ objectFit: 'cover' }}
39 // default lazy loading
40 />
41 </div>
42 ))}
43 </div>
44 </div>
45 );
46}2. Blog with Long Articles
1export default function BlogPost({ post }) {
2 return (
3 <article className="max-w-3xl mx-auto py-8">
4 {/* Main article image - priority */}
5 <div className="relative aspect-video mb-8">
6 <Image
7 src={post.featuredImage}
8 alt={post.title}
9 fill
10 style={{ objectFit: 'cover' }}
11 preload
12 />
13 </div>
14
15 <h1 className="text-3xl font-bold mb-4">{post.title}</h1>
16 <div className="prose max-w-none">
17 {post.content.map((block, index) => {
18 if (block.type === 'paragraph') {
19 return <p key={index}>{block.content}</p>;
20 }
21 if (block.type === 'image') {
22 return (
23 <div key={index} className="relative aspect-video my-8">
24 <Image
25 src={block.url}
26 alt={block.caption || ''}
27 fill
28 style={{ objectFit: 'contain' }}
29 // default lazy loading for images in content
30 />
31 {block.caption && (
32 <p className="text-sm text-gray-500 mt-2 text-center">
33 {block.caption}
34 </p>
35 )}
36 </div>
37 );
38 }
39 return null;
40 })}
41 </div>
42
43 {/* Related articles - all lazy */}
44 <div className="mt-12">
45 <h2 className="text-2xl font-bold mb-6">Related Articles</h2>
46 <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
47 {post.relatedPosts.map(relatedPost => (
48 <div key={relatedPost.id} className="relative aspect-video">
49 <Image
50 src={relatedPost.thumbnail}
51 alt={relatedPost.title}
52 fill
53 style={{ objectFit: 'cover' }}
54 // All lazy (default)
55 />
56 </div>
57 ))}
58 </div>
59 </div>
60 </article>
61 );
62}Summary
Choosing the right image loading strategy is crucial for optimizing performance and user experience. Just like in the transportation system of Metropolis Quantum, where vehicles with different priorities move with varying urgency, in a web application images can be loaded with different strategies depending on their importance:
Priority loading - for the most important "above the fold" images that have a direct impact on LCP and the user's first impression
Eager loading - for images that are less critical but still important, and will likely be needed soon after page loading
Lazy loading - for most images, especially those below the fold, which will be loaded only when the user reaches them
When choosing a loading strategy for your images, consider:
- Image visibility without scrolling
- Impact on key performance metrics
- Content type and its importance
- Your users' devices and internet connections
By properly applying image loading strategies, you can create an application that loads quickly, efficiently manages resources, and provides a smooth user experience - just like the intelligent transportation system of Metropolis Quantum, which ensures smooth flow of vehicles with different priorities.
In the next chapter, we will focus on font optimization and the Font component, which will allow us to further improve the performance and aesthetics of our Next.js 16 application.
Code for this lesson: app/page.tsx
1// Image Loading Strategies - lazy, eager, priority
2'use client';
3
4import { useState } from 'react';
5
6export default function LoadingStrategiesDemo() {
7 const [activeStrategy, setActiveStrategy] = useState('lazy');
8
9 const strategies = [
10 {
11 id: 'lazy',
12 name: 'Lazy Loading',
13 icon: '',
14 color: 'blue',
15 description: 'Default strategy. Images load only when approaching the viewport.',
16 code: `<Image
17 src="/photo.jpg"
18 alt="Gallery image"
19 width={800}
20 height={600}
21 loading="lazy" // default
22/>`,
23 useCase: 'Below-the-fold images, galleries, blog content images',
24 pros: ['Saves bandwidth', 'Faster initial page load', 'Default behavior'],
25 cons: ['Slight delay when scrolling to image', 'Not ideal for hero images'],
26 },
27 {
28 id: 'eager',
29 name: 'Eager Loading',
30 icon: '',
31 color: 'green',
32 description: 'Images load immediately, regardless of position on page.',
33 code: `<Image
34 src="/photo.jpg"
35 alt="Important image"
36 width={800}
37 height={600}
38 loading="eager"
39/>`,
40 useCase: 'Images likely to be seen quickly, carousels, critical UI elements',
41 pros: ['No loading delay', 'Good for near-viewport images'],
42 cons: ['Uses more bandwidth', 'Can slow initial load'],
43 },
44 {
45 id: 'priority',
46 name: 'Priority Loading',
47 icon: '',
48 color: 'purple',
49 description: 'Highest priority loading. Preloaded in <head> for fastest display.',
50 code: `<Image
51 src="/hero.jpg"
52 alt="Hero banner"
53 width={1920}
54 height={1080}
55 priority // preloads in <head>
56/>`,
57 useCase: 'Hero images, LCP elements, above-the-fold content',
58 pros: ['Fastest loading', 'Improves LCP score', 'Preloaded in head'],
59 cons: ['Use sparingly (1-2 per page)', 'Increases initial payload'],
60 },
61 ];
62
63 const active = strategies.find(s => s.id === activeStrategy);
64
65 return (
66 <div className="min-h-screen bg-gray-900 text-white p-8">
67 <div className="max-w-4xl mx-auto">
68 <h1 className="text-3xl font-bold mb-2">Image Loading Strategies</h1>
69 <p className="text-gray-400 mb-8">
70 Choose the right strategy for each image in your Next.js app
71 </p>
72
73 {/* Strategy Tabs */}
74 <div className="flex gap-4 mb-8">
75 {strategies.map(s => (
76 <button
77 key={s.id}
78 onClick={() => setActiveStrategy(s.id)}
79 className={`px-6 py-3 rounded-lg font-medium transition-colors ${
80 activeStrategy === s.id
81 ? 'bg-blue-600 text-white'
82 : 'bg-gray-800 text-gray-400 hover:bg-gray-700'
83 }`}
84 >
85 {s.icon} {s.name}
86 </button>
87 ))}
88 </div>
89
90 {/* Active Strategy Details */}
91 {active && (
92 <div className="bg-gray-800 rounded-xl p-8">
93 <h2 className="text-2xl font-bold mb-2">
94 {active.icon} {active.name}
95 </h2>
96 <p className="text-gray-400 mb-6">{active.description}</p>
97
98 {/* Code Example */}
99 <div className="bg-gray-900 rounded-lg p-4 mb-6">
100 <pre className="text-sm text-green-400 overflow-x-auto">
101 {active.code}
102 </pre>
103 </div>
104
105 {/* Use Cases */}
106 <div className="mb-6">
107 <h3 className="font-semibold mb-2">When to use:</h3>
108 <p className="text-gray-400">{active.useCase}</p>
109 </div>
110
111 {/* Pros & Cons */}
112 <div className="grid grid-cols-2 gap-4">
113 <div>
114 <h3 className="font-semibold text-green-400 mb-2">Pros</h3>
115 <ul className="space-y-1">
116 {active.pros.map((p, i) => (
117 <li key={i} className="text-sm text-gray-400">• {p}</li>
118 ))}
119 </ul>
120 </div>
121 <div>
122 <h3 className="font-semibold text-red-400 mb-2">Cons</h3>
123 <ul className="space-y-1">
124 {active.cons.map((c, i) => (
125 <li key={i} className="text-sm text-gray-400">• {c}</li>
126 ))}
127 </ul>
128 </div>
129 </div>
130 </div>
131 )}
132
133 {/* Priority Guide */}
134 <div className="mt-8 bg-gray-800 rounded-xl p-6">
135 <h2 className="text-xl font-bold mb-4">Strategy Map</h2>
136 <div className="space-y-3">
137 <div className="flex items-center gap-4 p-3 bg-purple-900/30 rounded-lg">
138 <span className="text-2xl"></span>
139 <div>
140 <span className="font-semibold text-purple-400">Priority</span>
141 <span className="text-gray-400 ml-2">→ Hero image, main banner (1-2 per page)</span>
142 </div>
143 </div>
144 <div className="flex items-center gap-4 p-3 bg-green-900/30 rounded-lg">
145 <span className="text-2xl"></span>
146 <div>
147 <span className="font-semibold text-green-400">Eager</span>
148 <span className="text-gray-400 ml-2">→ Near-viewport images, carousel slides</span>
149 </div>
150 </div>
151 <div className="flex items-center gap-4 p-3 bg-blue-900/30 rounded-lg">
152 <span className="text-2xl"></span>
153 <div>
154 <span className="font-semibold text-blue-400">Lazy</span>
155 <span className="text-gray-400 ml-2">→ Everything else (default)</span>
156 </div>
157 </div>
158 </div>
159 </div>
160 </div>
161 </div>
162 );
163}Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. Styled-components is a library for:
2. Mobile-first design means:
Hands-on tasks in the game
- Code editor
Create a file with CSS variables defining a complete design system (colors, spacing, typography, shadows)
- Vertical ordering
Arrange the steps for using CSS Modules in Next.js in the correct order:
- Click in order
Arrange the className syntax with Tailwind CSS classes in JSX
- Code editor
Create keyframes animation for a fade-in effect and hover transition for a button