構造化データ(JSON-LD)は、検索エンジンにページの内容を機械可読な形式で伝える仕組みです。リッチスニペットの表示やSEO評価の向上に直結するため、ECサイトやブログでは実装しておきたい要素です。
本記事では、AstroベースのECサイトで5種のJSON-LDを実装した手順と、途中で遭遇したパンくず二重表示バグ、メタタグのコピペミス検出などの実体験を記録します。
実装した5種のJSON-LD
| 種類 | 用途 | 対象ページ |
|---|---|---|
| Product | 商品情報(価格・在庫状況・画像) | 商品詳細ページ |
| Article | 記事情報(著者・公開日・更新日) | ブログ記事ページ |
| Breadcrumb | パンくずリスト(階層構造) | 全ページ |
| FAQ | よくある質問(Q&A形式) | FAQページ |
| LocalBusiness | 店舗情報(住所・営業時間) | Aboutページ |
Product(商品情報)のJSON-LD
ECサイトの商品ページでは、Product構造化データが最も重要です。
1---2interface Props {3 name: string;4 description: string;5 image: string;6 price: number;7 currency: string;8 availability: "InStock" | "OutOfStock" | "PreOrder";9 sku: string;10}11
12const { name, description, image, price, currency, availability, sku } = Astro.props;13
14const jsonLd = {16 collapsed lines
15 "@context": "https://schema.org",16 "@type": "Product",17 name,18 description,19 image,20 sku,21 offers: {22 "@type": "Offer",23 price,24 priceCurrency: currency,25 availability: `https://schema.org/${availability}`,26 },27};28---29
30<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />ポイント: Astroでは set:html ディレクティブを使うことで、JSON文字列がエスケープされずにそのまま出力されます。
Article(記事情報)のJSON-LD
ブログ記事にはArticle構造化データを設定します。
1---2interface Props {3 title: string;4 description: string;5 image: string;6 datePublished: string;7 dateModified: string;8 authorName: string;9}10
11const { title, description, image, datePublished, dateModified, authorName } = Astro.props;12
13const jsonLd = {14 "@context": "https://schema.org",14 collapsed lines
15 "@type": "Article",16 headline: title,17 description,18 image,19 datePublished,20 dateModified,21 author: {22 "@type": "Person",23 name: authorName,24 },25};26---27
28<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />画像の構造化データ出力も忘れずに設定します。image フィールドには絶対URLを渡す必要があります。
1// 画像URLの生成例2const imageUrl = new URL(`/images/${image}`, Astro.site).toString();Breadcrumb(パンくずリスト)— 二重表示バグの解決
実装
1---2interface BreadcrumbItem {3 name: string;4 url: string;5}6
7interface Props {8 items: BreadcrumbItem[];9}10
11const { items } = Astro.props;12
13const jsonLd = {14 "@context": "https://schema.org",11 collapsed lines
15 "@type": "BreadcrumbList",16 itemListElement: items.map((item, index) => ({17 "@type": "ListItem",18 position: index + 1,19 name: item.name,20 item: item.url,21 })),22};23---24
25<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />二重表示バグ
実装後、Google Search Consoleの構造化データレポートで「BreadcrumbListが2つ検出されている」と警告が出ました。
原因: レイアウトコンポーネントとページコンポーネントの両方でBreadcrumb JSON-LDを出力していたためです。
1Layout.astro → BreadcrumbJsonLd(1つ目)2└── ProductPage.astro → BreadcrumbJsonLd(2つ目) ← 重複!解決策: パンくずの構造化データはレイアウト側でのみ出力するように統一しました。ページコンポーネントからは breadcrumbItems をpropsとして渡し、レイアウトが一元的に出力する設計に変更しました。
1---2interface Props {3 breadcrumbItems?: BreadcrumbItem[];4 // ...他のprops5}6
7const { breadcrumbItems } = Astro.props;8---9
10<html>11 <head>12 {breadcrumbItems && <BreadcrumbJsonLd items={breadcrumbItems} />}13 </head>14 <!-- ... -->1 collapsed line
15</html>FAQ(よくある質問)のJSON-LD
1---2interface FaqItem {3 question: string;4 answer: string;5}6
7interface Props {8 items: FaqItem[];9}10
11const { items } = Astro.props;12
13const jsonLd = {14 "@context": "https://schema.org",13 collapsed lines
15 "@type": "FAQPage",16 mainEntity: items.map((item) => ({17 "@type": "Question",18 name: item.question,19 acceptedAnswer: {20 "@type": "Answer",21 text: item.answer,22 },23 })),24};25---26
27<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />LocalBusiness(店舗情報)のJSON-LD
1---2interface Props {3 name: string;4 description: string;5 address: {6 streetAddress: string;7 addressLocality: string;8 addressRegion: string;9 postalCode: string;10 addressCountry: string;11 };12 telephone: string;13 openingHours: string[];14 image: string;20 collapsed lines
15}16
17const { name, description, address, telephone, openingHours, image } = Astro.props;18
19const jsonLd = {20 "@context": "https://schema.org",21 "@type": "LocalBusiness",22 name,23 description,24 image,25 telephone,26 address: {27 "@type": "PostalAddress",28 ...address,29 },30 openingHoursSpecification: openingHours,31};32---33
34<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />メタタグのコピペミス検出
JSON-LD実装と並行して、既存のメタタグにコピペミスがあることを発見しました。具体的には、あるページのOGP descriptionが別ページの内容のままになっていました。
Claude Codeで全ページのメタタグを一括チェックし、不一致を検出・修正しました。構造化データの実装は、こうした既存の問題を発見する良い機会にもなります。
sitemap.xml対応
JSON-LDの実装とあわせて、sitemap.xmlの生成も対応しました。Astroでは @astrojs/sitemap インテグレーションで簡単に設定できます。
1import sitemap from "@astrojs/sitemap";2
3export default defineConfig({4 site: "https://example.com", // ※要確認:実際のドメイン5 integrations: [sitemap()],6});構造化データの検証方法
実装後は必ずGoogleの検証ツールで確認します。
- リッチリザルトテスト — 個別ページの構造化データを検証
- Google Search Console — サイト全体の構造化データのエラー・警告を監視
- ビルド時のチェック —
astro buildで生成されたHTMLにapplication/ld+jsonが含まれているか確認
1# ビルド後のHTML内にJSON-LDが含まれているか確認2grep -r "application/ld+json" dist/ | wc -lまとめ
5種のJSON-LDを実装して得た教訓は以下の通りです。
- コンポーネント化が鍵 — 各JSON-LDを独立したAstroコンポーネントにすることで、再利用性と保守性が向上する
- 二重出力に注意 — 特にBreadcrumbはレイアウトとページの両方で出力しやすい。出力責任を一箇所に集約する設計が重要
- メタタグの棚卸しを兼ねる — 構造化データの実装は、既存のSEO設定を見直す良い機会になる
set:htmlを使う — AstroでJSON-LDを出力する際は、文字列のエスケープを避けるためにset:htmlディレクティブが必須