Display custom post type in hierarchical order with get_terms

Try something like this:

<?php
$parent_terms = get_terms(
    'name_of_your_taxonomy',
    array(
        'parent' => 0, 
    )
);

foreach ( $parent_terms as $parent_term ) {

    $child_terms = get_terms(
        'name_of_your_taxonomy'
        array(
            'child_of' => $parent_term->term_id,
        )
    );
    
    foreach ( $child_terms as $child_term ) {
    
        $args = array(
            'post_type' => 'product',
            'tax_query' => array(
                array(
                    'taxonomy'      => 'name_of_your_taxonomy',
                    'field'         => 'slug',
                    'terms'         => $child_term->slug,
                ),
            ),
        );
    
        $loop = new WP_Query($args);
        
        if ( $loop->have_posts() ) :
        
            while ( $loop->have_posts() ) :
                $loop->the_post();
            
                echo '<h3>' . $parent_term->name . '</h3>';
                echo '<h4>' . $parent_term->name . '</h4>';
                echo get_the_title();

            endwhile;
            wp_reset_postdata();
        endif;
    }
}

Essentially what you’re doing is:

  1. Getting all parent terms;
  2. Looping through the parent terms and getting the children of each term;
  3. Looping through the children terms and getting posts associated with the child.