Plugin

Freshet Feeds

The gripe

Every feed plugin tries to own the markup: a styling panel, an iframe, somebody else's JavaScript on my pages.

The plugin

Should fetch the feed, hand me the items, and let my theme render them.

Freshet Feeds shows external feeds inside WordPress: RSS and Atom, YouTube channels and Bluesky profiles. Every provider is normalised into one item model and rendered through one template chain, and that chain is overridable from your theme.

It is for people who build themes and want the feed to look like the rest of the site, not like the plugin. If you have ever copied a WooCommerce template into your theme to change it, you already know how this one works.

Free, and the whole plugin

Every provider, every template, every hook, unlimited feeds. Nothing in the download is locked, limited or time-barred.

Your theme owns the markup
Templates resolve the way WooCommerce's do: child theme, then parent theme, then the plugin's own templates/ folder. Copy item.php into {your-theme}/freshet-feeds/ and edit it; you copy only what you change and everything else falls through. Items have their own hierarchy on top of that, most specific first: item-{feed-slug}.php for one feed, item-{provider}.php for every feed of that type, then item.php for the rest. So an RSS feed and a YouTube channel on the same page each get markup that fits, and two YouTube channels can look different from each other. A custom layout is a file you drop in, not something you register: add layout-carousel.php and pass carousel. The override chain and the item hierarchy are in the docs.
Three live providers, no API keys
Any RSS 2.0 or Atom URL, which also covers Mastodon, subreddits, podcasts and most news sites; content:encoded, media thumbnails and image enclosures are handled. YouTube channels through the public channel feed, no API key and no quota, with a video-shaped default template that links to YouTube rather than embedding an iframe. Bluesky profiles through the public API, no authentication; reposts, replies and pins are filtered out, embedded images keep their alt text. There is no X provider, because X has no free read API and no RSS, and the plugin does not build on scraping. A provider of your own is one PHP class and one hook, and inherits the cache, the templates and the block. Each provider's settings are in the docs.
A Mock provider for building before the feed exists
The fourth provider ships a set of realistic sample posts: single image, multiple images, a shared article, text only, image only, and a long multi-paragraph one. No credentials, no network call. Mock posts run through the same normaliser, item model and template chain as live posts, so what you build against them works unchanged once you switch the feed's provider. It is hidden from the provider dropdown on sites reporting a production environment type, and refuses to fetch there even for a feed created earlier, so fixture posts cannot end up on a live site. It is also the fastest way to evaluate the plugin.
Pages never wait on a remote API
Items are stored durably in the database, not in transients an object cache might evict. On render, whatever is cached is served immediately; if it is past the feed's TTL, an hour by default and settable per feed, a background refresh is scheduled so the next view is fresh. The one exception is a brand-new feed's very first fetch. A failed fetch never clobbers cached items: the last good data keeps rendering and the error shows in the admin feed list. Feed images are downloaded into your own uploads directory, because some platforms serve signed image URLs that expire within days, and served from your domain. Nothing enters the media library, and visitors' browsers never contact the source platforms. Caching and image localisation are in the docs.
A Feed block for the editor
In the block editor, add the Feed block, pick a feed in the sidebar, choose a layout and an item count. The preview is server-rendered through the same template chain as the front end, so what an editor sees in the sidebar is what ships, including any override in your theme. The content is in your DOM rather than in an iframe, so it is real SEO with no consent baggage. The block's source is unminified and in the plugin, alongside the build setup.
A loop API of four functions
freshet_feeds( 'my-feed' ) returns the cached items as an iterable, countable collection of item objects, never blocking on the remote API; an unknown slug gives an empty collection, not an error. Each item exposes getters for title, date, excerpt, image and the rest, returning raw values so escaping happens in your template, where WordPress developers expect to do it. freshet_feeds_render( 'my-feed' ) runs the full template chain, with layout and count as arguments; freshet_feeds_item() renders one item through the hierarchy and freshet_feeds_template() is get_template_part() for your own partials. WP-CLI gets wp freshet-feeds status and fetch. The four functions and every item getter are in the docs.

Needs WordPress 6.5 and PHP 8.2 or newer. Download it from wordpress.org, or read the source on GitHub. Or Try it in WordPress Playground first: a throwaway site in your browser, the plugin active and a feed already on the front page. The template chain, the item API, the providers and every hook are in the Feeds docs.

Something not covered here? The FAQ has a Feeds section.