To set up schema markup for news articles, you add one JSON-LD block to your article template that labels the page as a news story and declares its headline, author, publication and modification dates, publisher and image. Then you validate it with Google’s tools. On a small news site the whole job is an hour, and most of that hour goes into deciding what belongs in the template rather than typing code.
Schema markup is structured data: a machine-readable label that tells a search engine what a page is. For a newsroom it does three practical things. It supplies the headline, byline, date and thumbnail Google shows in article results, it removes the guesswork when a crawler tries to work out whether a page is news, and it gives AI answer engines a clean entity record to summarise and cite.
Here is the part worth hearing early. Schema is not a ranking factor, and it will not get you into Google News on its own. Forum threads on r/SEO are full of people who added Article markup and watched their rankings sit flat, which is the normal outcome. Inclusion in Google News is driven by your Publisher Center account, your RSS feeds and your sitemaps. What markup does is remove errors from that process.
Table of Contents
- What You Need
- How to Set Up Schema Markup for News Articles Step by Step
- 1. Identify the page and choose the schema format
- 2. Gather accurate article information for NewsArticle schema
- 3. Create the NewsArticle JSON-LD properties
- 4. Add the markup to the article template or CMS
- 5. Validate and test the NewsArticle structured data
- 6. Publish, monitor, and keep the markup current
- Common Mistakes
- Frequently Asked Questions
- Conclusion
What You Need
You need access to the article template, accurate article data, the schema.org vocabulary, two validators, and a decision about how the markup gets into the page. That is the whole list.
- Access to the template or the CMS. If you run WordPress, that means a plugin or the theme’s functions file. If you run a headless stack, it means the article component and wherever your JSON-LD is assembled server side.
- Article details that already exist somewhere. Headline, byline, publish date, modified date, section, hero image and canonical URL. If your CMS does not store the modification date separately, add that field before you start. It is the field people skip and then wish they had.
- The schema.org vocabulary. NewsArticle and its properties, plus Organization for the publisher. Read Google’s Article structured data documentation once so your property names match exactly; casing matters.
- Two validators. Google’s Rich Results Test for search eligibility and warnings, and the Schema Markup Validator at validator.schema.org for vocabulary and syntax errors.
- A publisher logo. A square or near-square image at least 60 by 60 pixels, at a stable URL, plus the social profile URLs that go in the same block.
- A decision on implementation. Plugin, hand-coded template, or CMS schema variables. Doing this by hand on every post will not survive a hundred articles, so decide how it scales on day one.
How to Set Up Schema Markup for News Articles Step by Step

1. Identify the page and choose the schema format
NewsArticle is a subtype of Article, and you should use it only when the page really is reported news. Opinions, reviews, explainers and post-mortems have their own schema types, and labelling them all NewsArticle is how publishers end up with markup that contradicts what readers see.
| Type | Use it for | Watch out for |
|---|---|---|
| NewsArticle | Straight reported news: a council vote, a court ruling, a fire, an election result | The safe default for a daily news file |
| AnalysisNewsOpinion | Columns, editorials, opinion pieces | Google has tightened how this type is treated, so test it before rolling it out sitewide |
| ReviewNewsArticle | Film, restaurant, gear and book reviews | Not the same as Product review schema |
| ReportageNewsArticle | Feature reporting and long-form investigations | Still a news subtype, but a narrower signal |
| BackgroundNewsArticle | Explainer and context pieces about an ongoing story | Easy to overuse on evergreen explainers |
| BlogPosting | Personal or newsletter-style posts on a news site | The default most WordPress plugins emit, which is often not what you want |
| Article | The generic parent type | Valid, but it carries no news-specific meaning |
On format, JSON-LD is the one to use. It is a single script block, it is separated from your HTML so a template change cannot silently break it, and Google recommends it for that reason. Microdata and RDFa still work, and both are more fragile in the hands of a newsroom that redeploys templates weekly.
| Format | Where it lives | Effort to maintain | Recommendation |
|---|---|---|---|
| JSON-LD | A script tag with type application/ld plus json | Low once templated | Use this |
| Microdata | itemprop attributes on the visible elements | High; every markup change touches the design | Only for legacy templates you cannot edit |
| RDFa | vocabulary and property attributes | High and rarely well understood in a newsroom | Skip it |
2. Gather accurate article information for NewsArticle schema
Collect the values before you write anything. Structured data has to match what a person sees on the page, so every field below is a field you already publish somewhere. The properties split into two groups, and newsrooms that chase the wrong group waste the most time on that.
| Property | Status | What it must contain |
|---|---|---|
| headline | Required | The visible headline, roughly 110 characters or fewer |
| image | Required | Absolute URLs to representative images, at least one in the 1200 by 675 range |
| datePublished | Required | ISO 8601 with a timezone offset |
| author | Required | A Person with a name, ideally with a profile URL |
| dateModified | Recommended, strongly so for news | ISO 8601; must be the same as or later than datePublished |
| publisher | Recommended | An Organization with a name, url and logo |
| mainEntityOfPage | Recommended | A WebPage reference with the canonical URL |
| articleSection | Recommended | The desk or section name as text |
| description | Recommended | The meta description, matching the visible page |
| wordCount | Recommended | An integer, not a string |
| isAccessibleForFree | Recommended | True or false. Required if you use paywalled content markup |
| inLanguage | Recommended | Language code such as en-US |
3. Create the NewsArticle JSON-LD properties
Build the block as a graph so the article and the publisher each get their own entity with a shared identifier. This is the template most newsrooms end up with, and it validates cleanly once you swap the values.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "NewsArticle",
"@id": "https://example.com/news/council-funds-harbor-bridge#article",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://example.com/news/council-funds-harbor-bridge"
},
"headline": "Council votes to fund Harbor Bridge repairs ahead of schedule",
"description": "The council approved 4.2 million dollars for overnight repairs to begin in March.",
"image": [
"https://example.com/img/harbor-bridge-1200x675.jpg"
],
"datePublished": "2026-09-14T07:30:00-04:00",
"dateModified": "2026-09-15T11:05:00-04:00",
"author": [
{
"@type": "Person",
"name": "Dana Whitfield",
"url": "https://example.com/authors/dana-whitfield"
}
],
"publisher": { "@id": "https://example.com/#organization" },
"articleSection": [ "Local", "Infrastructure" ],
"wordCount": 1184,
"isAccessibleForFree": true,
"inLanguage": "en-US"
},
{
"@type": "Organization",
"@id": "https://example.com/#organization",
"name": "Example Newsroom",
"url": "https://example.com",
"logo": {
"@type": "ImageObject",
"url": "https://example.com/assets/logo-600x60.png",
"width": 600,
"height": 60
},
"sameAs": [
"https://example.com/masthead",
"https://example.org/newsroom"
]
}
]
}
</script>
Three details in that block matter more than the rest. Image URLs are absolute, so a path like /img/hero.jpg fails because a crawler has no hostname to resolve it against. Dates carry a timezone offset, because 2026-09-14T07:30:00 on its own is ambiguous and Google asks for the offset. And the publisher is referenced by an id that matches the Organization entry, which is what stops you from emitting two separate publisher entities on the same page.
For a subscription newsroom, swap isAccessibleForFree to false and add a hasPart marking the gated section, so the markup matches what a non-subscriber actually sees:
"isAccessibleForFree": false,
"hasPart": {
"@type": "WebPageElement",
"isAccessibleForFree": false,
"cssSelector": ".premium-content"
}
4. Add the markup to the article template or CMS
On WordPress, the plugin route is the sane default. Yoast SEO and Rank Math both emit Article-family schema automatically, and both let you change the subtype per post or per post type through their schema settings. The catch, and the source of most confusion in the r/SEO threads about this, is that they default to BlogPosting rather than NewsArticle. Rank Math exposes the type under the schema editor for the post; Yoast does it in its schema tab. Switch both to NewsArticle for your news post type, and check that you now have one article entity rather than two.
Schema Pro and Schema Premium work the same way, with an added location rules panel that scopes a schema type to a specific post type. That scoping is the feature that keeps a site with both news and opinion sections from labelling everything as news.
If you would rather not depend on a plugin, the developer route is to build the block from template variables at the point the article renders. The shape of it matters more than the language:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "NewsArticle",
"headline": {{ article.headline | json }},
"image": [ {{ article.hero_image.url | json }} ],
"datePublished": {{ article.published_at | iso8601 | json }},
"dateModified": {{ article.modified_at | iso8601 | json }},
"author": { "@type": "Person", "name": {{ article.author.name | json }} },
"publisher": { "@type": "Organization", "name": {{ site.name | json }},
"logo": { "@type": "ImageObject", "url": {{ site.logo | json }} } },
"mainEntityOfPage": {{ article.canonical_url | json }},
"isAccessibleForFree": true
}
</script>
Note the escaping. Serialising each value rather than concatenating a string is what stops a quote in a headline from breaking the whole block, and it is also how you avoid injecting raw HTML into a script tag. In a Next.js newsroom the same block usually goes through a metadata export or a server component rather than a head manager.
Where the block goes matters less than people think. Head or body both work. Put it in the head if you generate it during the request, and in the body if your framework renders client side. One rule is firm: one article page should produce one NewsArticle entity.
5. Validate and test the NewsArticle structured data
Run every URL through two tools, because each catches what the other misses. The Rich Results Test tells you whether the page is eligible for article results and Google News appearance. The Schema Markup Validator tells you whether your vocabulary is correct, which is where typos in property names show up.
| Tool | What it checks | When to use it |
|---|---|---|
| Rich Results Test | Search eligibility and required or recommended warnings | Before every template deploy and after any plugin change |
| Schema Markup Validator | Syntax, vocabulary, typed values and the full schema.org graph | While you are building the template |
| Search Console enhancement reports | Errors across your indexed URLs over time | Weekly, once the markup is live sitewide |
| view-source on the live page | Whether the block survived rendering and caching | Whenever the tools pass but the page looks wrong |
If both tools pass and you still see no rich result, the markup is probably not the problem. Check the page in view-source first, because a block injected by client-side JavaScript may never reach the HTML a crawler receives. After that, look at content matching: a headline in the markup that differs from the rendered headline, or an image that is not on the page, is a mismatch and the listing can be suppressed.
6. Publish, monitor, and keep the markup current
After launch, watch the Search Console enhancement report for article markup. It shows valid, valid with issues and error counts by URL, which is the fastest way to find a template that broke or a section nobody updated.
Then set a rhythm. Update dateModified whenever you materially edit a story, not on every typo fix, because a date that moves on every save tells a crawler nothing. When an author leaves, fix their entity rather than leaving a dangling name. And re-test a sample URL after every plugin or theme update; SEO plugins overwrite each other more often than anyone expects, and a theme update can drop a custom block silently.
One more piece sits alongside schema. Keep a Google News sitemap, keep your RSS feed clean with accurate publication timestamps and full URLs, and keep your Publisher Center account active with real contact details. Newsrooms that report an empty Google News dashboard almost always have a feed or sitemap problem, not a markup problem.
Common Mistakes
Nearly every broken news schema I have seen comes down to one of six things. Read the validator message, find the row, fix the cause.
| What the validator says | Cause | Fix |
|---|---|---|
| Missing field headline, image, datePublished, author | A required property was left empty or never templated | Add the field to the template and confirm it renders a real value, not null |
| Invalid URL in image or mainEntityOfPage | Relative paths such as /img/hero.jpg | Output the full absolute URL including protocol and hostname |
| Date is not in ISO 8601 format | Month names or a local format such as 14/09/2026 | Use 2026-09-14T07:30:00-04:00 with the timezone offset |
| dateModified is earlier than datePublished | A template default or a migrated post with a bad modified date | Fall back to datePublished whenever the stored modified date precedes it |
| Two article entities on one page | An SEO plugin emitting Article alongside your hand-coded NewsArticle | Switch the plugin’s type to NewsArticle or disable its schema output on the news post type |
| Valid markup, no rich result shown | Client-side injection, a cache stripping the block, or a headline or image that does not match the page | Check view-source, then compare markup against rendered content field by field |
Two more that get skipped. Missing author is the most common single warning in news feeds, and adding a real Person entity with a working profile page fixes it in a few minutes. And JSON-LD containing raw quotation marks from an unescaped headline breaks the entire block, which is why serialising values beats string-building.
A few working habits. Mark up what the reader sees, never what you wish the story were about. Keep one article entity per URL. If you are unsure whether a type is eligible, test it on staging before you apply it to the whole archive. And accept that markup will not move rankings; it makes the listing better, which is a smaller win and a real one.
Frequently Asked Questions
Google has narrowed FAQ rich results to a small set of authoritative government and health sites, so most publishers will not see an FAQ carousel from their FAQ markup. The schema is still worth keeping on an article page because it makes the questions and answers explicit to crawlers and to AI answer engines summarising your page.
What is schema markup for news articles?
Schema markup for news articles is a block of structured data, usually JSON-LD, that labels a page as a news story and declares its headline, author, publication and modification dates, publisher, image and canonical URL. Search engines read it at crawl time to build article listings, supply bylines and thumbnails, and understand the page for Google News and AI answers.
Do I need NewsArticle schema for every journalism page?
Use NewsArticle for reported news stories, including local and breaking coverage. Opinion columns fit AnalysisNewsOpinion, reviews fit ReviewNewsArticle, and long features can use ReportageNewsArticle. Marking every page as NewsArticle is a common mistake because it contradicts what the reader sees, and mismatched types are treated as a quality problem rather than an optimisation.
Should I use JSON-LD or microdata for news articles?
Use JSON-LD. It is a single script tag kept separate from your HTML, so a template or design change cannot silently break it, and it is the format Google recommends. Microdata works but embeds itemprop attributes on visible elements, which makes every design change a potential markup bug. Most WordPress plugins also emit JSON-LD, so the choice is already made for you.
How do I add NewsArticle schema to WordPress or another CMS?
On WordPress, install Yoast SEO or Rank Math, then open a news post and change the schema type from BlogPosting to NewsArticle in the plugin’s schema settings. Both templates also let you apply the type to a whole post type, which is the right way to do it. On a headless CMS, build the JSON-LD block from article fields at render time and serialise each value.
Does adding news schema guarantee rich results?
No. Schema makes a page eligible, not selected, and it is not a ranking factor. Eligibility also depends on content quality, indexability and Google’s own thresholds. Schema also will not put you into Google News: that depends on your Publisher Center account, RSS feeds and sitemaps. What valid markup does is stop your metadata from being guessed wrong.
Conclusion
How to set up schema markup for news articles comes down to five moves: choose the right type, gather fields your page already shows, write one JSON-LD block with an @graph publisher, inject it from the template rather than by hand, and validate on every deploy. Start by mapping your existing article template to accurate NewsArticle properties, then paste the block above into a staging URL and run it through both validators. Once it passes, keep your feeds and sitemaps in good shape too, because that is what Google News actually reads.


