React

sitemap.ts e robots.ts generati

Due file TypeScript producono /sitemap.xml e /robots.txt dallo stesso codice delle rotte. Tipi, hreflang, il tetto di 50.000 URL e il file statico.

In sintesi

  • app/sitemap.ts e app/robots.ts sono rotte: producono /sitemap.xml e /robots.txt.
  • Il tipo MetadataRoute impone la forma, non valida gli URL.
  • Oltre 50.000 URL per file serve generateSitemaps.
  • Un public/robots.txt convive male con app/robots.ts: tieni una fonte sola.

Due file TypeScript generano gli artefatti che i crawler leggono: app/sitemap.ts produce /sitemap.xml e app/robots.ts produce /robots.txt. Sono rotte come le altre, con gli stessi limiti di caching.

La sitemap

// app/sitemap.ts
import type { MetadataRoute } from "next";

const BASE = "https://ui.eneawork.it";
const PAGES = ["", "/pricing", "/blog"];

export default function sitemap(): MetadataRoute.Sitemap {
  return PAGES.map((path) => ({
    url: `${BASE}${path}`,
    lastModified: new Date(),
    changeFrequency: "weekly" as const,
    priority: path === "" ? 1 : 0.7,
    alternates: {
      languages: { it: `${BASE}/it${path}`, en: `${BASE}/en${path}` },
    },
  }));
}

Il tipo MetadataRoute.Sitemap accetta url, lastModified, changeFrequency, priority e alternates.languages. Il campo alternates scrive gli xhtml:link per hreflang: senza, la sitemap resta valida ma non dice nulla sulle traduzioni.

Il robots

// app/robots.ts
import type { MetadataRoute } from "next";

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [{ userAgent: "*", allow: "/", disallow: ["/api/", "/admin/"] }],
    sitemap: "https://ui.eneawork.it/sitemap.xml",
    host: "https://ui.eneawork.it",
  };
}

Il tetto dei 50.000

Un file di sitemap regge al massimo 50.000 URL e 50 MB non compressi. Oltre quella soglia si usa generateSitemaps, che restituisce un array di id e lascia a te generare un file per id.

// app/sitemap.ts
export async function generateSitemaps() {
  return [{ id: 0 }, { id: 1 }, { id: 2 }];
}

export default async function sitemap({ id }: { id: number }) {
  const rows = await loadUrls();
  return rows.slice(id * 50000, id * 50000 + 50000);
}

Statico o dinamico

FileRottaQuando è statico
app/sitemap.ts/sitemap.xmlSe non legge cookie, header o query
app/robots.ts/robots.txtQuasi sempre
public/sitemap.xml/sitemap.xmlSempre, ma va rimosso se esiste app/sitemap.ts

Le rotte che finiscono in sitemap sono pagine: blocchi blog, paginazione e breadcrumb coprono l'archivio senza inventare URL.

Indice o file unico è in sitemap index vs file unico; l'invio a Google in Search Console e indicizzazione; i link interni in strategia di link interni. Confronto: Next.js vs Vite per un sito marketing.

curl -s http://localhost:3000/sitemap.xml

Limite noto: la sitemap dichiara URL, non li verifica. Una page.tsx rimossa senza togliere la voce produce un 404 che Search Console segnala; il controllo va messo in CI, non fatto a occhio.

Domande frequenti

sitemap.ts e robots.ts sono statici o dinamici?

Dipende dal codice. Se non leggono cookie, header o query, Next.js li genera una volta in build; se leggono dati dinamici vengono eseguiti a ogni richiesta.

Posso superare i 50.000 URL?

Sì, con generateSitemaps: restituisci un array di id e Next.js espone un file per id, ognuno sotto il tetto.

Dove dichiaro le lingue nella sitemap?

Nel campo alternates.languages di ogni voce. Diventa un blocco xhtml:link con hreflang per ogni lingua.

Meglio public/robots.txt o app/robots.ts?

app/robots.ts, se vuoi che i valori vengano dal codice. Il file statico va bene solo se non cambia mai, e non deve convivere con la rotta.

La sitemap aiuta il ranking?

No, aiuta la scoperta. Serve a far trovare URL nuovi o profondi; la posizione dipende da contenuto, link e qualità della pagina.