HTML Sitemap for WordPress

An HTML sitemap (as opposed to an XML sitemap) is often mentioned as being useful for SEO. They certainly are if you use them wisely (and especially Bing seems to like them at times), but I like them even more for the fact that users like them a lot.

HTML sitemap for wordpress, as shown on Yoast

There’s plenty of plugins out there that will help you make an HTML sitemap. It’s not a feature in my WordPress SEO plugin just yet, but it might become one. The issue is though, that in most cases, you’ll want to do specific things with your sitemaps, include or exclude certain pages / post types, show certain taxonomies, etc. That’s why I tend to advise people to create a Sitemap Page template in their theme and use that.

In fact, I advise you to use a theme partial, so you can reuse your HTML sitemap template on your WordPress 404 error pages too. To do that, follow these steps: first of all, create a partials folder within your theme folder. In that partials folder, create a file called sitemap.php.

Paste the following code into that file and adapt as needed for your site:

<h2>Authors</h2>
<ul>
<?php wp_list_authors( array(
  'exclude_admin' => false
) ); ?>
</ul>

<h2>Pages</h2>
<ul>
<?php
wp_list_pages( array( 
  'exclude' => '',
  'title_li' => '',
) ); ?>
</ul>

<h2>Posts</h2>
<?php 
$cats = get_categories('exclude=');
foreach ($cats as $cat) {
  echo '<h3>' . $cat->cat_name . '</h3>';
  echo '<ul>';
  query_posts('posts_per_page=-1&cat=' . $cat->cat_ID);
  while(have_posts()) {
    the_post();
    $category = get_the_category();
    if ($category[0]->cat_ID == $cat->cat_ID) {
      echo '<li><a href="' . get_permalink() . '">' . get_the_title() . '</a></li>'; 
    }
  }
  echo '</ul>';
}

Now, wherever you need that HTML sitemap “bit” in your WordPress theme, use this:

<?php get_template_part('/partials/sitemap'); ?>

HTML Sitemap WordPress Page Template

You could do this, for instance, for a sitemap page template. To create a sitemap page template using this code, duplicate your page.php file and rename it to page-sitemap.php. Now open it, and below the call to the_content(); that’s in there, add the get_template_part() bit mentioned above. Now go to the first line of the file, and after the opening <?php (but before get_header()), add this comment:

/*
Template Name: Sitemap Page
*/

That’ll make WordPress recognize it as an HTML Sitemap template. This will allow you to write some introductory text for your HTML sitemap, after which the full sitemap shows.

Add Custom Post Types to your HTML Sitemap

Update: If you need custom post types in your HTML sitemap too, add this code underneath the other code:

<?php
foreach( get_post_types( array('public' => true) ) as $post_type ) {
  if ( in_array( $post_type, array('post','page','attachment') ) ) {
    continue;
  }
  
  $pt = get_post_type_object( $post_type );

  echo '<h2>' . $pt->labels->name . '</h2>';
  echo '<ul>';
  query_posts('post_type=' . $post_type . '&posts_per_page=-1');
  while( have_posts() ) {
    the_post();
    echo '<li><a href="' . get_permalink() . '">' . get_the_title() . '</a></li>';
  }
  echo '</ul>';
}
?>

Read more: Why you should buy Yoast SEO Premium »

Discussion (63)

  • Thanks! This was exactly what I was looking for. I even managed to hack the code to display my custom post types in the site map.

  • I am still having the loop issue. Any help would be great:

    http://blog.firstusahomeloans.info/sitemap/

  • Thanks for the great tip. It made short work of giving me exactly what I wanted in a site map. I created a shortcode with the code you provided for the template, then just used that in the body of a new page, no new template necessary.


    // Call Sitemap Data
    add_shortcode('sitemap','getsitemap');
    function getsitemap() {
    echo
    get_template_part('/partials/sitemap');
    ;
    }

  • Hi all,

    Has anyone tried Unlimited Sitemap Generator?
    http://www.xml-sitemaps.com/standalone-google-sitemap-generator.html

    I’m debating HTML Sitemap for WordPress vs. Unlimited Sitemap Generator. USG seem less troubling. Optional extras along with XML and HTML include: images, video, mobile and news sitemap.

  • Thanks this has really helped, as has many of your other articles.

  • great post joost!
    i tried creating shortcode, but it’s not working for soe reason
    code of page is
    <?php
    function anime_list() {
    $cats = get_categories('exclude=156');
    foreach ($cats as $cat) {
    echo '’;
    echo ‘‘.$cat->cat_name.’‘;
    query_posts(‘posts_per_page=-1&cat=’.$cat->cat_ID);
    while(have_posts()) {
    the_post();
    $category = get_the_category();
    // Only display a post link once, even if it’s in multiple categories
    if ($category[0]->cat_ID == $cat->cat_ID) {
    echo ‘‘.get_the_title().’‘;
    }
    }
    }
    }

    add_shortcode(‘anime-list’, ‘anime_list’);
    ?>
    so, i tried inserting shortcode [anime-list] into created page, but it’s not showing what it should. it’s just showing [anime-list] on page. hope some one can help me with this one

  • What if you have like 5000+ pages? Is it still a good idea to create a html sitemap?

  • I’ve had the infinite loop problem too. I’m trying to learn PHP coding, and understand the loop statement is causing the infinite loop, but I’m sure it was added there for a reason. So what’s missing to stop the loop?

  • Thanks for the post Yoast. I’ll keep an eye out for your plugin and the html function, I’d really like to see it function.

  • Sorry I entered the wrong code in the my last post. The code in the sitemap.php file is –

    Authors

    false,
    )
    );
    ?>

    Pages

    ”,
    ‘title_li’ => ”,
    )
    );
    ?>

    Posts

    <?php
    // Add categories you'd like to exclude in the exclude here
    $cats = get_categories('exclude=');
    foreach ($cats as $cat) {
    echo "”.$cat->cat_name.””;
    echo “”;
    query_posts(‘posts_per_page=-1&cat=’.$cat->cat_ID);
    while(have_posts()) {
    the_post();
    $category = get_the_category();
    // Only display a post link once, even if it’s in multiple categories
    if ($category[0]->cat_ID == $cat->cat_ID) {
    echo ‘”.get_the_title().”;‘;
    }
    }
    echo “”;
    echo “”;
    }
    ?>

    And the code in the page-sitemap.php file is –

    <div id="post-“>

    <?php the_content('Read the rest of this page’); ?>

    Pages: ‘, ‘after’ => ”, ‘next_or_number’ => ‘number’)); ?>

  • I’m a complete wordpress newbie and not sure what I’m doing wrong but I don’t have a sitemap – am I missing something really obvious? What location should I put the sitemap.php file in? Your help would be very much appreciated. Below are the steps I’ve taken.

    1. created partials folder under the images folder
    2. created sitemap.php file and put this file in the partials folder with the code below:
    Authors

    false,
    )
    );
    ?>

    Pages

    ”,
    ‘title_li’ => ”,
    )
    );
    ?>

    Posts

    <?php
    // Add categories you'd like to exclude in the exclude here
    $cats = get_categories('exclude=');
    foreach ($cats as $cat) {
    echo "”.$cat->cat_name.””;
    echo “”;
    query_posts(‘posts_per_page=-1&cat=’.$cat->cat_ID);
    while(have_posts()) {
    the_post();
    $category = get_the_category();
    // Only display a post link once, even if it’s in multiple categories
    if ($category[0]->cat_ID == $cat->cat_ID) {
    echo ‘”.get_the_title().”;‘;
    }
    }
    echo “”;
    echo “”;
    }
    ?>

    3. Created the sitemap.php file and code below

    Authors

    false,
    )
    );
    ?>

    Pages

    ”,
    ‘title_li’ => ”,
    )
    );
    ?>

    Posts

    <?php
    // Add categories you'd like to exclude in the exclude here
    $cats = get_categories('exclude=');
    foreach ($cats as $cat) {
    echo "”.$cat->cat_name.””;
    echo “”;
    query_posts(‘posts_per_page=-1&cat=’.$cat->cat_ID);
    while(have_posts()) {
    the_post();
    $category = get_the_category();
    // Only display a post link once, even if it’s in multiple categories
    if ($category[0]->cat_ID == $cat->cat_ID) {
    echo ‘”.get_the_title().”;‘;
    }
    }
    echo “”;
    echo “”;
    }
    ?>

  • Nice post, keep share

  • I got this working just fine.

    You can see my sitemap here: http://billbennett.co.nz/sitemap/

    and download the code from:

    http://shorttext.com/cmk9hzzinwg

  • Removing the WHILE statement throws an error. If someone has successful removed it and stopped the loop problem, please post entire syntax … I would greatly appreciate it.

  • @yoast thanks for this cool snippit. Just added it to the WP template I use for our webshop. Am curious about the results!

    @all About the infinite loop thingy: make sure you remove the WHILE loop from sitemap page. That takes care of the loop problem.

  • I’m having the same infinite loop problem as Andy and Lou. Does anyone know how to solve this?

  • Great stuff again.

    1. Why there is not a link on categories?
    2. Is there any best praxis to nofollow link to the Sitemap page or No-index this page?

  • Does anyone know how to modify this code so that the category hierarchy is maintained? Some of my categories look out of place without the parent category shown as a parent.

    Example:
    Category 1
    post
    post
    Category 2
    Category child 1
    post
    post
    Category child 2
    post
    post

  • I emailed you about this a while ago Joost, and its nice to see you have acted on my comments :-) I am going to implement this on my philosophy blog very soon, the server is playing up at the moment not :-(

    Thanks Joost, very useful!

  • I tried to implement this using a partial and just get a completely blank page. Any thoughts?

    http://www.riverwoodphotography.com/sitemap

  • @Andy. I got the same thing.

  • Hi …I’ve got a (almost) infinite loop problem with this one …am I the only one?

    :-) Andy

  • Great post, certainly saves time having to hand code the pages in there, would also be good to have max pages featured and as a wishlist, to have so you could only feature certain catagories. Still very handy!!

  • Hi Joost,
    I’ve been using this plugin for several years. It is customizable and easy to use (easier than straight coding for me):

    http://www.dagondesign.com/articles/sitemap-generator-plugin-for-wordpress/

    I’d love to get your feedback.

    Thanks for your advice!

    -Josh

  • I hit your sitemap page quite a bit a couple of weeks ago so I could implement it in the new site we launched, funny you come out with it now :)

    I’ve noticed you didn’t use any titles in the links, any reason for that?

  • For those of you having problems: I just updated the code in the post to use the right quotes around get_the_title. Thanks to everyone who noticed!

  • Great contribution! The truth is that it simplifies the accessibility to the search engines. Thank you Joost

  • Yeah … I am getting the same error as Bill Bennett and Cindi. @Bill or Cindi, if you come up with the fix, please re-post. Thanks.

  • Question: I was thinking about creating an archives page for my blog. Would having an HTML sitemap along with the archives be bad for SEO, perhaps viewed as duplicate content? I’m not sure.

  • Good post Joost, I like the partial idea for easy reuse. :-) One small note: it seems that you forgot to escape the ” where you echo the list item.


    echo "".get_the_title()."";

  • I’m getting a similar error:

    Parse error: syntax error, unexpected T_CONSTANT_ENCAPSED_STRING, expecting ‘,’ or ‘;’ in

    public_html/wp-content/themes/Raupo/partials/sitemap.php on line 39

    the line is:
    echo ““.get_the_title().”“;

  • Nice post, Joost. I just implemented this in my Thematic template. =) I’d been using Dagon Sitemap for a while, but I prefer this much more.

    Awesome.

  • I had to make some adjustments to the code. I was getting unexpected T_CONSTANT_ENCAPSED_STRING error.
    echo "".get_the_title()."";

    I’m not a coder, but this worked for me.
    echo ''.get_the_title().'';

  • Thank you
    Zibbi

  • Thanks for this post Yoast!
    I used the pages piece of your code to create a WP shortcode that I placed in a WP page called ‘Sitemap’.

    // jlc 2011.02.08 | Shortcodes for HTML Sitemap
    if ( ! function_exists( ‘sitemap_page’ ) ) {
    function sitemap_page() {
    // Add social bookmarking URL with button
    echo ”;
    echo ”;
    // Add pages you’d like to exclude in the exclude here
    wp_list_pages(
    array(
    ‘exclude’ => ”,
    ‘title_li’ => ”,
    )
    );
    echo ”;

    // Close the
    echo ”;
    echo ”;

    }
    }
    add_shortcode(‘sitemap’, ‘sitemap_page’);
    //

  • Yost
    You continually amaze me with amount of content you turn out with out sacrificing quality.

    I’ve had a chance to try this yet but I’m using a multi-site setup using sub-domains.my question is do you think this will automatically search through all the subdomains? Or is there a way to modify the program make it go deep.?

    I am using your WP SCO plug-in but not having any luck with the generate XML site part.

    Thanks a lot for your country continuing contributions.
    best regards
    Ron

  • It is all new for me, but thank you so much for sharing. I am looking forward to learn more about SEO and your plugin.

  • Appreciate your posts as always! And I’m enjoying your SEO plugin for WP.

  • Thanks for the post Yoast! Im using Headway as a theme and found it very touchy. Do you know if I’m better off using a Plugin or can I still create this file? Really looking forward to your plug-in.

  • Thanks for considering our suggestion.

    http://wordpress.org/support/topic/plugin-wordpress-seo-by-yoast-suggestion-sitemap-page

    By the way, we consider crucial for any website to have a sitemap page. Also for SEO, helps to have indexed all pages for site, which is not bad :)

  • Indeed, I had the same problems. I have just pasted the sitemap-code in my page-sitemap.php (after the div content).

  • Sadly, this doesn’t seem to work for me. Any ideas what could be going wrong?

    I can use the page-sitemap as a template and build my sitemap page, but it doesn’t bring in the actual sitemap. I absolutely do have get_template_part below the_content.

  • Good post. I had some difficulties to get it in a page, but I managed to create my own html-sitemap for my blog: http://www.dailybits.be/about/sitemap/