diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
index 5310165bc13312f875d12a39df42ba6db3ca8032..b2cef7c37b98b76305de0c5f6637110cb8956b71 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
@@ -1,8 +1,8 @@
 <script>
-import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
+import { GlAlert, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
 import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
 import getUserCallouts from '~/graphql_shared/queries/get_user_callouts.query.graphql';
-import { __ } from '~/locale';
+import { __, s__ } from '~/locale';
 import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
 import { DEFAULT, DRAW_FAILURE, LOAD_FAILURE } from '../../constants';
 import DismissPipelineGraphCallout from '../../graphql/mutations/dismiss_pipeline_notification.graphql';
@@ -34,6 +34,7 @@ export default {
   components: {
     GlAlert,
     GlLoadingIcon,
+    GlSprintf,
     GraphViewSelector,
     LocalStorageSync,
     PipelineGraph,
@@ -62,6 +63,7 @@ export default {
       pipeline: null,
       skipRetryModal: false,
       showAlert: false,
+      showJobCountWarning: false,
       showLinks: false,
     };
   },
@@ -166,7 +168,12 @@ export default {
           },
         );
       },
-      result({ error }) {
+      result({ data, error }) {
+        const stages = data?.project?.pipeline?.stages?.nodes || [];
+
+        this.showJobCountWarning = stages.some((stage) => {
+          return stage.groups.nodes.length >= 100;
+        });
         /*
           If there is a successful load after a failure, clear
           the failure notification to avoid confusion.
@@ -273,14 +280,38 @@ export default {
       this.currentViewType = type;
     },
   },
+  i18n: {
+    jobLimitWarning: {
+      title: s__('Pipeline|Only the first 100 jobs per stage are displayed'),
+      desc: s__('Pipeline|To see the remaining jobs, go to the %{boldStart}Jobs%{boldEnd} tab.'),
+    },
+  },
   viewTypeKey: VIEW_TYPE_KEY,
 };
 </script>
 <template>
   <div>
-    <gl-alert v-if="showAlert" :variant="alert.variant" @dismiss="hideAlert">
+    <gl-alert
+      v-if="showAlert"
+      :variant="alert.variant"
+      data-testid="error-alert"
+      @dismiss="hideAlert"
+    >
       {{ alert.text }}
     </gl-alert>
+    <gl-alert
+      v-if="showJobCountWarning"
+      variant="warning"
+      :dismissible="false"
+      :title="$options.i18n.jobLimitWarning.title"
+      data-testid="job-count-warning"
+    >
+      <gl-sprintf :message="$options.i18n.jobLimitWarning.desc">
+        <template #bold="{ content }">
+          <b>{{ content }}</b>
+        </template>
+      </gl-sprintf>
+    </gl-alert>
     <local-storage-sync
       :storage-key="$options.viewTypeKey"
       :value="currentViewType"
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 8027563f4511a3f3f6d1141876cc4226bf1e80c7..e175d64ddf866f5e2c964caf75a828b2f805dc3d 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -33977,6 +33977,9 @@ msgstr ""
 msgid "Pipeline|No failed jobs in this pipeline 🎉"
 msgstr ""
 
+msgid "Pipeline|Only the first 100 jobs per stage are displayed"
+msgstr ""
+
 msgid "Pipeline|Passed"
 msgstr ""
 
@@ -34061,6 +34064,9 @@ msgstr ""
 msgid "Pipeline|To run a merge request pipeline, the jobs in the CI/CD configuration file %{linkStart}must be configured%{linkEnd} to run in merge request pipelines."
 msgstr ""
 
+msgid "Pipeline|To see the remaining jobs, go to the %{boldStart}Jobs%{boldEnd} tab."
+msgstr ""
+
 msgid "Pipeline|Trigger author"
 msgstr ""
 
diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
index 9599b5e6b7b391ce5405499e5e24609e5dfb20c4..7b59d82ae6f8e991cc538153764bd580411e8195 100644
--- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
+++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
@@ -34,7 +34,11 @@ import getPipelineHeaderData from '~/pipelines/graphql/queries/get_pipeline_head
 import * as sentryUtils from '~/pipelines/utils';
 import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
 import { mockRunningPipelineHeaderData } from '../mock_data';
-import { mapCallouts, mockCalloutsResponse } from './mock_data';
+import {
+  mapCallouts,
+  mockCalloutsResponse,
+  mockPipelineResponseWithTooManyJobs,
+} from './mock_data';
 
 const defaultProvide = {
   graphqlResourceEtag: 'frog/amphibirama/etag/',
@@ -49,7 +53,10 @@ describe('Pipeline graph wrapper', () => {
 
   let wrapper;
   let requestHandlers;
-  const findAlert = () => wrapper.findComponent(GlAlert);
+  let pipelineDetailsHandler;
+
+  const findAlert = () => wrapper.findByTestId('error-alert');
+  const findJobCountWarning = () => wrapper.findByTestId('job-count-warning');
   const findDependenciesToggle = () => wrapper.findByTestId('show-links-toggle');
   const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
   const findLinksLayer = () => wrapper.findComponent(LinksLayer);
@@ -83,7 +90,6 @@ describe('Pipeline graph wrapper', () => {
   const createComponentWithApollo = ({
     calloutsList = [],
     data = {},
-    getPipelineDetailsHandler = jest.fn().mockResolvedValue(mockPipelineResponse),
     mountFn = shallowMountExtended,
     provide = {},
   } = {}) => {
@@ -92,7 +98,7 @@ describe('Pipeline graph wrapper', () => {
     requestHandlers = {
       getUserCalloutsHandler: jest.fn().mockResolvedValue(mockCalloutsResponse(callouts)),
       getPipelineHeaderDataHandler: jest.fn().mockResolvedValue(mockRunningPipelineHeaderData),
-      getPipelineDetailsHandler,
+      getPipelineDetailsHandler: pipelineDetailsHandler,
     };
 
     const handlers = [
@@ -105,24 +111,29 @@ describe('Pipeline graph wrapper', () => {
     createComponent({ apolloProvider, data, provide, mountFn });
   };
 
+  beforeEach(() => {
+    pipelineDetailsHandler = jest.fn();
+    pipelineDetailsHandler.mockResolvedValue(mockPipelineResponse);
+  });
+
   describe('when data is loading', () => {
-    it('displays the loading icon', () => {
+    beforeEach(() => {
       createComponentWithApollo();
+    });
+
+    it('displays the loading icon', () => {
       expect(findLoadingIcon().exists()).toBe(true);
     });
 
     it('does not display the alert', () => {
-      createComponentWithApollo();
       expect(findAlert().exists()).toBe(false);
     });
 
     it('does not display the graph', () => {
-      createComponentWithApollo();
       expect(findGraph().exists()).toBe(false);
     });
 
     it('skips querying headerPipeline', () => {
-      createComponentWithApollo();
       expect(wrapper.vm.$apollo.queries.headerPipeline.skip).toBe(true);
     });
   });
@@ -153,11 +164,25 @@ describe('Pipeline graph wrapper', () => {
     });
   });
 
+  describe('when a stage has 100 jobs or more', () => {
+    beforeEach(async () => {
+      pipelineDetailsHandler.mockResolvedValue(mockPipelineResponseWithTooManyJobs);
+      createComponentWithApollo();
+      await waitForPromises();
+    });
+
+    it('show a warning alert', () => {
+      expect(findJobCountWarning().exists()).toBe(true);
+      expect(findJobCountWarning().props().title).toBe(
+        'Only the first 100 jobs per stage are displayed',
+      );
+    });
+  });
+
   describe('when there is an error', () => {
     beforeEach(async () => {
-      createComponentWithApollo({
-        getPipelineDetailsHandler: jest.fn().mockRejectedValue(new Error('GraphQL error')),
-      });
+      pipelineDetailsHandler.mockRejectedValue(new Error('GraphQL error'));
+      createComponentWithApollo();
       await waitForPromises();
     });
 
@@ -270,13 +295,12 @@ describe('Pipeline graph wrapper', () => {
         errors: [{ message: 'timeout' }],
       };
 
-      const failSucceedFail = jest
-        .fn()
+      pipelineDetailsHandler
         .mockResolvedValueOnce(errorData)
         .mockResolvedValueOnce(mockPipelineResponse)
         .mockResolvedValueOnce(errorData);
 
-      createComponentWithApollo({ getPipelineDetailsHandler: failSucceedFail });
+      createComponentWithApollo();
       await waitForPromises();
     });
 
@@ -438,9 +462,9 @@ describe('Pipeline graph wrapper', () => {
 
         localStorage.setItem(VIEW_TYPE_KEY, LAYER_VIEW);
 
+        pipelineDetailsHandler.mockResolvedValue(nonNeedsResponse);
         createComponentWithApollo({
           mountFn: mountExtended,
-          getPipelineDetailsHandler: jest.fn().mockResolvedValue(nonNeedsResponse),
         });
 
         await waitForPromises();
@@ -460,9 +484,9 @@ describe('Pipeline graph wrapper', () => {
         const nonNeedsResponse = { ...mockPipelineResponse };
         nonNeedsResponse.data.project.pipeline.usesNeeds = false;
 
+        pipelineDetailsHandler.mockResolvedValue(nonNeedsResponse);
         createComponentWithApollo({
           mountFn: mountExtended,
-          getPipelineDetailsHandler: jest.fn().mockResolvedValue(nonNeedsResponse),
         });
 
         jest.runOnlyPendingTimers();
diff --git a/spec/frontend/pipelines/graph/mock_data.js b/spec/frontend/pipelines/graph/mock_data.js
index b012e7f66e1099667fb7da9aa235ac49ce2c773c..8d06d6931ed4c20a508f810f811c00d051b353d2 100644
--- a/spec/frontend/pipelines/graph/mock_data.js
+++ b/spec/frontend/pipelines/graph/mock_data.js
@@ -1,3 +1,4 @@
+import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
 import { unwrapPipelineData } from '~/pipelines/components/graph/utils';
 import {
   BUILD_KIND,
@@ -5,6 +6,14 @@ import {
   RETRY_ACTION_TITLE,
 } from '~/pipelines/components/graph/constants';
 
+// We mock this instead of using fixtures for performance reason.
+const mockPipelineResponseCopy = JSON.parse(JSON.stringify(mockPipelineResponse));
+const groups = new Array(100).fill({
+  ...mockPipelineResponse.data.project.pipeline.stages.nodes[0].groups.nodes[0],
+});
+mockPipelineResponseCopy.data.project.pipeline.stages.nodes[0].groups.nodes = groups;
+export const mockPipelineResponseWithTooManyJobs = mockPipelineResponseCopy;
+
 export const downstream = {
   nodes: [
     {