diff --git a/reviewboard/static/rb/js/reviewRequestPage/models/entryModel.es6.js b/reviewboard/static/rb/js/reviewRequestPage/models/entryModel.es6.js
index cad4783987efc4c5e6d792177a0dd759ec925428..ed8cc0c41d7786133f1c38be0c9bb9519482ddac 100644
--- a/reviewboard/static/rb/js/reviewRequestPage/models/entryModel.es6.js
+++ b/reviewboard/static/rb/js/reviewRequestPage/models/entryModel.es6.js
@@ -19,6 +19,9 @@
  *
  *         This is used along with ``updatedTimestamp`` to determine if an
  *         entry has new content.
+ * 
+ *     loaded (boolean):
+ *         Whether this entry has been loaded.
  *
  *     page (RB.ReviewRequestPage):
  *         The page that owns this entry.
@@ -41,6 +44,7 @@ RB.ReviewRequestPage.Entry = Backbone.Model.extend({
         addedTimestamp: null,
         collapsed: false,
         etag: null,
+        loaded: false,
         page: null,
         reviewRequestEditor: null,
         typeID: null,
@@ -66,6 +70,7 @@ RB.ReviewRequestPage.Entry = Backbone.Model.extend({
                             ? attrs.addedTimestamp
                             : moment.utc(attrs.addedTimestamp).toDate(),
             etag: attrs.etag || null,
+            loaded: attrs.loaded,
             updatedTimestamp: _.isDate(attrs.updatedTimestamp)
                               ? attrs.updatedTimestamp
                               : moment.utc(attrs.updatedTimestamp).toDate(),
@@ -152,4 +157,17 @@ RB.ReviewRequestPage.Entry = Backbone.Model.extend({
     stopWatchingUpdates() {
         this.get('page').stopWatchingEntryUpdates(this);
     },
+
+    /**
+     * Ensure entry content has been loaded.
+     *
+     * For the collapsed entry of type review, we check if it has
+     * been loaded. If not, we call fetchEntryUpdates to load 
+     * the entry's attributes and HTML.
+     */
+    ensureContentLoaded() {
+        if (!this.loaded) {
+            this.get('page').fetchEntryUpdates(this);
+        }
+    },
 });
diff --git a/reviewboard/static/rb/js/reviewRequestPage/models/reviewRequestPageModel.es6.js b/reviewboard/static/rb/js/reviewRequestPage/models/reviewRequestPageModel.es6.js
index fb3ea3025209a2e7aa0880f908e8e11aa01883a5..518a69f4ff49b73659a4fbd2e71c54e842bf1902 100644
--- a/reviewboard/static/rb/js/reviewRequestPage/models/reviewRequestPageModel.es6.js
+++ b/reviewboard/static/rb/js/reviewRequestPage/models/reviewRequestPageModel.es6.js
@@ -158,6 +158,40 @@ RB.ReviewRequestPage.ReviewRequestPage = RB.ReviewablePage.extend({
         }
     },
 
+    /**
+     * Give immediate update to an entry.
+     *
+     * This function will be triggered when trying to expand a collapsed
+     * entry that doesn't have content loaded beforehand.  This function
+     * will first locate the existing entry on the page, and then update
+     * the entry's attributes and HTML.
+     *
+     * Args:
+     *     entry (RB.ReviewRequestPage.Entry):
+     *         The entry needs for immediate updates.
+     */
+     fetchEntryUpdates(entry) {
+        const updatesURL = this.get('updatesURL');
+        const typeID = entry.get('typeID');
+        const urlQueryStr = `?entries=${typeID}:${entry.id}`;
+
+        Backbone.sync(
+            'read',
+            this,
+            {
+                url: `${updatesURL}${urlQueryStr}`,
+                dataType: 'arraybuffer',
+                noActivityIndicator: true,
+                success: arrayBuffer => {
+                    const dataView = new DataView(arrayBuffer);
+                    const parsed = this._processUpdateFromPayload(arrayBuffer, dataView, 0);
+                    parsed.load((metadata, html) =>
+                        this._reloadFromUpdate(entry, metadata, html));
+                }
+            });
+        this.trigger('updatesProcessed');
+    },
+
     /**
      * Schedule the next updates check.
      *
@@ -165,7 +199,7 @@ RB.ReviewRequestPage.ReviewRequestPage = RB.ReviewablePage.extend({
      * being watched. Any data returned in the check will trigger reloads
      * of parts of the page.
      */
-    _scheduleCheckUpdates() {
+     _scheduleCheckUpdates() {
         if (this._watchedUpdatesTimeout !== null ||
             this._watchedUpdatesPeriodMS === null) {
             return;
@@ -466,4 +500,4 @@ RB.ReviewRequestPage.ReviewRequestPage = RB.ReviewablePage.extend({
 
         this.trigger(`appliedUpdate:${metadata.type}`, metadata, html);
     },
-});
+});
\ No newline at end of file
diff --git a/reviewboard/static/rb/js/reviewRequestPage/views/entryView.es6.js b/reviewboard/static/rb/js/reviewRequestPage/views/entryView.es6.js
index 56ae8962589da720ec29964412dc0ec20a7c522d..d356859746addd178e477eb781dfe9bc71f5107b 100644
--- a/reviewboard/static/rb/js/reviewRequestPage/views/entryView.es6.js
+++ b/reviewboard/static/rb/js/reviewRequestPage/views/entryView.es6.js
@@ -4,6 +4,7 @@
 RB.ReviewRequestPage.EntryView = Backbone.View.extend({
     events: {
         'click .collapse-button': '_onToggleCollapseClicked',
+        'mouseover .collapse-button': '_onToggleCollapseMouseover',
     },
 
     /**
@@ -47,6 +48,7 @@ RB.ReviewRequestPage.EntryView = Backbone.View.extend({
             .addClass('rb-icon-collapse-review');
 
         this.model.set('collapsed', false);
+        this.model.set('loaded', true);
     },
 
     /**
@@ -104,4 +106,13 @@ RB.ReviewRequestPage.EntryView = Backbone.View.extend({
             this.collapse();
         }
     },
+
+    /**
+     * Handle a mouseover on the collapse button.
+     *
+     * Ensure entry content has been loaded.
+     */
+    _onToggleCollapseMouseover() {
+        this.model.ensureContentLoaded();
+    },
 });
diff --git a/reviewboard/static/rb/js/reviewRequestPage/views/reviewRequestPageView.es6.js b/reviewboard/static/rb/js/reviewRequestPage/views/reviewRequestPageView.es6.js
index a0f97881fabadc67bcac1f8536f37fbba2b02572..9334fde73d4e9a8911b6c530016aa77b18be12e5 100644
--- a/reviewboard/static/rb/js/reviewRequestPage/views/reviewRequestPageView.es6.js
+++ b/reviewboard/static/rb/js/reviewRequestPage/views/reviewRequestPageView.es6.js
@@ -18,6 +18,7 @@ RB.ReviewRequestPage.ReviewRequestPageView = RB.ReviewablePageView.extend({
     events: _.extend({
         'click #collapse-all': '_onCollapseAllClicked',
         'click #expand-all': '_onExpandAllClicked',
+        'mouseover #expand-all': '_onExpandAllMouseover',
     }, RB.ReviewablePageView.prototype.events),
 
     /**
@@ -351,6 +352,22 @@ RB.ReviewRequestPage.ReviewRequestPageView = RB.ReviewablePageView.extend({
         this._entryViews.forEach(entryView => entryView.expand());
     },
 
+    /**
+     * Handle a mouseover on the Expand All button.
+     *
+     * Ensure entry content has been loaded.
+     *
+     * Args:
+     *     e (Event):
+     *         The event which triggered the action.
+     */
+     _onExpandAllMouseover(e) {
+        e.preventDefault();
+        e.stopPropagation();
+
+        this._entryViews.forEach(entryView => entryView.model.ensureContentLoaded());
+    },
+
     /**
      * Handler for when an issue in the issue summary table is clicked.
      *
diff --git a/reviewboard/static/rb/js/reviewRequestPage/views/tests/entryViewTests.es6.js b/reviewboard/static/rb/js/reviewRequestPage/views/tests/entryViewTests.es6.js
new file mode 100644
index 0000000000000000000000000000000000000000..ea9999dca74cbcd7a9c748c597068d6b25af87f2
--- /dev/null
+++ b/reviewboard/static/rb/js/reviewRequestPage/views/tests/entryViewTests.es6.js
@@ -0,0 +1,154 @@
+suite('rb/reviewRequestPage/views/entryView', function() {
+    const templateWithoutBody = dedent`
+        <div id="review123"
+             class="review-request-page-entry review has-avatar">
+         <div class="review-request-page-entry-contents">
+          <div class="header">
+           <div class="collapse-button btn"><div class="rb-icon
+                rb-icon-collapse-review"></div></div>
+            <div class="header-details">
+             <div class="summary"> <span
+                  class="review-request-page-entry-title">
+              <a href="/users/cynthialan/" class="user">Lanhui Shi</a>
+                 </span></div>
+              <a href="#review123" class="timestamp">
+                 <time class="timesince"
+                 datetime="2021-11-07T01:12:44.628904+00:00"></time></a>
+             </div>
+            </div>
+          <div class="banners"></div>
+         </div>
+        </div>
+    `;
+    const templateWithBody = dedent`
+        <div id="review123"
+             class="review-request-page-entry review has-avatar">
+         <div class="review-request-page-entry-contents">
+          <div class="header">
+           <div class="collapse-button btn"><div class="rb-icon
+                rb-icon-collapse-review"></div></div>
+            <div class="header-details">
+             <div class="summary"> <span
+                  class="review-request-page-entry-title">
+              <a href="/users/cynthialan/" class="user">Lanhui Shi</a>
+                 </span></div>
+              <a href="#review123" class="timestamp">
+                 <time class="timesince"
+                 datetime="2021-11-07T01:12:44.628904+00:00"></time></a>
+             </div>
+            </div>
+          <div class="banners"></div>
+          <div class="body"></div>
+           <ol class="review-comments">
+            <li>
+             <div class="review-comment-details">
+              <div class="review-comment">
+               <pre class="reviewtext body_top"></pre>
+              </div>
+             </div>
+             <div class="review-comment-thread">
+              <div class="comment-section"
+                   data-context-type="body_top"
+                   data-reply-anchor-prefix="header-reply">
+               <a class="add_comment_link"></a>
+               <ul class="reply-comments">
+                <li class="draft" data-comment-id="456">
+                 <pre class="reviewtext"></pre>
+                </li>
+               </ul>
+              </div>
+             </div>
+            </li>
+            <li>
+             <div class="review-comment-thread">
+              <div class="comment-section" data-context-id="123"
+                   data-context-type="diff_comments"
+                   data-reply-anchor-prefix="comment">
+               <a class="add_comment_link"></a>
+               <ul class="reply-comments"></ul>
+              </div>
+             </div>
+            </li>
+            <li>
+             <div class="review-comment-details">
+              <div class="review-comment">
+               <pre class="reviewtext body_bottom"></pre>
+              </div>
+             </div>
+             <div class="review-comment-thread">
+              <div class="comment-section"
+                   data-context-type="body_bottom"
+                   data-reply-anchor-prefix="footer-reply">
+               <a class="add_comment_link"></a>
+               <ul class="reply-comments"></ul>
+              </div>
+             </div>
+            </div>
+           </li>
+          </ol>
+         </div>
+        </div>
+    `;
+
+    describe('Actions', function() {
+        it('loading entry content for entry with typeID: review', function() {
+            const model = new RB.ReviewRequestPage.ReviewEntry({
+                id: '123',
+                typeID: 'review',
+            });
+            const $el = $(templateWithoutBody).appendTo($testsScratch);
+            const view = new RB.ReviewRequestPage.EntryView({
+                el: $el,
+                model: model,
+            });
+
+            view.render();
+            spyOn(view.model, 'fetchUpdates');
+            spyOn(view.model, 'ensureContentLoaded').and.callThrough();
+
+            view.$('.collapse-button').mouseover();
+            expect(view.model.ensureContentLoaded).toHaveBeenCalled();
+            expect(view.model.fetchUpdates).toHaveBeenCalled();
+        });
+
+        it('loading entry content for entry with typeID: initial_status_updates', function() {
+            const model = new RB.ReviewRequestPage.ReviewEntry({
+                id: '123456',
+                typeID: 'initial_status_updates',
+            });
+            const $el = $(templateWithoutBody).appendTo($testsScratch);
+            const view = new RB.ReviewRequestPage.EntryView({
+                el: $el,
+                model: model,
+            });
+
+            view.render();
+            spyOn(view.model, 'fetchUpdates');
+            spyOn(view.model, 'ensureContentLoaded').and.callThrough();
+
+            view.$('.collapse-button').mouseover();
+            expect(view.model.ensureContentLoaded).toHaveBeenCalled();
+            expect(view.model.fetchUpdates).not.toHaveBeenCalled();
+        });
+
+        it('expand entry that already has content loaded', function() {
+            const model = new RB.ReviewRequestPage.ReviewEntry({
+                id: '123',
+                typeID: 'review',
+            });
+            const $el_with_body = $(templateWithBody).appendTo($testsScratch);
+            const view = new RB.ReviewRequestPage.EntryView({
+                el: $el_with_body,
+                model: model,
+            });
+
+            view.render();
+            spyOn(view.model, 'fetchUpdates');
+            spyOn(view.model, 'ensureContentLoaded').and.callThrough();
+
+            view.$('.collapse-button').mouseover();
+            expect(view.model.ensureContentLoaded).toHaveBeenCalled();
+            expect(view.model.fetchUpdates).not.toHaveBeenCalled();
+        });
+    });
+});
diff --git a/reviewboard/staticbundles.py b/reviewboard/staticbundles.py
index 1ce2c2aabc6e2521ce0d93d4025612c95947e5ea..cc2b1623afc10bfef571e85129ed0e47caf5ed2b 100644
--- a/reviewboard/staticbundles.py
+++ b/reviewboard/staticbundles.py
@@ -119,6 +119,7 @@ PIPELINE_JAVASCRIPT = dict({
             'rb/js/reviewRequestPage/models/tests/reviewRequestPageModelTests.es6.js',
             'rb/js/reviewRequestPage/models/tests/statusUpdatesEntryModelTests.es6.js',
             'rb/js/reviewRequestPage/views/tests/baseStatusUpdatesEntryViewTests.es6.js',
+            'rb/js/reviewRequestPage/views/tests/entryViewTests.es6.js',
             'rb/js/reviewRequestPage/views/tests/issueSummaryTableViewTests.es6.js',
             'rb/js/reviewRequestPage/views/tests/reviewEntryViewTests.es6.js',
             'rb/js/reviewRequestPage/views/tests/reviewReplyDraftBannerViewTests.es6.js',
diff --git a/reviewboard/templates/reviews/entries/_review_body.html b/reviewboard/templates/reviews/entries/_review_body.html
index 6bbec8db5061f1b6eca75169448b2f186fa807b2..50cd5c584eb2445df0d21fc6c47d2739e41c3f24 100644
--- a/reviewboard/templates/reviews/entries/_review_body.html
+++ b/reviewboard/templates/reviews/entries/_review_body.html
@@ -1,6 +1,6 @@
 {% load djblets_utils i18n rb_extensions reviewtags %}
-
-{% if review.body_top or always_show_body_top %}
+{% if not entry.collapsed %}
+{%  if review.body_top or always_show_body_top %}
 <li>
  <div class="review-comment-details {{review.body_top|yesno:',comment-details-empty'}}">
   <div class="review-comment">
@@ -12,59 +12,59 @@
   {% reply_section review '' 'body_top' 'rcbt' review.body_top %}
  </div>
 </li>
-{% endif %}
+{%  endif %}
 
 {# General comments #}
-{% for comment in general_comments %}
-{%  include "reviews/entries/_review_comment.html" with comment_type="general_comments" %}
-{% endfor %}
+{%  for comment in general_comments %}
+{%   include "reviews/entries/_review_comment.html" with comment_type="general_comments" %}
+{%  endfor %}
 
 {# Screenshot comments #}
-{% for comment in screenshot_comments %}
-{%  definevar "comment_context" %}
+{%  for comment in screenshot_comments %}
+{%   definevar "comment_context" %}
  <div class="review-comment-screenshot">
   <a class="filename" href="{{comment.screenshot.get_absolute_url}}">
    {{comment.screenshot.caption|default_if_none:comment.screenshot.filename}}
   </a>
   <div class="thumbnail">{{comment.image|safe}}</div>
  </div>
-{%  enddefinevar %}
-{%  include "reviews/entries/_review_comment.html" with comment_type="screenshot_comments" %}
-{% endfor %}
+{%   enddefinevar %}
+{%   include "reviews/entries/_review_comment.html" with comment_type="screenshot_comments" %}
+{%  endfor %}
 
 {# File attachment comments #}
-{% for comment in file_attachment_comments %}
-{%  definevar "comment_context" %}
+{%  for comment in file_attachment_comments %}
+{%   definevar "comment_context" %}
  <div class="review-comment-file-attachment">
   <a class="filename" href="{{comment.get_absolute_url}}">
    {{comment.get_link_text}}
-{%  if comment.file_attachment.attachment_revision %}
+{%   if comment.file_attachment.attachment_revision %}
    <span class="diffrevision">
-{%   if comment.diff_against_file_attachment %}
-{%    blocktrans with revision1=comment.diff_against_file_attachment.attachment_revision revision2=comment.file_attachment.attachment_revision %}
+{%    if comment.diff_against_file_attachment %}
+{%     blocktrans with revision1=comment.diff_against_file_attachment.attachment_revision revision2=comment.file_attachment.attachment_revision %}
     (Revisions {{revision1}} - {{revision2}})
-{%    endblocktrans %}
-{%   else %}
-{%    blocktrans with revision=comment.file_attachment.attachment_revision %}
+{%     endblocktrans %}
+{%    else %}
+{%     blocktrans with revision=comment.file_attachment.attachment_revision %}
     (Revision {{revision}})
-{%    endblocktrans %}
-{%   endif %}
+{%     endblocktrans %}
+{%    endif %}
    </span>
-{%  endif %}
+{%   endif %}
   </a>
-{%  with comment.thumbnail as thumbnail %}
-{%   if thumbnail %}
+{%   with comment.thumbnail as thumbnail %}
+{%    if thumbnail %}
   <div class="thumbnail">{{thumbnail|default:''|safe}}</div>
-{%   endif %}
-{%  endwith %}
+{%    endif %}
+{%   endwith %}
  </div>
-{%  enddefinevar %}
-{%  include "reviews/entries/_review_comment.html" with comment_type="file_attachment_comments" %}
-{% endfor %}
+{%   enddefinevar %}
+{%   include "reviews/entries/_review_comment.html" with comment_type="file_attachment_comments" %}
+{%  endfor %}
 
 {# Diff comments #}
-{% for comment in diff_comments %}
-{%  definevar "comment_context" %}
+{%  for comment in diff_comments %}
+{%   definevar "comment_context" %}
  <div class="review-comment-diff" id="comment_container_{{comment.pk}}">
   <table class="sidebyside loading">
    <thead>
@@ -72,30 +72,30 @@
      <th class="filename">
       <a href="{{comment.get_absolute_url}}">{{comment.filediff.dest_file_display}}</a>
       <span class="diffrevision">
-{%  if comment.interfilediff %}
-{%   blocktrans with revision1=comment.filediff.diffset.revision revision2=comment.interfilediff.diffset.revision %}
+{%   if comment.interfilediff %}
+{%    blocktrans with revision1=comment.filediff.diffset.revision revision2=comment.interfilediff.diffset.revision %}
        (Diff revisions {{revision1}} - {{revision2}})
-{%   endblocktrans %}
-{%  else %}
-{%   blocktrans with revision=comment.filediff.diffset.revision %}
+{%    endblocktrans %}
+{%   else %}
+{%    blocktrans with revision=comment.filediff.diffset.revision %}
        (Diff revision {{revision}})
-{%   endblocktrans %}
-{%  endif %}
+{%    endblocktrans %}
+{%   endif %}
       </span>
      </th>
     </tr>
    </thead>
    <tbody>
     <tr><td><pre>&nbsp;</pre></td></tr>{# header #}
-{%  for i in comment.num_lines|default_if_none:1|range %}
+{%   for i in comment.num_lines|default_if_none:1|range %}
     <tr><td><pre>&nbsp;</pre></td></tr>
-{%  endfor %}
+{%   endfor %}
    </tbody>
   </table>
  </div>
-{%  enddefinevar %}
-{%  include "reviews/entries/_review_comment.html" with comment_type="diff_comments" %}
-{% endfor %}
+{%   enddefinevar %}
+{%   include "reviews/entries/_review_comment.html" with comment_type="diff_comments" %}
+{%  endfor %}
 
 <li{% if not review.body_bottom %} style="display: none;"{% endif %}>
  <div class="review-comment-details">
@@ -108,3 +108,4 @@
   {% reply_section review '' 'body_bottom' 'rcbb' review.body_bottom %}
  </div>
 </li>
+{% endif %}
diff --git a/reviewboard/templates/reviews/entries/entry.js b/reviewboard/templates/reviews/entries/entry.js
index 5ee78160442963f76347704b32455876e5a7fff9..e1b1ef286f2d53a421d6044bd6bc71cfe0bfc2b4 100644
--- a/reviewboard/templates/reviews/entries/entry.js
+++ b/reviewboard/templates/reviews/entries/entry.js
@@ -7,6 +7,7 @@ page.addEntryView(new {{entry.js_view_class}}({
     model: new {{entry.js_model_class}}({
         id: '{{entry.entry_id|escapejs}}',
         collapsed: {{entry.collapsed|yesno:'true,false'}},
+        loaded: {{entry.loaded|yesno:'true,false'}},
         addedTimestamp: {{entry.added_timestamp|json_dumps}},
         updatedTimestamp: {{entry.updated_timestamp|json_dumps}},
         typeID: '{{entry.entry_type_id|escapejs}}',
