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.
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 »
Coming up next!
-
Event
Wordcamp Asia 2025
February 20 - 22, 2025 Team Yoast is at Attending, Sponsoring, Yoast Booth Wordcamp Asia 2025! Click through to see who will be there, what we will do, and more! See where you can find us next » -
SEO webinar
Webinar: How to start with SEO (January 16, 2025)
16 January 2025 Learn how to start your SEO journey the right way with our free webinar. Get practical tips and answers to all your questions in the live Q&A! All Yoast SEO webinars »
Thank you Joost :) It was an awesome idea :) and I will donate for your WordPress SEO plugin soon.
Thanks for the post Yoast…nice work …….will start on this one..
Thank for such an awesome tutorial! I did it.
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.
Hey Matt,
Would you mind sharing your code??
Thanks!
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.
Bruce
After reading you post I tried it. The him him XML site maps are excellent. In a mu sites using WordPress SCO I was unable to get a sitemap for each of the subdomains. XML – Sitemaps handle that with flying colors.
The HTML site maps suck!
regards
Ron
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
i somehow managed to make shortcode work. now, how can be posts and categories sorted by alphabet?
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
Thanks for the help Bill
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.
That worked for me too.
@Andy, can you post the code with the WHILE statement removed? Still throwing an error for me.
Ok …can’t post the cod here …
You have to remove the “while” and “endwhile” lines …they look something like this:
And:
They can look a little different depending on your theeme.
Then insert the codeblock and theme-name comment as described in the article.
If you use the theme TwentyTen …youwould end up with a file called “page-sitemap.php” with the following code:
<div class="post" id="post_”>
<a href="/”> »
<?php the_content(__('Read the rest of this page »’,’arthemia’)); ?>
Skribenter
false,
)
);
?>
Sider
”,
‘title_li’ => ”,
)
);
?>
Indlæg
<?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 “”;
}
?>
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
I got it to work using a template instead of a partial. Not sure what I was doing wrong, but oh well, it’s working now.
@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!!
Hmmm … updated the partial with the need code and got a funny result. It seems to be repeating itself down the page. Odd. http://www.quickfireinternet.com/site-map
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
does it still include a link back to it’s own plugin page on the generated sitemap?
Yes. Here is an example:
http://fialkoffconsulting.com/sitemap/
I agree Joost! I will give your method a shot. ;-)
That’s a reason for not using it right there. I hate plugins that do that.
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.
Copy the second line of my post above over the one that looks just like it. I changed some ” to ‘.
Here’s the adjusted code:
echo ''.get_the_title().'';
It is hard to see the difference, but they are different. Works great for me. Thanks Yoast. A few little adjustments with CSS and I have a wonderful sitemap.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()."";
Ah, now I see that i’m not the first one who brought this up and that comment code isn’t properly encoded. :-)
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().”“;
Cutting and pasting the code didn’t work. Trying again with the code command.
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/