diff --git a/app/assets/javascripts/discourse/app/components/sidebar/tags-section.js b/app/assets/javascripts/discourse/app/components/sidebar/tags-section.js
new file mode 100644
index 00000000000..c763d22bfe7
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/components/sidebar/tags-section.js
@@ -0,0 +1,13 @@
+import { cached } from "@glimmer/tracking";
+
+import GlimmerComponent from "discourse/components/glimmer";
+import TagSectionLink from "discourse/lib/sidebar/tags-section/tag-section-link";
+
+export default class SidebarTagsSection extends GlimmerComponent {
+  @cached
+  get sectionLinks() {
+    return this.currentUser.trackedTags.map((trackedTag) => {
+      return new TagSectionLink({ tag: trackedTag });
+    });
+  }
+}
diff --git a/app/assets/javascripts/discourse/app/lib/sidebar/tags-section/tag-section-link.js b/app/assets/javascripts/discourse/app/lib/sidebar/tags-section/tag-section-link.js
new file mode 100644
index 00000000000..ff78f2db4e0
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/lib/sidebar/tags-section/tag-section-link.js
@@ -0,0 +1,25 @@
+export default class TagSectionLink {
+  constructor({ tag }) {
+    this.tag = tag;
+  }
+
+  get name() {
+    return this.tag;
+  }
+
+  get model() {
+    return this.tag;
+  }
+
+  get currentWhen() {
+    return "tag.show tag.showNew tag.showUnread tag.showTop";
+  }
+
+  get route() {
+    return "tag.show";
+  }
+
+  get text() {
+    return this.tag;
+  }
+}
diff --git a/app/assets/javascripts/discourse/app/templates/components/sidebar.hbs b/app/assets/javascripts/discourse/app/templates/components/sidebar.hbs
index 9bbd1203aaf..9c69d3ae7c8 100644
--- a/app/assets/javascripts/discourse/app/templates/components/sidebar.hbs
+++ b/app/assets/javascripts/discourse/app/templates/components/sidebar.hbs
@@ -3,6 +3,10 @@
     <div class="sidebar-container">
       <Sidebar::TopicsSection />
       <Sidebar::CategoriesSection />
+
+      {{#if this.siteSettings.tagging_enabled}}
+        <Sidebar::TagsSection />
+      {{/if}}
     </div>
   </div>
 {{/if}}
diff --git a/app/assets/javascripts/discourse/app/templates/components/sidebar/tags-section.hbs b/app/assets/javascripts/discourse/app/templates/components/sidebar/tags-section.hbs
new file mode 100644
index 00000000000..60c9b0fbbb3
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/templates/components/sidebar/tags-section.hbs
@@ -0,0 +1,23 @@
+<Sidebar::Section
+  @sectionName="tags"
+  @headerRoute="tags"
+  @headerLinkText={{i18n "sidebar.sections.tags.header_link_text"}}
+  @headerLinkTitle={{i18n "sidebar.sections.tags.header_link_title"}} >
+
+  {{#if (gt this.sectionLinks.length 0)}}
+    {{#each this.sectionLinks as |sectionLink|}}
+      <Sidebar::SectionLink
+        @linkName={{sectionLink.name}}
+        @route={{sectionLink.route}}
+        @title={{sectionLink.title}}
+        @content={{sectionLink.text}}
+        @currentWhen={{sectionLink.currentWhen}}
+        @model={{sectionLink.model}}>
+      </Sidebar::SectionLink>
+    {{/each}}
+  {{else}}
+    <Sidebar::SectionMessage>
+      {{i18n "sidebar.sections.tags.no_tracked_tags"}}
+    </Sidebar::SectionMessage>
+  {{/if}}
+</Sidebar::Section>
diff --git a/app/assets/javascripts/discourse/tests/acceptance/sidebar-tags-section-test.js b/app/assets/javascripts/discourse/tests/acceptance/sidebar-tags-section-test.js
new file mode 100644
index 00000000000..bed88ce3d04
--- /dev/null
+++ b/app/assets/javascripts/discourse/tests/acceptance/sidebar-tags-section-test.js
@@ -0,0 +1,229 @@
+import I18n from "I18n";
+
+import { click, currentURL, visit } from "@ember/test-helpers";
+
+import {
+  acceptance,
+  conditionalTest,
+  exists,
+  query,
+  queryAll,
+  updateCurrentUser,
+} from "discourse/tests/helpers/qunit-helpers";
+import { isLegacyEmber } from "discourse-common/config/environment";
+import discoveryFixture from "discourse/tests/fixtures/discovery-fixtures";
+import { cloneJSON } from "discourse-common/lib/object";
+
+acceptance("Sidebar - Tags section - tagging disabled", function (needs) {
+  needs.settings({
+    tagging_enabled: false,
+  });
+
+  needs.user({ experimental_sidebar_enabled: true });
+
+  conditionalTest(
+    "tags section is not shown",
+    !isLegacyEmber(),
+    async function (assert) {
+      await visit("/");
+
+      assert.ok(
+        !exists(".sidebar-section-tags"),
+        "does not display the tags section"
+      );
+    }
+  );
+});
+
+acceptance("Sidebar - Tags section", function (needs) {
+  needs.settings({
+    tagging_enabled: true,
+  });
+
+  needs.user({
+    experimental_sidebar_enabled: true,
+    tracked_tags: ["tag1"],
+    watched_tags: ["tag2", "tag3"],
+    watching_first_post_tags: [],
+  });
+
+  needs.pretender((server, helper) => {
+    server.get("/tag/:tagId/notifications", (request) => {
+      return helper.response({
+        tag_notification: { id: request.params.tagId },
+      });
+    });
+
+    ["latest", "top", "new", "unread"].forEach((type) => {
+      server.get(`/tag/:tagId/l/${type}.json`, () => {
+        return helper.response(
+          cloneJSON(discoveryFixture["/tag/important/l/latest.json"])
+        );
+      });
+    });
+  });
+
+  conditionalTest(
+    "clicking on section header link",
+    !isLegacyEmber(),
+    async function (assert) {
+      await visit("/");
+      await click(".sidebar-section-tags .sidebar-section-header-link");
+
+      assert.strictEqual(
+        currentURL(),
+        "/tags",
+        "it should transition to the tags page"
+      );
+    }
+  );
+
+  conditionalTest(
+    "section content when user does not have any tracked tags",
+    !isLegacyEmber(),
+    async function (assert) {
+      updateCurrentUser({
+        tracked_tags: [],
+        watched_tags: [],
+        watching_first_post_tags: [],
+      });
+
+      await visit("/");
+
+      assert.strictEqual(
+        query(
+          ".sidebar-section-tags .sidebar-section-message"
+        ).textContent.trim(),
+        I18n.t("sidebar.sections.tags.no_tracked_tags"),
+        "the no tracked tags message is displayed"
+      );
+    }
+  );
+
+  conditionalTest(
+    "tag section links for tracked tags",
+    !isLegacyEmber(),
+    async function (assert) {
+      await visit("/");
+
+      assert.strictEqual(
+        queryAll(".sidebar-section-tags .sidebar-section-link").length,
+        3,
+        "3 section links under the section"
+      );
+
+      assert.strictEqual(
+        query(".sidebar-section-link-tag1").textContent.trim(),
+        "tag1",
+        "displays the tag1 name for the link text"
+      );
+
+      assert.strictEqual(
+        query(".sidebar-section-link-tag2").textContent.trim(),
+        "tag2",
+        "displays the tag2 name for the link text"
+      );
+
+      assert.strictEqual(
+        query(".sidebar-section-link-tag3").textContent.trim(),
+        "tag3",
+        "displays the tag3 name for the link text"
+      );
+
+      await click(".sidebar-section-link-tag1");
+
+      assert.strictEqual(
+        currentURL(),
+        "/tag/tag1",
+        "it should transition to tag1's topics discovery page"
+      );
+
+      assert.strictEqual(
+        queryAll(".sidebar-section-tags .sidebar-section-link.active").length,
+        1,
+        "only one link is marked as active"
+      );
+
+      assert.ok(
+        exists(`.sidebar-section-link-tag1.active`),
+        "the tag1 section link is marked as active"
+      );
+
+      await click(".sidebar-section-link-tag2");
+
+      assert.strictEqual(
+        currentURL(),
+        "/tag/tag2",
+        "it should transition to tag2's topics discovery page"
+      );
+
+      assert.strictEqual(
+        queryAll(".sidebar-section-tags .sidebar-section-link.active").length,
+        1,
+        "only one link is marked as active"
+      );
+
+      assert.ok(
+        exists(`.sidebar-section-link-tag2.active`),
+        "the tag2 section link is marked as active"
+      );
+    }
+  );
+
+  conditionalTest(
+    "visiting tag discovery top route for tracked tags",
+    !isLegacyEmber(),
+    async function (assert) {
+      await visit(`/tag/tag1/l/top`);
+
+      assert.strictEqual(
+        queryAll(".sidebar-section-tags .sidebar-section-link.active").length,
+        1,
+        "only one link is marked as active"
+      );
+
+      assert.ok(
+        exists(".sidebar-section-link-tag1.active"),
+        "the tag1 section link is marked as active for the top route"
+      );
+    }
+  );
+
+  conditionalTest(
+    "visiting tag discovery new route for tracked tags",
+    !isLegacyEmber(),
+    async function (assert) {
+      await visit(`/tag/tag1/l/new`);
+
+      assert.strictEqual(
+        queryAll(".sidebar-section-tags .sidebar-section-link.active").length,
+        1,
+        "only one link is marked as active"
+      );
+
+      assert.ok(
+        exists(".sidebar-section-link-tag1.active"),
+        "the tag1 section link is marked as active for the new route"
+      );
+    }
+  );
+
+  conditionalTest(
+    "visiting tag discovery unread route for tracked tags",
+    !isLegacyEmber(),
+    async function (assert) {
+      await visit(`/tag/tag1/l/unread`);
+
+      assert.strictEqual(
+        queryAll(".sidebar-section-tags .sidebar-section-link.active").length,
+        1,
+        "only one link is marked as active"
+      );
+
+      assert.ok(
+        exists(".sidebar-section-link-tag1.active"),
+        "the tag1 section link is marked as active for the unread route"
+      );
+    }
+  );
+});
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 1260392cb0e..3a58d4bb83a 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -4041,6 +4041,10 @@ en:
       unread_count: "%{count} unread"
       new_count: "%{count} new"
       sections:
+        tags:
+          no_tracked_tags: "You are not tracking any tags."
+          header_link_title: "all tags"
+          header_link_text: "Tags"
         categories:
           no_tracked_categories: "You are not tracking any categories."
           header_link_title: "all categories"