I'd like to make a custom walker that works with wp_list_categories to generate a menu of nested uls using my category, sub-category, and sub-sub-category(?) structure. Any category that has sub-categories can't be a link so it can be clicked in order to reveal the sub-categories (or sub-sub-categories).

Here's what I want (only items with a πŸ”— should be links):

    T-shirts πŸ”—
    Longsleeves πŸ”—
    Hoodies πŸ”—
    Raincoats πŸ”—
  Boots πŸ”—
  Formal πŸ”—
  Shorts πŸ”—
  Longs πŸ”—
Bags πŸ”—

See also this CodePen demo with the hard-coded HTML I'd like to generate, ideally with a custom walker for wp_list_categories.

  1. I need to show a list of categories and subcategories in a series of nested uls.
  2. If a given category has subcategories, that category can't display as a link (a div or similar instead).
  3. If a given category does not have any subcategories, that category will display as a link.

My menu system is at most three levels deep, and categories without descendants could be anywhere. The reason I'd really like to use wp_list_categories is so my editors have the flexibility to reorder categories. If I hard-coded in each top-level category they can't reorder them freely using WP's built-in category editor.

Goal: Right now, wp_list_categories does output my categories properly. I just need to figure out how to override the code that is making category titles with subcategories be links. I need them to just be plain text.

I'm trying to make a custom walker myself but can't seem to figure out where it decides if a category is a sub (or a "sub of a sub") in order to omit links where they're not wanted.

1 Answer 1


If you say the wp_list_categories output format is ok to you and all you need is only to remove the links from parent categories, try the following code (you can add it to your functions.php):

add_filter( 'wp_list_categories', 'sanitize_list_categories_links', 10, 2 );
function sanitize_list_categories_links( $list, $args ) {
    if ( isset( $args['sanitize_links'] ) && $args['sanitize_links'] ) {
        $list = preg_replace( '/(<li(?:|\s[^>]*)>)<a(?:|\s[^>]*)>(.*?)<\/a>(\s*<ul(?:|\s[^>]*)>)/', '$1$2$3', $list );
    return $list;

and then add an extra parameter to your wp_list_categories arguments list:

wp_list_categories( array( ..., 'sanitize_links' => 1 ) );

This regex will remove <a ...> and </a> HTML tags from <li ...><a ...>Category name</a> string if this string is followed with <ul ...> HTML tag meaning the category has at least one child and left those tags untouched otherwise.


To get the HTML markup similar to your codepen example, you can use

add_filter( 'wp_list_categories', 'sanitize_list_categories_links', 10, 2 );
function sanitize_list_categories_links( $list, $args ) {
    if ( isset( $args['sanitize_links'] ) && $args['sanitize_links'] ) {
        $list = preg_replace( '/(<li(?:|\s[^>]*)>)<a(?:|\s[^>]*)>(.*?)<\/a>(\s*<ul(?:|\s[^>]*)>)/',
                              '$1<div class="title">$2</div>$3', $list );
    return $list;
  • This achieved what I wanted and I've accepted it. RegEex gives me a headache if I so much as think about it but this is a really smart way to avoid having to make a custom walker. Thank you very much! :)
    – EdA
    Commented Nov 19, 2020 at 1:19
  • @EdA Check the update, it should replace <a>...</a> with <div>...</div> on elements that have at least one child. Commented Nov 19, 2020 at 1:26

Your Answer

By clicking β€œPost Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.