Blog Schema: JSON-LD Structured Data Generation

Generate complete JSON-LD schema markup for blog posts with Article/BlogPosting, Person, Organization, BreadcrumbList, ImageObject, and optional FAQPage.

How to use it

  1. Hit Copy the whole skill.
  2. Claude: ⋯ → Download .md, then Customize → Skills → Add → Upload skill.
    ChatGPT: make a Project and paste it into Instructions.
    Neither? Paste it at the top of a new chat — it works for that chat.
  3. Describe your job in plain words. The AI follows the skill from there.
Claude Code — installs the whole folder, not just SKILL.md
npx degit AgriciDaniel/claude-blog/skills/blog-schema#main ~/.claude/skills/blog-schema-2

For one project only, change the path to .claude/skills/blog-schema-2.

Not working?
  • Check which app you pasted it into — the steps above name the right one.
  • Some skills need the paid tier of Claude or ChatGPT.
Step-by-step guide with screenshots · Ask in the forum

Paste into Claude, ChatGPT or Cursor.

Show the full text308 lines
blog-schema-2/SKILL.md308 lines10.4 KBpushed 60d agoRawView on GitHub

Blog Schema: JSON-LD Structured Data Generation

Generates complete, validated JSON-LD schema markup for blog posts using the @graph pattern. Combines multiple schema types into a single script tag with stable @id references for entity linking.

Workflow

Step 1: Read Content

Read the blog post and extract all schema-relevant data:

  • Title (headline)
  • Author (name, job title, social links, credentials)
  • Dates (datePublished, dateModified / lastUpdated)
  • Description (meta description)
  • FAQ section (question and answer pairs)
  • Images (cover image URL, dimensions, alt text; inline images)
  • Organization info (site name, URL, logo)
  • Word count (approximate from content length)
  • Tags/categories (for BreadcrumbList category)
  • Slug (from filename or frontmatter)

Step 2: Generate BlogPosting Schema

Complete BlogPosting with recommended properties when applicable:

{
  "@type": "BlogPosting",
  "@id": "{siteUrl}/blog/{slug}#article",
  "headline": "Concise post title",
  "description": "Concise page-specific meta description",
  "datePublished": "YYYY-MM-DD",
  "dateModified": "YYYY-MM-DD",
  "author": { "@id": "{siteUrl}/author/{author-slug}#person" },
  "publisher": { "@id": "{siteUrl}#organization" },
  "image": { "@id": "{siteUrl}/blog/{slug}#primaryimage" },
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "{siteUrl}/blog/{slug}"
  },
  "wordCount": 2400,
  "articleBody": "First 200 characters of content as excerpt..."
}

Google's Article structured data docs do not define required Article properties. Include headline, datePublished, author, publisher, and image when applicable, validate with the Rich Results Test, and treat missing fields as warnings unless the target surface requires them. Recommended properties: description, dateModified, mainEntityOfPage, wordCount, articleBody (excerpt).

Step 3: Generate Person Schema

Author schema with stable @id for cross-referencing:

{
  "@type": "Person",
  "@id": "{siteUrl}/author/{author-slug}#person",
  "name": "Author Name",
  "jobTitle": "Role or Title",
  "url": "{siteUrl}/author/{author-slug}",
  "sameAs": [
    "https://twitter.com/handle",
    "https://linkedin.com/in/handle",
    "https://github.com/handle"
  ]
}

Optional properties (include when available):

  • alumniOf - Educational institution (Organization type)
  • worksFor - Employer (reference to Organization @id if same entity)

Step 4: Generate Organization Schema

Blog's parent organization entity:

{
  "@type": "Organization",
  "@id": "{siteUrl}#organization",
  "name": "Organization Name",
  "url": "{siteUrl}",
  "logo": {
    "@type": "ImageObject",
    "url": "{siteUrl}/logo.png",
    "width": 600,
    "height": 60
  },
  "sameAs": [
    "https://twitter.com/org",
    "https://linkedin.com/company/org",
    "https://github.com/org"
  ]
}

Logo requirements: use a valid crawlable image URL and follow the active Organization and Article documentation for the target surface. Do not invent hard logo dimensions unless the project or current docs require them.

Step 5: Generate BreadcrumbList

Navigation breadcrumb schema showing content hierarchy:

{
  "@type": "BreadcrumbList",
  "@id": "{siteUrl}/blog/{slug}#breadcrumb",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "Home",
      "item": "{siteUrl}"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "Category Name",
      "item": "{siteUrl}/blog/category/{category-slug}"
    },
    {
      "@type": "ListItem",
      "position": 3,
      "name": "Post Title",
      "item": "{siteUrl}/blog/{slug}"
    }
  ]
}

If no category is available, use "Blog" as the second breadcrumb item with {siteUrl}/blog as the URL.

Step 6: Generate FAQPage Entity Schema (Optional)

Extract Q&A pairs from the blog post's FAQ section:

{
  "@type": "FAQPage",
  "@id": "{siteUrl}/blog/{slug}#faq",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What is the question?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The complete visible answer text."
      }
    }
  ]
}

Google retired FAQ rich results for all sites on 2026-05-07. FAQPage is not a Google rich-result or generative-AI optimization path, and it earns no SEO or AI-readiness credit. Only emit it when a visible FAQ genuinely helps readers, with at least one valid Question and matching visible answer. Do not pad an answer to a target length or add an FAQ solely for markup.

Do not substitute QAPage. Google supports QAPage for a page focused on one question where users can submit answers. Editorial FAQs, support FAQs, and blog Q&A sections do not meet that model.

Step 7: Generate VideoObject (if videos present)

For each YouTube video embedded in the post, generate a VideoObject schema:

{
  "@type": "VideoObject",
  "@id": "{siteUrl}/blog/{slug}#video-{index}",
  "name": "Video title",
  "description": "Video description excerpt (first 200 chars)",
  "thumbnailUrl": "https://img.youtube.com/vi/{videoId}/hqdefault.jpg",
  "uploadDate": "{ISO 8601 date}",
  "contentUrl": "https://www.youtube.com/watch?v={videoId}",
  "embedUrl": "https://www.youtube.com/embed/{videoId}",
  "duration": "PT{M}M{S}S",
  "interactionStatistic": {
    "@type": "InteractionCounter",
    "interactionType": { "@type": "WatchAction" },
    "userInteractionCount": {viewCount}
  }
}

Add each VideoObject to the @graph array. Use #video-1, #video-2 etc. for the @id fragment. Extract video metadata from the embed's noscript fallback or from YouTube Data API if available via blog-google.

Step 7.5: Generate ImageObject

Cover image schema for the post's primary image:

{
  "@type": "ImageObject",
  "@id": "{siteUrl}/blog/{slug}#primaryimage",
  "url": "https://cdn.pixabay.com/photo/.../image.jpg",
  "width": 1200,
  "height": 630,
  "caption": "Descriptive caption matching alt text"
}

Image requirements:

  • URL must be crawlable and publicly accessible
  • Width and height should reflect actual image dimensions
  • Caption should match or closely align with the image alt text
  • Preferred dimensions: 1200x630 (OG-compatible) or 1920x1080

Step 8: Validate & Warn

Check per-surface support before recommending schema types:

Type Google Search status Valid entity/context use
HowTo No current Google rich-result experience Valid schema.org type for genuine how-to content
Dataset Used by Dataset Search, not general Google Search rich results Valid only for an actual dataset
QAPage Supported for one question with user-submitted answers Do not use for editorial FAQ content
Course Course list remains distinct from the retired Course Info experience Use only when the current Course list documentation and visible content match
ClaimReview, SpecialAnnouncement, Course Info, Estimated Salary, Learning Video, Vehicle Listing Former Google Search experiences; support was retired May remain schema.org-valid, but never recommend them for Google eligibility
PracticeProblem Removed from Google Search and its documentation Do not recommend for Google eligibility
Sitelinks Search Box No dedicated Google Search visual element Google generates sitelinks algorithmically

Validation checks:

  1. All @id references resolve to entities within the @graph
  2. dateModified is equal to or after datePublished
  3. headline is concise. Warn when it may truncate or becomes unclear
  4. description is concise, page-specific, and not duplicated across posts
  5. All URLs are absolute (not relative)
  6. Image dimensions are positive integers
  7. BreadcrumbList positions are sequential starting from 1
  8. If FAQPage is emitted, visible Q&A content exists and includes at least 1 valid Question

Generative AI note: Structured data is not required for Google generative AI search, and there is no special AI schema. Prioritize accurate, visible-content-consistent Article/BlogPosting, Person, Organization, and BreadcrumbList entities. Add ImageObject or VideoObject when the assets exist. FAQPage remains optional reader-facing markup and adds no Google AI advantage.

Step 9: Output

Combine all schemas into a single <script> tag using the @graph pattern:

Security requirement: build the JSON-LD with a real JSON encoder, never string interpolation. Before embedding in HTML, make the JSON text script-safe by escaping closing script sequences and literal less-than characters, for example replace </ with <\/ and < with \u003c. User-controlled fields such as headline, description, author name, image URL, and breadcrumb labels must only enter the block as JSON-encoded values.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@graph": [
    { "@type": "BlogPosting", ... },
    { "@type": "Person", ... },
    { "@type": "Organization", ... },
    { "@type": "BreadcrumbList", ... },
    { "@type": "FAQPage", ... },
    { "@type": "VideoObject", ... },
    { "@type": "ImageObject", ... }
  ]
}
</script>

@graph pattern benefits:

  • Single script tag instead of multiple - cleaner HTML
  • Entity linking via stable @id references (e.g., author references Person by @id)
  • Google and AI systems parse @graph arrays correctly
  • Easier to maintain and update as a single block

Output options:

  • Embedded HTML - Ready to paste into <head> or before </body>
  • Standalone JSON - For CMS schema fields or API injection
  • MDX component - If the project uses MDX, wrap in a component

Save the generated schema to the blog post file or to a separate schema file as the user prefers.

Google can process JSON-LD generated by JavaScript when it is present in the rendered DOM. Server-rendered markup is still more portable for non-Google crawlers, but source-only JSON-LD is not a Google requirement. For dynamic markup, validate the rendered URL, confirm the values match visible content, and avoid delayed or failed client requests that leave the rendered DOM empty.

1---
2name: blog-schema
3description: >
4 Generate complete JSON-LD schema markup for blog posts with Article/BlogPosting,
5 Person, Organization, BreadcrumbList, ImageObject, and optional FAQPage. Validates
6 against Google requirements and warns about deprecated types. Use when user
7 says "schema", "blog schema", "json-ld", "structured data", "schema markup",
8 "generate schema".
9user-invokable: true
10argument-hint: "<file-path>"
11license: MIT
12---
13 
14# Blog Schema: JSON-LD Structured Data Generation
15 
16Generates complete, validated JSON-LD schema markup for blog posts using the
17@graph pattern. Combines multiple schema types into a single script tag with
18stable @id references for entity linking.
19 
20## Workflow
21 
22### Step 1: Read Content
23 
24Read the blog post and extract all schema-relevant data:
25- **Title** (headline)
26- **Author** (name, job title, social links, credentials)
27- **Dates** (datePublished, dateModified / lastUpdated)
28- **Description** (meta description)
29- **FAQ section** (question and answer pairs)
30- **Images** (cover image URL, dimensions, alt text; inline images)
31- **Organization info** (site name, URL, logo)
32- **Word count** (approximate from content length)
33- **Tags/categories** (for BreadcrumbList category)
34- **Slug** (from filename or frontmatter)
35 
36### Step 2: Generate BlogPosting Schema
37 
38Complete BlogPosting with recommended properties when applicable:
39 
40```json
41{
42 "@type": "BlogPosting",
43 "@id": "{siteUrl}/blog/{slug}#article",
44 "headline": "Concise post title",
45 "description": "Concise page-specific meta description",
46 "datePublished": "YYYY-MM-DD",
47 "dateModified": "YYYY-MM-DD",
48 "author": { "@id": "{siteUrl}/author/{author-slug}#person" },
49 "publisher": { "@id": "{siteUrl}#organization" },
50 "image": { "@id": "{siteUrl}/blog/{slug}#primaryimage" },
51 "mainEntityOfPage": {
52 "@type": "WebPage",
53 "@id": "{siteUrl}/blog/{slug}"
54 },
55 "wordCount": 2400,
56 "articleBody": "First 200 characters of content as excerpt..."
57}
58```
59 
60Google's Article structured data docs do not define required Article
61properties. Include `headline`, `datePublished`, `author`, `publisher`, and
62`image` when applicable, validate with the Rich Results Test, and treat missing
63fields as warnings unless the target surface requires them. Recommended
64properties: description, dateModified, mainEntityOfPage, wordCount, articleBody
65(excerpt).
66 
67### Step 3: Generate Person Schema
68 
69Author schema with stable @id for cross-referencing:
70 
71```json
72{
73 "@type": "Person",
74 "@id": "{siteUrl}/author/{author-slug}#person",
75 "name": "Author Name",
76 "jobTitle": "Role or Title",
77 "url": "{siteUrl}/author/{author-slug}",
78 "sameAs": [
79 "https://twitter.com/handle",
80 "https://linkedin.com/in/handle",
81 "https://github.com/handle"
82 ]
83}
84```
85 
86Optional properties (include when available):
87- `alumniOf` - Educational institution (Organization type)
88- `worksFor` - Employer (reference to Organization @id if same entity)
89 
90### Step 4: Generate Organization Schema
91 
92Blog's parent organization entity:
93 
94```json
95{
96 "@type": "Organization",
97 "@id": "{siteUrl}#organization",
98 "name": "Organization Name",
99 "url": "{siteUrl}",
100 "logo": {
101 "@type": "ImageObject",
102 "url": "{siteUrl}/logo.png",
103 "width": 600,
104 "height": 60
105 },
106 "sameAs": [
107 "https://twitter.com/org",
108 "https://linkedin.com/company/org",
109 "https://github.com/org"
110 ]
111}
112```
113 
114Logo requirements: use a valid crawlable image URL and follow the active
115Organization and Article documentation for the target surface. Do not invent
116hard logo dimensions unless the project or current docs require them.
117 
118### Step 5: Generate BreadcrumbList
119 
120Navigation breadcrumb schema showing content hierarchy:
121 
122```json
123{
124 "@type": "BreadcrumbList",
125 "@id": "{siteUrl}/blog/{slug}#breadcrumb",
126 "itemListElement": [
127 {
128 "@type": "ListItem",
129 "position": 1,
130 "name": "Home",
131 "item": "{siteUrl}"
132 },
133 {
134 "@type": "ListItem",
135 "position": 2,
136 "name": "Category Name",
137 "item": "{siteUrl}/blog/category/{category-slug}"
138 },
139 {
140 "@type": "ListItem",
141 "position": 3,
142 "name": "Post Title",
143 "item": "{siteUrl}/blog/{slug}"
144 }
145 ]
146}
147```
148 
149If no category is available, use "Blog" as the second breadcrumb item with
150`{siteUrl}/blog` as the URL.
151 
152### Step 6: Generate FAQPage Entity Schema (Optional)
153 
154Extract Q&A pairs from the blog post's FAQ section:
155 
156```json
157{
158 "@type": "FAQPage",
159 "@id": "{siteUrl}/blog/{slug}#faq",
160 "mainEntity": [
161 {
162 "@type": "Question",
163 "name": "What is the question?",
164 "acceptedAnswer": {
165 "@type": "Answer",
166 "text": "The complete visible answer text."
167 }
168 }
169 ]
170}
171```
172 
173Google retired FAQ rich results for all sites on 2026-05-07. FAQPage is not a
174Google rich-result or generative-AI optimization path, and it earns no SEO or
175AI-readiness credit. Only emit it when a visible FAQ genuinely helps readers,
176with at least one valid `Question` and matching visible answer. Do not pad an
177answer to a target length or add an FAQ solely for markup.
178 
179Do not substitute QAPage. Google supports QAPage for a page focused on one
180question where users can submit answers. Editorial FAQs, support FAQs, and blog
181Q&A sections do not meet that model.
182 
183### Step 7: Generate VideoObject (if videos present)
184 
185For each YouTube video embedded in the post, generate a VideoObject schema:
186 
187```json
188{
189 "@type": "VideoObject",
190 "@id": "{siteUrl}/blog/{slug}#video-{index}",
191 "name": "Video title",
192 "description": "Video description excerpt (first 200 chars)",
193 "thumbnailUrl": "https://img.youtube.com/vi/{videoId}/hqdefault.jpg",
194 "uploadDate": "{ISO 8601 date}",
195 "contentUrl": "https://www.youtube.com/watch?v={videoId}",
196 "embedUrl": "https://www.youtube.com/embed/{videoId}",
197 "duration": "PT{M}M{S}S",
198 "interactionStatistic": {
199 "@type": "InteractionCounter",
200 "interactionType": { "@type": "WatchAction" },
201 "userInteractionCount": {viewCount}
202 }
203}
204```
205 
206Add each VideoObject to the @graph array. Use `#video-1`, `#video-2` etc. for
207the @id fragment. Extract video metadata from the embed's noscript fallback or
208from YouTube Data API if available via `blog-google`.
209 
210### Step 7.5: Generate ImageObject
211 
212Cover image schema for the post's primary image:
213 
214```json
215{
216 "@type": "ImageObject",
217 "@id": "{siteUrl}/blog/{slug}#primaryimage",
218 "url": "https://cdn.pixabay.com/photo/.../image.jpg",
219 "width": 1200,
220 "height": 630,
221 "caption": "Descriptive caption matching alt text"
222}
223```
224 
225Image requirements:
226- URL must be crawlable and publicly accessible
227- Width and height should reflect actual image dimensions
228- Caption should match or closely align with the image alt text
229- Preferred dimensions: 1200x630 (OG-compatible) or 1920x1080
230 
231### Step 8: Validate & Warn
232 
233Check per-surface support before recommending schema types:
234 
235| Type | Google Search status | Valid entity/context use |
236|------|----------------------|--------------------------|
237| HowTo | No current Google rich-result experience | Valid schema.org type for genuine how-to content |
238| Dataset | Used by Dataset Search, not general Google Search rich results | Valid only for an actual dataset |
239| QAPage | Supported for one question with user-submitted answers | Do not use for editorial FAQ content |
240| Course | Course list remains distinct from the retired Course Info experience | Use only when the current Course list documentation and visible content match |
241| ClaimReview, SpecialAnnouncement, Course Info, Estimated Salary, Learning Video, Vehicle Listing | Former Google Search experiences; support was retired | May remain schema.org-valid, but never recommend them for Google eligibility |
242| PracticeProblem | Removed from Google Search and its documentation | Do not recommend for Google eligibility |
243| Sitelinks Search Box | No dedicated Google Search visual element | Google generates sitelinks algorithmically |
244 
245**Validation checks:**
2461. All @id references resolve to entities within the @graph
2472. dateModified is equal to or after datePublished
2483. headline is concise. Warn when it may truncate or becomes unclear
2494. description is concise, page-specific, and not duplicated across posts
2505. All URLs are absolute (not relative)
2516. Image dimensions are positive integers
2527. BreadcrumbList positions are sequential starting from 1
2538. If FAQPage is emitted, visible Q&A content exists and includes at least 1 valid `Question`
254 
255**Generative AI note:** Structured data is not required for Google generative
256AI search, and there is no special AI schema. Prioritize accurate,
257visible-content-consistent Article/BlogPosting, Person, Organization, and
258BreadcrumbList entities. Add ImageObject or VideoObject when the assets exist.
259FAQPage remains optional reader-facing markup and adds no Google AI advantage.
260 
261### Step 9: Output
262 
263Combine all schemas into a single `<script>` tag using the @graph pattern:
264 
265Security requirement: build the JSON-LD with a real JSON encoder, never string
266interpolation. Before embedding in HTML, make the JSON text script-safe by
267escaping closing script sequences and literal less-than characters, for example
268replace `</` with `<\/` and `<` with `\u003c`. User-controlled fields such as
269headline, description, author name, image URL, and breadcrumb labels must only
270enter the block as JSON-encoded values.
271 
272```html
273<script type="application/ld+json">
274{
275 "@context": "https://schema.org",
276 "@graph": [
277 { "@type": "BlogPosting", ... },
278 { "@type": "Person", ... },
279 { "@type": "Organization", ... },
280 { "@type": "BreadcrumbList", ... },
281 { "@type": "FAQPage", ... },
282 { "@type": "VideoObject", ... },
283 { "@type": "ImageObject", ... }
284 ]
285}
286</script>
287```
288 
289**@graph pattern benefits:**
290- Single script tag instead of multiple - cleaner HTML
291- Entity linking via stable @id references (e.g., author references Person by @id)
292- Google and AI systems parse @graph arrays correctly
293- Easier to maintain and update as a single block
294 
295**Output options:**
296- **Embedded HTML** - Ready to paste into `<head>` or before `</body>`
297- **Standalone JSON** - For CMS schema fields or API injection
298- **MDX component** - If the project uses MDX, wrap in a component
299 
300Save the generated schema to the blog post file or to a separate schema file
301as the user prefers.
302 
303Google can process JSON-LD generated by JavaScript when it is present in the
304rendered DOM. Server-rendered markup is still more portable for non-Google
305crawlers, but source-only JSON-LD is not a Google requirement. For dynamic
306markup, validate the rendered URL, confirm the values match visible content,
307and avoid delayed or failed client requests that leave the rendered DOM empty.
308 

Discussion

Alternatives

Also in SEO & keywords