Przejdź do treści

Wzorzec · TypeScript

Dynamiczny sitemap.xml w Next.js (app/sitemap.ts)

Jedna mapa witryny ze ścieżkami statycznymi i treściowymi; zgodność z collectIndexableUrls i adresem kanonicznym.

Poziom: średniSzacunek czasu: ~40 min

Generator sitemap zbiera stabilne ścieżki i slugi z CMS/plików; lastmod pochodzi z daty treści, jeśli jest dostępna.

  • Każdy URL w sitemap musi zgadzać się z kanonicznym
  • nie umieszczaj stron noindex
  • przy dużym katalogu użyj podziału lub indeksu sitemap

app/sitemap.ts w App Router zwraca tablicę adresów URL. W jednym przebiegu zbierz trasy statyczne i dynamiczne slugi (blog, biblioteka, case studies).

Kod

Poniżej — faktyczna logika z app/sitemap.ts w tym projekcie (ścieżki statyczne, case’y, wpisy, wzorce biblioteki):

import type { MetadataRoute } from 'next'
import { caseSlugOrder } from '@/data/cases'
import { SITE_STATIC_PATHS } from '@/data/site-static-paths'
import { getAllPosts, getPostModifiedDate } from '@/lib/blog'
import { getAllLibraryEntries, getLibraryModifiedDate } from '@/lib/library'
import { getStaticPagesLastModified } from '@/lib/site-static-lastmod'
import { getSiteUrl } from '@/lib/site'

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const site = getSiteUrl()
  const base = site.replace(/\/$/, '')
  const staticLastMod = getStaticPagesLastModified()
  const posts = await getAllPosts()
  const library = await getAllLibraryEntries()

  const entries: MetadataRoute.Sitemap = [
    ...SITE_STATIC_PATHS.map((path) => ({
      url: `${base}${path}`,
      lastModified: staticLastMod,
      changeFrequency: 'weekly' as const,
      priority: path === '/' ? 1 : 0.7,
    })),
    ...caseSlugOrder.map((slug) => ({
      url: `${base}/kejsy/${slug}/`,
      lastModified: staticLastMod,
      changeFrequency: 'monthly' as const,
      priority: 0.65,
    })),
    ...posts.map((p) => ({
      url: `${base}/blog/${p.slug}/`,
      lastModified: new Date(getPostModifiedDate(p)),
      changeFrequency: 'monthly' as const,
      priority: 0.6,
    })),
    ...library.map((e) => ({
      url: `${base}/biblioteka/${e.slug}/`,
      lastModified: new Date(getLibraryModifiedDate(e)),
      changeFrequency: 'monthly' as const,
      priority: 0.62,
    })),
  ]
  return entries
}

Weryfikacja

Co sprawdzić:

  1. Plik się otwiera. W przeglądarce wejdź na https://twoja-domena/sitemap.xml — powinien załadować się XML bez błędu 404 w HTML. Jeśli widzisz aplikację Next zamiast XML, upewnij się, że w app/ jest sitemap.ts (lub sitemap.js) i że eksportuje funkcję domyślną.

  2. Search Console. W Google Search Console sekcja „Mapy witryn” → dodaj URL https://twoja-domena/sitemap.xml. Status „Nie pobrano” lub błędy parsowania oznaczają, że plik jest niedostępny dla robota albo XML jest niepoprawny.

  3. Zgodność z canonical. Wybierz kilka URL z pliku, otwórz strony, w źródle znajdź <link rel="canonical" ...>. Adres musi zgadzać się z URL w sitemap (włącznie z końcowym /, jeśli tak ustawiono kanoniczny na stronie).

  4. Spójność z noindex. Strony z robots: { index: false } lub <meta name="robots" content="noindex"> zwykle nie umieszczaj w sitemap — w przeciwnym razie wysyłasz sprzeczne sygnały do wyszukiwarki.

Źródła

Wdrożyć pod Twoją domenę i stack?

Krótki formularz: imię, telefon, strona. Po wysłaniu — odpowiedź z kolejnością prac i orientacją etapów; szczegóły doprecyzujemy na kontakcie.