Close Menu
GeekBlog

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    What's Hot

    Instagram Already Has an Off Switch for the Algorithm. It Resets Every Time You Close the App.

    September 12, 2026

    Anthropic Banned Five Groups of Working Scientists. It Says It Cannot Prove Any of Them Meant Harm.

    September 12, 2026

    Google Made Its Best Paid Gemini Feature Free. The Price Is Access to Your Inbox.

    September 12, 2026
    Facebook X (Twitter) Instagram Threads
    GeekBlog
    • Home
    • Mobile
    • Tech News
    • Blog
    • Gaming
    • Smartwatch
    • How-To Guides
    • AI & Software
    Facebook
    GeekBlog
    Home»Blog»How to Filter Posts in WordPress by Category (WP_Query, pre_get_posts, REST API and Blocks)
    Blog

    How to Filter Posts in WordPress by Category (WP_Query, pre_get_posts, REST API and Blocks)

    Ethan CaldwellBy Ethan CaldwellSeptember 9, 202611 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Email Copy Link
    Workplace with a laptop showing program code for a WordPress site
    Share
    Facebook Twitter LinkedIn Pinterest Email Copy Link

    To filter WordPress posts by category, pass a category argument to WP_Query (cat for IDs, category_name for slugs, or tax_query for anything more precise), or hook pre_get_posts to change what an existing archive shows. The REST API takes ?categories=ID, the block editor’s Query Loop has a taxonomy filter in the sidebar, and a small shortcode or AJAX handler covers front end filter buttons. This guide walks through each method with code you can drop into a child theme or plugin.

    Quick answer: Use new WP_Query(['category_name' => 'news', 'posts_per_page' => 10]) in a template for a custom list, use add_action('pre_get_posts', ...) with $query->set('cat', 12) to change the main blog loop, use /wp-json/wp/v2/posts?categories=12 for JavaScript, and use the Query Loop block’s Filters panel when you do not want to write PHP. Exclude a category by giving cat a negative ID, for example 'cat' => '-7'.

    Which method you pick depends on where the filtered list needs to appear and who maintains the site. Template code gives full control but requires a child theme; pre_get_posts is the correct way to bend the main query without duplicating it; the REST API and AJAX suit interactive filtering; the block editor is for site owners who never open a PHP file. If you need a scratch site to try these on, installing WordPress on a local host takes about ten minutes.

    Find the category ID or slug first

    Every method needs either the category’s numeric ID or its slug. In the dashboard go to Posts, then Categories, hover a category and read tag_ID= in the status bar URL, or open the category and read the same value in the address bar. The slug is shown in the table. In code, get_cat_ID('News') returns the ID for a name, and get_category_by_slug('news') returns the full term object. WP CLI users can run wp term list category --fields=term_id,name,slug.

    Filter with WP_Query in a template

    The WP_Query category parameters in the developer reference are the definitive list. The three you will use most are cat, category_name and tax_query.

    <?php
    // By ID (comma separated for OR, includes child categories)
    $q = new WP_Query( array(
        'cat'            => '12,15',
        'posts_per_page' => 10,
    ) );
    
    // By slug (comma for OR, plus sign for AND)
    $q = new WP_Query( array(
        'category_name'  => 'news+featured',
        'posts_per_page' => 10,
    ) );
    
    // Precise control with tax_query
    $q = new WP_Query( array(
        'post_type'      => 'post',
        'posts_per_page' => 10,
        'tax_query'      => array(
            array(
                'taxonomy'         => 'category',
                'field'            => 'slug',
                'terms'            => array( 'news', 'reviews' ),
                'operator'         => 'IN',
                'include_children' => false,
            ),
        ),
    ) );
    
    if ( $q->have_posts() ) {
        echo '<ul>';
        while ( $q->have_posts() ) {
            $q->the_post();
            printf( '<li><a href="%s">%s</a></li>', esc_url( get_permalink() ), esc_html( get_the_title() ) );
        }
        echo '</ul>';
        wp_reset_postdata();
    }

    The differences are worth knowing. cat and category_name automatically include posts in child categories. tax_query lets you switch that off with include_children, combine multiple clauses with a relation of AND or OR, and use operators such as NOT IN and AND. Always call wp_reset_postdata() after a custom loop or the template tags that follow will read the wrong post.

    ParameterAcceptsChildren includedBest for
    catID, comma list, negative ID to excludeYesQuick include or exclude
    category_nameSlug, comma for OR, plus for ANDYesReadable template code
    category__in / category__not_inArray of IDsNoExact category only
    category__andArray of IDsNoPosts in all listed categories
    tax_queryNested arrayConfigurableMultiple taxonomies, custom logic

    Change the main loop with pre_get_posts

    When the goal is to alter what the home page, a category archive or search results show, do not write a second query in the template. Hook pre_get_posts and modify the main query before it runs. This keeps pagination, canonical URLs and theme templates working.

    Recommended for you:

    How to Use the TikTok for Developers Documentation
    Blog·Sep 9, 2026

    How to Use the TikTok for Developers Documentation

    <?php
    add_action( 'pre_get_posts', function ( $query ) {
        if ( is_admin() || ! $query->is_main_query() ) {
            return;
        }
        // Hide the "Internal" category (ID 7) from the blog home page
        if ( $query->is_home() ) {
            $query->set( 'cat', '-7' );
        }
        // Only show "Tutorials" in search results
        if ( $query->is_search() ) {
            $query->set( 'category_name', 'tutorials' );
        }
    } );

    The two guards at the top matter. Without is_admin() you will filter the Posts screen in the dashboard, and without is_main_query() you will also filter every widget and sidebar query on the page, which is a classic source of “my related posts disappeared” bugs.

    A shortcode for editors

    A shortcode lets a content editor drop a filtered list anywhere in a post or page with [posts_by_category slug="news" count="5"]. Put this in a small plugin so it survives theme changes.

    <?php
    add_shortcode( 'posts_by_category', function ( $atts ) {
        $a = shortcode_atts( array( 'slug' => '', 'count' => 5 ), $atts );
        $q = new WP_Query( array(
            'category_name'  => sanitize_text_field( $a['slug'] ),
            'posts_per_page' => absint( $a['count'] ),
            'no_found_rows'  => true,
        ) );
        if ( ! $q->have_posts() ) {
            return '<p>No posts found.</p>';
        }
        $out = '<ul class="posts-by-category">';
        while ( $q->have_posts() ) {
            $q->the_post();
            $out .= sprintf( '<li><a href="%s">%s</a></li>', esc_url( get_permalink() ), esc_html( get_the_title() ) );
        }
        wp_reset_postdata();
        return $out . '</ul>';
    } );

    no_found_rows skips the count query that pagination needs, which is a free speed win for any list that does not paginate.

    Filter through the REST API

    Headless front ends, mobile apps and plain JavaScript widgets should use the REST API. The posts endpoint accepts categories (IDs, comma separated) and categories_exclude, documented in the REST API posts reference.

    // Ten most recent posts in category 12, with featured images embedded
    fetch('/wp-json/wp/v2/posts?categories=12&per_page=10&_embed')
      .then(r => r.json())
      .then(posts => {
        const list = document.querySelector('#news');
        list.innerHTML = posts.map(p => `<li><a href="${p.link}">${p.title.rendered}</a></li>`).join('');
      });
    
    // Everything except category 7
    fetch('/wp-json/wp/v2/posts?categories_exclude=7');

    Only IDs are accepted here. If you have a slug, resolve it first with /wp-json/wp/v2/categories?slug=news and read the id field from the response.

    Filter in the block editor with Query Loop

    For block themes and anyone avoiding PHP, add a Query Loop block, choose a layout, then open the block settings sidebar. Turn off “Inherit query from template”, click the plus icon next to Filters and choose Taxonomies. Pick one or more categories from the Categories field and the block updates live. The same panel lets you set post count, order, sticky post behavior and an author filter. If the block sits in a category archive template you normally want “Inherit query from template” left on so the archive shows its own category, and only turn it off for lists that should ignore the current page context.

    AJAX filter buttons on the front end

    A row of buttons that swaps the post list without a page reload is the most requested version of this feature. The clean approach uses the REST API from the section above, so there is no admin-ajax.php handler to write and responses are cacheable. Print the buttons in PHP with the category IDs as data attributes, then handle clicks in JavaScript.

    <?php
    // In the template: one button per category
    foreach ( get_categories( array( 'hide_empty' => true ) ) as $c ) {
        printf( '<button class="cat-filter" data-cat="%d">%s</button>', $c->term_id, esc_html( $c->name ) );
    }
    echo '<ul id="filtered-posts"></ul>';
    ?>
    <script>
    document.querySelectorAll('.cat-filter').forEach(btn => {
      btn.addEventListener('click', async () => {
        const res = await fetch(`/wp-json/wp/v2/posts?categories=${btn.dataset.cat}&per_page=12`);
        const posts = await res.json();
        document.querySelector('#filtered-posts').innerHTML =
          posts.map(p => `<li><a href="${p.link}">${p.title.rendered}</a></li>`).join('');
      });
    });
    </script>

    Move the script into a file enqueued with wp_enqueue_script for production, and add a loading state so users see something while the request completes. If you prefer the classic admin-ajax.php route, register an action with wp_ajax_ and wp_ajax_nopriv_ hooks, verify a nonce, and return the rendered HTML with wp_send_json_success. The nonce and hook mechanics are the same ones used in sending mail in WordPress without a plugin, so that code is a good reference for the handler shape.

    Excluding categories

    Exclusion is easy to get wrong because the parameters behave differently. A negative cat value such as '-7' excludes that category and its children. category__not_in excludes only the exact IDs listed and leaves child categories alone. A tax_query clause with 'operator' => 'NOT IN' is the most explicit, and it is the only choice when you also need to exclude by tag or a custom taxonomy in the same query. To hide a category site wide, including feeds and search, combine the pre_get_posts example with a check that skips is_category() so the category’s own archive still works.

    Note: Excluding a category does not make its posts private. Anyone with the URL, or anyone using the REST API without the exclusion parameter, can still read them. Use post visibility or a membership plugin for actual access control.

    Performance

    Category filtering itself is cheap because WordPress joins wp_term_relationships and wp_term_taxonomy on indexed columns. Problems come from the surrounding query. Set no_found_rows to true when you do not paginate, set update_post_meta_cache and update_post_term_cache to false for lists that only print titles and links, and cache the whole rendered list with get_transient and set_transient when it appears on every page. Avoid tax_query clauses with 'field' => 'name', which forces an extra lookup, and never filter in PHP after fetching everything; let MySQL do the work. Before changing queries on a production site, take a backup as described in how to back up a WordPress website manually.

    Troubleshooting

    The filtered list shows posts from other categories

    Child categories are included by cat and category_name. Switch to category__in or a tax_query with 'include_children' => false if you want an exact match.

    pre_get_posts broke the admin Posts screen or widgets

    Add the is_admin() and is_main_query() guards shown above. Without them the hook fires on every query WordPress runs, including the dashboard list table and sidebar widgets.

    Pagination returns 404 on page 2

    A secondary WP_Query in a template does not know about the page URL. Pass 'paged' => get_query_var('paged', 1) to the query, or better, use pre_get_posts so the main query handles paging.

    Recommended for you:

    How to Use Regular Expressions in Jinja2
    Blog·Sep 9, 2026

    How to Use Regular Expressions in Jinja2

    The REST call returns an empty array for a valid slug

    The categories parameter needs a numeric ID. Resolve the slug through the categories endpoint first, or check that the posts are published, since drafts are hidden from unauthenticated requests.

    Template tags after my loop show the wrong post

    You forgot wp_reset_postdata(). Call it right after the custom loop finishes so the global post object returns to the main query’s current post.

    Frequently asked questions

    What is the difference between cat and category_name in WP_Query?

    cat takes numeric category IDs, and category_name takes slugs. Both include posts from child categories, both accept comma separated lists for OR logic, and category_name also accepts a plus sign between slugs for AND logic. Slugs make template code easier to read, while IDs are stable if a slug ever changes.

    How do I show posts from multiple categories at once?

    Pass a comma separated list, such as 'cat' => '12,15' or 'category_name' => 'news,reviews', to match posts in any of them. To require posts to be in all listed categories, use category__and with an array of IDs, or a tax_query clause with the AND operator.

    Can I filter posts by category without writing code?

    Yes. In the block editor, insert a Query Loop block, disable “Inherit query from template”, add a Taxonomies filter and select the categories you want. Classic theme users can install a widget or filter plugin, but the Query Loop block covers most needs and ships with WordPress core.

    How do I exclude a category from the home page?

    Hook pre_get_posts, check is_home() and is_main_query(), then call $query->set('cat', '-7') with the ID of the category to hide. The negative sign means exclude. Place the code in a child theme’s functions file or a small custom plugin so updates do not overwrite it.

    Does the REST API support filtering by category slug?

    Not directly. The categories and categories_exclude parameters on the posts endpoint accept only numeric IDs. Request /wp-json/wp/v2/categories?slug=your-slug first, read the id from the response, and use that ID in the posts request.

    The bottom line

    WordPress gives you several correct ways to filter posts by category, and the right one depends on where the list lives. Use WP_Query in templates and shortcodes, pre_get_posts for the main loop, the REST API for JavaScript, and the Query Loop block when no code is wanted.

    Whichever route you choose, remember that cat and category_name include child categories, always reset post data after a custom loop, and keep queries lean with no_found_rows and transients when the list appears on busy pages.

    Share. Facebook Twitter Pinterest LinkedIn Tumblr Telegram Email Copy Link
    Previous ArticleHow to Run Node.js on DigitalOcean: Droplet vs App Platform
    Next Article How to Integrate Facebook Messenger Into Your App (Webhooks, Send API, App Review)
    Ethan Caldwell

      Ethan Caldwell is GeekBlog's resident Apple specialist, covering the entire Apple ecosystem - iPhone, iPad, Mac, Apple Watch, AirPods and the software that ties them together. A longtime iOS user and gadget collector, Ethan tracks Cupertino's every move, breaking down Apple keynotes, A- and M-series chip benchmarks, iOS feature updates and the rumor mill into clear, practical takes that help readers decide whether the latest Apple hardware is worth the upgrade.

      Related Posts

      11 Mins Read

      South Carolina vs Michigan: Which State Is Better to Live In?

      11 Mins Read

      Are There Cordless Vacuums With Replaceable Batteries?

      12 Mins Read

      How to Use Parabolic SAR (Stop and Reverse) for Day Trading

      10 Mins Read

      Michigan vs Illinois: Which State Is Better to Live In?

      11 Mins Read

      California or Florida: Which State Is Better to Move To?

      11 Mins Read

      Best Front End Development Books to Learn From in 2026

      Top Posts

      How to Use YouTube: A Beginner’s Guide

      July 7, 20266 Views

      Gal Gadot’s Lawyers Spent Six Months on One AI Clause. Then SAG Called Them for Pointers.

      September 2, 20264 Views

      Every iPhone Camera Ranked in 2026 (Best to Worst)

      July 6, 20263 Views
      Stay In Touch
      • Facebook

      Subscribe to Updates

      Get the latest tech news from FooBar about tech, design and biz.

      Most Popular

      How to Change HEIC to JPG on iPhone, Mac, Android and Windows (No Software Needed)

      September 3, 20266 Views

      Gal Gadot’s Lawyers Spent Six Months on One AI Clause. Then SAG Called Them for Pointers.

      September 2, 20265 Views

      How to Spot AI Generated Images in 2026 (The Old Tricks Stopped Working)

      September 3, 20263 Views
      Our Picks

      Instagram Already Has an Off Switch for the Algorithm. It Resets Every Time You Close the App.

      September 12, 2026

      Anthropic Banned Five Groups of Working Scientists. It Says It Cannot Prove Any of Them Meant Harm.

      September 12, 2026

      Google Made Its Best Paid Gemini Feature Free. The Price Is Access to Your Inbox.

      September 12, 2026

      Subscribe to Updates

      Get the latest creative news from FooBar about art, design and business.

      HEICJPG.online - Convert HEIC to JPG online
      Facebook
      • About Us
      • Contact us
      • Privacy Policy
      • Disclaimer
      • Terms and Conditions
      • Editorial Policy
      • Cookie Policy
      © 2026 GeekBlog

      Type above and press Enter to search. Press Esc to cancel.