Display Recent Posts on a Static Page (With and Without Code)

You want a simple setup. A static page with a short introduction, followed by a list of your latest posts. Maybe just from one specific category.

Years ago, this meant hacking your PHP files and crossing your fingers, or installing a heavy widget plugin that dragged your site speed down to a crawl.

Now? It is built right into the editor. But developers still need options.

Here is how you actually get this done, depending on how deep into the codebase you want to go.

Method 1: The Visual Route (No Code)

If you use the default block editor, stop looking for third-party solutions. You don’t need them.

  1. Open your page in the editor.
  2. Click the + icon to add a new block.
  3. Search for the Query Loop block.

That is it. The Query Loop lets you visually build the layout exactly how you want it—titles, excerpts, featured images.

Look at the sidebar on the right. You can filter by categories, change the ordering, and cap the number of posts.

Pro tip: If you care about performance and run a clean theme like GeneratePress, pair it with GenerateBlocks. You get complete control over the design without the drag of old-school add-ons.

Method 2: The Shortcode (For Dropping Anywhere)

Sometimes the block editor isn’t enough. You need to drop a dynamic list into a random custom field, an old widget area, or a page builder.

Drop this into your child theme’s functions.php file. Unlike older tutorials that force you to hardcode the category, this snippet uses attributes. It gives you actual flexibility.

function custom_recent_posts_shortcode( $atts ) {
    // Set default attributes
    $atts = shortcode_atts( array(
        'category' => '', // Category slug (leave blank for all)
        'posts'    => 5,  // Number of posts to show
    ), $atts );

    // Setup query arguments
    $args = array(
        'posts_per_page' => intval( $atts['posts'] ),
        'post_status'    => 'publish',
    );

    // Filter by category if provided
    if ( ! empty( $atts['category'] ) ) {
        $args['category_name'] = sanitize_text_field( $atts['category'] );
    }

    $the_query = new WP_Query( $args );
    
    // Initialize the output variable
    $output = ''; 

    if ( $the_query->have_posts() ) {
        $output .= '<ul class="custom-recent-posts">';
        while ( $the_query->have_posts() ) {
            $the_query->the_post();
            $output .= '<li>';
            
            // Output thumbnail if it exists
            if ( has_post_thumbnail() ) {
                $output .= '<a href="' . get_permalink() . '" class="post-thumb">' . get_the_post_thumbnail( get_the_ID(), array( 50, 50 ) ) . '</a> ';
            }
            
            // Output title
            $output .= '<a href="' . get_permalink() . '" rel="bookmark">' . get_the_title() . '</a>';
            $output .= '</li>';
        }
        $output .= '</ul>';
        
        // Always reset post data after a custom query
        wp_reset_postdata(); 
    } else {
        $output .= '<p>No recent posts found.</p>';
    }

    return $output;
}
add_shortcode('recent_posts', 'custom_recent_posts_shortcode');

How to use it: Paste [recent_posts] into any page to show the latest five posts.

Want 10 posts from your “news” category? Type [recent_posts category="news" posts="10"]. Simple.

Method 3: The Raw PHP (For Classic Themes)

Maybe you are maintaining a legacy site or building a custom page template (page-custom.php). In that case, write the query directly into the file.

Here is a clean snippet that grabs ten posts and spits out the titles and excerpts:

<div class="template-recent-posts">
    <?php 
    // Define the query arguments
    $args = array(
        'posts_per_page' => 10,
        // 'category_name' => 'news', // Uncomment this to filter by category slug
    );
    
    $the_query = new WP_Query( $args ); 
    
    if ( $the_query->have_posts() ) : ?>
        <ul>
            <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
                <li>
                    <h3><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
                    <div class="post-excerpt">
                        <?php the_excerpt(); ?>
                    </div>
                </li>
            <?php endwhile; ?>
        </ul>
        <?php wp_reset_postdata(); // Essential to restore the global $post variable ?>
    <?php else : ?>
        <p>No posts to display.</p>
    <?php endif; ?>
</div>

Don’t Fall for the Plugin Trap

The WordPress repository is full of single-use tools promising “Advanced Recent Posts.”

Ignore them.

Relying on the native Query Loop—or writing a clean shortcode yourself—keeps your database light and your site secure. Keep your plugin count low. Let WordPress do the heavy lifting it was designed to do, and move on to the next task.

If you liked this post, please consider sharing it with your friends:

LinkedIn
X.com
Pinterest

Leave a Comment