CodeWorlds

Komponent Image i optymalizacja obrazów w Next.js 16

Kurs Next.js · Moduł 3: Stylowanie i optymalizacja wizualna

W Metropolis Quantum, wizualna komunikacja odgrywa kluczową rolę. Futurystyczne hologramy, cyfrowe billboardy i immersyjne wyświetlacze są wszechobecne w krajobrazie miasta. Jednak, podobnie jak w przypadku rzeczywistych miast, inżynierowie muszą dbać o wydajność systemów wyświetlania, aby obrazy ładowały się szybko i nie zużywały zbyt wiele zasobów.

W świecie aplikacji webowych, obrazy stanowią jedną z największych przeszkód w osiąganiu optymalnej wydajności stron. Duże, nieskompresowane zdjęcia mogą znacząco spowolnić ładowanie strony i pogorszyć doświadczenie użytkownika. Na szczęście, Next.js 16 oferuje potężne narzędzie do optymalizacji obrazów - komponent Image.

Dlaczego optymalizacja obrazów jest ważna?

Zanim zagłębimy się w szczegóły komponentu Image, warto zrozumieć, dlaczego optymalizacja obrazów jest tak istotna:

  1. Wydajność strony - obrazy stanowią często ponad 50% wagi strony internetowej
  2. Core Web Vitals - metryki takie jak LCP (Largest Contentful Paint) są bezpośrednio związane z czasem ładowania obrazów
  3. SEO - szybsze strony są lepiej pozycjonowane w wynikach wyszukiwania
  4. Doświadczenie użytkownika - nikt nie lubi czekać na załadowanie się zawartości
  5. Zużycie danych - dla użytkowników mobilnych, duże obrazy mogą szybko zużyć limit danych
  6. Dostępność - odpowiednio zoptymalizowane obrazy są bardziej dostępne dla osób z wolniejszym połączeniem internetowym

Tradycyjne podejście do obrazów w HTML

W tradycyjnym HTML, obrazy są zazwyczaj dodawane za pomocą tagu <img>:

1<img src="/images/quantum-city.jpg" alt="Panorama Metropolis Quantum" />

To proste podejście ma jednak kilka wad:

  1. Brak automatycznej optymalizacji - obrazy są serwowane w oryginalnym formacie i rozmiarze
  2. Brak responsywności - obrazy nie dostosowują się automatycznie do różnych rozmiarów ekranu
  3. Brak leniwego ładowania - wszystkie obrazy są ładowane od razu, niezależnie od ich widoczności
  4. Brak priorytetyzacji - nie można łatwo określić, które obrazy są krytyczne dla wyświetlenia strony

Komponent Image w Next.js 16

Komponent Image z Next.js rozwiązuje te problemy i oferuje szereg dodatkowych korzyści:

  1. Automatyczna optymalizacja - konwersja do nowoczesnych formatów (WebP, AVIF)
  2. Dostosowanie rozmiaru - serwowanie obrazów w odpowiednim rozmiarze dla urządzenia
  3. Leniwe ładowanie - obrazy są ładowane tylko wtedy, gdy zbliżają się do viewport
  4. Unikanie przesunięć układu - rezerwacja przestrzeni dla obrazów przed ich załadowaniem
  5. Wsparcie dla wielu źródeł - łatwe określenie różnych obrazów dla różnych rozdzielczości
  6. Optymalizacja pod kątem Core Web Vitals - poprawa metryk takich jak LCP i CLS

Podstawowe użycie komponentu Image

Aby zacząć korzystać z komponentu Image, najpierw należy go zaimportować z next/image:

1import Image from 'next/image';
2
3export default function QuantumCityView() {
4  return (
5    <div className="city-view">
6      <h1>Panorama Metropolis Quantum</h1>
7      <Image
8        src="/images/quantum-city.jpg"
9        alt="Panorama Metropolis Quantum z lotu ptaka"
10        width={1200}
11        height={600}
12      />
13    </div>
14  );
15}

W powyższym przykładzie, komponent Image wymaga kilku atrybutów:

  • src - ścieżka do obrazu
  • alt - alternatywny tekst dla dostępności
  • width i height - szerokość i wysokość obrazu w pikselach

Statyczne importy obrazów

Next.js pozwala również na statyczne importowanie obrazów, co daje dodatkowe korzyści, takie jak automatyczne określanie wymiarów:

1import Image from 'next/image';
2import quantumCityImage from '@/public/images/quantum-city.jpg';
3
4export default function QuantumCityView() {
5  return (
6    <div className="city-view">
7      <h1>Panorama Metropolis Quantum</h1>
8      <Image
9        src={quantumCityImage}
10        alt="Panorama Metropolis Quantum z lotu ptaka"
11        // width i height są automatycznie określane z metadanych obrazu
12      />
13    </div>
14  );
15}

Responsywne obrazy

Komponent Image można łatwo dostosować do różnych rozmiarów ekranu za pomocą stylów CSS:

1import Image from 'next/image';
2import quantumCityImage from '@/public/images/quantum-city.jpg';
3
4export default function QuantumCityView() {
5  return (
6    <div className="city-view">
7      <h1>Panorama Metropolis Quantum</h1>
8      <div className="relative w-full h-[50vh]">
9        <Image
10          src={quantumCityImage}
11          alt="Panorama Metropolis Quantum z lotu ptaka"
12          fill
13          style={{ objectFit: 'cover' }}
14        />
15      </div>
16    </div>
17  );
18}

W tym przykładzie:

  • Atrybut fill powoduje, że obraz wypełnia kontener rodzica
  • Style objectFit: 'cover' zapewnia, że obraz zachowuje proporcje, przycinając go w razie potrzeby
  • Kontener rodzica musi mieć pozycję relative lub absolute

Priorytetyzacja obrazów

Dla obrazów, które są widoczne "above the fold" (czyli w górnej części strony bez przewijania), warto użyć atrybutu preload. W Next.js 16 zastąpił on przestarzały atrybut priority, który wciąż działa, ale nie należy go już używać w nowym kodzie:

1<Image
2  src="/images/hero-quantum-city.jpg"
3  alt="Panorama Metropolis Quantum"
4  width={1200}
5  height={600}
6  preload
7/>

Atrybut preload powoduje, że:

  • Next.js wstawia do <head> znacznik <link rel="preload"> dla tego obrazu
  • Przeglądarka zaczyna pobierać obraz, zanim dotrze do niego w treści strony
  • Obraz jest dostępny jak najszybciej, co poprawia LCP

Dokumentacja Next.js 16 radzi używać preload tylko dla jednego obrazu LCP, np. zdjęcia hero. W pozostałych przypadkach wystarczy loading="eager" albo fetchPriority="high".

Obsługa obrazów z CMS lub zewnętrznych źródeł

Aby używać obrazów z zewnętrznych źródeł (np. CMS, CDN, API), trzeba wskazać Next.js dozwolone serwery w pliku next.config.js. Starsze projekty robią to opcją domains:

1// next.config.js
2module.exports = {
3  images: {
4    domains: ['cdn.quantum-city.com', 'images.cms-provider.com'],
5  },
6}

Ta opcja jest przestarzała od Next.js 14, dlatego w Next.js 16 używaj "remotePatterns", które dają bardziej precyzyjną kontrolę:

1// next.config.js
2module.exports = {
3  images: {
4    remotePatterns: [
5      {
6        protocol: 'https',
7        hostname: 'cdn.quantum-city.com',
8        port: '',
9        pathname: '/images/**',
10      },
11      {
12        protocol: 'https',
13        hostname: '**.cms-provider.com',
14        port: '',
15        pathname: '/assets/**',
16      },
17    ],
18  },
19}

Placeholders i efekt rozmazania

Next.js 16 oferuje kilka sposóbów na poprawę doświadczenia użytkownika podczas ładowania obrazów:

Placeholder z efektem blur

1<Image
2  src="/images/quantum-city.jpg"
3  alt="Panorama Metropolis Quantum"
4  width={1200}
5  height={600}
6  placeholder="blur"
7  blurDataURL="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1200' height='600'%3E%3Crect width='100%25' height='100%25' fill='%23071731'/%3E%3C/svg%3E"
8/>

W tym przykładzie:

  • placeholder="blur" włącza efekt rozmazania podczas ładowania obrazu
  • blurDataURL określa kolor tła używany podczas ładowania obrazu (zakodowany jako URL base64)

Automatyczny efekt rozmazania dla statycznych importów

Kiedy używasz statycznego importu obrazu, Next.js automatycznie generuje wersję rozmazaną:

1import Image from 'next/image';
2import quantumCityImage from '@/public/images/quantum-city.jpg';
3
4export default function QuantumCityView() {
5  return (
6    <Image
7      src={quantumCityImage}
8      alt="Panorama Metropolis Quantum"
9      placeholder="blur"
10      // blurDataURL jest generowany automatycznie
11    />
12  );
13}

Animacje ładowania obrazów

Możesz dodać animacje ładowania obrazu za pomocą CSS:

1import Image from 'next/image';
2import styles from './image.module.css';
3
4export default function QuantumCityView() {
5  return (
6    <Image
7      src="/images/quantum-city.jpg"
8      alt="Panorama Metropolis Quantum"
9      width={1200}
10      height={600}
11      className={styles.imageTransition}
12    />
13  );
14}
1/* image.module.css */
2.imageTransition {
3  opacity: 0;
4  transition: opacity 0.5s ease-in-out;
5}
6
7.imageTransition:global(.loaded) {
8  opacity: 1;
9}

Zaawansowane funkcje komponentu Image

Adaptatywne obrazy i art direction

W przypadku, gdy chcesz wyświetlać różne obrazy dla różnych rozmiarów ekranu, możesz użyć atrybutu srcSet:

1<div className="relative aspect-video w-full">
2  <picture>
3    <source
4      media="(max-width: 640px)"
5      srcSet="/images/quantum-city-mobile.jpg"
6    />
7    <source
8      media="(max-width: 1024px)"
9      srcSet="/images/quantum-city-tablet.jpg"
10    />
11    <source
12      media="(min-width: 1025px)"
13      srcSet="/images/quantum-city-desktop.jpg"
14    />
15    <Image
16      src="/images/quantum-city-desktop.jpg"
17      alt="Panorama Metropolis Quantum"
18      fill
19      style={{ objectFit: 'cover' }}
20    />
21  </picture>
22</div>

Obsługa obrazów w różnych formatach

Choć Next.js automatycznie konwertuje obrazy do WebP, możesz również użyć tagu <picture> do obsługi różnych formatów:

1<picture>
2  <source srcSet="/images/quantum-city.avif" type="image/avif" />
3  <source srcSet="/images/quantum-city.webp" type="image/webp" />
4  <Image
5    src="/images/quantum-city.jpg"
6    alt="Panorama Metropolis Quantum"
7    width={1200}
8    height={600}
9  />
10</picture>

Obsługa obrazów z transparentnością

Dla obrazów z transparentnością (PNG, SVG), warto rozważyć wyłączenie konwersji do WebP:

1<Image
2  src="/images/quantum-logo.png"
3  alt="Logo Metropolis Quantum"
4  width={200}
5  height={200}
6  style={{ objectFit: 'contain' }}
7  unoptimized
8/>

Atrybut unoptimized wyłącza optymalizację Next.js dla danego obrazu, co może być przydatne dla:

  • Obrazów z transparentnością
  • Już zoptymalizowanych obrazów
  • Animowanych GIF-ów

Konfigurowanie optymalizacji obrazów w Next.js 16

Next.js oferuje wiele opcji konfiguracyjnych dla optymalizacji obrazów w pliku next.config.js:

1// next.config.js
2module.exports = {
3  images: {
4    // Przestarzałe od Next.js 14 - zamiast domains używaj remotePatterns
5    domains: ['cdn.quantum-city.com'],
6
7    // Bardziej szczegółowa konfiguracja dla zewnętrznych obrazów
8    remotePatterns: [
9      {
10        protocol: 'https',
11        hostname: '**.cms-provider.com',
12        port: '',
13        pathname: '/assets/**',
14      },
15    ],
16
17    // Obsługiwane formaty
18    formats: ['image/webp', 'image/avif'],
19
20    // Dozwolone wartości quality (od Next.js 16 domyślnie tylko [75])
21    qualities: [50, 75, 90],
22
23    // Konfiguracja cache (od Next.js 16 domyślnie 14400 s, czyli 4 godziny)
24    minimumCacheTTL: 60, // w sekundach
25
26    // Limit rozmiarów obrazów
27    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
28    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], // od Next.js 16 wartości 16 nie ma w domyślnej liście
29
30    // Własny loader dla obrazów (np. Cloudinary, imgix)
31    loader: 'default',
32
33    // Ścieżka do endpointu optymalizacji obrazów
34    path: '/_next/image',
35
36    // Wyłączenie automatycznej optymalizacji dla wszystkich obrazów
37    unoptimized: false,
38  },
39}

Next.js 16 zmienił kilka wartości domyślnych: minimumCacheTTL wynosi teraz 4 godziny (wcześniej 60 sekund), z imageSizes zniknęła szerokość 16 pikseli, a qualities pozwala domyślnie tylko na jakość 75. Doszły też opcje bezpieczeństwa: maximumRedirects (domyślnie najwyżej 3 przekierowania) i dangerouslyAllowLocalIP, bez której Next.js nie optymalizuje obrazów z adresów lokalnych.

Użycie loaderów zewnętrznych dostawców

Next.js obsługuje również popularne serwisy optymalizacji obrazów:

1// next.config.js
2module.exports = {
3  images: {
4    loader: 'cloudinary',
5    loaderFile: './my/image/loader.js',
6    // Domena Cloudinary
7    domains: ['res.cloudinary.com'],
8  },
9}

Możesz również stworzyć własny loader:

1// my/image/loader.js
2export default function myImageLoader({ src, width, quality }) {
3  return `https://cdn.quantum-city.com/images/${src}?w=${width}&q=${quality || 75}`
4}

I użyć go w komponencie Image:

1import Image from 'next/image';
2import myLoader from '@/my/image/loader';
3
4export default function QuantumCityView() {
5  return (
6    <Image
7      loader={myLoader}
8      src="quantum-city.jpg"
9      alt="Panorama Metropolis Quantum"
10      width={1200}
11      height={600}
12    />
13  );
14}

Przykłady praktycznego zastosowania

1. Galeria obrazów z lazy loading

1// components/quantum-gallery.tsx
2import { useState } from 'react';
3import Image from 'next/image';
4
5const images = [
6  { src: '/images/quantum-city-1.jpg', alt: 'Panorama Metropolis Quantum' },
7  { src: '/images/quantum-city-2.jpg', alt: 'Dzielnica Business Quantum' },
8  { src: '/images/quantum-city-3.jpg', alt: 'Parki Kwantowe' },
9  { src: '/images/quantum-city-4.jpg', alt: 'Architektura Kwantowa' },
10  { src: '/images/quantum-city-5.jpg', alt: 'Transport Kwantowy' },
11  // więcej obrazów...
12];
13
14export default function QuantumGallery() {
15  const [selectedImage, setSelectedImage] = useState(null);
16
17  return (
18    <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
19      <h2 className="text-3xl font-bold mb-6">Galeria Metropolis Quantum</h2>
20
21      {/* Wyświetlanie wybranego obrazu */}
22      {selectedImage && (
23        <div className="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50">
24          <div className="max-w-4xl mx-auto p-4">
25            <div className="relative w-full" style={{ height: '80vh' }}>
26              <Image
27                src={selectedImage.src}
28                alt={selectedImage.alt}
29                fill
30                style={{ objectFit: 'contain' }}
31                preload
32              />
33            </div>
34            <button
35              className="absolute top-4 right-4 text-white text-2xl"
36              onClick={() => setSelectedImage(null)}
37            >
38              &times;
39            </button>
40          </div>
41        </div>
42      )}
43
44      {/* Siatka obrazów */}
45      <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
46        {images.map((image, index) => (
47          <div
48            key={index}
49            className="relative aspect-square overflow-hidden rounded-lg cursor-pointer hover:opacity-90 transition-opacity"
50            onClick={() => setSelectedImage(image)}
51          >
52            <Image
53              src={image.src}
54              alt={image.alt}
55              fill
56              style={{ objectFit: 'cover' }}
57              sizes="(max-width: 640px) 100vw, (max-width: 768px) 50vw, (max-width: 1024px) 33vw, 25vw"
58              // Pierwszy obraz jest priorytetowy, pozostałe są ładowane leniwie
59              preload={index === 0}
60            />
61          </div>
62        ))}
63      </div>
64    </div>
65  );
66}

2. Responsywny banner z różnymi obrazami dla różnych urządzeń

1// components/quantum-hero.tsx
2import Image from 'next/image';
3
4export default function QuantumHero() {
5  return (
6    <div className="relative">
7      {/* Obraz dla urządzeń mobilnych */}
8      <div className="sm:hidden relative h-[50vh]">
9        <Image
10          src="/images/quantum-hero-mobile.jpg"
11          alt="Metropolis Quantum - Miasto Przyszłości"
12          fill
13          style={{ objectFit: 'cover' }}
14          preload
15        />
16      </div>
17
18      {/* Obraz dla tabletów */}
19      <div className="hidden sm:block md:hidden relative h-[60vh]">
20        <Image
21          src="/images/quantum-hero-tablet.jpg"
22          alt="Metropolis Quantum - Miasto Przyszłości"
23          fill
24          style={{ objectFit: 'cover' }}
25          preload
26        />
27      </div>
28
29      {/* Obraz dla desktopów */}
30      <div className="hidden md:block relative h-[70vh]">
31        <Image
32          src="/images/quantum-hero-desktop.jpg"
33          alt="Metropolis Quantum - Miasto Przyszłości"
34          fill
35          style={{ objectFit: 'cover' }}
36          preload
37        />
38      </div>
39
40      {/* Zawartość nakładkowa */}
41      <div className="absolute inset-0 flex items-center justify-center">
42        <div className="text-center text-white p-4 backdrop-blur-sm bg-black/30 rounded-lg max-w-2xl">
43          <h1 className="text-4xl sm:text-5xl md:text-6xl font-bold mb-4">
44            Metropolis Quantum
45          </h1>
46          <p className="text-xl sm:text-2xl md:text-3xl">
47            Miasto przyszłości czeka na Ciebie
48          </p>
49        </div>
50      </div>
51    </div>
52  );
53}

3. Karty produktów z obrazami

1// components/quantum-product-card.tsx
2import Image from 'next/image';
3import { useState } from 'react';
4
5interface ProductCardProps {
6  product: {
7    id: string;
8    name: string;
9    image: string;
10    price: number;
11    description: string;
12  };
13}
14
15export default function ProductCard({ product }: ProductCardProps) {
16  const [isLoading, setIsLoading] = useState(true);
17
18  return (
19    <div className="bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden">
20      <div className="relative h-64 w-full bg-gray-200 dark:bg-gray-700">
21        <Image
22          src={product.image}
23          alt={product.name}
24          fill
25          style={{ objectFit: 'cover' }}
26          sizes="(max-width: 768px) 100vw, 33vw"
27          className={`
28            duration-700 ease-in-out
29            ${isLoading ? 'scale-110 blur-xl grayscale' : 'scale-100 blur-0 grayscale-0'}
30          `}
31          onLoad={() => setIsLoading(false)}
32        />
33      </div>
34      <div className="p-4">
35        <h3 className="text-xl font-semibold mb-2 dark:text-white">{product.name}</h3>
36        <p className="text-gray-600 dark:text-gray-300 mb-2">{product.description}</p>
37        <div className="flex justify-between items-center">
38          <span className="text-lg font-bold dark:text-white">
39            {new Intl.NumberFormat('pl-PL', { style: 'currency', currency: 'PLN' }).format(product.price)}
40          </span>
41          <button className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md">
42            Dodaj do koszyka
43          </button>
44        </div>
45      </div>
46    </div>
47  );
48}

Dobre praktyki przy pracy z komponentem Image

  1. Zawsze określaj wymiary - dla obrazów z atrybutem src zawsze definiuj width i height lub używaj fill z kontenerem o określonych wymiarach
  2. Używaj atrybutu sizes - pomaga w dostarczaniu odpowiednich rozmiarów obrazu dla różnych urządzeń
  3. Priorytetyzuj obrazy "above the fold" - używaj preload (dawniej priority) dla najważniejszego obrazu widocznego bez przewijania
  4. Używaj odpowiednich formatów - pozwól Next.js na automatyczną konwersję do WebP/AVIF
  5. Optymalizuj pod kątem Core Web Vitals - utrzymuj niskie LCP i CLS
  6. Używaj statycznych importów dla obrazów lokalnych
  7. Nie nadużywaj unoptimized - korzystaj tylko w uzasadnionych przypadkach
  8. Używaj znaczących wartości alt - dla dostępności i SEO
  9. Obsługuj różne formaty - konfiguruj obsługę AVIF i WebP w next.config.js

Obsługa formatów obrazów w Next.js 16

Next.js obsługuje automatyczną konwersję obrazów do nowoczesnych formatów. Domyślnie, Next.js używa formatu WebP, który oferuje dobrą kompresję przy zachowaniu jakości. Jednak możesz również skonfigurować obsługę formatu AVIF, który oferuje jeszcze lepszą kompresję:

1// next.config.js
2module.exports = {
3  images: {
4    formats: ['image/avif', 'image/webp'],
5  },
6}

Porównanie formatów obrazów

| Format | Zalety | Wady | Wsparcie przeglądarek | |--------|--------|------|----------------------| | JPEG | Szeroko wspierany, dobry dla fotografii | Brak transparentności, większy rozmiar niż nowsze formaty | Wszystkie przeglądarki | | PNG | Obsługuje transparentność, bezstratny | Duży rozmiar pliku | Wszystkie przeglądarki | | WebP | Mniejszy rozmiar niż JPEG/PNG, obsługuje transparentność | Niepełne wsparcie w starszych przeglądarkach | ~95% przeglądarek | | AVIF | Najlepsza kompresja, dobra jakość | Ograniczone wsparcie w przeglądarkach, wolniejsze kodowanie | ~70% przeglądarek |

Debugowanie i rozwiązywanie problemów

Problem: Obrazy są rozmyte lub niskiej jakości

Rozwiązanie: Dostosuj parametr quality:

1<Image
2  src="/images/quantum-city.jpg"
3  alt="Panorama Metropolis Quantum"
4  width={1200}
5  height={600}
6  quality={90} // Domyślnie 75
7/>

W Next.js 16 wartość quality musi znajdować się na liście images.qualities w next.config. Domyślnie lista zawiera tylko 75, więc bez dopisania 90 Next.js użyje najbliższej dozwolonej wartości, czyli 75:

1// next.config.js
2module.exports = {
3  images: {
4    qualities: [50, 75, 90],
5  },
6};

Teraz obraz powstanie w jakości 90, a pozostałe obrazy mogą dalej korzystać z domyślnego 75.

Problem: Przesunięcia układu (Layout Shift) podczas ładowania obrazów

Rozwiązanie: Zawsze określaj wymiary obrazu lub używaj fill z kontenerem o określonych wymiarach:

1<div className="relative w-full aspect-video">
2  <Image
3    src="/images/quantum-city.jpg"
4    alt="Panorama Metropolis Quantum"
5    fill
6    style={{ objectFit: 'cover' }}
7  />
8</div>

Problem: Obrazy nie są responsywne na różnych urządzeniach

Rozwiązanie: Używaj atrybutu sizes:

1<Image
2  src="/images/quantum-city.jpg"
3  alt="Panorama Metropolis Quantum"
4  width={1200}
5  height={600}
6  sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
7/>

Problem: Obrazy SVG nie są odpowiednio wyświetlane

Rozwiązanie: Dla plików SVG, często najlepszą opcją jest użycie unoptimized:

1<Image
2  src="/images/logo.svg"
3  alt="Logo Metropolis Quantum"
4  width={200}
5  height={200}
6  unoptimized
7/>

Optymalizacja obrazów poza komponentem Image

Oprócz komponentu Image, warto również stosować inne techniki optymalizacji obrazów:

1. Optymalizacja obrazów przed dodaniem do projektu

Przed dodaniem obrazów do projektu, warto je zoptymalizować za pomocą narzędzi takich jak:

  • ImageOptim
  • Squoosh
  • TinyPNG
  • SVGO (dla plików SVG)

2. Użycie CDN do serwowania obrazów

Korzystanie z CDN (Content Delivery Network) może znacząco przyspieszyć dostarczanie obrazów:

1// next.config.js
2module.exports = {
3  images: {
4    loader: 'akamai',
5    path: 'https://cdn.quantum-city.com',
6  },
7}

3. Używanie techniki "art direction" dla różnych urządzeń

1<picture>
2  <source media="(max-width: 640px)" srcSet="/images/banner-mobile.jpg" />
3  <source media="(max-width: 1024px)" srcSet="/images/banner-tablet.jpg" />
4  <source media="(min-width: 1025px)" srcSet="/images/banner-desktop.jpg" />
5  <img src="/images/banner-fallback.jpg" alt="Banner" />
6</picture>

4. Używanie techniki "responsive images" z srcset

1<img
2  src="/images/quantum-city.jpg"
3  srcSet="
4    /images/quantum-city-small.jpg 640w,
5    /images/quantum-city-medium.jpg 1024w,
6    /images/quantum-city-large.jpg 1920w
7  "
8  sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
9  alt="Panorama Metropolis Quantum"
10/>

Podsumowanie

Optymalizacja obrazów jest kluczowym aspektem tworzenia wydajnych aplikacji webowych. Next.js 16 oferuje potężny komponent Image, który automatycznie zajmuje się wieloma aspektami optymalizacji:

  1. Automatyczna optymalizacja - konwersja do nowoczesnych formatów (WebP, AVIF)
  2. Dostosowanie rozmiaru - serwowanie obrazów w odpowiednim rozmiarze dla urządzenia
  3. Leniwe ładowanie - obrazy są ładowane tylko wtedy, gdy zbliżają się do viewport
  4. Unikanie przesunięć układu - rezerwacja przestrzeni dla obrazów przed ich załadowaniem
  5. Wsparcie dla wielu źródeł - łatwe określenie różnych obrazów dla różnych rozdzielczości

Dzięki tym funkcjom, możesz tworzyć aplikacje, które ładują się szybciej, zużywają mniej danych i zapewniają lepsze doświadczenie użytkownika. Podobnie jak inżynierowie Metropolis Quantum optymalizują systemy wyświetlania hologramów i cyfrowych billboardów, tak ty możesz optymalizować obrazy w swojej aplikacji Next.js, aby zapewnić użytkownikom płynne i wydajne doświadczenie.

W następnym rozdziale poznamy różne strategie ładowania obrazów (lazy, eager, priority), które pozwolą jeszcze bardziej dostosować zachowanie obrazów do potrzeb twojej aplikacji.

Kod do tej lekcji: app/page.tsx
1// Next.js Image Component - Optimization Demo
2'use client';
3
4import { useState } from 'react';
5
6// Note: In a real Next.js project, you'd use:
7// import Image from 'next/image';
8// Here we demonstrate the concepts with standard img tags
9
10export default function ImageOptimizationDemo() {
11  const [showComparison, setShowComparison] = useState(false);
12
13  return (
14    <div className="min-h-screen bg-gray-900 text-white p-8">
15      <div className="max-w-4xl mx-auto">
16        <h1 className="text-3xl font-bold mb-2">Image Optimization in Next.js 16</h1>
17        <p className="text-gray-400 mb-8">
18          Automatic optimization, lazy loading, and responsive images
19        </p>
20
21        {/* Traditional vs Next.js Image comparison */}
22        <section className="mb-12">
23          <h2 className="text-xl font-semibold mb-4">Traditional HTML vs Next.js Image</h2>
24
25          <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
26            {/* Traditional approach */}
27            <div className="bg-gray-800 rounded-lg p-6">
28              <h3 className="text-red-400 font-mono mb-3">Traditional {"<img>"}</h3>
29              <pre className="bg-gray-900 p-4 rounded text-sm overflow-x-auto">
30{`<img
31  src="/photo.jpg"
32  alt="City panorama"
33  width="800"
34  height="600"
35/>`}
36              </pre>
37              <ul className="mt-4 space-y-2 text-sm text-gray-400">
38                <li>• No automatic optimization</li>
39                <li>• No lazy loading by default</li>
40                <li>• No responsive sizing</li>
41                <li>• Can cause layout shift (CLS)</li>
42              </ul>
43            </div>
44
45            {/* Next.js Image approach */}
46            <div className="bg-gray-800 rounded-lg p-6">
47              <h3 className="text-green-400 font-mono mb-3">Next.js {"<Image>"}</h3>
48              <pre className="bg-gray-900 p-4 rounded text-sm overflow-x-auto">
49{`import Image from 'next/image';
50
51<Image
52  src="/photo.jpg"
53  alt="City panorama"
54  width={800}
55  height={600}
56  placeholder="blur"
57/>`}
58              </pre>
59              <ul className="mt-4 space-y-2 text-sm text-gray-400">
60                <li>Auto WebP/AVIF conversion</li>
61                <li>Built-in lazy loading</li>
62                <li>Responsive srcset</li>
63                <li>Prevents layout shift</li>
64              </ul>
65            </div>
66          </div>
67        </section>
68
69        {/* Image properties demo */}
70        <section className="mb-12">
71          <h2 className="text-xl font-semibold mb-4">Key Properties</h2>
72          <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
73            <div className="bg-gray-800 rounded-lg p-4">
74              <h3 className="text-blue-400 font-semibold mb-2">fill</h3>
75              <p className="text-sm text-gray-400">
76                Image fills parent container. Use with position: relative on parent.
77              </p>
78              <code className="text-xs text-green-400 mt-2 block">
79                {"<Image fill style={{objectFit: 'cover'}} />"}
80              </code>
81            </div>
82            <div className="bg-gray-800 rounded-lg p-4">
83              <h3 className="text-purple-400 font-semibold mb-2">sizes</h3>
84              <p className="text-sm text-gray-400">
85                Responsive breakpoints for different viewport widths.
86              </p>
87              <code className="text-xs text-green-400 mt-2 block">
88                {'sizes="(max-width: 768px) 100vw, 50vw"'}
89              </code>
90            </div>
91            <div className="bg-gray-800 rounded-lg p-4">
92              <h3 className="text-yellow-400 font-semibold mb-2">placeholder</h3>
93              <p className="text-sm text-gray-400">
94                Show blur or empty placeholder while image loads.
95              </p>
96              <code className="text-xs text-green-400 mt-2 block">
97                {'placeholder="blur" blurDataURL="..."'}
98              </code>
99            </div>
100          </div>
101        </section>
102      </div>
103    </div>
104  );
105}

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. W Tailwind CSS klasa 'flex justify-center items-center' oznacza:

  2. 2. CSS-in-JS pozwala na:

Zadania praktyczne w grze

  • Edytor kodu

    Utwórz CSS z media queries dla mobile-first responsive design (sm, md, lg, xl breakpoints)

  • Układanie w poziomie

    Ułóż elementy w prawidłowej kolejności: return { → notFound: → true

  • Klikanie w kolejności

    Ułóż składnię media query z min-width

  • Edytor kodu

    Utwórz layout strony z headerem, sidebarem, main content i footerem używając CSS Grid

Przydatne artykuły