Concevoir des microdonnées optimisées (Schema.org, JSON-LD)
Structurez et automatisez vos microdonnées pour enrichir vos pages et maximiser leur visibilité sur Google.

Utilisez JSON-LD, générez-le à partir des mêmes données que la page visible, et ne balisez que ce qu’un visiteur peut réellement voir. Ces trois règles couvrent l’essentiel de ce qui dérape avec les données structurées sur les sites programmatiques.
Un mot de vocabulaire, puisque le titre de ce chapitre parle de microdonnées. Schema.org est le vocabulaire. JSON-LD, Microdata et RDFa sont trois façons de l’écrire. Microdata mêle des attributs à votre HTML, ce qui devient pénible dès que les templates évoluent. JSON-LD tient dans une seule balise script, et c’est le format recommandé par Google quand votre configuration le permet. Sur des pages générées, je n’utilise que celui-là.
Pour les exemples, prenons un annuaire d’espaces de coworking : une page par espace, une page par ville.
Ce que les données structurées font, et ce qu’elles ne font pas
Les données structurées disent explicitement de quoi parle votre page : ceci est un établissement, voici son adresse, ce nombre est une note sur 5, issue de tant d’avis. Un humain le comprend grâce à la mise en page. Une machine doit le deviner, et le balisage lui évite de deviner.
Elles peuvent aussi rendre une page éligible à un affichage enrichi dans Google, comme les prix ou les étoiles d’avis. L’éligibilité est tout ce que vous obtenez. Google décide de ce qu’il montre, et le balisage seul ne fera ni monter une page dans les résultats ni indexer une page. Voyez-le comme un moyen d’être mieux compris et parfois de vous distinguer, puis jugez-le sur le taux de clic une fois en ligne.
Les réponses générées par IA lisent aussi ces pages. Des faits propres et explicites ne peuvent pas nuire, mais la documentation de Google sur les fonctionnalités d’IA précise qu’aucun balisage particulier n’est requis pour y apparaître. J’écris donc le JSON-LD pour la justesse et pour les fonctionnalités de recherche, et je m’assure que les faits figurent aussi dans le texte visible.
Les types qui valent la peine d’être générés
La prise en charge des résultats enrichis évolue. Avant de construire, vérifiez la galerie de recherche actuelle : schema.org compte des centaines de types et Google n’en affiche qu’une petite partie.
| Type | À utiliser pour | Ce qu’on peut attendre de Google |
|---|---|---|
Product + Offer |
Fiches produit, marketplaces | Prix et disponibilité peuvent apparaître |
Review / AggregateRating |
Vrais avis que vous collectez | Étoiles possibles, sous conditions strictes |
LocalBusiness |
Un établissement physique | Alimente la compréhension locale |
Event |
Événements datés avec une vraie date de début | Affichage événement possible |
Article / BlogPosting |
Guides et pages éditoriales | Meilleure compréhension de l’auteur et des dates |
BreadcrumbList |
Presque toutes les pages | Peu coûteux, clarifie la hiérarchie |
FAQPage |
Blocs de questions-réponses visibles | Aucun résultat enrichi pour la quasi-totalité des sites |
Cette dernière ligne mérite une précision, car beaucoup de guides restent à côté de la plaque sur ce point. En août 2023, Google a réservé les résultats enrichis FAQ aux sites gouvernementaux et de santé faisant autorité, et il a retiré les résultats enrichis HowTo. Si une agence vous promet des FAQ dépliables dans les résultats, elle vend quelque chose qui n’existe plus pour votre site. Un bloc FAQ sur la page reste utile aux visiteurs et aux réponses d’IA, alors gardez-le si les questions sont réelles. J’explique comment les construire dans le chapitre sur les FAQ. Ajouter FAQPage par-dessus reste un détail : le type décrit fidèlement le contenu, il n’obtiendra simplement pas d’affichage spécial.
Un exemple complet pour une page
Une page d’espace combine deux entités dans un seul @graph. L’adresse, la note et le fil d’Ariane viennent tous de colonnes de la base.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "LocalBusiness",
"@id": "https://example.com/coworking/lyon/sample-workspace#business",
"name": "Espace Exemple",
"url": "https://example.com/coworking/lyon/sample-workspace",
"image": "https://example.com/images/sample-workspace.jpg",
"priceRange": "€€",
"address": {
"@type": "PostalAddress",
"streetAddress": "1 rue de l'Exemple",
"addressLocality": "Lyon",
"postalCode": "69002",
"addressCountry": "FR"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": 4.6,
"reviewCount": 38
}
},
{
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Espaces de coworking",
"item": "https://example.com/coworking"
},
{
"@type": "ListItem",
"position": 2,
"name": "Lyon",
"item": "https://example.com/coworking/lyon"
},
{
"@type": "ListItem",
"position": 3,
"name": "Espace Exemple"
}
]
}
]
}
</script>
Le dernier élément du fil d’Ariane n’a pas d’URL item parce qu’il s’agit de la page courante, ce que la documentation de Google autorise. Le reste du fil doit correspondre aux liens que voit le visiteur, comme le décrit le chapitre sur le maillage interne.
Générez-le par une fonction, sans champ vide
Le bug programmatique classique, c’est "ratingValue": null ou un prix à 0 sur 3 000 pages, parce qu’une colonne était vide et que le template l’a affichée quand même. Construisez l’objet dans le code et n’ajoutez les propriétés optionnelles que si la donnée existe. Le fil d’Ariane suit le même principe, je l’ai donc laissé de côté ici.
// src/lib/schema.ts
type Space = {
name: string;
url: string;
street: string;
city: string;
postalCode: string;
image?: string;
rating?: number;
reviewCount: number;
};
export function spaceSchema(space: Space) {
const business: Record<string, unknown> = {
'@type': 'LocalBusiness',
'@id': `${space.url}#business`,
name: space.name,
url: space.url,
address: {
'@type': 'PostalAddress',
streetAddress: space.street,
addressLocality: space.city,
postalCode: space.postalCode,
addressCountry: 'FR',
},
};
if (space.image) business.image = space.image;
// Pas d'avis sur la page, pas de note dans le balisage.
if (space.rating && space.reviewCount > 0) {
business.aggregateRating = {
'@type': 'AggregateRating',
ratingValue: space.rating,
reviewCount: space.reviewCount,
};
}
return { '@context': 'https://schema.org', '@graph': [business] };
}
Dans un template Astro, affichez-le ainsi. Échapper < empêche une valeur parasite de vos données de fermer la balise script.
---
import { spaceSchema } from '../lib/schema';
const { space } = Astro.props;
const json = JSON.stringify(spaceSchema(space)).replace(/</g, '\\u003c');
---
<script type="application/ld+json" set:html={json} />
Les autres stacks arrivent au même résultat autrement. Sur WordPress, Rank Math et Yoast SEO couvrent les types courants et les champs personnalisés gèrent le reste. Sur Webflow, les pages de template CMS acceptent du code personnalisé dans les réglages de la page, où vous pouvez insérer directement les champs de la collection dans un bloc JSON-LD. Sur le projet Deux.io, les schémas faisaient partie des 90+ variables de page, traités comme les titres et les images.
Les règles qui vous évitent les ennuis
Les règles de Google sur les données structurées se lisent en dix minutes. Trois d’entre elles comptent beaucoup à grande échelle.
Le balisage doit décrire un contenu visible sur la page. Une note qui n’existe que dans le JSON-LD est un signal de spam, et une action manuelle sur un template touche toutes les pages qui en sont issues.
Les avis doivent être réels et liés à l’élément concerné. Générer un aggregateRating plausible relève de la fabrication, tout comme recopier la moyenne d’un autre site. Une entreprise qui balise les avis sur elle-même sur son propre site est aussi exclue des étoiles : sur un annuaire, la configuration ci-dessus convient, alors que sur les pages d’un établissement, elle ne conviendrait pas.
Les dates et les prix doivent être exacts. Si un prix change dans la base, le balisage et la page doivent changer dans le même build.
Testez par template, puis par cas limite
Passez le test des résultats enrichis sur une URL par template, puis sur les lignes délicates : l’espace sans avis, celui sans photo, le nom le plus long, le nom qui contient des guillemets ou une esperluette. Le validateur Schema Markup repère les erreurs de vocabulaire au-delà des fonctionnalités propres à Google.
À grande échelle, Screaming Frog peut extraire et valider les données structurées pendant un crawl. La Search Console indique les erreurs par type de résultat enrichi une fois vos pages traitées par Google, et ce sont ces chiffres que je surveille après une mise en ligne. Ahrefs ou Semrush vous disent quelles pages affichent réellement une fonctionnalité de SERP. Ajoutez aussi un contrôle au build qui analyse chaque bloc JSON-LD, car un seul caractère non échappé casse tout un template, et le chapitre sur l’automatisation des templates montre où cela s’insère.
Votre prochaine étape : listez vos types de pages, attribuez à chacun un type du tableau après avoir vérifié la galerie de recherche, et reliez chaque propriété voulue à une vraie colonne. Toute propriété sans colonne disparaît.