$npx -y skills add AgriciDaniel/claude-blog --skill blog-schemaGenerate complete JSON-LD schema markup for blog posts including BlogPosting, Person, Organization, BreadcrumbList, FAQPage, and ImageObject. Validates against Google requirements and warns about deprecated types. Use when user says "schema", "blog schema", "json-ld", "structured
| 1 | # Blog Schema: JSON-LD Structured Data Generation |
| 2 | |
| 3 | Generates complete, validated JSON-LD schema markup for blog posts using the |
| 4 | @graph pattern. Combines multiple schema types into a single script tag with |
| 5 | stable @id references for entity linking. |
| 6 | |
| 7 | ## Workflow |
| 8 | |
| 9 | ### Step 1: Read Content |
| 10 | |
| 11 | Read the blog post and extract all schema-relevant data: |
| 12 | - **Title** (headline) |
| 13 | - **Author** (name, job title, social links, credentials) |
| 14 | - **Dates** (datePublished, dateModified / lastUpdated) |
| 15 | - **Description** (meta description) |
| 16 | - **FAQ section** (question and answer pairs) |
| 17 | - **Images** (cover image URL, dimensions, alt text; inline images) |
| 18 | - **Organization info** (site name, URL, logo) |
| 19 | - **Word count** (approximate from content length) |
| 20 | - **Tags/categories** (for BreadcrumbList category) |
| 21 | - **Slug** (from filename or frontmatter) |
| 22 | |
| 23 | ### Step 2: Generate BlogPosting Schema |
| 24 | |
| 25 | Complete BlogPosting with all required and recommended properties: |
| 26 | |
| 27 | ```json |
| 28 | { |
| 29 | "@type": "BlogPosting", |
| 30 | "@id": "{siteUrl}/blog/{slug}#article", |
| 31 | "headline": "Post title (max 110 chars)", |
| 32 | "description": "Meta description (150-160 chars)", |
| 33 | "datePublished": "YYYY-MM-DD", |
| 34 | "dateModified": "YYYY-MM-DD", |
| 35 | "author": { "@id": "{siteUrl}/author/{author-slug}#person" }, |
| 36 | "publisher": { "@id": "{siteUrl}#organization" }, |
| 37 | "image": { "@id": "{siteUrl}/blog/{slug}#primaryimage" }, |
| 38 | "mainEntityOfPage": { |
| 39 | "@type": "WebPage", |
| 40 | "@id": "{siteUrl}/blog/{slug}" |
| 41 | }, |
| 42 | "wordCount": 2400, |
| 43 | "articleBody": "First 200 characters of content as excerpt..." |
| 44 | } |
| 45 | ``` |
| 46 | |
| 47 | Required properties: @type, headline, datePublished, author, publisher, image. |
| 48 | Recommended properties: description, dateModified, mainEntityOfPage, wordCount, |
| 49 | articleBody (excerpt). |
| 50 | |
| 51 | ### Step 3: Generate Person Schema |
| 52 | |
| 53 | Author schema with stable @id for cross-referencing: |
| 54 | |
| 55 | ```json |
| 56 | { |
| 57 | "@type": "Person", |
| 58 | "@id": "{siteUrl}/author/{author-slug}#person", |
| 59 | "name": "Author Name", |
| 60 | "jobTitle": "Role or Title", |
| 61 | "url": "{siteUrl}/author/{author-slug}", |
| 62 | "sameAs": [ |
| 63 | "https://twitter.com/handle", |
| 64 | "https://linkedin.com/in/handle", |
| 65 | "https://github.com/handle" |
| 66 | ] |
| 67 | } |
| 68 | ``` |
| 69 | |
| 70 | Optional properties (include when available): |
| 71 | - `alumniOf` - Educational institution (Organization type) |
| 72 | - `worksFor` - Employer (reference to Organization @id if same entity) |
| 73 | |
| 74 | ### Step 4: Generate Organization Schema |
| 75 | |
| 76 | Blog's parent organization entity: |
| 77 | |
| 78 | ```json |
| 79 | { |
| 80 | "@type": "Organization", |
| 81 | "@id": "{siteUrl}#organization", |
| 82 | "name": "Organization Name", |
| 83 | "url": "{siteUrl}", |
| 84 | "logo": { |
| 85 | "@type": "ImageObject", |
| 86 | "url": "{siteUrl}/logo.png", |
| 87 | "width": 600, |
| 88 | "height": 60 |
| 89 | }, |
| 90 | "sameAs": [ |
| 91 | "https://twitter.com/org", |
| 92 | "https://linkedin.com/company/org", |
| 93 | "https://github.com/org" |
| 94 | ] |
| 95 | } |
| 96 | ``` |
| 97 | |
| 98 | Logo requirements: must be a valid image URL. Google recommends logos be |
| 99 | 112x112px minimum, 600px wide maximum. Rectangular logos preferred for |
| 100 | BlogPosting publishers. |
| 101 | |
| 102 | ### Step 5: Generate BreadcrumbList |
| 103 | |
| 104 | Navigation breadcrumb schema showing content hierarchy: |
| 105 | |
| 106 | ```json |
| 107 | { |
| 108 | "@type": "BreadcrumbList", |
| 109 | "@id": "{siteUrl}/blog/{slug}#breadcrumb", |
| 110 | "itemListElement": [ |
| 111 | { |
| 112 | "@type": "ListItem", |
| 113 | "position": 1, |
| 114 | "name": "Home", |
| 115 | "item": "{siteUrl}" |
| 116 | }, |
| 117 | { |
| 118 | "@type": "ListItem", |
| 119 | "position": 2, |
| 120 | "name": "Category Name", |
| 121 | "item": "{siteUrl}/blog/category/{category-slug}" |
| 122 | }, |
| 123 | { |
| 124 | "@type": "ListItem", |
| 125 | "position": 3, |
| 126 | "name": "Post Title", |
| 127 | "item": "{siteUrl}/blog/{slug}" |
| 128 | } |
| 129 | ] |
| 130 | } |
| 131 | ``` |
| 132 | |
| 133 | If no category is available, use "Blog" as the second breadcrumb item with |
| 134 | `{siteUrl}/blog` as the URL. |
| 135 | |
| 136 | ### Step 6: Generate FAQPage Schema |
| 137 | |
| 138 | Extract Q&A pairs from the blog post's FAQ section: |
| 139 | |
| 140 | ```json |
| 141 | { |
| 142 | "@type": "FAQPage", |
| 143 | "@id": "{siteUrl}/blog/{slug}#faq", |
| 144 | "mainEntity": [ |
| 145 | { |
| 146 | "@type": "Question", |
| 147 | "name": "What is the question?", |
| 148 | "acceptedAnswer": { |
| 149 | "@type": "Answer", |
| 150 | "text": "The complete answer text (40-60 words with statistic)." |
| 151 | } |
| 152 | } |
| 153 | ] |
| 154 | } |
| 155 | ``` |
| 156 | |
| 157 | Important note: Google restricted FAQ rich results to government and health |
| 158 | sites since August 2023. However, FAQ schema markup still provides value |
| 159 | because: |
| 160 | - AI systems (ChatGPT, Perplexity, Gemini) extract FAQ data for citations |
| 161 | - It structures content for future rich result eligibility changes |
| 162 | - It improves content organization signals |
| 163 | |
| 164 | ### Step 7: Generate VideoObject (if videos present) |
| 165 | |
| 166 | For each YouTube video embedded in the post, generate a VideoObject schema: |
| 167 | |
| 168 | ```json |
| 169 | { |
| 170 | "@type": "VideoObject", |
| 171 | "@id": "{siteUrl}/blog/{slug}#video-{index}", |