Accueil / Articles

SEO technique dans le Next.js App Router : métadonnées, cartes du site et JSON-LD

Apprenez comment les outils de métadonnées partagés, les paramètres par défaut du layout racine, robots.ts, un sitemap dynamique, des données JSON-LD fiables et des audits de page permettent à une application Next.js d’avoir une base SEO solide.

Éditeur : REACTAPP.TOP4 min de lecture1024 mots

Sources: How I Got a Next.js Project to 100% SEO on Lighthouse

Une application Next.js peut sembler terminée tout en étant difficile à lire pour les moteurs de recherche : les pages manquent d’URL canoniques, les règles pour les crawlers ne sont pas définies, et le nouveau contenu n’arrive jamais dans un sitemap. Cette démarche suit un site API utilisant des données fictives, avec de nombreuses pages de ressources, depuis sa première audit SEO Lighthouse jusqu’à un score de 100, en couvrant les sept modifications apportées grâce aux API de métadonnées intégrées à l’App Router.

Générer les métadonnées des pages à partir d’une seule fonction utilitaire

Rédiger manuellement les métadonnées dans chaque page entraîne des incohérences, comme l’absence d’une URL canonique ici ou un titre Open Graph obsolète là-bas. Une petite fonction utilitaire résout ce problème : chaque page ne transmet que son titre, sa description, son chemin ainsi qu’un indicateur noIndex facultatif, et cette fonction les transforme en champs canoniques, robots et Open Graph.

export function createPageMetadata({
  title,
  description,
  path,
  noIndex = false,
}: PageMetadata) {
  return {
    title,
    description,
    alternates: {
      canonical: path,
    },
    robots: noIndex ? "noindex, nofollow" : "index, follow",
    openGraph: {
      title,
      description,
      url: path,
      type: "website",
    },
  };
}

L’indexation est désactivable : un indicateur permet de passer une page en mode noindex, nofollow. Comme le chemin est relatif, l’outil s’appuie sur une URL de base définie dans le layout racine.

Placer les paramètres par défaut pour tout le site dans le layout racine

Tout ce qui est partagé sur l’ensemble du site doit figurer dans l’export metadata de layout.tsx :

  • metadataBase
  • un modèle de titre
  • une description par défaut
  • les paramètres par défaut pour Open Graph
  • les paramètres de la carte Twitter
  • les paramètres par défaut pour robots
  • l’attribut lang sur l’élément <html>

Une version simplifiée de cet export ressemble à ceci :

export const metadata: Metadata = {
  metadataBase: new URL("https://mockdata.ir"),
  title: {
    default: "MockData",
    template: "%s | MockData",
  },
  description: "...",
};

Avec le modèle %s | MockData, une page intitulée "Users API" s’affiche sous le nom de "Users API | MockData", tandis que les pages sans titre utilisent la valeur default. C’est metadataBase qui convertit les valeurs relatives canonical et url d’Open Graph du helper en adresses URL absolues. Notez que lang n’est pas un champ de métadonnées ; il doit être défini sur la balise <html> que génère le layout.

Définir des règles de crawl avec robots.ts

Au lieu de laisser les règles de crawl non définies, générer un robots.txt à partir d’un fichier app/robots.ts qui renvoie un objet MetadataRoute.Robots de type spécifié :

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: "*",
      allow: "/",
    },
    sitemap: "https://mockdata.ir/sitemap.xml",
  };
}

Celui-ci est délibérément minimal : tous les agents utilisateur peuvent crawler tout le contenu, et l’emplacement du sitemap est indiqué.

Générer le sitemap à partir de vos données

Avec des pages de ressources pour les utilisateurs, les articles, les produits et bien plus encore, un sitemap XML géré manuellement ne permet pas une expansion suffisante. Une exportation par défaut de sitemap.ts peut être async, ce qui lui permet de consulter la même source que celle utilisée par les pages :

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const resources = await getPublicResources();

return [
    {
      url: "https://mockdata.ir",
      lastModified: new Date(),
    },
    ...resources.map((resource) => ({
      url: `https://mockdata.ir/${resource.slug}`,
      lastModified: resource.updatedAt,
    })),
  ];
}

Chaque ressource est associée à une URL où updatedAt remplace lastModified, de sorte que les nouvelles ressources publiques s’ajoutent automatiquement au sitemap. Une amélioration possible : utiliser new Date() pour la page d’accueil afin de signaler un changement à chaque génération ; une date réelle du contenu serait plus fiable.

Ne ajoutez des données structurées que lorsque c’est pertinent

JSON-LD avec le vocabulaire Schema.org indique aux moteurs de recherche ce que représente une page. L’application elle-même est décrite comme une SoftwareApplication, et les types utilisés sont :

  • WebSite
  • Organization
  • SoftwareApplication
  • BreadcrumbList
  • Visez la précision plutôt que le volume : le marquage doit décrire le contenu qui se trouve réellement sur la page. Des avis, des notes ou d’autres propriétés inventées sont trompeurs et peuvent enfreindre les directives des moteurs de recherche.

    Assurez une cohérence entre les chemins d’ navigation et les schémas

    Les pages de documentation imbriquées affichent un parcours visible tel que Accueil → Documentation → API des utilisateurs, et la même hiérarchie est générée sous forme de données structurées BreadcrumbList. Le parcours visible et le marquage doivent toujours décrire la même structure.

    Vérifiez manuellement chaque page indexable

    Finalement, examinez chaque page publique individuellement :

    • Y a-t-il exactement un H1 clair ?
    • Le titre décrit-il bien cette page spécifique ?
    • La description correspond-elle au contenu réel ?
    • Y a-t-il des liens internes utiles vers et depuis cette page ?
    • Faut-il vraiment indexer cette page ?
    • Y a-t-il des pages vides ou de faible valeur exposées aux robots d’indexation ?

    Toutes les URLs ne doivent pas figurer dans les résultats de recherche ; l’attribut noindex est souvent la solution appropriée. Pour en savoir plus sur la structure des titres, consultez ce guide sur l’HTML sémantique concernant la mise en forme du contenu.

    Que signifie et ne signifie pas un score SEO parfait de Lighthouse

    Après ces corrections, Lighthouse a indiqué un score SEO de 100. Ce chiffre vérifie les aspects techniques de base tels que les titres, les descriptions et la capacité d’indexation ; il ne dit rien au sujet du classement. Le classement dépend de la qualité du contenu, de l’intention de recherche, de l’autorité du site, des liens entrants, de la concurrence, des performances et de nombreux autres facteurs.

    La véritable valeur réside dans cette base solide. Effectuer un audit avant de déployer n’importe quelle application Next.js est peu coûteux et permet de détecter de nombreuses petites erreurs.

    Points clés

    • Centralisez les métadonnées par page dans un fichier d’aide et définissez des valeurs par défaut partagées, y compris metadataBase, dans le layout racine.
    • Utilisez robots.ts ainsi qu’un sitemap.ts asynchrone afin que les règles des robots et les URLs restent en synchronisation avec vos données.
    • Ajoutez uniquement des données structurées qui décrivent fidèlement la page, et assurez-vous que les chemins d’affichage soient identiques tant dans l’interface utilisateur que dans le schéma.
    • Considérez noindex comme un choix légitime pour les pages qui ne doivent pas apparaître dans les résultats de recherche.
    • Interprétez un score Lighthouse de 100 comme étant « techniquement solide », et non comme une garantie de classement.

    Lectures complémentaires