I would like to have a Top 10 Tags list in descending order. I'm able to list and limit 10 tags used, but the order parameters are limited to name, group, and date essentially.

  • just as an FYI, I did set a count but trying order(count desc) doesn't do a thing. :P Because I don't think that's "allowed"?
    – lealea
    Jul 14, 2014 at 22:33
  • 1
    count isn't a property of craft.tags. I'm working on it though, after your last question about tag clouds!
    – Matt Stein
    Jul 14, 2014 at 23:02
  • If you aren't in a hurry lea, you better wait for @matt's plugin. You can do lots of things with twig, but it's a template language and such thing like my code below are actually considered bad practice!
    – carlcs
    Jul 14, 2014 at 23:29

5 Answers 5


This is based on Matt's answer to your previous question. Likewise this should better be used within cache tags!

This is untested, but it hopefully works out for you.



This is still untested but it came to my ears, that it could make problems to assign variables as array keys. That's why I changed the code to use objects instead. If anyone uses this code, I'd be happy to hear if it does actually work!

{# Get all tags #}
{% set allTags = craft.tags.find() %}

{# Define object outside the for loop #}
{% set weightedTags = [] %}

{# Iterate over all tags #}
{% for tag in allTags %}

    {# Get total number of tag occurrences in entries belonging to 'blog' section #}
    {% set count = craft.entries.section('blog').relatedTo(tag).total() %}

    {# Save tag id and count to our weightedTags object #}
    {% set weightedTags = weightedTags|merge([{ tagId: tag.id, tagCount: count }]) %}

{% endfor %}

{# Sort, reverse-sort and pick top 10 elements of the array #}
{% set weightedTags = weightedTags|sort|reverse|slice(0, 10) %}

{# To print the top 10 list iterate over our weightedTags array #}
{% for tagObject in weightedTags %}

    {# Get tag object for each tagId #}
    {% set tagEntry = craft.tags.id(tagObject.tagId).first() %}

    {# Return the list item #}
    <a href="/entries/{{ tagEntry.slug }}" data-count="{{ tagObject.tagCount }}">{{ tagEntry.name }}</a>

{% endfor %}
  • 1
    This seems to work if you set tagCount as first param in your weightedTags.
    – Jan_dh
    May 15, 2018 at 6:55

I couldn't quite get Carls answer to work (the "weightedTags"-array wasn't properly sorted), so I went and wrote a quick variable and service for achieving what I was after.

First off, the super-simple variable:

public function getPopularTags($limit = NULL, $tagGroup = 'tags') {
    return craft()->PLUGIN_SERVICENAME->getPopularTags($limit, $tagGroup);

Simply replace "PLUGIN_SERVICENAME" with whatever your plugin and service is called, per example "myPlugin_tagFinder"

And the service is almost as simple:

namespace Craft;
class PLUGIN_SERVICENAMEService extends BaseApplicationComponent
  public function getPopularTags($limit, $tagGroup = 'tags')
    $sql = craft()->db->createCommand();
    $sql->select('tags.*, i18n.slug');
    $sql->from(['taggroups tagGroups', 'tags tags', 'relations relations', 'elements elements', 'elements_i18n i18n']);
                 "tagGroups.handle = '$tagGroup'",
                 'tags.groupId = tagGroups.id',
                 'relations.targetId = tags.id',
                 'elements.id = relations.sourceId',
                 "elements.enabled = '1'",
                 'i18n.elementId = tags.id']);
    $sql->order('COUNT(elements.id) DESC');
    if (!is_null($limit) && is_numeric($limit)) {

    return TagModel::populateModels($sql->queryAll());

Again, replace "PLUGIN_SERVICENAME" with whatever your plugin and service is called, per example "MyPlugin_tagFinderService"

Once that is done (caveat: And your plugin is, naturally, installed/activated) you can get the tags you require from your template simply by calling:

{%set tags = craft.myPlugin.getPopularTags() %}

From there on you'll loop through them just as if you fetched them through the craft.tags.find()-methodology.

  • Caveats: The method above flat out ignores which field the relation is built through, and also does not bother to look at locales. If you need to customize it to take either into account, feel free to do so or even to poke me for assistance. May 29, 2017 at 13:56
  • This is a much better answer!
    – carlcs
    May 29, 2017 at 14:27

Have had success using this plugin: https://github.com/philbirnie/CraftPopularTags


I was working the last days on this query for craft 3.

I want to share it with u.

With this query u get the most used tags connected to other elements (wich are not revisions or drafts).

U also get the actual count of usages for every tag.

Hope it helps the next one.

   $query = (new Query())
            '{{relations}}.targetId as id',
            'COUNT({{relations.targetId}}) as count',
            '{{content}}.title as title'
        ->leftJoin('tags', '{{relations}}.targetId={{tags}}.id')
        ->leftJoin('content', '{{content}}.elementId={{tags}}.id')
        ->leftJoin('elements', '{{elements}}.id={{tags}}.id')
        ->leftJoin('elements source', '{{source}}.id={{relations}}.sourceId')
            '{{elements}}.enabled' => 1,
            '{{source}}.enabled' => 1,
            '{{source}}.revisionId' => NULL,
            '{{source}}.draftId' => NULL,
        ->groupBy('id, title')
        ->orderBy( 'count DESC, title ASC')


    if (is_numeric($limit) && !is_null($limit)) {

    $results = $query->all();

    foreach ($results as $key => $value) {
        $id = $value['id'];
        $results[$key] = Tag::find()->id($id)->one();

    return $results;

Also needed this one, but the example from carlcs resulted in a timeout in my browser due to the large dataset (20.000 entries, 10.000 tags). I got it fixed by using a custom query, which returned the top 10 list in 3-4 seconds;

{% set query = craft.query()
                'relations.targetId as id',
                'COUNT(relations.targetId) as count',
                'content.title as title'
        .leftJoin('tags', 'relations.targetId=tags.id')
        .leftJoin('content', 'content.elementId=tags.id')
        .leftJoin('elements', 'elements.id=tags.id')
        .leftJoin('elements source', 'source.id=relations.sourceId')
                'elements.enabled': 1,
                'source.enabled': 1,
                'source.revisionId': null,
                'source.draftId': null
        .groupBy('id, title')
        .orderBy('count DESC, title ASC')
        .limit(10) %}

{% set results = query.all() %}

{% for result in results %}
    {% set id = result['id'] %}
    {% set title = result['title'] %}
    {% set count = result['count'] %}
{% endfor %}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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