There's a pattern I keep seeing. A developer spends real time writing a useful technical post — not SEO fluff, actual useful content — it gets indexed, and then they search for the exact topic it covers. Their competitor's article shows author attribution, a publish date, maybe a thumbnail image. Theirs shows a plain blue link with a meta description.
Article Schema is usually part of that gap. Not all of it — domain authority matters too — but structured data is the piece you actually control.
What's in this guide: the difference between Article, NewsArticle, and BlogPosting (it's not obvious), image size requirements that most guides get wrong, how author markup connects to E-E-A-T scoring, and working implementations for Next.js, React, and WordPress.
What Is Article Schema Markup?
Article Schema is a structured data type from schema.org/Article that tells search engines — in machine-readable JSON-LD format — that a page contains editorial content: an article, blog post, news story, or tutorial.
Without it, Google has to infer this from your HTML structure, heading hierarchy, and content signals. With it, you're stating it explicitly. That unlocks specific rich result types that aren't available to unstructured pages.
The script block sits in your <head> or <body>:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Your Article Title Here"
}
</script>
Article vs NewsArticle vs BlogPosting: Which Type to Use
Schema.org has a hierarchy for article content. Choosing the wrong type won't break anything, but choosing the right one signals your content category more accurately to Google.
| Type | Use when | Rich result eligibility |
|---|---|---|
Article |
General editorial content, catch-all | Standard article results |
NewsArticle |
Time-sensitive news from a recognized publisher | Top Stories carousel, News tab |
BlogPosting |
Personal or company blog posts | Standard article results |
TechArticle |
Technical documentation, tutorials, how-to guides | Standard article results |
Quick decision logic:
- News publisher →
NewsArticle - Dev blog, company blog →
BlogPosting - Tutorials, API docs, technical how-tos →
TechArticle - Everything else →
Article
BlogPosting and TechArticle are both subtypes of Article, so they inherit all the same fields. NewsArticle has a few additional fields specific to journalism (like dateline and printEdition).
For most developers writing technical content on dev.to, personal blogs, or company engineering blogs: BlogPosting or TechArticle is the correct choice.
What Rich Results Article Schema Unlocks
Article structured data doesn't guarantee specific rich result features — Google decides what to display based on query context, site authority, and content freshness. But it makes you eligible for things that are otherwise impossible:
| Feature | What appears | Requirements |
|---|---|---|
| Top Stories carousel | Large image + headline + source in a horizontal carousel |
NewsArticle, AMP or fast page, news publisher |
| Article image in results | Thumbnail next to the snippet |
image with correct dimensions |
| Author attribution | "By [Author Name]" in some result layouts |
author with @type: Person
|
| Breadcrumb in URL |
Site › Category › Article format |
BreadcrumbList schema (separate) |
| Sitelinks | Sub-links below the main result |
WebSite schema + site authority |
| Paywalled content | Paywall indicator and preview snippet |
isAccessibleForFree + hasPart
|
For an independent developer blog, the realistic wins are image display in search results and author E-E-A-T signals. Top Stories requires a news publisher relationship with Google. Sitelinks come from site authority, not schema. Know which goals are actually achievable before you start.
Google's Image Requirements for Article Rich Results
This is where most implementations fail silently. Google's Rich Results Test will show your schema as valid, but the image won't appear because it doesn't meet the dimension requirements.
The rules:
- Minimum 50,000 pixels (width × height ≥ 50,000) — for example, 300×167px qualifies; anything smaller is ignored
- Recommended aspect ratios: 16:9, 4:3, and 1:1 — provide all three for maximum placement flexibility across different result layouts
- Format: JPG, PNG, WebP — avoid SVG or GIF
-
Must be crawlable — no auth walls, no
X-Robots-Tag: noindexon the image URL - Must represent the article content — a logo, icon, or decorative image doesn't qualify
In practice, aim for much higher resolution than the technical minimum. A 1200×675px image (16:9, ~810K pixels) is the widely accepted practical target — it satisfies the pixel requirement with significant headroom and renders sharply across all display contexts.
The safest approach is to provide three image URLs covering all three aspect ratios:
"image": [
"https://example.com/articles/js-tips-16x9.jpg",
"https://example.com/articles/js-tips-4x3.jpg",
"https://example.com/articles/js-tips-1x1.jpg"
]
If you only have one image, 1200×675px (16:9) is the practical sweet spot — well above the 50K pixel floor and covers the most common display context.
The Minimum Valid Article Schema
Google states explicitly: Article schema has no required properties. Instead, it recommends including the fields that apply to your content. In practice, headline, image, datePublished, and author are the four highest-impact fields — omitting any of them significantly reduces the chance of rich result display. Here's the smallest useful block:
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "How I Optimized My React App's Bundle Size by 60%",
"image": "https://example.com/articles/bundle-optimization.jpg",
"datePublished": "2026-05-08",
"author": {
"@type": "Person",
"name": "Alex Johnson"
}
}
This passes validation. But it's still the floor. A production article deserves the full set.
The Complete Article Schema: All Recommended Fields
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "How I Optimized My React App's Bundle Size by 60%",
"description": "A practical breakdown of code splitting, tree shaking, and lazy loading techniques that reduced a production React app's initial bundle from 1.8MB to 720KB.",
"image": [
"https://example.com/articles/bundle-optimization-16x9.jpg",
"https://example.com/articles/bundle-optimization-4x3.jpg",
"https://example.com/articles/bundle-optimization-1x1.jpg"
],
"datePublished": "2026-05-08T09:00:00+00:00",
"dateModified": "2026-05-08T09:00:00+00:00",
"author": {
"@type": "Person",
"name": "Alex Johnson",
"url": "https://example.com/authors/alex-johnson",
"sameAs": [
"https://twitter.com/alexjohnson",
"https://github.com/alexjohnson",
"https://linkedin.com/in/alexjohnson"
]
},
"publisher": {
"@type": "Organization",
"name": "Example Dev Blog",
"logo": {
"@type": "ImageObject",
"url": "https://example.com/logo.png",
"width": 600,
"height": 60
}
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://example.com/articles/react-bundle-optimization"
},
"url": "https://example.com/articles/react-bundle-optimization",
"wordCount": 2400,
"articleSection": "Performance",
"keywords": ["React", "bundle size", "webpack", "code splitting", "performance"],
"inLanguage": "en-US"
}
Field Reference: Required, Recommended, and Optional
| Field | Type | Priority | Notes |
|---|---|---|---|
headline |
Text | Strongly recommended | Max 110 characters. Match your <h1>, not the <title>
|
image |
URL or array | Strongly recommended | Min 50K pixels (width × height); 1200×675px is the practical target |
datePublished |
DateTime | Strongly recommended | ISO 8601: "2026-05-08" or "2026-05-08T09:00:00+00:00" |
author |
Person or Organization | Strongly recommended | Must include name; url strongly recommended; no job titles in name
|
dateModified |
DateTime | Strongly recommended | Signals freshness to Google; update on significant edits |
publisher |
Organization | Strongly recommended | Required for Top Stories; include logo
|
description |
Text | Recommended | Different from meta description — can be longer |
mainEntityOfPage |
WebPage | Recommended | Links the article to its canonical URL |
url |
URL | Recommended | Canonical URL of the article |
wordCount |
Integer | Optional | Helps Google understand content depth |
articleSection |
Text | Optional | Category name ("Technology", "Performance", "Security") |
keywords |
Text or array | Optional | Topic tags — not a ranking signal, but improves understanding |
inLanguage |
Text | Optional | BCP 47 language tag: "en-US", "uk", "de" |
isAccessibleForFree |
Boolean | Conditional | Required if content is paywalled |
Author Markup and E-E-A-T
Google's E-E-A-T (Experience, Expertise, Authoritativeness, Trustworthiness) guidelines place heavy weight on knowing who wrote a piece of content, especially for health, finance, legal, and technical topics.
Author markup in structured data is one of the signals Google uses to evaluate this. A bare "name": "Alex" is valid but weak. A full author object with a linked author page and verified social profiles is significantly stronger:
"author": {
"@type": "Person",
"name": "Roman Popovych",
"url": "https://devtools.abect.com/about",
"sameAs": [
"https://linkedin.com/in/yourprofile",
"https://github.com/yourhandle",
"https://twitter.com/yourhandle"
],
"jobTitle": "Full-Stack Software Engineer",
"worksFor": {
"@type": "Organization",
"name": "Freelance"
}
}
Important: Google explicitly states that author.name should contain only the person's name — no job titles, honorifics (Dr., Prof.), or organizational affiliations. Put those in jobTitle and worksFor, not inside name. Violating this causes a validation warning.
The sameAs array is particularly valuable. It links your author entity to established profiles on platforms Google already trusts. This helps Google build a knowledge graph entity for you as an author — which strengthens the authority signal for everything you publish.
Your url should point to a dedicated author page (not your homepage) that includes your name, photo, bio, and links to your work. That page should also contain Person JSON-LD that mirrors this markup.
Multiple Authors
When an article has more than one author, author accepts an array:
"author": [
{
"@type": "Person",
"name": "Alex Johnson",
"url": "https://example.com/authors/alex"
},
{
"@type": "Person",
"name": "Maria Chen",
"url": "https://example.com/authors/maria"
}
]
For organizational authorship (when no individual author is credited), use Organization instead of Person:
"author": {
"@type": "Organization",
"name": "Example Engineering Team",
"url": "https://example.com/team"
}
Implementation in Next.js (App Router)
In Next.js App Router, generate the JSON-LD object in the server component and inject it as a <script> tag. Don't generate it client-side — Google needs it in the initial HTML response.
// app/blog/[slug]/page.tsx
import { getPost } from '@/lib/posts'
export default async function BlogPost({ params }) {
const post = await getPost(params.slug)
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.title,
description: post.excerpt,
image: post.coverImage,
datePublished: post.publishedAt,
dateModified: post.updatedAt ?? post.publishedAt,
author: {
'@type': 'Person',
name: post.author.name,
url: `https://yoursite.com/authors/${post.author.slug}`,
},
publisher: {
'@type': 'Organization',
name: 'Your Blog Name',
logo: {
'@type': 'ImageObject',
url: 'https://yoursite.com/logo.png',
width: 600,
height: 60,
},
},
mainEntityOfPage: {
'@type': 'WebPage',
'@id': `https://yoursite.com/blog/${params.slug}`,
},
url: `https://yoursite.com/blog/${params.slug}`,
wordCount: post.wordCount,
articleSection: post.category,
keywords: post.tags,
inLanguage: 'en-US',
}
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<article>
<h1>{post.title}</h1>
{/* article content */}
</article>
</>
)
}
For generateMetadata, keep it separate — don't try to merge JSON-LD into the metadata object. They serve different purposes.
Implementation in React (with react-helmet-async)
import { Helmet } from 'react-helmet-async'
function BlogPost({ post }) {
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.title,
description: post.excerpt,
image: post.coverImage,
datePublished: post.publishedAt,
dateModified: post.updatedAt || post.publishedAt,
author: {
'@type': 'Person',
name: post.author.name,
url: post.author.profileUrl,
},
publisher: {
'@type': 'Organization',
name: 'Your Blog',
logo: {
'@type': 'ImageObject',
url: 'https://yoursite.com/logo.png',
width: 600,
height: 60,
},
},
mainEntityOfPage: {
'@type': 'WebPage',
'@id': post.url,
},
url: post.url,
}
return (
<>
<Helmet>
<script type="application/ld+json">
{JSON.stringify(jsonLd)}
</script>
</Helmet>
<article>
<h1>{post.title}</h1>
{/* content */}
</article>
</>
)
}
For React SPAs: Google processes JavaScript in a second crawl wave, which can delay indexing. If SEO matters for your blog, invest in SSR or static generation.
Implementation in WordPress (without a plugin)
WordPress with a classic theme doesn't add Article schema by default. Add it to functions.php for single post pages:
function add_article_schema() {
if ( ! is_single() ) return;
$author_id = get_post_field( 'post_author', get_the_ID() );
$schema = [
'@context' => 'https://schema.org',
'@type' => 'BlogPosting',
'headline' => get_the_title(),
'description' => get_the_excerpt(),
'image' => get_the_post_thumbnail_url( get_the_ID(), 'full' ),
'datePublished' => get_the_date( 'c' ),
'dateModified' => get_the_modified_date( 'c' ),
'url' => get_permalink(),
'author' => [
'@type' => 'Person',
'name' => get_the_author_meta( 'display_name', $author_id ),
'url' => get_author_posts_url( $author_id ),
],
'publisher' => [
'@type' => 'Organization',
'name' => get_bloginfo( 'name' ),
'logo' => [
'@type' => 'ImageObject',
'url' => get_site_icon_url( 512 ),
],
],
'mainEntityOfPage' => [
'@type' => 'WebPage',
'@id' => get_permalink(),
],
'wordCount' => str_word_count( get_the_content() ),
'articleSection' => implode( ', ', wp_list_pluck(
get_the_category(), 'name'
)),
'keywords' => implode( ', ', wp_list_pluck(
get_the_tags() ?: [], 'name'
)),
'inLanguage' => get_bloginfo( 'language' ),
];
echo '<script type="application/ld+json">' . wp_json_encode( $schema ) . '</script>';
}
add_action( 'wp_head', 'add_article_schema' );
If you're using the Block Editor (Gutenberg), the get_the_content() function returns the full serialized block content. For word count, filter it through strip_tags( apply_filters( 'the_content', get_the_content() ) ) to get clean text.
The 6 Most Common Article Schema Mistakes
1. headline longer than 110 characters
Google truncates headlines beyond 110 characters and may not display the rich result at all. Keep it tight. If your <title> is longer, write a shorter headline — they don't have to be identical.
// Wrong — 130 characters
"headline": "A Very Thorough and Complete Investigation Into the Many Different Ways You Can Optimize Your JavaScript Bundle"
// Correct — 68 characters
"headline": "How to Optimize Your JavaScript Bundle: A Practical Guide"
2. Image too small
The single most common reason article images don't show in rich results. If your featured image is 800×450px, it won't qualify. Google requires minimum 1200px width. Always check with the Rich Results Test and look at the image dimensions in the report.
3. datePublished without dateModified
Google uses dateModified as a freshness signal. If you update an article significantly and don't update dateModified, Google may still treat it as the original publish date. Always update both when you make meaningful changes.
"datePublished": "2025-11-01T10:00:00+00:00",
"dateModified": "2026-05-08T14:30:00+00:00"
4. author with only a name and no URL
A name alone is a weak signal. Without an url pointing to an author page, Google can't verify the author entity. This reduces E-E-A-T value significantly for topics where authorship matters.
5. publisher missing the logo
For NewsArticle type specifically, a publisher logo is required for Top Stories eligibility. For BlogPosting it's recommended but not strictly required. Either way, leaving it out is a missed opportunity.
Logo requirements:
- Rectangular (not square)
- Max 600×60 pixels
- Logo must be clearly visible on white background
6. headline not matching the visible <h1>
Google cross-validates your structured data against the visible page content. If your JSON-LD headline says one thing and your <h1> says something different, it's a signal inconsistency that can suppress rich results. They don't need to be character-for-character identical, but they should clearly refer to the same content.
Paywalled Content: The isAccessibleForFree Pattern
If your content is behind a subscription or paywall — even partially — you need to declare it. Presenting paywalled content as fully accessible in structured data is a Google policy violation.
For metered paywall (first N articles free):
{
"@context": "https://schema.org",
"@type": "NewsArticle",
"headline": "Exclusive: Inside the Next Generation of JavaScript Runtimes",
"isAccessibleForFree": false,
"hasPart": {
"@type": "WebPageElement",
"isAccessibleForFree": false,
"cssSelector": ".paywall-content"
}
}
Google will show a paywall indicator in search results and still index your content for ranking — it just won't show the full text in snippets.
Validating Article Schema
Google Rich Results Test — search.google.com/test/rich-results
Paste your URL or raw JSON-LD. The report will show which rich results your article is eligible for and list every error and warning. Pay attention to the image section — it shows the detected dimensions, which is the fastest way to confirm your image meets the 1200px minimum.
Schema.org Validator — validator.schema.org
More thorough than Google's tool. Will catch type errors, unknown properties, and structural issues that Google sometimes ignores.
Google Search Console → Search Appearance → Articles
After deploying and indexing, this shows real crawl data across all your article pages. Errors here directly affect performance. Monitor it 7–14 days after initial deployment and after any schema changes.
Validation checklist before publishing:
-
headline≤ 110 characters - Image ≥ 1200px wide, aspect ratio 16:9 / 4:3 / 1:1
-
datePublishedin ISO 8601 format -
authorincludesnameandurl -
publisherincludesnameandlogo - No fields contradict visible page content
Article Schema and Search Console: What to Expect
After deploying Article schema across your blog, here's the realistic timeline:
Days 1–7: Google crawls and reprocesses indexed pages. No visible change in Search Console yet.
Days 7–14: Enhancements tab starts showing data. Expect some warnings — especially around images that don't meet dimension requirements.
Weeks 3–6: Rich results begin appearing in search listings for indexed articles. The first visible signs are usually article images in result snippets.
Month 2+: Meaningful CTR data accumulates. The comparison between pages with and without rich results becomes visible in the Performance report if you filter by article URLs.
Don't expect overnight changes. Structured data's impact on CTR is real but gradual — Google needs time to validate, index, and begin displaying the enhanced results.
Wrapping Up
Article schema won't move you from page 3 to page 1. But if you're already getting traffic, the author E-E-A-T signals are genuinely useful long-term, and getting your article image to display in search results is one of the few direct visual improvements you can make without changing rankings.
A few things to get right before shipping:
- Right type first:
BlogPostingfor blog posts,TechArticlefor tutorials and docs,NewsArticleonly if you're an actual news publisher - Image needs to be at least 50K pixels (width × height) — in practice, target 1200×675px and you're safely above it
-
author.nameis just the name — no job title, no "Dr.", no "CEO at Company" — put those injobTitleandworksFor - Update
dateModifiedwhen you make real edits, not just republish with the same date - Validate with the Rich Results Test before going live, and then check Search Console 2 weeks later to see how it processed across your actual crawled pages
If you'd rather generate the JSON-LD through a form instead of writing it by hand, there's a free browser-based tool: Article Schema Generator. No signup, everything runs locally.
Working with a schema setup that passes validation but still doesn't produce rich results? Drop the specifics in the comments — there are a few edge cases where that happens and they're usually diagnosable.
Top comments (0)