Templates & overrides

All rendering flows through plain PHP templates that resolve WooCommerce-style:

{child theme}/thewpfeeds/{name}.php
{parent theme}/thewpfeeds/{name}.php
{plugin}/templates/{name}.php          ← shipped defaults

Copy any shipped template into a thewpfeeds/ folder in your theme and edit. You only copy what you change; everything else falls through to the plugin.

The template files

Template Receives Responsibility
feed.php $feed, $items, $layout, $args Outer wrapper; delegates to layout-{$layout}.php
layout-grid.php, layout-list.php same The loop structure; calls thewpfeeds_item() per item
item.php $item, $feed One card — the file you'll override 90% of the time
item-youtube.php $item, $feed Provider default for YouTube (thumbnail + play overlay)
empty.php $feed, $args No-items state; error details shown to admins only

The item hierarchy

Different content wants different markup — a video is not a LinkedIn post. Items resolve through their own hierarchy, most specific first:

item-{feed-slug}.php     e.g. item-yt-tutorials.php   → one specific feed
item-{provider}.php      e.g. item-youtube.php        → all feeds of that type
item.php                                              → everything else

Each name is tried through the full theme → plugin chain before falling to the next. This solves both directions:

For styling-only differences you don't need a template at all — the wrapper carries the feed slug as a class:

.thewpfeeds--yt-events .thewpfeeds__item { /* ... */ }

Custom layouts

Layout names are open-ended. Drop thewpfeeds/layout-carousel.php in your theme and pass carousel as the layout (block sidebar, feed default, or render args) — no registration, no plugin changes. A layout receives the collection and loops it:

<?php /* {theme}/thewpfeeds/layout-carousel.php */ ?>
<div class="my-carousel" data-carousel>
    <?php foreach ( $items as $item ) : ?>
        <div class="my-carousel__slide"><?php thewpfeeds_item( $item, $feed ); ?></div>
    <?php endforeach; ?>
</div>

The escaping contract

Item getters return raw values. Escaping happens in templates — esc_html(), esc_url(), esc_attr() — exactly where WordPress developers expect to do it. The shipped templates demonstrate the discipline; $item->imageTag() is the one helper that returns pre-escaped HTML.

Template versioning

Every shipped template carries an @version header. It only changes when the template's contract changes (new variables, changed structure) — cosmetic edits don't bump it. If you keep overrides long-term, diff against the shipped file when the version moves.