Tutorial: Implementing NewsArticle Schema Markup for Rich Results

Implement NewsArticle schema markup correctly to enable rich results in Google News, Top Stories, and search results.

Prerequisites

Step 1: Determine Your Schema Template

Static vs. Dynamic Schema

For news sites, schema must be dynamic—generated per article:

Step 2: Base Template Implementation

WordPress Implementation

function generate_news_article_schema() {
    if (!is_single()) return;
    
    global $post;
    
    $schema = array(
        '@context' => 'https://schema.org',
        '@type' => 'NewsArticle',
        'headline' => get_the_title(),
        'datePublished' => get_the_date('c'),
        'dateModified' => get_the_modified_date('c'),
        'author' => array(
            '@type' => 'Person',
            'name' => get_the_author_meta('display_name', $post->post_author),
            'url' => get_author_posts_url($post->post_author)
        ),
        'publisher' => array(
            '@type' => 'Organization',
            'name' => get_bloginfo('name'),
            'logo' => array(
                '@type' => 'ImageObject',
                'url' => 'https://example.com/logo.png'
            )
        ),
        'image' => get_the_post_thumbnail_url($post, 'full'),
        'description' => get_the_excerpt(),
        'mainEntityOfPage' => array(
            '@type' => 'WebPage',
            '@id' => get_permalink()
        )
    );
    
    echo '<script type="application/ld+json">' . json_encode($schema) . '</script>';
}
add_action('wp_head', 'generate_news_article_schema');

Next.js Implementation

export function NewsArticleSchema({ article }) {
  const schema = {
    '@context': 'https://schema.org',
    '@type': 'NewsArticle',
    headline: article.title,
    datePublished: article.publishedAt,
    dateModified: article.updatedAt,
    author: {
      '@type': 'Person',
      name: article.author.name,
      url: `/author/${article.author.slug}`,
    },
    publisher: {
      '@type': 'Organization',
      name: 'Example News',
      logo: {
        '@type': 'ImageObject',
        url: 'https://example.com/logo.png',
      },
    },
    image: article.featuredImage,
    description: article.excerpt,
    mainEntityOfPage: {
      '@type': 'WebPage',
      '@id': article.url,
    },
    articleSection: article.category,
    wordCount: article.wordCount,
  };

  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
    />
  );
}

Step 3: Add Advanced Properties

Complete Schema with All Recommended Fields

const fullSchema = {
  '@context': 'https://schema.org',
  '@type': 'NewsArticle',
  
  // Required
  headline: article.title,
  datePublished: article.publishedAt,
  dateModified: article.updatedAt,
  author: {
    '@type': 'Person',
    name: article.author.name,
    url: `/author/${article.author.slug}`,
    jobTitle: article.author.title,
    sameAs: article.author.socialLinks,
  },
  publisher: {
    '@type': 'Organization',
    name: 'Example News',
    url: 'https://example.com',
    logo: {
      '@type': 'ImageObject',
      url: 'https://example.com/logo.png',
      width: 600,
      height: 60,
    },
    sameAs: ['https://twitter.com/examplenews'],
  },
  image: {
    '@type': 'ImageObject',
    url: article.featuredImage,
    width: 1200,
    height: 630,
  },
  
  // Recommended
  description: article.excerpt,
  articleSection: article.category,
  wordCount: article.wordCount,
  keywords: article.tags,
  genre: 'News',
  alternativeHeadline: article.subtitle,
  
  // Free access
  isAccessibleForFree: !article.isPaywalled,
  
  // Paywall support
  ...(article.isPaywalled && {
    hasPart: {
      '@type': 'WebPageElement',
      isAccessibleForFree: false,
      cssSelector: '.paywall-content',
    },
  }),
  
  mainEntityOfPage: {
    '@type': 'WebPage',
    '@id': article.url,
  },
};

Step 4: Validate Your Schema

Testing Process

  1. Rich Results Test: Test each article URL
  2. Schema Markup Validator: Validate schema.org compliance
  3. Search Console: Monitor for errors
  4. Lighthouse: Check structured data audit

Common Validation Fixes

// Fix: Ensure headline matches H1
headline: document.querySelector('h1').textContent

// Fix: ISO 8601 date format
datePublished: new Date(article.publishedAt).toISOString()

// Fix: Proper image dimensions
image: {
  '@type': 'ImageObject',
  url: article.featuredImage,
  width: 1200,
  height: 630
}

Step 5: Monitor and Maintain

Monthly Schema Health Check

Automated Schema Testing

// Test schema in CI/CD pipeline
describe('NewsArticle Schema', () => {
  it('should have required fields', () => {
    const schema = getSchemaFromPage('/article/1');
    expect(schema.headline).toBeDefined();
    expect(schema.datePublished).toMatch(/^\d{4}-\d{2}-\d{2}/);
    expect(schema.author.name).toBeDefined();
    expect(schema.publisher.name).toBeDefined();
    expect(schema.image).toBeDefined();
  });
});

Checklist