45395 - シコウサクゴ -

Astro + MDXで5種の構造化データ(JSON-LD)を完全実装する方法

2026-04-08
プログラミング
Astro
JSON-LD
SEO
構造化データ
MDX
Last updated:2026-04-09
7 Minutes
1253 Words

構造化データ(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構造化データが最も重要です。

ProductJsonLd.astro
1
---
2
interface 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
12
const { name, description, image, price, currency, availability, sku } = Astro.props;
13
14
const 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構造化データを設定します。

ArticleJsonLd.astro
1
---
2
interface Props {
3
title: string;
4
description: string;
5
image: string;
6
datePublished: string;
7
dateModified: string;
8
authorName: string;
9
}
10
11
const { title, description, image, datePublished, dateModified, authorName } = Astro.props;
12
13
const 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の生成例
2
const imageUrl = new URL(`/images/${image}`, Astro.site).toString();

実装

BreadcrumbJsonLd.astro
1
---
2
interface BreadcrumbItem {
3
name: string;
4
url: string;
5
}
6
7
interface Props {
8
items: BreadcrumbItem[];
9
}
10
11
const { items } = Astro.props;
12
13
const 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を出力していたためです。

1
Layout.astro → BreadcrumbJsonLd(1つ目)
2
└── ProductPage.astro → BreadcrumbJsonLd(2つ目) ← 重複!

解決策: パンくずの構造化データはレイアウト側でのみ出力するように統一しました。ページコンポーネントからは breadcrumbItems をpropsとして渡し、レイアウトが一元的に出力する設計に変更しました。

Layout.astro
1
---
2
interface Props {
3
breadcrumbItems?: BreadcrumbItem[];
4
// ...他のprops
5
}
6
7
const { 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

FaqJsonLd.astro
1
---
2
interface FaqItem {
3
question: string;
4
answer: string;
5
}
6
7
interface Props {
8
items: FaqItem[];
9
}
10
11
const { items } = Astro.props;
12
13
const 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

LocalBusinessJsonLd.astro
1
---
2
interface 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
17
const { name, description, address, telephone, openingHours, image } = Astro.props;
18
19
const 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 インテグレーションで簡単に設定できます。

astro.config.mjs
1
import sitemap from "@astrojs/sitemap";
2
3
export default defineConfig({
4
site: "https://example.com", // ※要確認:実際のドメイン
5
integrations: [sitemap()],
6
});

構造化データの検証方法

実装後は必ずGoogleの検証ツールで確認します。

  1. リッチリザルトテスト — 個別ページの構造化データを検証
  2. Google Search Console — サイト全体の構造化データのエラー・警告を監視
  3. ビルド時のチェックastro build で生成されたHTMLに application/ld+json が含まれているか確認
Terminal window
1
# ビルド後のHTML内にJSON-LDが含まれているか確認
2
grep -r "application/ld+json" dist/ | wc -l

まとめ

5種のJSON-LDを実装して得た教訓は以下の通りです。

  1. コンポーネント化が鍵 — 各JSON-LDを独立したAstroコンポーネントにすることで、再利用性と保守性が向上する
  2. 二重出力に注意 — 特にBreadcrumbはレイアウトとページの両方で出力しやすい。出力責任を一箇所に集約する設計が重要
  3. メタタグの棚卸しを兼ねる — 構造化データの実装は、既存のSEO設定を見直す良い機会になる
  4. set:html を使う — AstroでJSON-LDを出力する際は、文字列のエスケープを避けるために set:html ディレクティブが必須
Article title:Astro + MDXで5種の構造化データ(JSON-LD)を完全実装する方法
Article author:45395
Release time:2026-04-08

記事へのご質問・ご感想をお聞かせください

フィードバックを送る