WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have a hierarchical taxonomy called 'geographical locations'. It contains continents on a first level, and then the countries for each one. Example :

- Ireland
- Spain
- Sweden
- Laos
- Thailand
- Vietnam


Using get_terms() I managed to output the full list of terms, but the continents get mixed up with the countries, in one big flat list.

How can I output a hierarchical list like above?

share|improve this question
In case anyone needs a hierarchical CHECKLIST (not the question here but related for people building custom UI for hierarchical taxonomies), the best answer is to use wp_terms_checklist() with your custom taxonomy. – jeremyclarke Feb 23 at 16:27
up vote 12 down vote accepted

Use wp_list_categories with the taxonomy argument, it's built for creating hierarchal category lists but will also support using a custom taxonomy..

Codex Example:
Display terms in a custom taxonomy

If the list comes back looking flat it's possible you just need a little CSS to add padding to the lists, so you can see their hierarchal structure.

share|improve this answer
Could this be reversed? Display children first.. – Arg Geo Mar 6 at 7:57

I realise this is a very old question, but if you have a need to build up an actual structure of terms, this might be a useful method for you:

 * Recursively sort an array of taxonomy terms hierarchically. Child categories will be
 * placed under a 'children' member of their parent term.
 * @param Array   $cats     taxonomy term objects to sort
 * @param Array   $into     result array to put them in
 * @param integer $parentId the current parent ID to put them in
function sort_terms_hierarchicaly(Array &$cats, Array &$into, $parentId = 0)
    foreach ($cats as $i => $cat) {
        if ($cat->parent == $parentId) {
            $into[$cat->term_id] = $cat;

    foreach ($into as $topCat) {
        $topCat->children = array();
        sort_terms_hierarchicaly($cats, $topCat->children, $topCat->term_id);

Usage is as follows:

$categories = get_terms('my_taxonomy_name', array('hide_empty' => false));
$categoryHierarchy = array();
sort_terms_hierarchicaly($categories, $categoryHierarchy);

share|improve this answer

I dont know of any function that does what you want but you can build up something like this:

    <?php $hiterms = get_terms("my_tax", array("orderby" => "slug", "parent" => 0)); ?>
    <?php foreach($hiterms as $key => $hiterm) : ?>
            <?php echo $hiterm->name; ?>
            <?php $loterms = get_terms("my_tax", array("orderby" => "slug", "parent" => $hiterm->term_id)); ?>
            <?php if($loterms) : ?>
                    <?php foreach($loterms as $key => $loterm) : ?>
                        <li><?php echo $loterm->name; ?></li>
                    <?php endforeach; ?>
            <?php endif; ?>
    <?php endforeach; ?>

I haven't tested this but you can see what I'm getting at. What the above code will do is give you only two levels

EDIT: ahh yes you can use wp_list_categories() to do what you after.

share|improve this answer
Actually this is quite useful, as I need to have custom links (with a GET param) on the term links, which doesn't seem possible with the wp_list_categories() way of doing it. – mike23 Apr 13 '11 at 15:04
Yes this method will give more control over your output. But you could do some nice bit of find and replace on the output of wp_list_categories() to add in your GET parameters. Or even better build a filter for the function to add in the bits you want. Don't ask me how you do that as I've not yet been able to get my head around it :( – Brady Apr 13 '11 at 15:19
I'd suggest using a custom category walker with wp_list_categories if you want greater control over the output, it'll make your code much more reusable.. – t31os Apr 13 '11 at 16:09

You can use wp_list_categories(), with a 'taxonomy' argument.

share|improve this answer

As I was looking for the same but to get terms of one post, finally I compiled this, and it works for me.

What it does :
• it gets all terms of a taxonomy name for a specific post.
• for a hierachical taxonomy with two levels (ex: level1:'country' and level2:'cities'), it creates a h4 with the level1 followed by an ul list of level2 and this for all level1 items.
• if the taxonomy is not hierarchical, it will create only an ul list of all items. here is the code (I write it for me so I tried to be as generic as I can but...) :

function finishingLister($heTerm){
    $myterm = $heTerm;
    $terms = get_the_terms($post->ID,$myterm);
        $count = count($terms);
        echo '<h3>'.$myterm;
        echo ((($count>1)&&(!endswith($myterm, 's')))?'s':"").'</h3>';
        echo '<div class="'.$myterm.'Wrapper">';
        foreach ($terms as $term) {
            if (0 == $term->parent) $parentsItems[] = $term;
            if ($term->parent) $childItems[] = $term; 
        if(is_taxonomy_hierarchical( $heTerm )){
            foreach ($parentsItems as $parentsItem){
                echo '<h4>'.$parentsItem->name.'</h4>';
                echo '<ul>';
                foreach($childItems as $childItem){
                    if ($childItem->parent == $parentsItem->term_id){
                        echo '<li>'.$childItem->name.'</li>';
                echo '</ul>';
            echo '<ul>';
            foreach($parentsItems as $parentsItem){
                echo '<li>'.$parentsItem->name.'</li>';
            echo '</ul>';
        echo '</div>';

So finally you call the function with this (obviously, you replace my_taxonomy by yours) : finishingLister('my_taxonomy');

I don't pretend it's perfect but as I said it works for me.

share|improve this answer

I had this problem and none of the answers here worked for me, for one reason or another.

Here is my updated and working version.

function locationSelector( $fieldName ) {
    $args = array('hide_empty' => false, 'hierarchical' => true, 'parent' => 0); 
    $terms = get_terms("locations", $args);

    $html = '';
    $html .= '<select name="' . $fieldName . '"' . 'class="chosen-select ' . $fieldName . '"' . '>';
        foreach ( $terms as $term ) {
            $html .= '<option value="' . $term->term_id . '">' . $term->name . '</option>';

            $args = array(
                'hide_empty'    => false, 
                'hierarchical'  => true, 
                'parent'        => $term->term_id
            $childterms = get_terms("locations", $args);

            foreach ( $childterms as $childterm ) {
                $html .= '<option value="' . $childterm->term_id . '">' . $term->name . ' > ' . $childterm->name . '</option>';

                $args = array('hide_empty' => false, 'hierarchical'  => true, 'parent' => $childterm->term_id); 
                $granchildterms = get_terms("locations", $args);

                foreach ( $granchildterms as $granchild ) {
                    $html .= '<option value="' . $granchild->term_id . '">' . $term->name . ' > ' . $childterm->name . ' > ' . $granchild->name . '</option>';
    $html .=  "</select>";

    return $html;

And usage:

$selector = locationSelector('locationSelectClass');
echo $selector;
share|improve this answer

The following code will generate drop-down with terms, but also can generate any other element/structure by editing the $outputTemplate variable, and editing str_replace lines:

function get_terms_hierarchical($terms, $output = '', $parent_id = 0, $level = 0) {
    //Out Template
    $outputTemplate = '<option value="%ID%">%PADDING%%NAME%</option>';

    foreach ($terms as $term) {
        if ($parent_id == $term->parent) {
            //Replacing the template variables
            $itemOutput = str_replace('%ID%', $term->term_id, $outputTemplate);
            $itemOutput = str_replace('%PADDING%', str_pad('', $level*12, '&nbsp;&nbsp;'), $itemOutput);
            $itemOutput = str_replace('%NAME%', $term->name, $itemOutput);

            $output .= $itemOutput;
            $output = get_terms_hierarchical($terms, $output, $term->term_id, $level + 1);
    return $output;

$terms = get_terms('taxonomy', array('hide_empty' => false));
$output = get_terms_hierarchical($terms);

echo '<select>' . $output . '</select>';  
share|improve this answer

Be sure that hierarchical=true is passed to your get_terms() call.

Note that hierarchical=true is the default, so really, just be sure that it hasn't been overridden to be false.

share|improve this answer
Hi Chip, yes 'hierarchical' is 'true' by default. – mike23 Apr 13 '11 at 14:37
Can you provide a link to a live example of the output? – Chip Bennett Apr 13 '11 at 14:44
This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post. – brasofilo Feb 11 '13 at 20:04
Commenting on an answer left almost two years ago? Really? Actually, it is a proposed answer, even if worded as a question. Shall I edit it to be a statement, rather than a question? – Chip Bennett Feb 11 '13 at 23:22
get_terms() will return a full list of the terms (as the OP stated) but not a hierarchical list showing parent / child relationship as requested. – jdm2112 Mar 17 at 5:00

Here I have four level dropdown select list with hidden first item

<select name="lokalizacja" id="ucz">
            <option value="">Wszystkie lokalizacje</option>
            $excluded_term = get_term_by('slug', 'podroze', 'my_travels_places');
            $args = array(
                'orderby' => 'slug',
                'hierarchical' => 'true',
                'exclude' => $excluded_term->term_id,
                'hide_empty' => '0',
                'parent' => $excluded_term->term_id,
            $hiterms = get_terms("my_travels_places", $args);
            foreach ($hiterms AS $hiterm) :
                echo "<option value='".$hiterm->slug."'".($_POST['my_travels_places'] == $hiterm->slug ? ' selected="selected"' : '').">".$hiterm->name."</option>\n";

                $loterms = get_terms("my_travels_places", array("orderby" => "slug", "parent" => $hiterm->term_id,'hide_empty' => '0',));
                if($loterms) :
                    foreach($loterms as $key => $loterm) :

                    echo "<option value='".$loterm->slug."'".($_POST['my_travels_places'] == $loterm->slug ? ' selected="selected"' : '').">&nbsp;-&nbsp;".$loterm->name."</option>\n";

                    $lo2terms = get_terms("my_travels_places", array("orderby" => "slug", "parent" => $loterm->term_id,'hide_empty' => '0',));
                    if($lo2terms) :
                        foreach($lo2terms as $key => $lo2term) :

                        echo "<option value='".$lo2term->slug."'".($_POST['my_travels_places'] == $lo2term->slug ? ' selected="selected"' : '').">&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;".$lo2term->name."</option>\n";



        <label>Wybierz rodzaj miejsca</label>
        <select name="rodzaj_miejsca" id="woj">
            <option value="">Wszystkie rodzaje</option>
            $theterms = get_terms('my_travels_places_type', 'orderby=name');
            foreach ($theterms AS $term) :
                echo "<option value='".$term->slug."'".($_POST['my_travels_places_type'] == $term->slug ? ' selected="selected"' : '').">".$term->name."</option>\n";                   
share|improve this answer
Please explain why that could solve the problem. – toscho Mar 25 '13 at 10:00
I think the logic is that it's a related problem. I found this post trying to figure out how to get a category-style hierarchical checklist and am tempted to add an answer here now that I've figured it out. I won't though because as you point out it doesn't answer the OQ. – jeremyclarke Feb 23 at 16:26

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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