Loop & Content

The Loop is where WordPress turns a database query into what a visitor actually sees. These hooks intercept that process — filtering content, changing which posts are fetched, and deciding which template renders them. 2 actions, 9 filters in this category.

the_content

loop-content

Filters post content before display

the_content is the last chance to change post HTML before it reaches the template. Always return the string — filters that echo will break the page.

Signature

apply_filters( 'the_content', string $content );

Example

add_filter( 'the_content', function( $content ) {
    if ( is_single() ) {
        $content .= '<p><strong>Thanks for reading!</strong></p>';
    }
    return $content;
} );

Common Use Cases

  • Append/prepend content to posts
  • Transform shortcodes manually
  • Add schema markup to content
  • Strip or sanitize specific HTML
since 0.71wp-includes/post-template.php

the_title

loop-content

Filters the post title

the_title changes how a title is displayed, not the value in the database. Guard with post ID so menus and admin lists are not rewritten by accident.

Signature

apply_filters( 'the_title', string $title, int $id );

Example

add_filter( 'the_title', function( $title, $id ) {
    if ( get_post_type( $id ) === 'book' ) {
        return 'Book: ' . $title;
    }
    return $title;
}, 10, 2 );

Common Use Cases

  • Prefix/suffix titles by post type
  • Truncate long titles
  • Add icons or labels to titles
  • Translate titles dynamically
since 0.71wp-includes/post-template.php

excerpt_length

loop-content

Controls the auto-generated excerpt word count

excerpt_length only affects auto excerpts, not the Excerpt box in the editor. Return an integer word count.

Signature

apply_filters( 'excerpt_length', int $number );

Example

add_filter( 'excerpt_length', function( $length ) {
    return 30;
} );

Common Use Cases

  • Shorten excerpts for card layouts
  • Lengthen excerpts for search results
  • Vary length by post type
  • Control reading time estimates
since 2.7.0wp-includes/formatting.php

the_excerpt

loop-content

Filters the displayed post excerpt

the_excerpt filters the final excerpt string, whether it was typed manually or auto-generated — use excerpt_length/excerpt_more to shape the auto version instead.

Signature

apply_filters( 'the_excerpt', string $post_excerpt );

Example

add_filter( 'the_excerpt', function( $excerpt ) {
    return '<div class="excerpt-wrap">' . $excerpt . '</div>';
} );

Common Use Cases

  • Wrap excerpts in custom markup
  • Append a 'read more' link consistently
  • Strip shortcodes left in excerpts
since 0.71wp-includes/post-template.php

excerpt_more

loop-content

Filters the '[...]' string appended to auto-generated excerpts

excerpt_more only changes the trailing string on auto-generated excerpts — it does nothing to a manually written excerpt.

Signature

apply_filters( 'excerpt_more', string $more_string );

Example

add_filter( 'excerpt_more', function() {
    return ' <a href="' . get_permalink() . '">Read more →</a>';
} );

Common Use Cases

  • Replace the ellipsis with a Read More link
  • Localize the trailing string
  • Add an icon after trimmed excerpts
since 1.8.0wp-includes/formatting.php

post_class

loop-content

Filters the CSS classes on a single post's wrapper element

post_class runs once per post inside the loop — unlike body_class, which runs once per page — so keep the callback fast if you're looping many posts.

Signature

apply_filters( 'post_class', string[] $classes, string[] $css_class, int $post_id );

Example

add_filter( 'post_class', function( $classes, $css_class, $post_id ) {
    if ( has_post_thumbnail( $post_id ) ) {
        $classes[] = 'has-thumbnail';
    }
    return $classes;
}, 10, 3 );

Common Use Cases

  • Add a class for featured-image presence
  • Highlight sticky or featured posts
  • Add taxonomy-term-based classes
  • Alternate row classes for grid layouts
since 2.7.0wp-includes/post-template.php

wp_nav_menu_items

loop-content

Filters the HTML list items of a rendered nav menu

wp_nav_menu_items lets you append or alter menu markup without editing the menu in the admin — useful for injecting a login/cart link at the end.

Signature

apply_filters( 'wp_nav_menu_items', string $items, stdClass $args );

Example

add_filter( 'wp_nav_menu_items', function( $items, $args ) {
    if ( 'primary' !== $args->theme_location ) {
        return $items;
    }
    $items .= is_user_logged_in()
        ? '<li><a href="' . wp_logout_url() . '">Log out</a></li>'
        : '<li><a href="' . wp_login_url() . '">Log in</a></li>';
    return $items;
}, 10, 2 );

Common Use Cases

  • Append a login/cart/search item to a menu
  • Inject a CTA button into navigation
  • Conditionally add items by user role
since 3.0.0wp-includes/nav-menu-template.php

template_redirect

loop-content

Fires right before WordPress decides which template to load

template_redirect is the standard place to short-circuit a request — custom redirects, maintenance mode, or blocking access — after the query is known but before any template file loads.

Signature

do_action( 'template_redirect' );

Example

add_action( 'template_redirect', function() {
    if ( is_page( 'old-page' ) ) {
        wp_safe_redirect( home_url( '/new-page/' ), 301 );
        exit;
    }
} );

Common Use Cases

  • Custom redirects based on conditional tags
  • Block access / maintenance mode
  • Serve a custom response instead of a template (e.g. JSON)
  • Force login on specific pages
since 1.5.0wp-includes/template-loader.php

pre_get_posts

loop-content

Modify any WP_Query before it runs

pre_get_posts fires for every query — main and secondary. Always check is_main_query() (and is_admin()) or you'll silently break widgets, related-post queries, and REST requests.

Signature

do_action( 'pre_get_posts', WP_Query $query );

Example

add_action( 'pre_get_posts', function( $query ) {
    if ( ! is_admin() && $query->is_main_query() && is_post_type_archive( 'book' ) ) {
        $query->set( 'posts_per_page', 24 );
    }
} );

Common Use Cases

  • Change posts_per_page for an archive
  • Include/exclude post types from search
  • Reorder posts on a custom query
  • Restrict a query by taxonomy or meta
since 2.0.0wp-includes/class-wp-query.php

template_include

loop-content

Filters the final template file path WordPress is about to load

template_include is the very last stop in the template hierarchy — return an absolute path to a PHP file and WordPress loads exactly that, bypassing the rest of the hierarchy.

Signature

apply_filters( 'template_include', string $template );

Example

add_filter( 'template_include', function( $template ) {
    if ( is_singular( 'book' ) ) {
        $custom = plugin_dir_path( __FILE__ ) . 'templates/single-book.php';
        if ( file_exists( $custom ) ) {
            return $custom;
        }
    }
    return $template;
} );

Common Use Cases

  • Ship custom templates from a plugin
  • Override a theme's template for a specific condition
  • Serve a completely custom page (landing pages, builders)
since 1.5.0wp-includes/template-loader.php

render_block

loop-content

Filters the final HTML output of every rendered block

render_block runs for every block on the page, core and custom alike — check $block['blockName'] or it will touch content you didn't intend to change.

Signature

apply_filters( 'render_block', string $block_content, array $block );

Example

add_filter( 'render_block', function( $block_content, $block ) {
    if ( 'core/image' !== $block['blockName'] ) {
        return $block_content;
    }
    return str_replace( '<img', '<img loading="lazy"', $block_content );
}, 10, 2 );

Common Use Cases

  • Modify markup of one specific block type
  • Inject wrapper markup around blocks
  • Add tracking attributes to CTA blocks
since 5.0.0wp-includes/class-wp-block.php