How to create an email newsletter from an RSS feed

In this tutorial, we'll use Events in Maizzle to fetch the contents of an RSS feed and display them in an HTML email newsletter.

You can preview the final result on CodePen.

Initial setup

Open your CLI and run the new command to scaffold a new project:

maizzle new example-rss

This will create an example-rss folder at your current location, and will install NPM dependencies for you.

Once it finishes, open the project folder in your favourite editor.

rss-parser

We'll be using rss-parser fetch the contents of the RSS feed, so let's install it:

npm install rss-parser

RSS Feed

We also need an RSS feed to fetch content from, so let's go with the best site for learning Laravel.

The Laracasts feed is available at:

https://laracasts.com/feed

Let's add that feed URL inside the build object in config.js:

// config.js
module.exports = {
  build: {
    feed: {
      url: 'https://laracasts.com/feed',
    }
    // ...
  },
}

Fetch Items

We can use rss-parser inside the beforeCreate event in Maizzle to fetch feed data.

Edit config.js, load rss-parser, and use it in the beforeCreate event:

const Parser = require('rss-parser')

module.exports = {
  // ...
  events: {
    async beforeCreate(config) {
      // create a new Parser instance
      const parser = new Parser({
        customFields: {
          feed: ['subtitle'],
          item: ['summary'],
        }
      })
      // fetch and parse the feed
      let feed = await parser.parseURL(config.build.feed.url)
      // store the feed data in our config
      config.build.feed.content = {
        title: feed.title,
        subtitle: feed.subtitle,
        link: feed.link,
        updated_at: feed.lastBuildDate,
        posts: feed.items,
      }
    }
  }
}

Date Format

We'll probably need to format the date of a feed item to something more readable than what the feed provides.

We can add a function to config.js and use it to format the item's date according to our audience's locale:

// config.js
module.exports = {
  // ...
  formattedDate(str) {
    const date = new Date(str)
    return date.toLocaleDateString('en-US', {day: 'numeric', month: 'short', year: 'numeric'})
  }
}

Template

Let's use a simplified version of the promotional.njk template from the Maizzle starter, where we show posts in full width cards.

Front Matter

First, let's define some Front Matter variables. While we're here, let's also set a feed variable with Nunjucks, so we write less code:

---
title: "The Laracasts Feed"
preheader: "👀 Check out what's new in this week's edition of the Laracasts Feed"
---

{% extends "src/layouts/default.njk" %}
{% set feed = page.build.feed.content %}

Now, we can access feed data from the feed variable. For example, here's the updated header row:

<tr>
  <td class="p-48 sm:py-32 sm:px-24 text-center">
    <a href="https://laracasts.com" target="_blank" rel="noopener noreferrer">
      <img src="laracasts-logo.png" width="157" alt="{{ feed.title }}">
    </a>
    <p class="m-0 mt-8 text-sm fonts-medium text-gray-600">{{ feed.subtitle }}</p>
  </td>
</tr>

Items Loop

Let's use the full width card component and show a list of all items from the feed:

{% for post in feed.posts %}
<tr>
  <td class="p-24 bg-white hover:shadow-xl rounded transition-shadow duration-300">
    <p class="m-0 mb-4 text-sm text-gray-500">{{ page.formattedDate(post.pubDate) }}</p>
    <h2 class="m-0 mb-16 text-2xl leading-24">
      <a href="{{ post.link }}" target="_blank" rel="noopener noreferrer" class="text-gray-800 hover:text-gray-700 no-underline transition-colors duration-300">{{ post.title }}</a>
    </h2>
    <p class="m-0 text-base text-gray-600">{{ post.summary | truncate(60) }}</p>
  </td>
</tr>
{% if loop.last === false %}
<tr>
  <td class="h-24"></td>
</tr>
{% endif %}
{% endfor %}

That's it! Take a look at the final result on CodePen.

Resources