diff --git a/app/graphql/types/namespace_type.rb b/app/graphql/types/namespace_type.rb
index 2522bae08a2ae1fbd6013efd0b66666f286762ff..f9bd4e300cec1df64f350cb00fd9f3fbb749f09d 100644
--- a/app/graphql/types/namespace_type.rb
+++ b/app/graphql/types/namespace_type.rb
@@ -107,6 +107,13 @@ class NamespaceType < BaseObject
       extension(::Gitlab::Graphql::Limit::FieldCallCount, limit: 1)
     end
 
+    field :sidebar,
+      Types::Namespaces::SidebarType,
+      null: true,
+      description: 'Data needed to render the sidebar for the namespace.',
+      method: :itself,
+      alpha: { milestone: '17.6' }
+
     markdown_field :description_html, null: true
 
     def achievements_path
diff --git a/app/graphql/types/namespaces/sidebar_type.rb b/app/graphql/types/namespaces/sidebar_type.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b95f469b517593b0214d3b66a54e20fc7b2b2ebc
--- /dev/null
+++ b/app/graphql/types/namespaces/sidebar_type.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Types
+  module Namespaces
+    class SidebarType < BaseObject # rubocop:disable Graphql/AuthorizeTypes -- parent is already authorized
+      graphql_name 'NamespaceSidebar'
+
+      alias_method :namespace, :object
+
+      field :open_issues_count,
+        GraphQL::Types::Int,
+        null: true,
+        description: 'Number of open issues of the namespace.'
+
+      field :open_merge_requests_count, # rubocop:disable GraphQL/ExtractType -- no need to extract these into a field named "open"
+        GraphQL::Types::Int,
+        null: true,
+        description: 'Number of open merge requests of the namespace.'
+
+      def open_issues_count
+        case namespace
+        when ::Group
+          group_open_issues_count
+        when ::Namespaces::ProjectNamespace
+          namespace.project.open_issues_count(context[:current_user])
+        end
+      end
+
+      def open_merge_requests_count
+        case namespace
+        when Group
+          ::Groups::MergeRequestsCountService.new(namespace, context[:current_user]).count
+        when ::Namespaces::ProjectNamespace
+          namespace.project.open_merge_requests_count
+        end
+      end
+
+      def group_open_issues_count
+        ::Groups::OpenIssuesCountService.new(namespace, context[:current_user], fast_timeout: true).count
+      rescue ActiveRecord::QueryCanceled => e # rubocop:disable Database/RescueQueryCanceled -- used with fast_read_statement_timeout to prevent this count from slowing down the rest of the request
+        Gitlab::ErrorTracking.log_exception(e, group_id: namespace.id, query: 'group_sidebar_issues_count')
+
+        nil
+      end
+    end
+  end
+end
+
+Types::Namespaces::SidebarType.prepend_mod
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 1a654d5e83a5f2bea91a62b03c1d4ad19aa3204f..db6e74ee44560d00c6727f621c203bab0fcbac57 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -23979,6 +23979,7 @@ GPG signature for a signed commit.
 | <a id="groupsecuritypolicyproject"></a>`securityPolicyProject` | [`Project`](#project) | Security policy project assigned to the namespace. |
 | <a id="groupsharewithgrouplock"></a>`shareWithGroupLock` | [`Boolean`](#boolean) | Indicates if sharing a project with another group within this group is prevented. |
 | <a id="groupsharedrunnerssetting"></a>`sharedRunnersSetting` | [`SharedRunnersSetting`](#sharedrunnerssetting) | Shared runners availability for the namespace and its descendants. |
+| <a id="groupsidebar"></a>`sidebar` **{warning-solid}** | [`NamespaceSidebar`](#namespacesidebar) | **Introduced** in GitLab 17.6. **Status**: Experiment. Data needed to render the sidebar for the namespace. |
 | <a id="groupstandardroles"></a>`standardRoles` **{warning-solid}** | [`StandardRoleConnection`](#standardroleconnection) | **Introduced** in GitLab 17.4. **Status**: Experiment. Standard roles available for the instance, available only for self-managed. |
 | <a id="groupstats"></a>`stats` | [`GroupStats`](#groupstats) | Group statistics. |
 | <a id="groupstoragesizelimit"></a>`storageSizeLimit` | [`Float`](#float) | The storage limit (in bytes) included with the root namespace plan. This limit only applies to namespaces under namespace limit enforcement. |
@@ -28856,6 +28857,7 @@ Product analytics events for a specific month and year.
 | <a id="namespacerootstoragestatistics"></a>`rootStorageStatistics` | [`RootStorageStatistics`](#rootstoragestatistics) | Aggregated storage statistics of the namespace. Only available for root namespaces. |
 | <a id="namespacesecuritypolicyproject"></a>`securityPolicyProject` | [`Project`](#project) | Security policy project assigned to the namespace. |
 | <a id="namespacesharedrunnerssetting"></a>`sharedRunnersSetting` | [`SharedRunnersSetting`](#sharedrunnerssetting) | Shared runners availability for the namespace and its descendants. |
+| <a id="namespacesidebar"></a>`sidebar` **{warning-solid}** | [`NamespaceSidebar`](#namespacesidebar) | **Introduced** in GitLab 17.6. **Status**: Experiment. Data needed to render the sidebar for the namespace. |
 | <a id="namespacestoragesizelimit"></a>`storageSizeLimit` | [`Float`](#float) | The storage limit (in bytes) included with the root namespace plan. This limit only applies to namespaces under namespace limit enforcement. |
 | <a id="namespacesubscriptionhistory"></a>`subscriptionHistory` **{warning-solid}** | [`GitlabSubscriptionHistoryConnection`](#gitlabsubscriptionhistoryconnection) | **Introduced** in GitLab 17.3. **Status**: Experiment. Find subscription history records. |
 | <a id="namespacetimelogcategories"></a>`timelogCategories` **{warning-solid}** | [`TimeTrackingTimelogCategoryConnection`](#timetrackingtimelogcategoryconnection) | **Introduced** in GitLab 15.3. **Status**: Experiment. Timelog categories for the namespace. |
@@ -29168,6 +29170,16 @@ four standard [pagination arguments](#pagination-arguments):
 | <a id="namespacecommitemailnamespace"></a>`namespace` | [`Namespace!`](#namespace) | Namespace. |
 | <a id="namespacecommitemailupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp the namespace commit email was last updated. |
 
+### `NamespaceSidebar`
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="namespacesidebaropenepicscount"></a>`openEpicsCount` | [`Int`](#int) | Number of open epics of the namespace. |
+| <a id="namespacesidebaropenissuescount"></a>`openIssuesCount` | [`Int`](#int) | Number of open issues of the namespace. |
+| <a id="namespacesidebaropenmergerequestscount"></a>`openMergeRequestsCount` | [`Int`](#int) | Number of open merge requests of the namespace. |
+
 ### `NestedEnvironment`
 
 Describes where code is deployed for a project organized by folder.
diff --git a/ee/app/graphql/ee/types/namespaces/sidebar_type.rb b/ee/app/graphql/ee/types/namespaces/sidebar_type.rb
new file mode 100644
index 0000000000000000000000000000000000000000..db0f0a9ce45a3776dc0e7e168295b2c838bca33a
--- /dev/null
+++ b/ee/app/graphql/ee/types/namespaces/sidebar_type.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module EE
+  module Types
+    module Namespaces
+      module SidebarType
+        extend ActiveSupport::Concern
+
+        prepended do
+          field :open_epics_count,
+            GraphQL::Types::Int,
+            null: true,
+            description: 'Number of open epics of the namespace.'
+        end
+
+        def open_epics_count
+          return unless namespace.is_a?(Group)
+
+          ::Groups::EpicsCountService.new(namespace, context[:current_user]).count
+        end
+      end
+    end
+  end
+end
diff --git a/ee/spec/graphql/ee/types/namespaces/sidebar_type_spec.rb b/ee/spec/graphql/ee/types/namespaces/sidebar_type_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f76b5de222b76bb714cf8cef11eaf7feaedf21d1
--- /dev/null
+++ b/ee/spec/graphql/ee/types/namespaces/sidebar_type_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['NamespaceSidebar'], feature_category: :navigation do
+  let(:fields) do
+    %i[open_issues_count open_merge_requests_count open_epics_count]
+  end
+
+  specify { expect(described_class).to have_graphql_fields(fields) }
+end
diff --git a/ee/spec/requests/api/graphql/namespaces/sidebar_spec.rb b/ee/spec/requests/api/graphql/namespaces/sidebar_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..94ced4c342eafe0c896d5de886073e77a7733375
--- /dev/null
+++ b/ee/spec/requests/api/graphql/namespaces/sidebar_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Namespace.sidebar', feature_category: :navigation do
+  include GraphqlHelpers
+
+  let_it_be(:group) { create(:group, :private) }
+  let_it_be(:project) { create(:project, :repository, namespace: group) }
+
+  let_it_be(:reporter) { create(:user, reporter_of: group) }
+
+  let(:query) do
+    <<~QUERY
+    query {
+      namespace(fullPath: "#{namespace.full_path}") {
+        sidebar {
+          openEpicsCount
+        }
+      }
+    }
+    QUERY
+  end
+
+  before_all do
+    create_list(:epic, 2, group: group)
+  end
+
+  before do
+    stub_licensed_features(epics: true)
+  end
+
+  context 'with a Group' do
+    let(:namespace) { group }
+
+    it 'returns the epic counts' do
+      post_graphql(query, current_user: reporter)
+
+      expect(response).to have_gitlab_http_status(:ok)
+
+      expect(graphql_data_at(:namespace, :sidebar)).to eq({
+        'openEpicsCount' => 2
+      })
+    end
+  end
+
+  context 'with a ProjectNamespace' do
+    let(:namespace) { project.project_namespace }
+
+    it 'returns nil epic count' do
+      post_graphql(query, current_user: reporter)
+
+      expect(response).to have_gitlab_http_status(:ok)
+
+      expect(graphql_data_at(:namespace, :sidebar)).to eq({
+        'openEpicsCount' => nil
+      })
+    end
+  end
+end
diff --git a/lib/sidebars/groups/menus/issues_menu.rb b/lib/sidebars/groups/menus/issues_menu.rb
index 76a3e759df35c13cc4b85af910314409c83e80c9..417f59af633d3d537da5540bc3542b4ba8524be8 100644
--- a/lib/sidebars/groups/menus/issues_menu.rb
+++ b/lib/sidebars/groups/menus/issues_menu.rb
@@ -44,6 +44,8 @@ def pill_count
           end
         rescue ActiveRecord::QueryCanceled => e # rubocop:disable Database/RescueQueryCanceled -- used with fast_read_statement_timeout to prevent counts from slowing down the request
           Gitlab::ErrorTracking.log_exception(e, group_id: context.group.id, query: 'group_sidebar_issues_count')
+
+          nil
         end
 
         override :pill_html_options
diff --git a/spec/graphql/types/namespace_type_spec.rb b/spec/graphql/types/namespace_type_spec.rb
index 331b87b02178f1a5908d43a4e0e7080bee8d9d35..0357117f6d0cb437905c51298ec166da6daf2105 100644
--- a/spec/graphql/types/namespace_type_spec.rb
+++ b/spec/graphql/types/namespace_type_spec.rb
@@ -12,6 +12,7 @@
       id name path full_name full_path achievements_path description description_html visibility
       lfs_enabled request_access_enabled projects root_storage_statistics shared_runners_setting
       timelog_categories achievements work_item pages_deployments import_source_users work_item_types
+      sidebar
     ]
 
     expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/namespaces/sidebar_type_spec.rb b/spec/graphql/types/namespaces/sidebar_type_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..62d27c456b1e11719c48e6a847b8f954d8d42fbf
--- /dev/null
+++ b/spec/graphql/types/namespaces/sidebar_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['NamespaceSidebar'], feature_category: :navigation do
+  let(:fields) do
+    %i[open_issues_count open_merge_requests_count]
+  end
+
+  specify { expect(described_class.graphql_name).to eq('NamespaceSidebar') }
+
+  specify { expect(described_class).to have_graphql_fields(fields).at_least }
+end
diff --git a/spec/lib/sidebars/groups/menus/issues_menu_spec.rb b/spec/lib/sidebars/groups/menus/issues_menu_spec.rb
index cac0cc1edc5047277e329c65c2ed05426c7bbe2c..74e10e9dea20dd33782fd2b311b645f6a5b4c985 100644
--- a/spec/lib/sidebars/groups/menus/issues_menu_spec.rb
+++ b/spec/lib/sidebars/groups/menus/issues_menu_spec.rb
@@ -64,7 +64,7 @@
     it 'logs the error and returns a null count' do
       expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
         ActiveRecord::QueryCanceled, group_id: group.id, query: 'group_sidebar_issues_count'
-      )
+      ).and_call_original
 
       expect(menu.pill_count).to be_nil
     end
diff --git a/spec/requests/api/graphql/namespaces/sidebar_spec.rb b/spec/requests/api/graphql/namespaces/sidebar_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1fcab4e687378bee37763e05b2334556a9e50018
--- /dev/null
+++ b/spec/requests/api/graphql/namespaces/sidebar_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Namespace.sidebar', feature_category: :navigation do
+  include GraphqlHelpers
+
+  let_it_be(:group) { create(:group, :private) }
+  let_it_be(:project) { create(:project, :repository, namespace: group) }
+
+  let_it_be(:reporter) { create(:user, reporter_of: group) }
+
+  let(:query) do
+    <<~QUERY
+    query {
+      namespace(fullPath: "#{namespace.full_path}") {
+        sidebar {
+          openIssuesCount
+          openMergeRequestsCount
+        }
+      }
+    }
+    QUERY
+  end
+
+  before_all do
+    create_list(:issue, 2, project: project)
+    create(:merge_request, source_project: project)
+  end
+
+  context 'with a Group' do
+    let(:namespace) { group }
+
+    it 'returns the group counts' do
+      post_graphql(query, current_user: reporter)
+
+      expect(response).to have_gitlab_http_status(:ok)
+
+      expect(graphql_data_at(:namespace, :sidebar)).to eq({
+        'openIssuesCount' => 2,
+        'openMergeRequestsCount' => 1
+      })
+    end
+
+    context 'when issue count query times out' do
+      before do
+        allow_next_instance_of(::Groups::OpenIssuesCountService) do |service|
+          allow(service).to receive(:count).and_raise(ActiveRecord::QueryCanceled)
+        end
+      end
+
+      it 'logs the error and returns a null issue count' do
+        expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
+          ActiveRecord::QueryCanceled, group_id: namespace.id, query: 'group_sidebar_issues_count'
+        ).and_call_original
+
+        post_graphql(query, current_user: reporter)
+
+        expect(response).to have_gitlab_http_status(:ok)
+
+        expect(graphql_data_at(:namespace, :sidebar)).to eq({
+          'openIssuesCount' => nil,
+          'openMergeRequestsCount' => 1
+        })
+      end
+    end
+  end
+
+  context 'with a ProjectNamespace' do
+    let(:namespace) { project.project_namespace }
+
+    it 'returns the project counts' do
+      post_graphql(query, current_user: reporter)
+
+      expect(response).to have_gitlab_http_status(:ok)
+
+      expect(graphql_data_at(:namespace, :sidebar)).to eq({
+        'openIssuesCount' => 2,
+        'openMergeRequestsCount' => 1
+      })
+    end
+  end
+end