DEV Community

Cover image for Connecting Headless WordPress RSS to Next.js: Building a Scalable Newsletter System
Khaled waleed
Khaled waleed

Posted on

Connecting Headless WordPress RSS to Next.js: Building a Scalable Newsletter System

In modern web development, the headless CMS approach has become increasingly popular for its flexibility and performance benefits. Recently, we tackled an interesting challenge: connecting a headless WordPress RSS feed to a Next.js website for automated newsletter distribution through Mailchimp.

This article walks through our journey, the challenges we faced, and the elegant solutions we implemented.

The Setup: Headless WordPress + Next.js + Mailchimp

Our architecture consisted of:

  • WordPress as a headless CMS (hosted on cms.example.com)
  • Next.js website as the frontend (hosted on Vercel at example.com)
  • Mailchimp for newsletter distribution using RSS-to-Email campaigns

The goal was to automatically pull content from WordPress RSS feeds and distribute them via beautifully designed newsletters that maintained our brand consistency.

Challenge 1: RSS Feed URL Mismatch

The Problem

Our WordPress RSS feed generated URLs like:

https://cms.example.com/sample-article-slug/
Enter fullscreen mode Exit fullscreen mode

But our Next.js website used URLs like:

https://example.com/news/sample-article-slug/
Enter fullscreen mode Exit fullscreen mode

When newsletter subscribers clicked on articles, they were being redirected to the WordPress staging domain instead of our production Next.js site.

Initial Attempts

We first tried using Mailchimp's *|RSSITEM:SLUG|* merge tag to construct custom URLs:

<a href="https://example.com/news/*|RSSITEM:SLUG|*">Read Article</a>
Enter fullscreen mode Exit fullscreen mode

However, this approach failed because:

  1. *|RSSITEM:SLUG|* isn't a standard Mailchimp RSS merge tag
  2. *|RSSITEM:TITLE|* produces URL-encoded titles with spaces (%20) instead of proper slugs
  3. Manual redirect mapping for each article wasn't scalable

The Solution: API-Based URL Redirection

We implemented a scalable solution using Next.js API routes:

1. Created an API route (src/app/api/redirect/route.ts):

import { NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {
  const { searchParams } = new URL(request.url)
  const originalUrl = searchParams.get('url')

  if (!originalUrl) {
    return NextResponse.redirect(new URL('/news', request.url), 301)
  }

  try {
    // Extract slug from WordPress URL
    const urlObj = new URL(originalUrl)
    const slug = urlObj.pathname.replace(/^\/+|\/+$/g, '')

    if (slug) {
      // Redirect to proper news URL
      const redirectUrl = new URL(`/news/${slug}`, request.url)
      return NextResponse.redirect(redirectUrl, 301)
    }
  } catch (error) {
    console.error('Error parsing redirect URL:', error)
  }

  // Fallback to news page
  return NextResponse.redirect(new URL('/news', request.url), 301)
}
Enter fullscreen mode Exit fullscreen mode

2. Updated newsletter template to use the API route:

<a href="https://example.com/api/redirect?url=*|RSSITEM:URL|*">
  Read Full Article
</a>
Enter fullscreen mode Exit fullscreen mode

Challenge 2: Newsletter Design Consistency

The Problem

The default Mailchimp RSS templates looked nothing like our website. We needed the newsletter to feel like an extension of our brand, matching the exact visual design of our Next.js site.

The Solution: Custom HTML Template

We created a custom Mailchimp template that replicated our website's design system:

Design Elements Matched:

  • Color Palette: #141414 backgrounds, #1c1c1c content areas, #FFE52C accent colors
  • Typography: Inter font family with exact same font weights and sizes
  • Component Styling: Rounded corners (20px), card layouts, gradient overlays
  • Responsive Design: Mobile-first approach matching website breakpoints

Key CSS Techniques:

.content-section {
  background-color: #1c1c1c;
  margin: 20px 15px;
  padding: 32px 25px;
  border-radius: 24px;
}

.category-tag {
  background-color: #FFE52C;
  color: #000000;
  padding: 6px 12px;
  border-radius: 16px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
}
Enter fullscreen mode Exit fullscreen mode

Complete Newsletter Template Structure:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>*|MC:SUBJECT|*</title>

  <style type="text/css">
    /* Email-safe CSS with !important declarations */
    .email-container {
      max-width: 600px;
      margin: 0 auto;
      background-color: #141414;
      font-family: 'Inter', Arial, sans-serif;
    }

    .content-section img,
    .rss-image-container img {
      width: 100% !important;
      max-width: 100% !important;
      height: auto !important;
      display: block !important;
      border-radius: 20px !important;
      margin: 0 auto 25px auto !important;
    }
  </style>
</head>

<body style="margin: 0; padding: 0; background-color: #141414;">
  <!-- RSS Feed Content -->
  *|RSSITEMS:START|*
  <div class="content-section">
    <div class="categories">
      <span class="category-tag">Newsletter</span>
    </div>

    <h2 class="article-title">
      <a href="https://example.com/api/redirect?url=*|RSSITEM:URL|*">
        *|RSSITEM:TITLE|*
      </a>
    </h2>

    <div class="meta-info">
      <span>*|RSSITEM:DATE|*</span>
      <span>By *|RSSITEM:AUTHOR|*</span>
    </div>

    <div class="rss-image-container">
      *|RSSITEM:IMAGE|*
    </div>

    <div class="article-content">
      *|RSSITEM:CONTENT_TEXT|*
    </div>

    <div style="text-align: center;">
      <a href="https://example.com/api/redirect?url=*|RSSITEM:URL|*"
         class="read-more-btn">
        Read Full Article
      </a>
    </div>
  </div>
  *|RSSITEMS:END|*
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Challenge 3: RSS Image Extraction

The Problem

Getting images from RSS feeds to display properly in email clients proved challenging. Different RSS merge tags (*|RSSITEM:IMAGE|*, *|RSSITEM:IMAGE_FULL|*, *|RSSITEM:IMAGE_URL|*) either didn't work or weren't supported.

The Solution: Simplified Approach

After testing various approaches, we found that *|RSSITEM:IMAGE|* works most reliably when:

  1. Wrapped in a container div for better control
  2. Styled with email-safe CSS using !important declarations
  3. Given fallback styling for different email clients
<div class="rss-image-container">
  *|RSSITEM:IMAGE|*
</div>
Enter fullscreen mode Exit fullscreen mode
.content-section img,
.rss-image-container img {
  width: 100% !important;
  max-width: 100% !important;
  height: auto !important;
  display: block !important;
  border-radius: 20px !important;
  margin: 0 auto 25px auto !important;
}
Enter fullscreen mode Exit fullscreen mode

Challenge 4: Scalability Concerns

The Problem

Initially, we considered several approaches that weren't scalable:

  • Manual redirects for each article in next.config.js
  • Hardcoded URL mappings
  • Modifying WordPress site URLs (which would break the headless setup)

The Solution: Dynamic URL Processing

Our API route solution scales automatically because it:

  1. Extracts slugs dynamically from any WordPress URL
  2. Requires no manual configuration for new articles
  3. Preserves the headless architecture without modifying WordPress
  4. Provides proper 301 redirects for SEO benefits

The Final Architecture

Here's how everything works together:

WordPress RSS Feed
       ↓
Mailchimp RSS Campaign
       ↓
Newsletter Email
       ↓
User Clicks Link
       ↓
example.com/api/redirect?url=cms.example.com/article-slug/
       ↓
Extract Slug from WordPress URL
       ↓
301 Redirect to /news/article-slug
       ↓
Next.js Page Loads
Enter fullscreen mode Exit fullscreen mode

Flow Example:

  1. RSS URL: https://cms.example.com/sample-article-title/
  2. Newsletter Link: https://example.com/api/redirect?url=https://cms.example.com/sample-article-title/
  3. Final Destination: https://example.com/news/sample-article-title/

Key Learnings

1. Email Client Limitations

Email clients have significant CSS limitations compared to web browsers. Using !important declarations and inline styles is often necessary for consistent rendering.

Email-Safe CSS Practices:

/* Always use !important for critical styles */
.newsletter-content {
  width: 100% !important;
  max-width: 600px !important;
  margin: 0 auto !important;
}

/* Inline styles as backup */
<div style="background-color: #141414; padding: 20px;">
Enter fullscreen mode Exit fullscreen mode

2. RSS Merge Tag Inconsistencies

Mailchimp's RSS merge tags aren't always well-documented, and availability can vary between account types. Testing different variations is essential.

Common RSS Merge Tags:

  • *|RSSITEM:TITLE|* - Article title
  • *|RSSITEM:URL|* - Article URL
  • *|RSSITEM:DATE|* - Publication date
  • *|RSSITEM:AUTHOR|* - Article author
  • *|RSSITEM:CONTENT_TEXT|* - Article content
  • *|RSSITEM:IMAGE|* - First image from content

3. Headless CMS URL Management

When using headless CMSs, URL management becomes more complex. Planning for URL structure differences early in the project saves significant refactoring later.

4. API Routes for Complex Logic

Next.js API routes are perfect for handling complex URL transformations that can't be solved with simple redirects or middleware.

Performance Benefits

Our final solution provides several performance advantages:

  • Single 301 Redirect: Users experience only one redirect, minimizing load time
  • Cached API Responses: Vercel caches API route responses for improved performance
  • SEO-Friendly: Proper 301 redirects maintain link equity
  • Scalable: No performance degradation as content volume grows

Implementation Steps

Step 1: Create the API Route

// src/app/api/redirect/route.ts
import { NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {
  const { searchParams } = new URL(request.url)
  const originalUrl = searchParams.get('url')

  if (!originalUrl) {
    return NextResponse.redirect(new URL('/news', request.url), 301)
  }

  try {
    const urlObj = new URL(originalUrl)
    const slug = urlObj.pathname.replace(/^\/+|\/+$/g, '')

    if (slug) {
      const redirectUrl = new URL(`/news/${slug}`, request.url)
      return NextResponse.redirect(redirectUrl, 301)
    }
  } catch (error) {
    console.error('Error parsing redirect URL:', error)
  }

  return NextResponse.redirect(new URL('/news', request.url), 301)
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Create Custom Mailchimp Template

  1. Go to Mailchimp Templates
  2. Create new template → Code your own
  3. Paste the custom HTML template
  4. Configure RSS campaign to use this template

Step 3: Configure RSS Campaign

  1. Create new RSS campaign in Mailchimp
  2. Enter your WordPress RSS feed URL: https://cms.example.com/category/newsletter/feed/
  3. Select your custom template
  4. Configure sending frequency and audience

Step 4: Test the Flow

  1. Send test newsletter
  2. Click article links
  3. Verify redirects work: example.com/api/redirect?url=...example.com/news/article-slug

Troubleshooting Common Issues

Images Not Loading

  • Ensure WordPress RSS feed includes proper image tags
  • Test different RSS image merge tags (*|RSSITEM:IMAGE|*, *|RSSITEM:IMAGE_FULL|*)
  • Check image URLs are absolute, not relative

Redirects Not Working

  • Verify API route is deployed to production
  • Check URL encoding in newsletter links
  • Test API route directly in browser

Newsletter Design Issues

  • Use !important for critical CSS
  • Test in multiple email clients
  • Provide fallback styles for unsupported properties

Security Considerations

URL Validation

// Validate URLs to prevent open redirects
const allowedDomains = ['cms.example.com']
const urlObj = new URL(originalUrl)

if (!allowedDomains.includes(urlObj.hostname)) {
  return NextResponse.redirect(new URL('/news', request.url), 301)
}
Enter fullscreen mode Exit fullscreen mode

Rate Limiting

Consider implementing rate limiting on the redirect API route to prevent abuse:

// Basic rate limiting example
const rateLimitMap = new Map()

export async function GET(request: NextRequest) {
  const ip = request.ip || 'unknown'
  const now = Date.now()
  const windowMs = 60000 // 1 minute
  const maxRequests = 100

  const requestLog = rateLimitMap.get(ip) || []
  const recentRequests = requestLog.filter(time => now - time < windowMs)

  if (recentRequests.length >= maxRequests) {
    return new Response('Too Many Requests', { status: 429 })
  }

  recentRequests.push(now)
  rateLimitMap.set(ip, recentRequests)

  // Continue with redirect logic...
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Building a scalable connection between headless WordPress RSS feeds and Next.js websites requires careful consideration of URL management, design consistency, and email client limitations. Our solution demonstrates that with the right architecture—using API routes for dynamic URL processing and custom HTML templates for brand consistency—you can create a seamless experience that scales automatically.

The key is to embrace the flexibility of modern web architecture while understanding the constraints of email clients and RSS feed limitations. By treating the newsletter as an extension of your website rather than a separate entity, you can maintain brand consistency and provide a superior user experience.

This approach has proven robust and scalable, handling growing content volume without requiring manual intervention for each new article. The investment in proper architecture pays dividends in reduced maintenance overhead and improved user experience.

What's Next?

Have you implemented similar solutions for headless CMS + newsletter integration? What challenges did you face? Share your experiences in the comments below!

If you found this helpful, consider following me for more JAMstack architecture insights and modern web development tutorials.


This implementation showcases the power of modern JAMstack architecture, demonstrating how headless CMSs, static site generators, and API-driven solutions can work together to create sophisticated, scalable web experiences.

Top comments (0)