Displaying Posts From A Category In WordPress

It may happen that you are designing some sort of archive page or a front page and you want to list all posts belonging to a specific category. In this case you can use WP_Query (or the simpler get_posts() helper) to pull in exactly the posts you need.

Using WP_Query to list posts from a category

The most flexible way to grab posts from a specific category is a custom WP_Query. Here’s a complete, working example:


$query = new WP_Query( array(
    'category_name'  => 'news',
    'posts_per_page' => 10,
) );

if ( $query->have_posts() ) {
    while ( $query->have_posts() ) {
        $query->the_post();

        // Do what ever you want with your posts
        echo '<article id="post-' . get_the_ID() . '">';
        echo '<h2><a href="' . esc_url( get_permalink() ) . '">' . esc_html( get_the_title() ) . '</a></h2>';
        echo '<div class="entry-summary">' . get_the_excerpt() . '</div>';
        echo '</article>';
    }

    // Always reset the query after a custom WP_Query loop
    wp_reset_postdata();
} else {
    echo '<p>No posts found in this category.</p>';
}

A couple of notes on the arguments:

  • category_name accepts the category slug (not the display name). Use a comma-separated list of slugs to match any of several categories.
  • If you’d rather reference the category by its numeric ID, use cat instead, e.g. 'cat' => 5. Prefix the ID with a minus sign ('cat' => -5) to exclude that category instead.
  • By default, WP_Query automatically includes posts from child categories of the one you specify, so you don’t need any extra logic to pull those in too.
  • Always pair a custom WP_Query loop with wp_reset_postdata() afterwards. It restores the global $post object to the main query so the rest of your template behaves correctly.

A quicker option: get_posts()

If you just need a simple list of posts (for example, in a sidebar widget or a “related posts” block) and don’t need the full WP_Query object, get_posts() is a convenient shorthand. It returns a plain array of post objects:


$posts = get_posts( array(
    'category_name' => 'news',
    'numberposts'   => 10,
) );

foreach ( $posts as $post ) {
    setup_postdata( $post );

    // Do what ever you want with your posts
    echo '<h2><a href="' . esc_url( get_permalink() ) . '">' . esc_html( get_the_title() ) . '</a></h2>';
}

wp_reset_postdata();

get_posts() accepts most of the same arguments as WP_Query (note it uses numberposts instead of posts_per_page). Because it still touches the global post data when you call setup_postdata(), remember to call wp_reset_postdata() once you’re done with the loop.

A more idiomatic alternative: pre_get_posts

If your goal is simply to change what shows up on the existing category archive page itself, rather than building a custom block somewhere in a template, you usually don’t need a separate query at all. Hook into pre_get_posts and modify the main query before it runs:


function ankurm_modify_category_query( $query ) {
    if ( ! is_admin() && $query->is_main_query() && is_category( 'news' ) ) {
        $query->set( 'posts_per_page', 10 );
    }
}
add_action( 'pre_get_posts', 'ankurm_modify_category_query' );

This approach is generally preferred over a fresh WP_Query when you’re working with an actual category archive page, since it avoids running a duplicate database query and keeps pagination, SEO plugins, and other code that relies on the main query working correctly. Reach for a custom WP_Query or get_posts() call instead when you need a category listing in a spot that isn’t the main query, such as a widget, a shortcode, or a block on the front page.

FAQs

How do I list posts from a category by ID instead of slug?

Use the cat argument with the category’s numeric ID, e.g. 'cat' => 5, instead of category_name.

Will child categories be included automatically?

Yes. Both WP_Query and get_posts() include posts from child categories of the specified category by default.

Do I need wp_reset_postdata() with get_posts()?

Only if you call setup_postdata() inside your loop (which is needed for template tags like the_title() to work). If you access post properties directly instead, you can skip both calls.

Conclusion

For most category-listing needs, a custom WP_Query loop or the simpler get_posts() helper will get the job done. But if you’re customizing an existing category archive page rather than building a new listing elsewhere, hooking into pre_get_posts is the cleaner, more idiomatic solution.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.