diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index a8ca17ab4dd590ae41d7b37f8db9654502b5ce72..5707e4d67f937350e41bad83a3cf7d2d1fe1cdd4 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -53,6 +53,7 @@ import DiffFile from './diff_file.vue';
 import HiddenFilesWarning from './hidden_files_warning.vue';
 import NoChanges from './no_changes.vue';
 import TreeList from './tree_list.vue';
+import VirtualScrollerScrollSync from './virtual_scroller_scroll_sync';
 
 export default {
   name: 'DiffsApp',
@@ -62,8 +63,7 @@ export default {
     DynamicScrollerItem: () =>
       import('vendor/vue-virtual-scroller').then(({ DynamicScrollerItem }) => DynamicScrollerItem),
     PreRenderer: () => import('./pre_renderer.vue').then((PreRenderer) => PreRenderer),
-    VirtualScrollerScrollSync: () =>
-      import('./virtual_scroller_scroll_sync').then((VSSSync) => VSSSync),
+    VirtualScrollerScrollSync,
     CompareVersions,
     DiffFile,
     NoChanges,
@@ -406,10 +406,8 @@ export default {
     this.unsubscribeFromEvents();
     this.removeEventListeners();
 
-    if (window.gon?.features?.diffsVirtualScrolling) {
-      diffsEventHub.$off('scrollToFileHash', this.scrollVirtualScrollerToFileHash);
-      diffsEventHub.$off('scrollToIndex', this.scrollVirtualScrollerToIndex);
-    }
+    diffsEventHub.$off('scrollToFileHash', this.scrollVirtualScrollerToFileHash);
+    diffsEventHub.$off('scrollToIndex', this.scrollVirtualScrollerToIndex);
   },
   methods: {
     ...mapActions(['startTaskList']),
@@ -522,32 +520,27 @@ export default {
         );
       }
 
-      if (
-        window.gon?.features?.diffsVirtualScrolling ||
-        window.gon?.features?.usageDataDiffSearches
-      ) {
-        let keydownTime;
-        Mousetrap.bind(['mod+f', 'mod+g'], () => {
-          keydownTime = new Date().getTime();
-        });
+      let keydownTime;
+      Mousetrap.bind(['mod+f', 'mod+g'], () => {
+        keydownTime = new Date().getTime();
+      });
 
-        window.addEventListener('blur', () => {
-          if (keydownTime) {
-            const delta = new Date().getTime() - keydownTime;
+      window.addEventListener('blur', () => {
+        if (keydownTime) {
+          const delta = new Date().getTime() - keydownTime;
 
-            // To make sure the user is using the find function we need to wait for blur
-            // and max 1000ms to be sure it the search box is filtered
-            if (delta >= 0 && delta < 1000) {
-              this.disableVirtualScroller();
+          // To make sure the user is using the find function we need to wait for blur
+          // and max 1000ms to be sure it the search box is filtered
+          if (delta >= 0 && delta < 1000) {
+            this.disableVirtualScroller();
 
-              if (window.gon?.features?.usageDataDiffSearches) {
-                api.trackRedisHllUserEvent('i_code_review_user_searches_diff');
-                api.trackRedisCounterEvent('diff_searches');
-              }
+            if (window.gon?.features?.usageDataDiffSearches) {
+              api.trackRedisHllUserEvent('i_code_review_user_searches_diff');
+              api.trackRedisCounterEvent('diff_searches');
             }
           }
-        });
-      }
+        }
+      });
     },
     removeEventListeners() {
       Mousetrap.unbind(keysFor(MR_PREVIOUS_FILE_IN_DIFF));
@@ -589,8 +582,6 @@ export default {
       this.virtualScrollCurrentIndex = -1;
     },
     scrollVirtualScrollerToDiffNote() {
-      if (!window.gon?.features?.diffsVirtualScrolling) return;
-
       const id = window?.location?.hash;
 
       if (id.startsWith('#note_')) {
@@ -605,11 +596,7 @@ export default {
       }
     },
     subscribeToVirtualScrollingEvents() {
-      if (
-        window.gon?.features?.diffsVirtualScrolling &&
-        this.shouldShow &&
-        !this.subscribedToVirtualScrollingEvents
-      ) {
+      if (this.shouldShow && !this.subscribedToVirtualScrollingEvents) {
         diffsEventHub.$on('scrollToFileHash', this.scrollVirtualScrollerToFileHash);
         diffsEventHub.$on('scrollToIndex', this.scrollVirtualScrollerToIndex);
 
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index 0e8506cd896baecb628a15e8bd653c4874b5679c..3cf1f69b08c1860ed200b8afd852255dac71ef04 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -209,7 +209,7 @@ export default {
       handler(val) {
         const el = this.$el.closest('.vue-recycle-scroller__item-view');
 
-        if (this.glFeatures.diffsVirtualScrolling && el) {
+        if (el) {
           // We can't add a style with Vue because of the way the virtual
           // scroller library renders the diff files
           el.style.zIndex = val ? '1' : null;
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index c2eefad8f404668e6214385e3f046213401c4fa3..bbe27c0dbd6a7dda0d2e61fc0b1262c348356442 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -29,8 +29,6 @@ export const UNFOLD_COUNT = 20;
 export const COUNT_OF_AVATARS_IN_GUTTER = 3;
 export const LENGTH_OF_AVATAR_TOOLTIP = 17;
 
-export const LINES_TO_BE_RENDERED_DIRECTLY = 100;
-
 export const DIFF_FILE_SYMLINK_MODE = '120000';
 export const DIFF_FILE_DELETED_MODE = '0';
 
diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js
index dfe29e767c96b2c89dd67d06bfe7140455cb3971..1691da34c6ddf90f77238b16fb8c1742dc376d7f 100644
--- a/app/assets/javascripts/diffs/index.js
+++ b/app/assets/javascripts/diffs/index.js
@@ -1,8 +1,7 @@
 import Vue from 'vue';
 import { mapActions, mapState, mapGetters } from 'vuex';
-import { getCookie, setCookie, parseBoolean, removeCookie } from '~/lib/utils/common_utils';
+import { getCookie, parseBoolean, removeCookie } from '~/lib/utils/common_utils';
 
-import { getParameterValues } from '~/lib/utils/url_utility';
 import eventHub from '../notes/event_hub';
 import diffsApp from './components/app.vue';
 
@@ -74,11 +73,6 @@ export default function initDiffsApp(store) {
           trackClick: false,
         });
       }
-
-      const vScrollingParam = getParameterValues('virtual_scrolling')[0];
-      if (vScrollingParam === 'false' || vScrollingParam === 'true') {
-        setCookie('diffs_virtual_scrolling', vScrollingParam);
-      }
     },
     methods: {
       ...mapActions('diffs', ['setRenderTreeList', 'setShowWhitespace']),
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 572949e585ad90ebea9e5e63aaa5b647962f21f7..e967be23f42af596f8ebe86dd14594a598ac9519 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -125,7 +125,7 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
         commit(types.SET_DIFF_DATA_BATCH, { diff_files });
         commit(types.SET_BATCH_LOADING_STATE, 'loaded');
 
-        if (window.gon?.features?.diffsVirtualScrolling && !scrolledVirtualScroller) {
+        if (!scrolledVirtualScroller) {
           const index = state.diffFiles.findIndex(
             (f) =>
               f.file_hash === hash || f[INLINE_DIFF_LINES_KEY].find((l) => l.line_code === hash),
@@ -195,9 +195,7 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
         commit(types.SET_BATCH_LOADING_STATE, 'error');
       });
 
-  return getBatch().then(
-    () => !window.gon?.features?.diffsVirtualScrolling && handleLocationHash(),
-  );
+  return getBatch();
 };
 
 export const fetchDiffFilesMeta = ({ commit, state }) => {
@@ -529,7 +527,7 @@ export const setCurrentFileHash = ({ commit }, hash) => {
   commit(types.SET_CURRENT_DIFF_FILE, hash);
 };
 
-export const scrollToFile = ({ state, commit, getters }, { path, setHash = true }) => {
+export const scrollToFile = ({ state, commit, getters }, { path }) => {
   if (!state.treeEntries[path]) return;
 
   const { fileHash } = state.treeEntries[path];
@@ -539,11 +537,9 @@ export const scrollToFile = ({ state, commit, getters }, { path, setHash = true
   if (getters.isVirtualScrollingEnabled) {
     eventHub.$emit('scrollToFileHash', fileHash);
 
-    if (setHash) {
-      setTimeout(() => {
-        window.history.replaceState(null, null, `#${fileHash}`);
-      });
-    }
+    setTimeout(() => {
+      window.history.replaceState(null, null, `#${fileHash}`);
+    });
   } else {
     document.location.hash = fileHash;
 
diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js
index 83c7f8c814b5801d93d08bbb77026ef4fa365ebc..3a85c1a9fe140538eaa1402f74e011a8dfc34209 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -1,6 +1,5 @@
-import { getCookie } from '~/lib/utils/common_utils';
-import { getParameterValues } from '~/lib/utils/url_utility';
 import { __, n__ } from '~/locale';
+import { getParameterValues } from '~/lib/utils/url_utility';
 import {
   PARALLEL_DIFF_VIEW_TYPE,
   INLINE_DIFF_VIEW_TYPE,
@@ -175,21 +174,11 @@ export function suggestionCommitMessage(state, _, rootState) {
 }
 
 export const isVirtualScrollingEnabled = (state) => {
-  const vSrollerCookie = getCookie('diffs_virtual_scrolling');
-
-  if (state.disableVirtualScroller) {
+  if (state.disableVirtualScroller || getParameterValues('virtual_scrolling')[0] === 'false') {
     return false;
   }
 
-  if (vSrollerCookie) {
-    return vSrollerCookie === 'true';
-  }
-
-  return (
-    !state.viewDiffsFileByFile &&
-    (window.gon?.features?.diffsVirtualScrolling ||
-      getParameterValues('virtual_scrolling')[0] === 'true')
-  );
+  return !state.viewDiffsFileByFile;
 };
 
 export const isBatchLoading = (state) => state.batchLoadingState === 'loading';
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index 3f1af68e37a5500da6b794eb7d2e21b2d284469d..f2028892a5ffeaaf8810cc8eb2c3c44b2d7f1ad2 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -9,7 +9,6 @@ import {
   NEW_LINE_TYPE,
   OLD_LINE_TYPE,
   MATCH_LINE_TYPE,
-  LINES_TO_BE_RENDERED_DIRECTLY,
   INLINE_DIFF_LINES_KEY,
   CONFLICT_OUR,
   CONFLICT_THEIR,
@@ -380,16 +379,9 @@ function prepareDiffFileLines(file) {
   return file;
 }
 
-function finalizeDiffFile(file, index) {
-  let renderIt = Boolean(window.gon?.features?.diffsVirtualScrolling);
-
-  if (!window.gon?.features?.diffsVirtualScrolling) {
-    renderIt =
-      index < 3 ? file[INLINE_DIFF_LINES_KEY].length < LINES_TO_BE_RENDERED_DIRECTLY : false;
-  }
-
+function finalizeDiffFile(file) {
   Object.assign(file, {
-    renderIt,
+    renderIt: true,
     isShowingFullFile: false,
     isLoadingFullFile: false,
     discussions: [],
@@ -417,15 +409,13 @@ export function prepareDiffData({ diff, priorFiles = [], meta = false }) {
     .map((file, index, allFiles) => prepareRawDiffFile({ file, allFiles, meta }))
     .map(ensureBasicDiffFileLines)
     .map(prepareDiffFileLines)
-    .map((file, index) => finalizeDiffFile(file, priorFiles.length + index));
+    .map((file) => finalizeDiffFile(file));
 
   return deduplicateFilesList([...priorFiles, ...cleanedFiles]);
 }
 
 export function getDiffPositionByLineCode(diffFiles) {
-  let lines = [];
-
-  lines = diffFiles.reduce((acc, diffFile) => {
+  const lines = diffFiles.reduce((acc, diffFile) => {
     diffFile[INLINE_DIFF_LINES_KEY].forEach((line) => {
       acc.push({ file: diffFile, line });
     });
diff --git a/app/assets/javascripts/notes/mixins/discussion_navigation.js b/app/assets/javascripts/notes/mixins/discussion_navigation.js
index ad529eb99b6b8acd6edc14e9950f8e1575296785..93236b05100d23d6392eecf115a6dfabeb7622cd 100644
--- a/app/assets/javascripts/notes/mixins/discussion_navigation.js
+++ b/app/assets/javascripts/notes/mixins/discussion_navigation.js
@@ -3,8 +3,6 @@ import { scrollToElementWithContext, scrollToElement } from '~/lib/utils/common_
 import { updateHistory } from '../../lib/utils/url_utility';
 import eventHub from '../event_hub';
 
-const isDiffsVirtualScrollingEnabled = () => window.gon?.features?.diffsVirtualScrolling;
-
 /**
  * @param {string} selector
  * @returns {boolean}
@@ -15,7 +13,7 @@ function scrollTo(selector, { withoutContext = false } = {}) {
 
   if (el) {
     scrollFunction(el, {
-      behavior: isDiffsVirtualScrollingEnabled() ? 'auto' : 'smooth',
+      behavior: 'auto',
     });
     return true;
   }
@@ -31,7 +29,7 @@ function updateUrlWithNoteId(noteId) {
     replace: true,
   };
 
-  if (noteId && isDiffsVirtualScrollingEnabled()) {
+  if (noteId) {
     // Temporarily mask the ID to avoid the browser default
     //    scrolling taking over which is broken with virtual
     //    scrolling enabled.
@@ -115,17 +113,13 @@ function handleDiscussionJump(self, fn, discussionId = self.currentDiscussionId)
   const isDiffView = window.mrTabs.currentAction === 'diffs';
   const targetId = fn(discussionId, isDiffView);
   const discussion = self.getDiscussion(targetId);
-  const setHash = !isDiffView && !isDiffsVirtualScrollingEnabled();
   const discussionFilePath = discussion?.diff_file?.file_path;
 
-  if (isDiffsVirtualScrollingEnabled()) {
-    window.location.hash = '';
-  }
+  window.location.hash = '';
 
   if (discussionFilePath) {
     self.scrollToFile({
       path: discussionFilePath,
-      setHash,
     });
   }
 
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 103622646ab3d6198bc7a43fb390a28eefb1e5e1..05452ef65786bcd9e4fb4677fa814fbd250a202c 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -42,7 +42,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
     push_frontend_feature_flag(:paginated_notes, @project, default_enabled: :yaml)
     push_frontend_feature_flag(:confidential_notes, @project, default_enabled: :yaml)
     push_frontend_feature_flag(:improved_emoji_picker, project, default_enabled: :yaml)
-    push_frontend_feature_flag(:diffs_virtual_scrolling, project, default_enabled: :yaml)
     push_frontend_feature_flag(:restructured_mr_widget, project, default_enabled: :yaml)
     push_frontend_feature_flag(:refactor_mr_widgets_extensions, @project, default_enabled: :yaml)
     push_frontend_feature_flag(:rebase_without_ci_ui, @project, default_enabled: :yaml)
diff --git a/config/feature_flags/development/diffs_virtual_scrolling.yml b/config/feature_flags/development/diffs_virtual_scrolling.yml
deleted file mode 100644
index add1297d8b8e6957e7b49f8b9d0e64e489b97823..0000000000000000000000000000000000000000
--- a/config/feature_flags/development/diffs_virtual_scrolling.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: diffs_virtual_scrolling
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60312
-rollout_issue_url:
-milestone: '13.12'
-type: development
-group: group::code review
-default_enabled: true
diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb
index a4aae7d8020f2b3e9bf1207f6ffb3094f0060717..eb98c7d5061f1a8454ba6058b3a12f4319fb1c9b 100644
--- a/spec/features/merge_request/batch_comments_spec.rb
+++ b/spec/features/merge_request/batch_comments_spec.rb
@@ -32,7 +32,7 @@
 
       expect(page).to have_selector('[data-testid="review_bar_component"]')
 
-      expect(find('.review-bar-content .btn-confirm')).to have_content('1')
+      expect(find('[data-testid="review_bar_component"] .btn-confirm')).to have_content('1')
     end
 
     it 'publishes review' do
@@ -150,10 +150,6 @@
 
       it 'adds draft comments to both sides' do
         write_parallel_comment('2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9')
-
-        # make sure line 9 is in the view
-        execute_script("window.scrollBy(0, -200)")
-
         write_parallel_comment('2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9', button_text: 'Add to review', text: 'Another wrong line')
 
         expect(find('.new .draft-note-component')).to have_content('Line is wrong')
@@ -255,13 +251,15 @@ def visit_overview
   end
 
   def write_diff_comment(**params)
-    click_diff_line(find("[id='#{sample_compare.changes[0][:line_code]}']"))
+    click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[0][:line_code]}']"))
 
     write_comment(**params)
   end
 
   def write_parallel_comment(line, **params)
-    find("div[id='#{line}']").hover
+    line_element = find_by_scrolling("[id='#{line}']")
+    scroll_to_elements_bottom(line_element)
+    line_element.hover
     find(".js-add-diff-note-button").click
 
     write_comment(selector: "form[data-line-code='#{line}']", **params)
diff --git a/spec/features/merge_request/user_comments_on_diff_spec.rb b/spec/features/merge_request/user_comments_on_diff_spec.rb
index 29c346593d507b6f0b17eb9e980141e3aad245fa..c06019f6b77429660c5ca79e3b23edf25a87b431 100644
--- a/spec/features/merge_request/user_comments_on_diff_spec.rb
+++ b/spec/features/merge_request/user_comments_on_diff_spec.rb
@@ -25,14 +25,15 @@
     context 'when toggling inline comments' do
       context 'in a single file' do
         it 'hides a comment' do
-          click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
+          line_element = find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']").find(:xpath, "..")
+          click_diff_line(line_element)
 
           page.within('.js-discussion-note-form') do
             fill_in('note_note', with: 'Line is wrong')
             click_button('Add comment now')
           end
 
-          page.within('.diff-files-holder > div:nth-child(6)') do
+          page.within(line_element.ancestor('[data-path]')) do
             expect(page).to have_content('Line is wrong')
 
             find('.js-diff-more-actions').click
@@ -45,7 +46,9 @@
 
       context 'in multiple files' do
         it 'toggles comments' do
-          click_diff_line(find("[id='#{sample_compare.changes[0][:line_code]}']"))
+          first_line_element = find_by_scrolling("[id='#{sample_compare.changes[0][:line_code]}']").find(:xpath, "..")
+          first_root_element = first_line_element.ancestor('[data-path]')
+          click_diff_line(first_line_element)
 
           page.within('.js-discussion-note-form') do
             fill_in('note_note', with: 'Line is correct')
@@ -54,11 +57,14 @@
 
           wait_for_requests
 
-          page.within('.diff-files-holder > div:nth-child(5) .note-body > .note-text') do
+          page.within(first_root_element) do
             expect(page).to have_content('Line is correct')
           end
 
-          click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
+          second_line_element = find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']")
+          second_root_element = second_line_element.ancestor('[data-path]')
+
+          click_diff_line(second_line_element)
 
           page.within('.js-discussion-note-form') do
             fill_in('note_note', with: 'Line is wrong')
@@ -68,7 +74,7 @@
           wait_for_requests
 
           # Hide the comment.
-          page.within('.diff-files-holder > div:nth-child(6)') do
+          page.within(second_root_element) do
             find('.js-diff-more-actions').click
             click_button 'Hide comments on this file'
 
@@ -77,37 +83,36 @@
 
           # At this moment a user should see only one comment.
           # The other one should be hidden.
-          page.within('.diff-files-holder > div:nth-child(5) .note-body > .note-text') do
+          page.within(first_root_element) do
             expect(page).to have_content('Line is correct')
           end
 
           # Show the comment.
-          page.within('.diff-files-holder > div:nth-child(6)') do
+          page.within(second_root_element) do
             find('.js-diff-more-actions').click
             click_button 'Show comments on this file'
           end
 
           # Now both the comments should be shown.
-          page.within('.diff-files-holder > div:nth-child(6) .note-body > .note-text') do
+          page.within(second_root_element) do
             expect(page).to have_content('Line is wrong')
           end
 
-          page.within('.diff-files-holder > div:nth-child(5) .note-body > .note-text') do
+          page.within(first_root_element) do
             expect(page).to have_content('Line is correct')
           end
 
           # Check the same comments in the side-by-side view.
-          execute_script("window.scrollTo(0,0);")
           find('.js-show-diff-settings').click
           click_button 'Side-by-side'
 
           wait_for_requests
 
-          page.within('.diff-files-holder > div:nth-child(6) .parallel .note-body > .note-text') do
+          page.within(second_root_element) do
             expect(page).to have_content('Line is wrong')
           end
 
-          page.within('.diff-files-holder > div:nth-child(5) .parallel .note-body > .note-text') do
+          page.within(first_root_element) do
             expect(page).to have_content('Line is correct')
           end
         end
@@ -121,7 +126,7 @@
 
   context 'when adding multiline comments' do
     it 'saves a multiline comment' do
-      click_diff_line(find("[id='#{sample_commit.line_code}']"))
+      click_diff_line(find_by_scrolling("[id='#{sample_commit.line_code}']").find(:xpath, '..'))
       add_comment('-13', '+14')
     end
 
@@ -133,13 +138,13 @@
       # In `files/ruby/popen.rb`
       it 'allows comments for changes involving both sides' do
         # click +15, select -13 add and verify comment
-        click_diff_line(find('div[data-path="files/ruby/popen.rb"] .right-side a[data-linenumber="15"]').find(:xpath, '../../..'), 'right')
+        click_diff_line(find_by_scrolling('div[data-path="files/ruby/popen.rb"] .right-side a[data-linenumber="15"]').find(:xpath, '../../..'), 'right')
         add_comment('-13', '+15')
       end
 
       it 'allows comments on previously hidden lines at the top of a file', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/285294' do
         # Click -9, expand up, select 1 add and verify comment
-        page.within('[data-path="files/ruby/popen.rb"]') do
+        page.within find_by_scrolling('[data-path="files/ruby/popen.rb"]') do
           all('.js-unfold-all')[0].click
         end
         click_diff_line(find('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="9"]').find(:xpath, '../..'), 'left')
@@ -148,7 +153,7 @@
 
       it 'allows comments on previously hidden lines the middle of a file' do
         # Click 27, expand up, select 18, add and verify comment
-        page.within('[data-path="files/ruby/popen.rb"]') do
+        page.within find_by_scrolling('[data-path="files/ruby/popen.rb"]') do
           all('.js-unfold-all')[1].click
         end
         click_diff_line(find('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="21"]').find(:xpath, '../..'), 'left')
@@ -157,7 +162,7 @@
 
       it 'allows comments on previously hidden lines at the bottom of a file' do
         # Click +28, expand down, select 37 add and verify comment
-        page.within('[data-path="files/ruby/popen.rb"]') do
+        page.within find_by_scrolling('[data-path="files/ruby/popen.rb"]') do
           all('.js-unfold-down:not([disabled])')[1].click
         end
         click_diff_line(find('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="30"]').find(:xpath, '../..'), 'left')
@@ -198,7 +203,7 @@ def add_comment(start_line, end_line)
 
   context 'when editing comments' do
     it 'edits a comment' do
-      click_diff_line(find("[id='#{sample_commit.line_code}']"))
+      click_diff_line(find_by_scrolling("[id='#{sample_commit.line_code}']"))
 
       page.within('.js-discussion-note-form') do
         fill_in(:note_note, with: 'Line is wrong')
@@ -224,7 +229,7 @@ def add_comment(start_line, end_line)
 
   context 'when deleting comments' do
     it 'deletes a comment' do
-      click_diff_line(find("[id='#{sample_commit.line_code}']"))
+      click_diff_line(find_by_scrolling("[id='#{sample_commit.line_code}']"))
 
       page.within('.js-discussion-note-form') do
         fill_in(:note_note, with: 'Line is wrong')
diff --git a/spec/features/merge_request/user_expands_diff_spec.rb b/spec/features/merge_request/user_expands_diff_spec.rb
index 52554f11d287bd1af1c613d3ac484bd8dcab378d..25c9584350d3e7c5b79c2640c5b77a85147d5b3d 100644
--- a/spec/features/merge_request/user_expands_diff_spec.rb
+++ b/spec/features/merge_request/user_expands_diff_spec.rb
@@ -7,7 +7,7 @@
   let(:merge_request) { create(:merge_request, source_branch: 'expand-collapse-files', source_project: project, target_project: project) }
 
   before do
-    allow(Gitlab::CurrentSettings).to receive(:diff_max_patch_bytes).and_return(100.kilobytes)
+    allow(Gitlab::CurrentSettings).to receive(:diff_max_patch_bytes).and_return(100.bytes)
 
     visit(diffs_project_merge_request_path(project, merge_request))
 
@@ -15,7 +15,7 @@
   end
 
   it 'allows user to expand diff' do
-    page.within find('[id="19763941ab80e8c09871c0a425f0560d9053bcb3"]') do
+    page.within find("[id='4c76a1271e41072d7da9fe40bf0f79f7384d472a']") do
       find('[data-testid="expand-button"]').click
 
       wait_for_requests
diff --git a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
index ffb9694e806596a05fc15ed9c306f6a8e392e6f3..7d67cde4bbb9d1d93ee2b023f38a2d758cd950b7 100644
--- a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
+++ b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
@@ -15,16 +15,14 @@
     visit diffs_project_merge_request_path(merge_request.project, merge_request)
     wait_for_requests
 
-    # Add discussion to first line of first file
-    click_diff_line(find('.diff-file.file-holder:first-of-type .line_holder .left-side:first-of-type'))
-    page.within('.js-discussion-note-form') do
+    click_diff_line(get_first_diff.find('[data-testid="left-side"]', match: :first))
+    page.within get_first_diff.find('.js-discussion-note-form') do
       fill_in('note_note', with: 'First Line Comment')
       click_button('Add comment now')
     end
 
-    # Add discussion to first line of last file
-    click_diff_line(find('.diff-file.file-holder:last-of-type .line_holder .left-side:first-of-type'))
-    page.within('.js-discussion-note-form') do
+    click_diff_line(get_second_diff.find('[data-testid="left-side"]', match: :first))
+    page.within get_second_diff.find('.js-discussion-note-form') do
       fill_in('note_note', with: 'Last Line Comment')
       click_button('Add comment now')
     end
@@ -36,17 +34,14 @@
     # Reload so we know the discussions are persisting across batch loads
     visit page.current_url
 
-    # Wait for JS to settle
     wait_for_requests
 
-    expect(page).to have_selector('.diff-files-holder .file-holder', count: 39)
-
     # Confirm discussions are applied to appropriate files (should be contained in multiple diff pages)
-    page.within('.diff-file.file-holder:first-of-type .notes .timeline-entry .note .note-text') do
+    page.within get_first_diff.find('.notes .timeline-entry .note .note-text') do
       expect(page).to have_content('First Line Comment')
     end
 
-    page.within('.diff-file.file-holder:last-of-type .notes .timeline-entry .note .note-text') do
+    page.within get_second_diff.find('.notes .timeline-entry .note .note-text') do
       expect(page).to have_content('Last Line Comment')
     end
   end
@@ -54,7 +49,7 @@
   context 'when user visits a URL with a link directly to to a discussion' do
     context 'which is in the first batched page of diffs' do
       it 'scrolls to the correct discussion' do
-        page.within('.diff-file.file-holder:first-of-type') do
+        page.within get_first_diff do
           click_link('just now')
         end
 
@@ -63,15 +58,15 @@
         wait_for_requests
 
         # Confirm scrolled to correct UI element
-        expect(page.find('.diff-file.file-holder:first-of-type .discussion-notes .timeline-entry li.note[id]').obscured?).to be_falsey
-        expect(page.find('.diff-file.file-holder:last-of-type .discussion-notes .timeline-entry li.note[id]').obscured?).to be_truthy
+        expect(get_first_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_falsey
+        expect(get_second_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_truthy
       end
     end
 
     context 'which is in at least page 2 of the batched pages of diffs' do
       it 'scrolls to the correct discussion',
          quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/293814' } do
-        page.within('.diff-file.file-holder:last-of-type') do
+        page.within get_first_diff do
           click_link('just now')
         end
 
@@ -80,8 +75,8 @@
         wait_for_requests
 
         # Confirm scrolled to correct UI element
-        expect(page.find('.diff-file.file-holder:first-of-type .discussion-notes .timeline-entry li.note[id]').obscured?).to be_truthy
-        expect(page.find('.diff-file.file-holder:last-of-type .discussion-notes .timeline-entry li.note[id]').obscured?).to be_falsey
+        expect(get_first_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_truthy
+        expect(get_second_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_falsey
       end
     end
   end
@@ -95,15 +90,21 @@
     end
 
     it 'has the correct discussions applied to files across batched pages' do
-      expect(page).to have_selector('.diff-files-holder .file-holder', count: 39)
-
-      page.within('.diff-file.file-holder:first-of-type .notes .timeline-entry .note .note-text') do
+      page.within get_first_diff.find('.notes .timeline-entry .note .note-text') do
         expect(page).to have_content('First Line Comment')
       end
 
-      page.within('.diff-file.file-holder:last-of-type .notes .timeline-entry .note .note-text') do
+      page.within get_second_diff.find('.notes .timeline-entry .note .note-text') do
         expect(page).to have_content('Last Line Comment')
       end
     end
   end
+
+  def get_first_diff
+    find('#a9b6f940524f646951cc28d954aa41f814f95d4f')
+  end
+
+  def get_second_diff
+    find('#b285a86891571c7fdbf1f82e840816079de1cc8b')
+  end
 end
diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb
index 76431d04e72ff4f31ac998e4b8a0ec420dd1d811..d803aec58950d2811a74ba406a50f97f56843922 100644
--- a/spec/features/merge_request/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb
@@ -29,54 +29,54 @@
 
     context 'with an old line on the left and no line on the right' do
       it 'allows commenting on the left side' do
-        should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_23_22"]'), 'left')
+        should_allow_commenting(find_by_scrolling('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_23_22"]'), 'left')
       end
 
       it 'does not allow commenting on the right side' do
-        should_not_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_23_22"]').find(:xpath, '..'), 'right')
+        should_not_allow_commenting(find_by_scrolling('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_23_22"]').find(:xpath, '..'), 'right')
       end
     end
 
     context 'with no line on the left and a new line on the right' do
       it 'does not allow commenting on the left side' do
-        should_not_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15"]').find(:xpath, '..'), 'left')
+        should_not_allow_commenting(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15"]').find(:xpath, '..'), 'left')
       end
 
       it 'allows commenting on the right side' do
-        should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15"]').find(:xpath, '..'), 'right')
+        should_allow_commenting(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15"]').find(:xpath, '..'), 'right')
       end
     end
 
     context 'with an old line on the left and a new line on the right' do
       it 'allows commenting on the left side', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/199050' do
-        should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"]').find(:xpath, '..'), 'left')
+        should_allow_commenting(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"]').find(:xpath, '..'), 'left')
       end
 
       it 'allows commenting on the right side' do
-        should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"]').find(:xpath, '..'), 'right')
+        should_allow_commenting(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"]').find(:xpath, '..'), 'right')
       end
     end
 
     context 'with an unchanged line on the left and an unchanged line on the right' do
       it 'allows commenting on the left side', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/196826' do
-        should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]', match: :first).find(:xpath, '..'), 'left')
+        should_allow_commenting(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]', match: :first).find(:xpath, '..'), 'left')
       end
 
       it 'allows commenting on the right side' do
-        should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]', match: :first).find(:xpath, '..'), 'right')
+        should_allow_commenting(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]', match: :first).find(:xpath, '..'), 'right')
       end
     end
 
     context 'with a match line' do
       it 'does not allow commenting' do
-        line_holder = find('.match', match: :first)
+        line_holder = find_by_scrolling('.match', match: :first)
         match_should_not_allow_commenting(line_holder)
       end
     end
 
     context 'with an unfolded line' do
       before do
-        page.within('.file-holder[id="a5cc2925ca8258af241be7e5b0381edf30266302"]') do
+        page.within find_by_scrolling('.file-holder[id="a5cc2925ca8258af241be7e5b0381edf30266302"]') do
           find('.js-unfold', match: :first).click
         end
 
@@ -84,12 +84,12 @@
       end
 
       it 'allows commenting on the left side' do
-        should_allow_commenting(first('#a5cc2925ca8258af241be7e5b0381edf30266302 .line_holder [data-testid="left-side"]'))
+        should_allow_commenting(find_by_scrolling('#a5cc2925ca8258af241be7e5b0381edf30266302').first('.line_holder [data-testid="left-side"]'))
       end
 
       it 'allows commenting on the right side' do
         # Automatically shifts comment box to left side.
-        should_allow_commenting(first('#a5cc2925ca8258af241be7e5b0381edf30266302 .line_holder [data-testid="right-side"]'))
+        should_allow_commenting(find_by_scrolling('#a5cc2925ca8258af241be7e5b0381edf30266302').first('.line_holder [data-testid="right-side"]'))
       end
     end
   end
@@ -101,44 +101,44 @@
 
     context 'after deleteing a note' do
       it 'allows commenting' do
-        should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
+        should_allow_commenting(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
 
         accept_gl_confirm(button_text: 'Delete Comment') do
           first('button.more-actions-toggle').click
           first('.js-note-delete').click
         end
 
-        should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
+        should_allow_commenting(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
       end
     end
 
     context 'with a new line' do
       it 'allows commenting' do
-        should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
+        should_allow_commenting(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
       end
     end
 
     context 'with an old line' do
       it 'allows commenting' do
-        should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'))
+        should_allow_commenting(find_by_scrolling('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'))
       end
     end
 
     context 'with an unchanged line' do
       it 'allows commenting' do
-        should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]'))
+        should_allow_commenting(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]'))
       end
     end
 
     context 'with a match line' do
       it 'does not allow commenting' do
-        match_should_not_allow_commenting(find('.match', match: :first))
+        match_should_not_allow_commenting(find_by_scrolling('.match', match: :first))
       end
     end
 
     context 'with an unfolded line' do
       before do
-        page.within('.file-holder[id="a5cc2925ca8258af241be7e5b0381edf30266302"]') do
+        page.within find_by_scrolling('.file-holder[id="a5cc2925ca8258af241be7e5b0381edf30266302"]') do
           find('.js-unfold', match: :first).click
         end
 
@@ -147,7 +147,7 @@
 
       # The first `.js-unfold` unfolds upwards, therefore the first
       # `.line_holder` will be an unfolded line.
-      let(:line_holder) { first('[id="a5cc2925ca8258af241be7e5b0381edf30266302_1_1"]') }
+      let(:line_holder) { find_by_scrolling('[id="a5cc2925ca8258af241be7e5b0381edf30266302_1_1"]') }
 
       it 'allows commenting' do
         should_allow_commenting line_holder
@@ -157,7 +157,7 @@
     context 'when hovering over a diff discussion' do
       before do
         visit diffs_project_merge_request_path(project, merge_request, view: 'inline')
-        should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]'))
+        should_allow_commenting(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]'))
         visit project_merge_request_path(project, merge_request)
       end
 
@@ -174,7 +174,7 @@
 
     context 'with a new line' do
       it 'allows dismissing a comment' do
-        should_allow_dismissing_a_comment(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
+        should_allow_dismissing_a_comment(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
       end
     end
   end
@@ -182,13 +182,13 @@
   describe 'with multiple note forms' do
     before do
       visit diffs_project_merge_request_path(project, merge_request, view: 'inline')
-      click_diff_line(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
-      click_diff_line(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'))
+      click_diff_line(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
+      click_diff_line(find_by_scrolling('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'))
     end
 
     describe 'posting a note' do
       it 'adds as discussion' do
-        should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'), asset_form_reset: false)
+        should_allow_commenting(find_by_scrolling('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'), asset_form_reset: false)
         expect(page).to have_css('.notes_holder .note.note-discussion', count: 1)
         expect(page).to have_field('Reply…')
       end
@@ -203,25 +203,25 @@
 
     context 'with a new line' do
       it 'allows commenting' do
-        should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
+        should_allow_commenting(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
       end
     end
 
     context 'with an old line' do
       it 'allows commenting' do
-        should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'))
+        should_allow_commenting(find_by_scrolling('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'))
       end
     end
 
     context 'with an unchanged line' do
       it 'allows commenting' do
-        should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]'))
+        should_allow_commenting(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]'))
       end
     end
 
     context 'with a match line' do
       it 'does not allow commenting' do
-        match_should_not_allow_commenting(find('.match', match: :first))
+        match_should_not_allow_commenting(find_by_scrolling('.match', match: :first))
       end
     end
   end
diff --git a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
index 261d1ad325bd6e7100f482bb1db8f27159e4c9ac..33c5a936b8d2c67853faf54e3f12e9d12c30d077 100644
--- a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
@@ -6,6 +6,7 @@
 RSpec.describe 'Merge request > User sees avatars on diff notes', :js do
   include NoteInteractionHelpers
   include Spec::Support::Helpers::ModalHelpers
+  include MergeRequestDiffHelpers
 
   let(:project)       { create(:project, :public, :repository) }
   let(:user)          { project.creator }
@@ -135,6 +136,7 @@
       end
 
       it 'adds avatar when commenting' do
+        find_by_scrolling('[data-discussion-id]', match: :first)
         find_field('Reply…', match: :first).click
 
         page.within '.js-discussion-note-form' do
@@ -154,6 +156,7 @@
 
       it 'adds multiple comments' do
         3.times do
+          find_by_scrolling('[data-discussion-id]', match: :first)
           find_field('Reply…', match: :first).click
 
           page.within '.js-discussion-note-form' do
@@ -192,7 +195,7 @@
   end
 
   def find_line(line_code)
-    line = find("[id='#{line_code}']")
+    line = find_by_scrolling("[id='#{line_code}']")
     line = line.find(:xpath, 'preceding-sibling::*[1][self::td]/preceding-sibling::*[1][self::td]') if line.tag_name == 'td'
     line
   end
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index 5e022598ac3319de6b6dfdf557cb7463904f4352..7cd9ef80874201964fcaa2b7798c5b4621c7269d 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -5,6 +5,7 @@
 RSpec.describe 'Merge request > User sees diff', :js do
   include ProjectForksHelper
   include RepoHelpers
+  include MergeRequestDiffHelpers
 
   let(:project) { create(:project, :public, :repository) }
   let(:merge_request) { create(:merge_request, source_project: project) }
@@ -58,12 +59,12 @@
     let(:changelog_id) { Digest::SHA1.hexdigest("CHANGELOG") }
 
     context 'as author' do
-      it 'shows direct edit link', :sidekiq_might_not_need_inline do
+      it 'contains direct edit link', :sidekiq_might_not_need_inline do
         sign_in(author_user)
         visit diffs_project_merge_request_path(project, merge_request)
 
         # Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
-        expect(page).to have_selector("[id=\"#{changelog_id}\"] .js-edit-blob", visible: false)
+        expect(page).to have_selector(".js-edit-blob", visible: false)
       end
     end
 
@@ -72,6 +73,8 @@
         sign_in(user)
         visit diffs_project_merge_request_path(project, merge_request)
 
+        find_by_scrolling("[id=\"#{changelog_id}\"]")
+
         # Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
         find("[id=\"#{changelog_id}\"] .js-diff-more-actions").click
         find("[id=\"#{changelog_id}\"] .js-edit-blob").click
@@ -107,6 +110,7 @@
           CONTENT
 
         file_name = 'xss_file.rs'
+        file_hash = Digest::SHA1.hexdigest(file_name)
 
         create_file('master', file_name, file_content)
         merge_request = create(:merge_request, source_project: project)
@@ -116,6 +120,8 @@
 
         visit diffs_project_merge_request_path(project, merge_request)
 
+        find_by_scrolling("[id='#{file_hash}']")
+
         expect(page).to have_text("function foo<input> {")
         expect(page).to have_css(".line[lang='rust'] .k")
       end
@@ -136,7 +142,7 @@
         end
 
         context 'when file is an image', :js do
-          let(:file_name) { 'files/lfs/image.png' }
+          let(:file_name) { 'a/image.png' }
 
           it 'shows an error message' do
             expect(page).not_to have_content('could not be displayed because it is stored in LFS')
@@ -144,7 +150,7 @@
         end
 
         context 'when file is not an image' do
-          let(:file_name) { 'files/lfs/ruby.rb' }
+          let(:file_name) { 'a/ruby.rb' }
 
           it 'shows an error message' do
             expect(page).to have_content('This source diff could not be displayed because it is stored in LFS')
@@ -153,7 +159,14 @@
       end
 
       context 'when LFS is not enabled' do
+        let(:file_name) { 'a/lfs_object.iso' }
+
         before do
+          allow(Gitlab.config.lfs).to receive(:disabled).and_return(true)
+          project.update_attribute(:lfs_enabled, false)
+
+          create_file('master', file_name, project.repository.blob_at('master', 'files/lfs/lfs_object.iso').data)
+
           visit diffs_project_merge_request_path(project, merge_request)
         end
 
diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb
index 5abf4e2f5adb795129cc12d077e1a5cac164276b..2b856811e0262022a131225bc70a8aa4498c3fd8 100644
--- a/spec/features/merge_request/user_sees_versions_spec.rb
+++ b/spec/features/merge_request/user_sees_versions_spec.rb
@@ -3,6 +3,8 @@
 require 'spec_helper'
 
 RSpec.describe 'Merge request > User sees versions', :js do
+  include MergeRequestDiffHelpers
+
   let(:merge_request) do
     create(:merge_request).tap do |mr|
       mr.merge_request_diff.destroy!
@@ -27,8 +29,12 @@
       diff_file_selector = ".diff-file[id='#{file_id}']"
       line_code = "#{file_id}_#{line_code}"
 
-      page.within(diff_file_selector) do
-        first("[id='#{line_code}']").hover
+      page.within find_by_scrolling(diff_file_selector) do
+        line_code_element = first("[id='#{line_code}']")
+        # scrolling to element's bottom is required in order for .hover action to work
+        # otherwise, the element could be hidden underneath a sticky header
+        scroll_to_elements_bottom(line_code_element)
+        line_code_element.hover
         first("[id='#{line_code}'] [role='button']").click
 
         page.within("form[data-line-code='#{line_code}']") do
diff --git a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
index 690a292937ab55a6ef03a176ec683998096d00b6..beb658bb7a0b4495ee585ba2641fd4b74f4ebc00 100644
--- a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
+++ b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
@@ -34,7 +34,7 @@ def expect_appliable_suggestions(amount)
 
   context 'single suggestion note' do
     it 'hides suggestion popover' do
-      click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
+      click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']"))
 
       expect(page).to have_selector('.diff-suggest-popover')
 
@@ -46,7 +46,7 @@ def expect_appliable_suggestions(amount)
     end
 
     it 'suggestion is presented' do
-      click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
+      click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']"))
 
       page.within('.js-discussion-note-form') do
         fill_in('note_note', with: "```suggestion\n# change to a comment\n```")
@@ -74,7 +74,7 @@ def expect_appliable_suggestions(amount)
     end
 
     it 'allows suggestions in replies' do
-      click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
+      click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']"))
 
       page.within('.js-discussion-note-form') do
         fill_in('note_note', with: "```suggestion\n# change to a comment\n```")
@@ -91,7 +91,7 @@ def expect_appliable_suggestions(amount)
     end
 
     it 'suggestion is appliable' do
-      click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
+      click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']"))
 
       page.within('.js-discussion-note-form') do
         fill_in('note_note', with: "```suggestion\n# change to a comment\n```")
@@ -273,7 +273,7 @@ def hash(path)
 
   context 'multiple suggestions in a single note' do
     it 'suggestions are presented', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/258989' do
-      click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
+      click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']"))
 
       page.within('.js-discussion-note-form') do
         fill_in('note_note', with: "```suggestion\n# change to a comment\n```\n```suggestion:-2\n# or that\n# heh\n```")
@@ -316,7 +316,7 @@ def hash(path)
 
   context 'multi-line suggestions' do
     before do
-      click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
+      click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']"))
 
       page.within('.js-discussion-note-form') do
         fill_in('note_note', with: "```suggestion:-3+5\n# change to a\n# comment\n# with\n# broken\n# lines\n```")
diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js
index dda758898e3511d14e7c376dce327a65cc2747d6..76e4a944d87d3627daedf96a250e2ea03d8d3321 100644
--- a/spec/frontend/diffs/components/app_spec.js
+++ b/spec/frontend/diffs/components/app_spec.js
@@ -69,6 +69,12 @@ describe('diffs/components/app', () => {
       },
       provide,
       store,
+      stubs: {
+        DynamicScroller: {
+          template: `<div><slot :item="$store.state.diffs.diffFiles[0]"></slot></div>`,
+        },
+        DynamicScrollerItem: true,
+      },
     });
   }
 
diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js
index c53789b480095d7c3cd036a593eb0900cc656a92..d6a2aa104cdc13e427b80e8551d65c13af4900b8 100644
--- a/spec/frontend/diffs/store/actions_spec.js
+++ b/spec/frontend/diffs/store/actions_spec.js
@@ -202,7 +202,7 @@ describe('DiffsStoreActions', () => {
       testAction(
         fetchDiffFilesBatch,
         {},
-        { endpointBatch, diffViewType: 'inline' },
+        { endpointBatch, diffViewType: 'inline', diffFiles: [] },
         [
           { type: types.SET_BATCH_LOADING_STATE, payload: 'loading' },
           { type: types.SET_RETRIEVING_BATCHES, payload: true },
diff --git a/spec/frontend/notes/mixins/discussion_navigation_spec.js b/spec/frontend/notes/mixins/discussion_navigation_spec.js
index 1f71947b3cdd01034165298dde2d679165f59de8..aba80789a013b1310c9b682d4cfeecaae1e13f3c 100644
--- a/spec/frontend/notes/mixins/discussion_navigation_spec.js
+++ b/spec/frontend/notes/mixins/discussion_navigation_spec.js
@@ -143,7 +143,7 @@ describe('Discussion navigation mixin', () => {
         it('scrolls to element', () => {
           expect(utils.scrollToElement).toHaveBeenCalledWith(
             findDiscussion('div.discussion', expected),
-            { behavior: 'smooth' },
+            { behavior: 'auto' },
           );
         });
       });
@@ -171,7 +171,7 @@ describe('Discussion navigation mixin', () => {
 
           expect(utils.scrollToElementWithContext).toHaveBeenCalledWith(
             findDiscussion('ul.notes', expected),
-            { behavior: 'smooth' },
+            { behavior: 'auto' },
           );
         });
       });
@@ -212,21 +212,15 @@ describe('Discussion navigation mixin', () => {
           it('scrolls to discussion', () => {
             expect(utils.scrollToElement).toHaveBeenCalledWith(
               findDiscussion('div.discussion', expected),
-              { behavior: 'smooth' },
+              { behavior: 'auto' },
             );
           });
         });
       });
     });
 
-    describe.each`
-      diffsVirtualScrolling
-      ${false}
-      ${true}
-    `('virtual scrolling feature is $diffsVirtualScrolling', ({ diffsVirtualScrolling }) => {
+    describe('virtual scrolling feature', () => {
       beforeEach(() => {
-        window.gon = { features: { diffsVirtualScrolling } };
-
         jest.spyOn(store, 'dispatch');
 
         store.state.notes.currentDiscussionId = 'a';
@@ -238,22 +232,22 @@ describe('Discussion navigation mixin', () => {
         window.location.hash = '';
       });
 
-      it('resets location hash if diffsVirtualScrolling flag is true', async () => {
+      it('resets location hash', async () => {
         wrapper.vm.jumpToNextDiscussion();
 
         await nextTick();
 
-        expect(window.location.hash).toBe(diffsVirtualScrolling ? '' : '#test');
+        expect(window.location.hash).toBe('');
       });
 
       it.each`
-        tabValue   | hashValue
-        ${'diffs'} | ${false}
-        ${'show'}  | ${!diffsVirtualScrolling}
-        ${'other'} | ${!diffsVirtualScrolling}
+        tabValue
+        ${'diffs'}
+        ${'show'}
+        ${'other'}
       `(
         'calls scrollToFile with setHash as $hashValue when the tab is $tabValue',
-        async ({ hashValue, tabValue }) => {
+        async ({ tabValue }) => {
           window.mrTabs.currentAction = tabValue;
 
           wrapper.vm.jumpToNextDiscussion();
@@ -262,7 +256,6 @@ describe('Discussion navigation mixin', () => {
 
           expect(store.dispatch).toHaveBeenCalledWith('diffs/scrollToFile', {
             path: 'test.js',
-            setHash: hashValue,
           });
         },
       );
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index f823683c274b234d54704070b2266d75618752f8..37e9ef1d994ed952a5e2b8e3c0e4f5420d9eba10 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -294,8 +294,6 @@
       # See https://gitlab.com/gitlab-org/gitlab/-/issues/33867
       stub_feature_flags(file_identifier_hash: false)
 
-      stub_feature_flags(diffs_virtual_scrolling: false)
-
       # The following `vue_issues_list` stub can be removed
       # once the Vue issues page has feature parity with the current Haml page
       stub_feature_flags(vue_issues_list: false)
diff --git a/spec/support/helpers/merge_request_diff_helpers.rb b/spec/support/helpers/merge_request_diff_helpers.rb
index 30afde7efede6baa4eefd544ffc6130ef4de1590..7515c789addeb8bd182d227d93876491fcd077b1 100644
--- a/spec/support/helpers/merge_request_diff_helpers.rb
+++ b/spec/support/helpers/merge_request_diff_helpers.rb
@@ -1,8 +1,11 @@
 # frozen_string_literal: true
 
 module MergeRequestDiffHelpers
+  PageEndReached = Class.new(StandardError)
+
   def click_diff_line(line_holder, diff_side = nil)
     line = get_line_components(line_holder, diff_side)
+    scroll_to_elements_bottom(line_holder)
     line_holder.hover
     line[:num].find('.js-add-diff-note-button').click
   end
@@ -27,4 +30,55 @@ def get_parallel_line_components(line_holder, diff_side = nil)
     line_holder.find('.diff-line-num', match: :first)
     { content: line_holder.all('.line_content')[side_index], num: line_holder.all('.diff-line-num')[side_index] }
   end
+
+  def has_reached_page_end
+    evaluate_script("(window.innerHeight + window.scrollY) >= document.body.offsetHeight")
+  end
+
+  def scroll_to_elements_bottom(element)
+    evaluate_script("(function(el) {
+      window.scrollBy(0, el.getBoundingClientRect().bottom - window.innerHeight);
+    })(arguments[0]);", element.native)
+  end
+
+  # we're not using Capybara's .obscured here because it also checks if the element is clickable
+  def within_viewport?(element)
+    evaluate_script("(function(el) {
+      var rect = el.getBoundingClientRect();
+      return (
+        rect.bottom >= 0 &&
+        rect.right >= 0 &&
+        rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
+        rect.left <= (window.innerWidth || document.documentElement.clientWidth)
+      );
+    })(arguments[0]);", element.native)
+  end
+
+  def find_within_viewport(selector, **options)
+    begin
+      element = find(selector, **options, wait: 2)
+    rescue Capybara::ElementNotFound
+      return
+    end
+    return element if within_viewport?(element)
+
+    nil
+  end
+
+  def find_by_scrolling(selector, **options)
+    element = find_within_viewport(selector, **options)
+    return element if element
+
+    page.execute_script "window.scrollTo(0,0)"
+    until element
+
+      if has_reached_page_end
+        raise PageEndReached, "Failed to find any elements matching a selector '#{selector}' by scrolling. Page end reached."
+      end
+
+      page.execute_script "window.scrollBy(0,window.innerHeight/1.5)"
+      element = find_within_viewport(selector, **options)
+    end
+    element
+  end
 end
diff --git a/spec/support/helpers/note_interaction_helpers.rb b/spec/support/helpers/note_interaction_helpers.rb
index a4322618cd35768e5280cd45bb46d016bb30983f..fa2705a64fa13fbd1510f5a887d50898c9ff1277 100644
--- a/spec/support/helpers/note_interaction_helpers.rb
+++ b/spec/support/helpers/note_interaction_helpers.rb
@@ -1,8 +1,10 @@
 # frozen_string_literal: true
 
 module NoteInteractionHelpers
+  include MergeRequestDiffHelpers
+
   def open_more_actions_dropdown(note)
-    note_element = find("#note_#{note.id}")
+    note_element = find_by_scrolling("#note_#{note.id}")
 
     note_element.find('.more-actions-toggle').click
     note_element.find('.more-actions .dropdown-menu li', match: :first)
diff --git a/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb b/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb
index e0a032b1a43d57cd2cdd5eacce08ebac27746c66..8a07e52019cb75e4492f8f85fec74da0e1aaa098 100644
--- a/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb
+++ b/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb
@@ -2,7 +2,7 @@
 
 RSpec.shared_examples 'comment on merge request file' do
   it 'adds a comment' do
-    click_diff_line(find("[id='#{sample_commit.line_code}']"))
+    click_diff_line(find_by_scrolling("[id='#{sample_commit.line_code}']"))
 
     page.within('.js-discussion-note-form') do
       fill_in(:note_note, with: 'Line is wrong')