NewsArticle: Reserve for time-sensitive news from recognized publishers. This type requires additional fields like dateline and is necessary for the Top Stories carousel, but it demands a relationship with Google News.
Article: Use only when content does not fit the above categories. It is the base type and offers the least specific signaling.
2. TypeScript Schema Builder
Avoid manual JSON construction. Use a builder pattern with TypeScript interfaces to ensure consistency, type safety, and maintainability. This approach centralizes schema logic and reduces the risk of field omission or formatting errors.
// schema-types.ts
export interface AuthorProfile {
name: string;
url: string;
sameAs: string[];
jobTitle?: string;
worksFor?: {
name: string;
url: string;
};
}
export interface ImageVariant {
url: string;
width: number;
height: number;
}
export interface ArticleSchemaConfig {
type: 'BlogPosting' | 'TechArticle' | 'NewsArticle' | 'Article';
headline: string;
description: string;
images: ImageVariant[];
datePublished: string;
dateModified?: string;
author: AuthorProfile | AuthorProfile[];
publisher: {
name: string;
logoUrl: string;
logoWidth: number;
logoHeight: number;
};
url: string;
keywords?: string[];
articleSection?: string;
inLanguage?: string;
isAccessibleForFree?: boolean;
}
// schema-builder.ts
export function buildArticleJsonLd(config: ArticleSchemaConfig): string {
const schemaPayload = {
'@context': 'https://schema.org',
'@type': config.type,
headline: config.headline,
description: config.description,
image: config.images.map((img) => img.url),
datePublished: config.datePublished,
dateModified: config.dateModified || config.datePublished,
author: Array.isArray(config.author)
? config.author.map(formatAuthor)
: formatAuthor(config.author),
publisher: {
'@type': 'Organization',
name: config.publisher.name,
logo: {
'@type': 'ImageObject',
url: config.publisher.logoUrl,
width: config.publisher.logoWidth,
height: config.publisher.logoHeight,
},
},
mainEntityOfPage: {
'@type': 'WebPage',
'@id': config.url,
},
url: config.url,
keywords: config.keywords,
articleSection: config.articleSection,
inLanguage: config.inLanguage || 'en-US',
isAccessibleForFree: config.isAccessibleForFree ?? true,
};
// Remove undefined fields to keep payload clean
const cleanPayload = JSON.parse(JSON.stringify(schemaPayload));
return `<script type="application/ld+json">${JSON.stringify(cleanPayload, null, 2)}</script>`;
}
function formatAuthor(author: AuthorProfile) {
const authorObj: Record<string, unknown> = {
'@type': 'Person',
name: author.name,
url: author.url,
sameAs: author.sameAs,
};
if (author.jobTitle) authorObj.jobTitle = author.jobTitle;
if (author.worksFor) authorObj.worksFor = author.worksFor;
return authorObj;
}
3. Image Strategy and Compliance
Image requirements are the most common point of failure. Google enforces a minimum pixel area of 50,000 pixels (width × height). However, relying on the minimum is risky. A 300×167 image passes the math but may be rejected for quality reasons.
Production Best Practice:
- Resolution: Target 1200×675 pixels (16:9 aspect ratio). This yields ~810,000 pixels, providing ample headroom above the 50k threshold and ensuring sharp rendering across devices.
- Variants: Provide an array of images covering 16:9, 4:3, and 1:1 aspect ratios. Different SERP layouts prioritize different ratios. Supplying all three maximizes placement flexibility.
- Format: Use WebP for efficiency, but ensure JPG or PNG fallbacks are available. Avoid SVG and GIF for schema images.
- Accessibility: Images must be publicly crawlable. Ensure no authentication barriers or
X-Robots-Tag: noindex headers block access.
4. Author Entity and E-E-A-T
Author markup is critical for E-E-A-T scoring. A simple name string is insufficient. The author object must link to a verified entity.
sameAs Array: Include URLs to authoritative profiles (LinkedIn, GitHub, Twitter/X). This helps Google connect the author name to a Knowledge Graph entity, strengthening trust signals.
name Field: Must contain only the person's name. Do not include job titles, honorifics, or affiliations in this field. Use jobTitle and worksFor for that data.
- Author Page: The
url should point to a dedicated author bio page containing consistent Person schema. This page reinforces the author's identity and expertise.
- Multiple Authors: Support arrays of
Person objects for collaborative content. For organizational authorship, use @type: Organization.
5. Injection Architecture
Inject the JSON-LD script into the document <head>. In modern frameworks, use server-side rendering or static generation to ensure the script is present in the initial HTML response. Client-side injection can delay indexing and may cause hydration mismatches.
Next.js App Router Example:
// app/blog/[slug]/page.tsx
import { buildArticleJsonLd } from '@/lib/schema-builder';
import { getPostData } from '@/lib/posts';
export default async function PostPage({ params }: { params: { slug: string } }) {
const post = await getPostData(params.slug);
const jsonLd = buildArticleJsonLd({
type: 'TechArticle',
headline: post.title,
description: post.excerpt,
images: [
{ url: post.coverImage16x9, width: 1200, height: 675 },
{ url: post.coverImage4x3, width: 800, height: 600 },
{ url: post.coverImage1x1, width: 600, height: 600 },
],
datePublished: post.publishDate,
dateModified: post.updatedDate,
author: {
name: post.authorName,
url: `/authors/${post.authorSlug}`,
sameAs: [
`https://github.com/${post.authorGitHub}`,
`https://linkedin.com/in/${post.authorLinkedIn}`,
],
jobTitle: post.authorRole,
},
publisher: {
name: 'Engineering Blog',
logoUrl: '/logo.png',
logoWidth: 600,
logoHeight: 60,
},
url: `https://example.com/blog/${post.slug}`,
keywords: post.tags,
articleSection: post.category,
});
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: jsonLd }}
/>
<article>
{/* Post content */}
</article>
</>
);
}
Pitfall Guide
-
Name Field Contamination
- Explanation: Including job titles or affiliations in
author.name (e.g., "name": "Dr. Jane Doe, Senior Engineer").
- Fix: Restrict
name to the person's name only. Use jobTitle and worksFor for professional details. Violations trigger validation warnings and weaken entity resolution.
-
Image Dimension Neglect
- Explanation: Using images that pass JSON validation but fail the 50,000-pixel area requirement or aspect ratio preferences.
- Fix: Implement a pre-flight check in the build process to verify
width * height >= 50000. Target 1200×675 for optimal results. Provide multiple aspect ratios.
-
Date Staleness
- Explanation: Failing to update
dateModified when content is revised.
- Fix: Update
dateModified on significant edits. This signals freshness to search engines and can improve ranking for updated content. Ensure datePublished remains the original publication date.
-
Type Mismatch
- Explanation: Using generic
Article for technical tutorials or news content.
- Fix: Map content types to specific schemas. Use
TechArticle for tutorials, NewsArticle for news, and BlogPosting for blogs. This improves context understanding and eligibility for specialized features.
-
Broken sameAs Links
- Explanation: Including URLs in
sameAs that return 404 errors or redirect incorrectly.
- Fix: Validate all social profile URLs. Broken links can degrade trust signals. Use canonical URLs for social profiles.
-
Hydration Mismatches
- Explanation: Injecting dynamic schema on the client that differs from the server-rendered HTML, causing React warnings.
- Fix: Generate schema during server-side rendering or static generation. Avoid client-side schema injection unless absolutely necessary, and ensure consistency.
-
Canonical URL Conflicts
- Explanation: The
url in schema does not match the page's canonical URL or actual URL.
- Fix: Ensure
mainEntityOfPage.@id and url match the canonical URL exactly. Inconsistencies can confuse search engines and dilute ranking signals.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Technical Documentation | TechArticle with how-to features | Signals instructional content; may trigger step-by-step rich results | Low (schema only) |
| Company Engineering Blog | BlogPosting with full author entity | Enables author attribution and image thumbnails; builds E-E-A-T | Low |
| News/Press Releases | NewsArticle with dateline | Required for Top Stories carousel; time-sensitive signaling | Medium (requires publisher accreditation) |
| Paywalled Content | Article with isAccessibleForFree: false | Complies with Google's paywall policies; enables preview snippets | Low |
| Multi-Author Collaboration | Array of Person objects | Accurately credits all contributors; strengthens collective authority | Low |
Configuration Template
{
"@context": "https://schema.org",
"@type": "TechArticle",
"headline": "Optimizing React Performance: A Deep Dive",
"description": "Techniques for reducing bundle size and improving render performance in React applications.",
"image": [
"https://example.com/images/optimization-16x9.webp",
"https://example.com/images/optimization-4x3.webp",
"https://example.com/images/optimization-1x1.webp"
],
"datePublished": "2026-05-08T09:00:00+00:00",
"dateModified": "2026-05-10T14:30:00+00:00",
"author": {
"@type": "Person",
"name": "Jordan Smith",
"url": "https://example.com/authors/jordan-smith",
"sameAs": [
"https://github.com/jordansmith",
"https://linkedin.com/in/jordansmith"
],
"jobTitle": "Senior Frontend Engineer",
"worksFor": {
"@type": "Organization",
"name": "TechCorp"
}
},
"publisher": {
"@type": "Organization",
"name": "TechCorp Engineering",
"logo": {
"@type": "ImageObject",
"url": "https://example.com/logo.png",
"width": 600,
"height": 60
}
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://example.com/blog/react-optimization"
},
"url": "https://example.com/blog/react-optimization",
"keywords": ["React", "Performance", "Bundle Size", "Web Vitals"],
"articleSection": "Frontend Engineering",
"inLanguage": "en-US"
}
Quick Start Guide
- Define Interfaces: Create TypeScript interfaces for
ArticleSchemaConfig, AuthorProfile, and ImageVariant to enforce structure.
- Implement Builder: Write a
buildArticleJsonLd function that maps your content data to the schema payload, handling arrays and optional fields.
- Inject Script: Add the generated JSON-LD script to the
<head> of your article pages using server-side rendering or static generation.
- Validate: Run the output through Google's Rich Results Test and Schema Markup Validator. Verify image dimensions and
sameAs links.
- Monitor: Track rich result impressions in Google Search Console to measure the impact of schema implementation on visibility.