# Better navigation for (structured) tags

**URL:** <https://meta.discourse.org/t/better-navigation-for-structured-tags/206826>\
**Category:** UX\
**Created:** [October 22, 2021, 8:15pm UTC](https://meta.discourse.org/t/better-navigation-for-structured-tags/206826 "2021-10-22T20:15:24Z")\
**Posts on this page:** 1\
**Showing post:** 8

<div class="post-metadata">

**Author:** ![Johani](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/johani/32/176920_2.png) [@Johani](https://meta.discourse.org/u/Johani)\
**Post date:** [October 23, 2021, 6:52pm UTC](https://meta.discourse.org/t/better-navigation-for-structured-tags/206826/8 "2021-10-23T18:52:41Z")

</div>

The issue here is a space limitation and not a technical limitation.

> [@ncaming215](#):
>
> We have just over 40 tags

as noted here

> [@IAmGav](#):
>
> with the amount of `parent > child` tags some sites can have, they wont all fit in there

Imagine your experience as a user; if you open a menu on a website, it looks like this.

You’d be overwhelmed, to say the least. Especially since that menu doesn’t have a search function to help you narrow down the results.

So, let’s try to find the middle ground between what you want to do and what users want to experience. How do we do that? We show a limited number of tag groups and indicate that there are more to look at. So, something like this:

 ![hamburger menu tags](https://global.discourse-cdn.com/meta/original/3X/2/f/2fed1702ac0a7f77acdecf39d7b90d4bd4a61692.png)

So, how do you do that?

Here’s the code:

```xml
<script type="text/discourse-plugin" version="0.8">
const MAX_TAGS_TO_SHOW = 20;
const Category = require("discourse/models/category").default;
const siteSettings = api.container.lookup("site-settings:main");
const tagStyle = siteSettings.tag_style;

const getNumberOfTags = (tags, categoryTagsGroups) => {
  let count = 0;
  count = tags.length;
  for (const categoryTagsGroup of categoryTagsGroups) {
    count = count + categoryTagsGroup.tags.length;
  }
  return count;
};

fetch("/tags.json")
  .then(response => response.json())
  .then(data => {
    try {
      const tags = data.tags;
      const hasCategoryTagGroups = data.extras?.categories;

      if (hasCategoryTagGroups) {
        const categoryTagsGroups = data.extras.categories;
        let moreCount = getNumberOfTags(tags, categoryTagsGroups);
        let visibleCount = 0;

        const content = [];
        for (const categoryTagsGroup of categoryTagsGroups) {
          const category = Category.findById(categoryTagsGroup.id);
          const name = category.name;
          const childTags = categoryTagsGroup.tags;
          const childTagNodes = [];

          childTags.forEach((tag, index) => {
            if (visibleCount <= MAX_TAGS_TO_SHOW) {
              childTagNodes.push(
                api.h(
                  "li.tag-link-item",
                  api.h(
                    `a.discourse-tag.tag-link.${tagStyle}`,
                    { href: `/tag/${tag.text}` },
                    tag.text
                  )
                )
              );
              moreCount--;
              visibleCount++;
            }
          });

          if (childTagNodes.length) {
            content.push([
              api.h("li.heading", api.h("span", name)),
              childTagNodes
            ]);
          }
        }

        api.decorateWidget("menu-links:after", helper => {
          if (helper.attrs.name !== "general-links") return;
          return api.h("div.clearfix", [
            api.h("ul.tag-links", [
              api.h("a.categories-link", { href: "/tags" }, [
                "Tags ",
                moreCount ? `(${moreCount} more)...` : ""
              ]),
              content
            ]),
            api.h("hr")
          ]);
        });
      }
    } catch (error) {
      console.error("There's an issue in the hamburger tags theme component");
      console.error(error);
    }
  })
  .catch(console.error);

</script>

```

This goes in the `header` tab of your theme. You can change

```javascript
const MAX_TAGS_TO_SHOW = 20;

```

at the top to the number of tags you want to show.

Then all you need to add is a bit of CSS to style the links. Here’s something basic to get you started.

```scss
.tag-links {
  .heading {
    padding: 0.25em 0.5em;
  }
  .tag-link-item {
    background-color: transparent;
    display: inline-flex;
    align-items: center;
    padding: 0.25em 0.5em;
    width: 50%;
    box-sizing: border-box;
    .tag-link {
      display: inline-flex;
      width: 100%;
      &:hover {
        color: var(--primary);
      }
    }
  }
}

```

Note that the javascript above will respect the tag style set in your site settings. Also, if your site relies very heavily on tags, then you probably don’t need to have the categories visible in that menu. Hiding them would help reduce user confusion.

Also, if you add an expanded tags section, then this link becomes redundant.

 ![default tags link in hamburger menu](https://global.discourse-cdn.com/meta/original/3X/9/7/97b7513bc6a8e9fdd12470edad8ac8312ba2fac0.png)

So, let’s hide them with something like this.

```scss
.panel-body {
  .category-links,
  .categories-separator,
  .widget-link[href="/tags"] {
    display: none;
  }
}

```

Finally, as noted here:

> [@JonathanShaw](#):
>
> The biggest obstacle to what I did, and what you want to do, is that discourse doesn’t preload the tag groups so they’re not available when you need them without making an API request

This data is not passed to the client by default unless you visit the `/tags`, so the code above adds an additional request on the homepage (runs only once per visit). Discourse tries to keep things very simple, so, unless the data is necessary, it won’t load it by default.

I don’t see this being added to Discourse core any time soon. So an extra request is pretty much your only choice here unless you want to write a plugin that runs on the server.

---

_[View the full topic](https://meta.discourse.org/t/better-navigation-for-structured-tags/206826)._
