Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Ikony, favicon i metadane wizualne w Next.js 15

W Metropolis Quantum, identyfikacja wizualna miasta jest starannie zaprojektowana, by każdy element - od logo na budynkach administracji miejskiej, przez oznakowanie dzielnic, aż po aplikacje miejskie - tworzył spójny wizerunek. Ta identyfikacja wizualna pomaga mieszkańcom i gościom rozpoznawać oficjalne elementy miasta, buduje zaufanie i profesjonalny wizerunek.

W świecie aplikacji internetowych, ikony, favicon i metadane wizualne pełnią podobną rolę. Są one kluczowe dla rozpoznawalności Twojej aplikacji, wpływają na pierwsze wrażenie użytkowników i ułatwiają identyfikację Twojej strony wśród wielu otwartych kart przeglądarki. Next.js 15 oferuje rozbudowane narzędzia do zarządzania tymi elementami, które poznamy w tym rozdziale.

Znaczenie ikon i metadanych wizualnych

Favicon i ikony aplikacji

Favicon (skrót od "favorite icon") to mała ikona wyświetlana w zakładce przeglądarki, na liście zakładek i w historii przeglądania. Jest pierwszym elementem wizualnym, który użytkownicy kojarzą z Twoją aplikacją, dlatego warto zadbać o jej jakość i rozpoznawalność.

W nowoczesnych aplikacjach webowych potrzebujemy nie tylko podstawowego favicon, ale całego zestawu ikon w różnych rozmiarach, które będą używane w różnych kontekstach:

  • Ikony zakładek przeglądarki
  • Ikony aplikacji na urządzeniach mobilnych
  • Ikony aplikacji na pulpicie (PWA)
  • Ikony w wynikach wyszukiwania
  • Ikony udostępniania w mediach społecznościowych

Metadane wizualne

Metadane wizualne to informacje, które określają, jak Twoja strona jest prezentowana, gdy jest udostępniana w mediach społecznościowych, komunikatorach czy wynikach wyszukiwania. Obejmują one:

  • Tytuł strony
  • Opis strony
  • Zdjęcia/grafiki podglądowe
  • Kolory motywu
  • Informacje o aplikacji (dla PWA)

Podstawowa konfiguracja favicon w Next.js 15

Next.js 15 wprowadza uproszczony sposób konfiguracji favicon i ikon przez API metadanych. Zacznijmy od podstawowej konfiguracji.

Umieszczenie ikon w katalogu public

Najpierw umieść pliki ikon w katalogu

public
:

1public/
2├── favicon.ico           # Klasyczny favicon (16x16 lub 32x32)
3├── icon.png              # Ikona główna (można użyć w metadata API)
4├── apple-icon.png        # Ikona dla urządzeń Apple
5└── icons/                # Katalog z różnymi rozmiarami ikon
6    ├── icon-16x16.png
7    ├── icon-32x32.png
8    ├── icon-192x192.png
9    └── icon-512x512.png

Konfiguracja podstawowego favicon

Najprostszy sposób to umieszczenie pliku

favicon.ico
w katalogu
public
. Next.js automatycznie rozpozna ten plik i użyje go jako favicon:

1// app/layout.tsx
2export default function RootLayout({
3  children,
4}: {
5  children: React.ReactNode;
6}) {
7  return (
8    <html lang="pl">
9      <body>{children}</body>
10    </html>
11  );
12}

W tym przypadku nie musisz robić nic więcej - przeglądarka automatycznie pobierze

/favicon.ico
.

Konfiguracja przez Metadata API

Next.js 15 oferuje zaawansowaną konfigurację ikon przez Metadata API:

1// app/layout.tsx
2import { Metadata } from 'next';
3
4export const metadata: Metadata = {
5  title: 'Metropolis Quantum',
6  description: 'Oficjalny portal miasta przyszłości',
7  icons: {
8    icon: '/icon.png',         // lub array: [{ url: '/icon-16x16.png', sizes: '16x16' }, ...]
9    shortcut: '/shortcut-icon.png',
10    apple: '/apple-icon.png',  // lub array: [{ url: '/apple-icon.png' }, ...]
11    other: {
12      rel: 'apple-touch-icon-precomposed',
13      url: '/apple-touch-icon-precomposed.png',
14    },
15  },
16};
17
18export default function RootLayout({
19  children,
20}: {
21  children: React.ReactNode;
22}) {
23  return (
24    <html lang="pl">
25      <body>{children}</body>
26    </html>
27  );
28}

Zaawansowana konfiguracja ikon

Dla profesjonalnych aplikacji warto przygotować kompletny zestaw ikon w różnych rozmiarach i formatach.

Rozszerzona konfiguracja ikon

1// app/layout.tsx
2import { Metadata } from 'next';
3
4export const metadata: Metadata = {
5  icons: {
6    icon: [
7      { url: '/icons/icon-16x16.png', sizes: '16x16', type: 'image/png' },
8      { url: '/icons/icon-32x32.png', sizes: '32x32', type: 'image/png' },
9      { url: '/icons/icon-192x192.png', sizes: '192x192', type: 'image/png' },
10      { url: '/icons/icon-512x512.png', sizes: '512x512', type: 'image/png' },
11    ],
12    shortcut: '/shortcut-icon.png',
13    apple: [
14      { url: '/icons/apple-icon-57x57.png', sizes: '57x57', type: 'image/png' },
15      { url: '/icons/apple-icon-72x72.png', sizes: '72x72', type: 'image/png' },
16      { url: '/icons/apple-icon-114x114.png', sizes: '114x114', type: 'image/png' },
17      { url: '/icons/apple-icon-180x180.png', sizes: '180x180', type: 'image/png' },
18    ],
19  },
20};

Rezultat w HTML

Powyższa konfiguracja wygeneruje następujące tagi w sekcji

<head>
dokumentu HTML:

1<link rel="icon" href="/icons/icon-16x16.png" sizes="16x16" type="image/png" />
2<link rel="icon" href="/icons/icon-32x32.png" sizes="32x32" type="image/png" />
3<link rel="icon" href="/icons/icon-192x192.png" sizes="192x192" type="image/png" />
4<link rel="icon" href="/icons/icon-512x512.png" sizes="512x512" type="image/png" />
5<link rel="shortcut icon" href="/shortcut-icon.png" />
6<link rel="apple-touch-icon" href="/icons/apple-icon-57x57.png" sizes="57x57" type="image/png" />
7<link rel="apple-touch-icon" href="/icons/apple-icon-72x72.png" sizes="72x72" type="image/png" />
8<link rel="apple-touch-icon" href="/icons/apple-icon-114x114.png" sizes="114x114" type="image/png" />
9<link rel="apple-touch-icon" href="/icons/apple-icon-180x180.png" sizes="180x180" type="image/png" />

Metadane Open Graph i Twitter Cards

Metadane Open Graph i Twitter Cards określają, jak Twoja strona wygląda, gdy jest udostępniana na platformach społecznościowych. Next.js 15 oferuje wbudowane wsparcie dla tych metadanych.

Konfiguracja Open Graph

1// app/layout.tsx
2import { Metadata } from 'next';
3
4export const metadata: Metadata = {
5  title: 'Metropolis Quantum',
6  description: 'Oficjalny portal miasta przyszłości',
7  openGraph: {
8    title: 'Metropolis Quantum - Miasto Przyszłości',
9    description: 'Odkryj technologię jutra w mieście Metropolis Quantum',
10    url: 'https://metropolisquantum.pl',
11    siteName: 'Metropolis Quantum',
12    images: [
13      {
14        url: '/images/og-image.jpg', // 1200x630 px rekomendowany rozmiar
15        width: 1200,
16        height: 630,
17        alt: 'Metropolis Quantum - Panorama miasta przyszłości',
18      },
19    ],
20    locale: 'pl_PL',
21    type: 'website',
22  },
23};

Konfiguracja Twitter Cards

1// app/layout.tsx
2import { Metadata } from 'next';
3
4export const metadata: Metadata = {
5  twitter: {
6    card: 'summary_large_image',
7    title: 'Metropolis Quantum - Miasto Przyszłości',
8    description: 'Odkryj technologię jutra w mieście Metropolis Quantum',
9    siteId: '1467726470533754880', // ID konta Twitter
10    creator: '@MetropolisQ',
11    creatorId: '1467726470533754880',
12    images: ['/images/twitter-image.jpg'], // 800x418 px rekomendowany rozmiar
13  },
14};

Testowanie metadanych Open Graph i Twitter Cards

Po wdrożeniu możesz przetestować swoje metadane za pomocą:

Dynamiczne metadane

W Next.js 15 możesz generować metadane dynamicznie, na podstawie ścieżki, parametrów URL lub danych zewnętrznych.

Dynamiczne metadane dla podstron

1// app/districts/[id]/page.tsx
2import { Metadata, ResolvingMetadata } from 'next';
3
4interface Props {
5  params: Promise<{ id: string }>;
6  searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
7}
8
9export async function generateMetadata(
10  { params, searchParams }: Props,
11  parent: ResolvingMetadata
12): Promise<Metadata> {
13  // Pobierz dane o dzielnicy
14  const districtId = (await params).id;
15  const district = await getDistrictData(districtId);
16  
17  // Opcjonalnie możesz połączyć z nadrzędnymi metadanymi
18  const previousImages = (await parent).openGraph?.images || [];
19  
20  return {
21    title: `${district.name} | Metropolis Quantum`,
22    description: district.description,
23    openGraph: {
24      images: [
25        {
26          url: `/images/districts/${districtId}.jpg`,
27          width: 1200,
28          height: 630,
29          alt: district.name,
30        },
31        ...previousImages,
32      ],
33    },
34    twitter: {
35      images: [`/images/districts/${districtId}-twitter.jpg`],
36    },
37  };
38}
39
40export default async function DistrictPage({ params }: Props) {
41  // Renderowanie strony
42}
43
44// Funkcja pobierająca dane o dzielnicy
45async function getDistrictData(id: string) {
46  // W rzeczywistej aplikacji: fetch z API lub bazy danych
47  return {
48    name: 'Dystrykt Kwantowy',
49    description: 'Najbardziej zaawansowana technologicznie dzielnica Metropolis Quantum.',
50  };
51}

Manifest aplikacji webowej (Web App Manifest)

Manifest aplikacji webowej to plik JSON, który dostarcza informacji o Twojej aplikacji dla przeglądarek i urządzeń mobilnych. Jest kluczowy dla Progressive Web Apps (PWA).

Tworzenie pliku manifest.json

Umieść plik

manifest.json
w katalogu
public
:

1{
2  "name": "Metropolis Quantum",
3  "short_name": "MetroQ",
4  "description": "Oficjalny portal miasta przyszłości",
5  "start_url": "/",
6  "display": "standalone",
7  "background_color": "#121212",
8  "theme_color": "#3a86ff",
9  "icons": [
10    {
11      "src": "/icons/icon-192x192.png",
12      "sizes": "192x192",
13      "type": "image/png",
14      "purpose": "any maskable"
15    },
16    {
17      "src": "/icons/icon-512x512.png",
18      "sizes": "512x512",
19      "type": "image/png"
20    }
21  ]
22}

Konfiguracja manifestu w Next.js

1// app/layout.tsx
2import { Metadata } from 'next';
3
4export const metadata: Metadata = {
5  // Inne metadane...
6  
7  // Manifest dla PWA
8  manifest: '/manifest.json',
9  
10  // Lub zdefiniuj manifest bezpośrednio:
11  /*
12  manifest: {
13    name: 'Metropolis Quantum',
14    short_name: 'MetroQ',
15    description: 'Oficjalny portal miasta przyszłości',
16    start_url: '/',
17    display: 'standalone',
18    background_color: '#121212',
19    theme_color: '#3a86ff',
20    icons: [
21      {
22        src: '/icons/icon-192x192.png',
23        sizes: '192x192',
24        type: 'image/png',
25        purpose: 'any maskable'
26      },
27      {
28        src: '/icons/icon-512x512.png',
29        sizes: '512x512',
30        type: 'image/png'
31      }
32    ]
33  }
34  */
35};

Inne metadane wizualne i funkcjonalne

Motyw kolorystyczny

1// app/layout.tsx
2import { Metadata } from 'next';
3
4export const metadata: Metadata = {
5  // Kolor motywu dla przeglądarek mobilnych
6  themeColor: [
7    { media: '(prefers-color-scheme: light)', color: '#3a86ff' },
8    { media: '(prefers-color-scheme: dark)', color: '#1e429f' },
9  ],
10  
11  // Lub pojedynczy kolor:
12  // themeColor: '#3a86ff',
13};

Obsługa Apple-specific

1// app/layout.tsx
2import { Metadata } from 'next';
3
4export const metadata: Metadata = {
5  // Inne metadane...
6  
7  // Ustawienia dla Safari na iOS
8  appleWebApp: {
9    title: 'Metropolis Quantum', // Alternatywny tytuł dla iOS
10    statusBarStyle: 'black-translucent',
11    startupImage: [
12      {
13        url: '/startup/apple-startup-2048x2732.png',
14        media: '(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)',
15      },
16      {
17        url: '/startup/apple-startup-1668x2388.png',
18        media: '(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)',
19      },
20      // Więcej rozmiarów...
21    ],
22  },
23};

Aplikacja mobilna alternatywna

1// app/layout.tsx
2import { Metadata } from 'next';
3
4export const metadata: Metadata = {
5  // Inne metadane...
6  
7  // Linki do aplikacji mobilnych
8  alternates: {
9    'android-app': 'android-app://com.metropolisquantum/https/metropolisquantum.pl',
10    'ios-app': 'ios-app://1234567890/https/metropolisquantum.pl',
11  },
12};

Generowanie ikon i metadanych

Ręczne tworzenie wszystkich rozmiarów ikon może być żmudne. Na szczęście istnieją narzędzia, które automatyzują ten proces.

Narzędzia do generowania ikon

  1. Favicon Generator (https://realfavicongenerator.net/) - generuje pełny zestaw ikon z jednego obrazu
  2. PWA Asset Generator (https://github.com/onderceylan/pwa-asset-generator) - narzędzie wiersza poleceń do generowania ikon PWA

Przykład użycia PWA Asset Generator

1# Instalacja
2npm i -g pwa-asset-generator
3
4# Generowanie ikon
5npx pwa-asset-generator ./logo.svg ./public/icons --manifest ./public/manifest.json --icon-only --favicon

Dynamiczne generowanie obrazów Open Graph

Czasem warto dynamicznie generować obrazy Open Graph dla różnych podstron. W Next.js 15 możemy to zrobić za pomocą Route Handlers.

Implementacja Route Handler dla dynamicznych obrazów OG

1// app/api/og/route.tsx
2import { ImageResponse } from 'next/og';
3import { NextRequest } from 'next/server';
4
5export const runtime = 'edge';
6
7export async function GET(request: NextRequest) {
8  const { searchParams } = new URL(request.url);
9  
10  // Pobierz parametry z URL
11  const title = searchParams.get('title') || 'Metropolis Quantum';
12  const description = searchParams.get('description') || 'Miasto przyszłości';
13  
14  // Opcjonalnie: pobierz dodatkowe dane z bazy lub API
15  // const data = await fetchData();
16  
17  // Wygeneruj obraz
18  return new ImageResponse(
19    (
20      <div
21        style={{
22          display: 'flex',
23          fontSize: 40,
24          color: 'white',
25          background: 'linear-gradient(to bottom, #3a86ff, #1e429f)',
26          width: '100%',
27          height: '100%',
28          padding: '50px 200px',
29          textAlign: 'center',
30          justifyContent: 'center',
31          alignItems: 'center',
32          flexDirection: 'column',
33        }}
34      >
35        <img
36          src={`${request.nextUrl.origin}/logo.png`}
37          alt="Logo"
38          width="200"
39          height="200"
40        />
41        <h1 style={{ fontSize: 70 }}>{title}</h1>
42        <p style={{ fontSize: 40 }}>{description}</p>
43      </div>
44    ),
45    {
46      width: 1200,
47      height: 630,
48    }
49  );
50}

Użycie dynamicznego obrazu OG w metadanych

1// app/districts/[id]/page.tsx
2import { Metadata } from 'next';
3
4interface Props {
5  params: Promise<{ id: string }>;
6}
7
8export async function generateMetadata({ params }: Props): Promise<Metadata> {
9  const district = await getDistrictData((await params).id);
10  
11  const ogImageUrl = new URL(`/api/og`, 'https://metropolisquantum.pl');
12  ogImageUrl.searchParams.append('title', district.name);
13  ogImageUrl.searchParams.append('description', district.description);
14  
15  return {
16    title: `${district.name} | Metropolis Quantum`,
17    description: district.description,
18    openGraph: {
19      images: [ogImageUrl.toString()],
20    },
21    twitter: {
22      images: [ogImageUrl.toString()],
23    },
24  };
25}

Strukturyzowane dane dla SEO

Strukturyzowane dane pomagają wyszukiwarkom lepiej rozumieć zawartość Twojej strony i mogą wpłynąć na sposób wyświetlania wyników wyszukiwania.

Implementacja JSON-LD

1// app/layout.tsx
2import { Metadata } from 'next';
3
4export const metadata: Metadata = {
5  // Inne metadane...
6};
7
8export default function RootLayout({
9  children,
10}: {
11  children: React.ReactNode;
12}) {
13  return (
14    <html lang="pl">
15      <head>
16        <script
17          type="application/ld+json"
18          dangerouslySetInnerHTML={{
19            __html: JSON.stringify({
20              '@context': 'https://schema.org',
21              '@type': 'Organization',
22              name: 'Metropolis Quantum',
23              url: 'https://metropolisquantum.pl',
24              logo: 'https://metropolisquantum.pl/logo.png',
25              contactPoint: {
26                '@type': 'ContactPoint',
27                telephone: '+48-123-456-789',
28                contactType: 'customer service',
29              },
30              sameAs: [
31                'https://facebook.com/metropolisquantum',
32                'https://twitter.com/metropolisq',
33                'https://instagram.com/metropolisquantum',
34              ],
35            }),
36          }}
37        />
38      </head>
39      <body>{children}</body>
40    </html>
41  );
42}

Dynamiczne JSON-LD dla podstron

1// components/JsonLd.tsx
2interface JsonLdProps {
3  data: any;
4}
5
6export function JsonLd({ data }: JsonLdProps) {
7  return (
8    <script
9      type="application/ld+json"
10      dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
11    />
12  );
13}
1// app/districts/[id]/page.tsx
2import { JsonLd } from '@/components/JsonLd';
3
4export default async function DistrictPage({ params }: { params: Promise<{ id: string }> }) {
5  const district = await getDistrictData((await params).id);
6  
7  const jsonLdData = {
8    '@context': 'https://schema.org',
9    '@type': 'Place',
10    name: district.name,
11    description: district.description,
12    address: {
13      '@type': 'PostalAddress',
14      addressLocality: 'Metropolis Quantum',
15      addressRegion: 'MQ',
16      postalCode: '00-000',
17      addressCountry: 'PL',
18    },
19    geo: {
20      '@type': 'GeoCoordinates',
21      latitude: district.coordinates.lat,
22      longitude: district.coordinates.lng,
23    },
24    image: `https://metropolisquantum.pl/images/districts/${(await params).id}.jpg`,
25  };
26  
27  return (
28    <div>
29      <JsonLd data={jsonLdData} />
30      <h1>{district.name}</h1>
31      {/* Reszta zawartości */}
32    </div>
33  );
34}

Przykłady kompletnej implementacji

Metadane dla aplikacji e-commerce

1// app/layout.tsx
2import { Metadata } from 'next';
3
4export const metadata: Metadata = {
5  metadataBase: new URL('https://metropolisquantum.pl'),
6  title: {
7    default: 'Metropolis Quantum Store',
8    template: '%s | Metropolis Quantum Store',
9  },
10  description: 'Sklep z najnowszymi technologiami przyszłości',
11  keywords: ['technologia', 'przyszłość', 'metropolis', 'quantum', 'sklep'],
12  authors: [{ name: 'Metropolis Quantum', url: 'https://metropolisquantum.pl' }],
13  creator: 'Zespół Metropolis Quantum',
14  publisher: 'Metropolis Quantum Corporation',
15  
16  // Ikony
17  icons: {
18    icon: [
19      { url: '/icons/icon-16x16.png', sizes: '16x16', type: 'image/png' },
20      { url: '/icons/icon-32x32.png', sizes: '32x32', type: 'image/png' },
21      { url: '/icons/icon-192x192.png', sizes: '192x192', type: 'image/png' },
22      { url: '/icons/icon-512x512.png', sizes: '512x512', type: 'image/png' },
23    ],
24    apple: [
25      { url: '/icons/apple-icon.png' },
26    ],
27  },
28  
29  // Open Graph
30  openGraph: {
31    type: 'website',
32    siteName: 'Metropolis Quantum Store',
33    title: 'Metropolis Quantum Store - Technologia przyszłości już dziś',
34    description: 'Sklep z najnowszymi technologiami przyszłości z Metropolis Quantum',
35    url: 'https://store.metropolisquantum.pl',
36    images: [
37      {
38        url: '/images/og-default.jpg',
39        width: 1200,
40        height: 630,
41        alt: 'Metropolis Quantum Store',
42      },
43    ],
44    locale: 'pl_PL',
45  },
46  
47  // Twitter
48  twitter: {
49    card: 'summary_large_image',
50    title: 'Metropolis Quantum Store',
51    description: 'Technologia przyszłości już dziś',
52    images: ['/images/twitter-image.jpg'],
53    creator: '@MetropolisQ',
54  },
55  
56  // Robots
57  robots: {
58    index: true,
59    follow: true,
60    googleBot: {
61      index: true,
62      follow: true,
63      'max-video-preview': -1,
64      'max-image-preview': 'large',
65      'max-snippet': -1,
66    },
67  },
68  
69  // Kolor motywu
70  themeColor: [
71    { media: '(prefers-color-scheme: light)', color: '#3a86ff' },
72    { media: '(prefers-color-scheme: dark)', color: '#1e429f' },
73  ],
74  
75  // Manifest PWA
76  manifest: '/manifest.json',
77  
78  // Alternatywne aplikacje
79  alternates: {
80    canonical: 'https://store.metropolisquantum.pl',
81    languages: {
82      'en-US': 'https://store.metropolisquantum.pl/en',
83      'de-DE': 'https://store.metropolisquantum.pl/de',
84    },
85    media: {
86      'only screen and (max-width: 640px)': 'https://m.store.metropolisquantum.pl',
87    },
88    types: {
89      'application/rss+xml': 'https://store.metropolisquantum.pl/rss',
90    },
91  },
92  
93  // Ustawienia dla aplikacji Apple
94  appleWebApp: {
95    title: 'MQ Store',
96    statusBarStyle: 'black-translucent',
97    capable: true,
98  },
99  
100  // Inne
101  formatDetection: {
102    telephone: true,
103    date: false,
104    address: true,
105    email: true,
106    url: true,
107  },
108};

Metadane dla strony produktu

1// app/products/[id]/page.tsx
2import { Metadata } from 'next';
3
4interface Props {
5  params: Promise<{ id: string }>;
6}
7
8export async function generateMetadata({ params }: Props): Promise<Metadata> {
9  const product = await getProductData((await params).id);
10  
11  const price = new Intl.NumberFormat('pl-PL', {
12    style: 'currency',
13    currency: 'PLN',
14  }).format(product.price);
15  
16  return {
17    title: product.name,
18    description: product.description,
19    openGraph: {
20      title: product.name,
21      description: product.description,
22      type: 'product',
23      images: [
24        {
25          url: product.images[0].url,
26          width: 800,
27          height: 600,
28          alt: product.name,
29        },
30      ],
31      availability: product.inStock ? 'in stock' : 'out of stock',
32      price: {
33        amount: product.price.toString(),
34        currency: 'PLN',
35      },
36    },
37  };
38}
39
40export default async function ProductPage({ params }: Props) {
41  const product = await getProductData((await params).id);
42  
43  const jsonLdData = {
44    '@context': 'https://schema.org',
45    '@type': 'Product',
46    name: product.name,
47    description: product.description,
48    image: product.images.map((img) => img.url),
49    offers: {
50      '@type': 'Offer',
51      price: product.price,
52      priceCurrency: 'PLN',
53      availability: product.inStock
54        ? 'https://schema.org/InStock'
55        : 'https://schema.org/OutOfStock',
56      url: `https://store.metropolisquantum.pl/products/${(await params).id}`,
57    },
58    brand: {
59      '@type': 'Brand',
60      name: 'Metropolis Quantum',
61    },
62    aggregateRating: {
63      '@type': 'AggregateRating',
64      ratingValue: product.rating.average,
65      reviewCount: product.rating.count,
66    },
67  };
68  
69  return (
70    <div>
71      <script
72        type="application/ld+json"
73        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLdData) }}
74      />
75      <h1>{product.name}</h1>
76      {/* Reszta zawartości strony produktu */}
77    </div>
78  );
79}
80
81async function getProductData(id: string) {
82  // W rzeczywistej aplikacji: fetch z API lub bazy danych
83  return {
84    id,
85    name: 'Kwantowy komunikator MQ-7',
86    description: 'Najnowszy komunikator wykorzystujący technologię kwantową do natychmiastowej komunikacji na dowolną odległość.',
87    price: 4999.99,
88    inStock: true,
89    images: [
90      { url: '/images/products/mq-7-main.jpg' },
91      { url: '/images/products/mq-7-side.jpg' },
92      { url: '/images/products/mq-7-back.jpg' },
93    ],
94    rating: {
95      average: 4.8,
96      count: 156,
97    },
98  };
99}

Najlepsze praktyki

  1. Przygotuj pełen zestaw ikon - uwzględnij wszystkie wymagane rozmiary dla różnych platform i urządzeń
  2. Optymalizuj obrazy - kompresuj i optymalizuj wszystkie ikony i obrazy Open Graph
  3. Testuj na różnych platformach - sprawdź, jak Twoje metadane wyglądają na Facebooku, Twitterze, WhatsApp i innych platformach
  4. Używaj znaczących nazw plików - nazwij swoje pliki w sposób opisowy (np.
    product-og-image.jpg
    zamiast
    image1.jpg
    )
  5. Dodaj strukturyzowane dane - używaj JSON-LD dla lepszego SEO
  6. Dostosuj metadane do każdej podstrony - generuj dynamiczne metadane dla każdej strony
  7. Pamiętaj o alternatywnych tekstach - zawsze dodawaj atrybut
    alt
    do obrazów
  8. Uwzględnij różne tryby (jasny/ciemny) - dostosuj kolory motywu do preferencji użytkownika
  9. Dostarczaj ikony w formacie SVG gdy to możliwe - dla lepszej skalowalności

Debugowanie metadanych

Narzędzia do testowania metadanych

  1. Facebook Sharing Debugger - https://developers.facebook.com/tools/debug/
  2. Twitter Card Validator - https://cards-dev.twitter.com/validator
  3. LinkedIn Post Inspector - https://www.linkedin.com/post-inspector/
  4. Rich Results Test (Google) - https://search.google.com/test/rich-results

Debugowanie w Next.js

Możesz sprawdzić wygenerowane metadane bezpośrednio w źródle HTML Twojej strony. Sprawdź sekcję

<head>
w narzędziach deweloperskich przeglądarki.

Podsumowanie

Ikony, favicon i metadane wizualne są kluczowym elementem identyfikacji wizualnej Twojej aplikacji Next.js 15. Podobnie jak w Metropolis Quantum, gdzie każdy element wizualny buduje spójny wizerunek miasta, tak w Twojej aplikacji starannie zaprojektowane ikony i metadane budują profesjonalny wizerunek i zwiększają rozpoznawalność.

Najważniejsze punkty:

  1. Używaj API metadanych Next.js 15 - upraszcza zarządzanie wszystkimi metadanymi
  2. Przygotuj kompletny zestaw ikon - dla różnych platform i urządzeń
  3. Konfiguruj Open Graph i Twitter Cards - dla lepszej widoczności w mediach społecznościowych
  4. Dodaj strukturyzowane dane - dla lepszego SEO i bogatszych wyników wyszukiwania
  5. Generuj dynamiczne metadane - dostosowane do każdej podstrony
  6. Testuj swoje metadane - sprawdź, jak wyglądają na różnych platformach

W następnym rozdziale zajmiemy się animacjami i przejściami w interfejsie użytkownika, które dodają dynamiki i interaktywności aplikacjom Next.js 15, podobnie jak holograficzne efekty i płynne animacje dodają życia architekturze Metropolis Quantum.

Ir a CodeWorlds