How to Build a Better Tag Cloud in WordPress

Contributing Editor

Once you’ve defined a great set of tags for your WordPress posts (or pages), you’ll want to display a tag cloud from somewhere in your template. This is normally achieved using the wp_tag_cloud() or wp_generate_tag_cloud() functions which do the hard work for you:

<a href="http://www.mysite.com/tag/word" class="tag-link-7" title="1 topic" style="font-size: 8pt;">word</a>
<a href="http://www.mysite.com/tag/tag" class="tag-link-5" title="2 topics" style="font-size: 14.3pt;">tag</a>
<a href="http://www.mysite.com/tag/phrase" class="tag-link-6" title="4 topics" style="font-size: 22pt;">phrase</a>
<a href="http://www.mysite.com/tag/subject" class="tag-link-4" title="1 topic" style="font-size: 8pt;">subject</a>

Perhaps you’re happy with that. I’m not…

  1. Inline styles? Didn’t we abandon those in 1998?
  2. The classes are pointless. I’ll probably never need to style an individual tag and, even if I do, referencing it by ID is fragile.
  3. I don’t need the fully-qualified URL.

wp_tag_cloud() offers several options but I want more control! As well as addressing the points above, I’d like to assign five or six classes to tags depending on their popularity, e.g. ‘tagged1′ for the least-used tag through to ‘tagged5′ for the most used.

Let’s write a PHP function which returns a customized tag cloud. It can be placed in your theme’s functions.php file (wp-content/themes/<themename>/functions.php) or a plugin.

First, we have our function name which accepts an array of named arguments and sets defaults:

// generate tag cloud
function My_TagCloud($params = array()) {

	extract(shortcode_atts(array(
		'orderby' => 'name',		// sort by name or count
		'order' => 'ASC',		// in ASCending or DESCending order
		'number' => '',			// limit the number of tags
		'wrapper' => '',		// a tag wrapped around tag links, e.g. li
		'sizeclass' => 'tagged',	// the tag class name
		'sizemin' => 1,			// the smallest number applied to the tag class
		'sizemax' => 5			// the largest number applied to the tab class
	), $params));

We now initialize $ret, our returned HTML, and $min and $max — the minimum and maximum number of times a tag is used:

	// initialize
	$ret = '';
	$min = 9999999; $max = 0;

The WordPress get_tags() function is now called. It returns an array of tag objects:

	// fetch all WordPress tags
	$tags = get_tags(array('orderby' => $orderby, 'order' => $order, 'number' => $number));

We now need to determine the the minimum and maximum number of times a tag is used in our site. The following loop sets $min and $max accordingly:

	// get minimum and maximum number tag counts
	foreach ($tags as $tag) {
		$min = min($min, $tag->count);
		$max = max($max, $tag->count);
	}

We can now create our custom tag cloud HTML. We need to loop through all tags a second time and fetch the URL and the link title — a message indicating how many articles use that tag:

	// generate tag list
	foreach ($tags as $tag) {
		$url = get_tag_link($tag->term_id);
		$title = $tag->count . ' article' . ($tag->count == 1 ? '' : 's');

Now for the tricky bit. By default, we want to assign a class ‘tagged1′ for the least-used tag through to ‘tagged5′ for the most used (the class name and numbers can be overridden by setting sizeclass, sizemin and sizemax parameters when calling the function).

We know the minimum and maximum number of times a tag can be used so a little math can determine the class name for us. However, the equation would cause a divide by zero error if each tag was used, say, only once. In that situation, we set the class to just $sizeclass:

		if ($max > $min) {
			$class = $sizeclass . floor((($tag->count - $min) / ($max - $min)) * ($sizemax - $sizemin) + $sizemin);
		}
		else {
			$class = $sizeclass;
		}

We now have enough information to create the HTML for our single tag and end the loop:

		$ret .= 
			($wrapper ? "<$wrapper>" : '') . 
			"<a href="$url" class="$class" title="$title">{$tag->name}</a>" .
			($wrapper ? "</$wrapper>" : '');
	}

Finally, we remove the blog domain URL from $ret, return the value and complete the function block:

	return str_replace(get_bloginfo('url'), '', $ret);
}

The function can be called in any theme file using My_TagCloud();. Arguments can be passed as an associative array, e.g. My_TagCloud(array('orderby'=>'count','order'=>'DESC'));. However, we could also permit content editors to add a tag cloud using a WordPress shortcode, e.g.

// enable [tagcloud] shortcode
add_shortcode('tagcloud', 'My_TagCloud');

In the following example we’ll create a tag cloud within an unordered list:

$tags = OW_Tags(array('wrapper' => 'li'));
if ($tags) {
	echo "<h2>Tags</h2>n<ul class="tagcloud">$tags</ul>n";
}

This produces far tidier HTML code:

<h2>Tags</h2>
<ul class="tagcloud">
<li><a href="/tag/word" class="tagged1" title="1 article">word</a></li>
<li><a href="/tag/tag" class="tagged2" title="2 articles">tag</a></li>
<li><a href="/tag/phrase" class="tagged5" title="4 articles">phrase</a></li>
<li><a href="/tag/subject" class="tagged1" title="1 article">subject</a></li>
</ul>

which is easier to style and maintain CSS:

ul.tagcloud, ul.tagcloud li
{
	font-size: 1em;
	list-style-type: none;
	padding: 0;
	margin: 0;
}

ul.tagcloud li
{
	display: inline;
}

ul.tagcloud a
{
	text-decoration: none;
	padding: 3px 4px;
}

a.tagged1 { font-size: 0.60em; }
a.tagged2 { font-size: 0.80em; }
a.tagged3 { font-size: 1.00em; }
a.tagged4 { font-size: 1.20em; }
a.tagged5 { font-size: 1.40em; }

I hope you find it useful. Please use and adapt the code however you like in your own WordPress projects.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://john.do/ John Saddington

    a “better” tag cloud? people still use those? blargh.

    • http://www.optimalworks.net/ Craig Buckler

      I guess you don’t, John! Admittedly, they’ve fallen a little out of favor but they’re still supported in WP and, used well, they can be very handy. Well I think so, anyway.

  • http://rkwebspace.com Pramod

    Hey Craig,

    I think its better to control & maintain it through CSS. Bravo man, the way you explained is extremely helpful.

    Thanks, Keep Posting :)

    • http://www.optimalworks.net/ Craig Buckler

      Thanks Pramod. I think CSS is a better option too — it makes maintenance far easier.

  • http://www.helenlindley.com/ Helen

    Thanks Craig, personally I like word clouds so I might make use of this soon!

  • John Walt

    Hi craig,
    I am a newbie in wp. I would request you to explain me in “what is meant by wrapper, what is its functionality and how is it useful?