diff --git a/app/Activity/CommentRepo.php b/app/Activity/CommentRepo.php
index f16767fcf..2aabab79d 100644
--- a/app/Activity/CommentRepo.php
+++ b/app/Activity/CommentRepo.php
@@ -7,27 +7,14 @@ use BookStack\Entities\Models\Entity;
 use BookStack\Facades\Activity as ActivityService;
 use League\CommonMark\CommonMarkConverter;
 
-/**
- * Class CommentRepo.
- */
 class CommentRepo
 {
-    /**
-     * @var Comment
-     */
-    protected $comment;
-
-    public function __construct(Comment $comment)
-    {
-        $this->comment = $comment;
-    }
-
     /**
      * Get a comment by ID.
      */
     public function getById(int $id): Comment
     {
-        return $this->comment->newQuery()->findOrFail($id);
+        return Comment::query()->findOrFail($id);
     }
 
     /**
@@ -36,7 +23,7 @@ class CommentRepo
     public function create(Entity $entity, string $text, ?int $parent_id): Comment
     {
         $userId = user()->id;
-        $comment = $this->comment->newInstance();
+        $comment = new Comment();
 
         $comment->text = $text;
         $comment->html = $this->commentToHtml($text);
@@ -83,7 +70,7 @@ class CommentRepo
             'allow_unsafe_links' => false,
         ]);
 
-        return $converter->convertToHtml($commentText);
+        return $converter->convert($commentText);
     }
 
     /**
@@ -91,9 +78,8 @@ class CommentRepo
      */
     protected function getNextLocalId(Entity $entity): int
     {
-        /** @var Comment $comment */
-        $comment = $entity->comments(false)->orderBy('local_id', 'desc')->first();
+        $currentMaxId = $entity->comments()->max('local_id');
 
-        return ($comment->local_id ?? 0) + 1;
+        return $currentMaxId + 1;
     }
 }
diff --git a/app/Activity/Tools/CommentTree.php b/app/Activity/Tools/CommentTree.php
new file mode 100644
index 000000000..559edccf3
--- /dev/null
+++ b/app/Activity/Tools/CommentTree.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace BookStack\Activity\Tools;
+
+use BookStack\Activity\Models\Comment;
+use BookStack\Entities\Models\Page;
+
+class CommentTree
+{
+    /**
+     * The built nested tree structure array.
+     * @var array{comment: Comment, depth: int, children: array}[]
+     */
+    protected array $tree;
+    protected array $comments;
+
+    public function __construct(
+        protected Page $page
+    ) {
+        $this->comments = $this->loadComments();
+        $this->tree = $this->createTree($this->comments);
+    }
+
+    public function enabled(): bool
+    {
+        return !setting('app-disable-comments');
+    }
+
+    public function empty(): bool
+    {
+        return count($this->tree) === 0;
+    }
+
+    public function count(): int
+    {
+        return count($this->comments);
+    }
+
+    public function get(): array
+    {
+        return $this->tree;
+    }
+
+    /**
+     * @param Comment[] $comments
+     */
+    protected function createTree(array $comments): array
+    {
+        $byId = [];
+        foreach ($comments as $comment) {
+            $byId[$comment->local_id] = $comment;
+        }
+
+        $childMap = [];
+        foreach ($comments as $comment) {
+            $parent = $comment->parent_id;
+            if (is_null($parent) || !isset($byId[$parent])) {
+                $parent = 0;
+            }
+
+            if (!isset($childMap[$parent])) {
+                $childMap[$parent] = [];
+            }
+            $childMap[$parent][] = $comment->local_id;
+        }
+
+        $tree = [];
+        foreach ($childMap[0] as $childId) {
+            $tree[] = $this->createTreeForId($childId, 0, $byId, $childMap);
+        }
+
+        return $tree;
+    }
+
+    protected function createTreeForId(int $id, int $depth, array &$byId, array &$childMap): array
+    {
+        $childIds = $childMap[$id] ?? [];
+        $children = [];
+
+        foreach ($childIds as $childId) {
+            $children[] = $this->createTreeForId($childId, $depth + 1, $byId, $childMap);
+        }
+
+        return [
+            'comment' => $byId[$id],
+            'depth' => $depth,
+            'children' => $children,
+        ];
+    }
+
+    protected function loadComments(): array
+    {
+        if (!$this->enabled()) {
+            return [];
+        }
+
+        return $this->page->comments()
+            ->with('createdBy')
+            ->get()
+            ->all();
+    }
+}
diff --git a/app/Entities/Controllers/PageController.php b/app/Entities/Controllers/PageController.php
index a6ef68dd7..e0444ecd2 100644
--- a/app/Entities/Controllers/PageController.php
+++ b/app/Entities/Controllers/PageController.php
@@ -3,6 +3,7 @@
 namespace BookStack\Entities\Controllers;
 
 use BookStack\Activity\Models\View;
+use BookStack\Activity\Tools\CommentTree;
 use BookStack\Entities\Models\Page;
 use BookStack\Entities\Repos\PageRepo;
 use BookStack\Entities\Tools\BookContents;
@@ -140,15 +141,10 @@ class PageController extends Controller
 
         $pageContent = (new PageContent($page));
         $page->html = $pageContent->render();
-        $sidebarTree = (new BookContents($page->book))->getTree();
         $pageNav = $pageContent->getNavigation($page->html);
 
-        // Check if page comments are enabled
-        $commentsEnabled = !setting('app-disable-comments');
-        if ($commentsEnabled) {
-            $page->load(['comments.createdBy']);
-        }
-
+        $sidebarTree = (new BookContents($page->book))->getTree();
+        $commentTree = (new CommentTree($page));
         $nextPreviousLocator = new NextPreviousContentLocator($page, $sidebarTree);
 
         View::incrementFor($page);
@@ -159,7 +155,7 @@ class PageController extends Controller
             'book'            => $page->book,
             'current'         => $page,
             'sidebarTree'     => $sidebarTree,
-            'commentsEnabled' => $commentsEnabled,
+            'commentTree'     => $commentTree,
             'pageNav'         => $pageNav,
             'next'            => $nextPreviousLocator->getNext(),
             'previous'        => $nextPreviousLocator->getPrevious(),
diff --git a/resources/sass/_components.scss b/resources/sass/_components.scss
index 1521e6eaa..bd85bb99f 100644
--- a/resources/sass/_components.scss
+++ b/resources/sass/_components.scss
@@ -704,6 +704,13 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   }
 }
 
+.comment-thread-indicator {
+  border-inline-start: 3px dotted #DDD;
+  @include lightDark(border-color, #DDD, #444);
+  margin-inline-start: $-xs;
+  width: $-l;
+}
+
 #tag-manager .drag-card {
   max-width: 500px;
 }
diff --git a/resources/views/comments/comment-branch.blade.php b/resources/views/comments/comment-branch.blade.php
new file mode 100644
index 000000000..d64dd4ade
--- /dev/null
+++ b/resources/views/comments/comment-branch.blade.php
@@ -0,0 +1,17 @@
+<div>
+    <div class="mb-m">
+        @include('comments.comment', ['comment' => $branch['comment']])
+    </div>
+    @if(count($branch['children']) > 0)
+        <div class="flex-container-row">
+            <div class="pb-m">
+                <div class="comment-thread-indicator fill-height"></div>
+            </div>
+            <div class="flex">
+                @foreach($branch['children'] as $childBranch)
+                    @include('comments.comment-branch', ['branch' => $childBranch])
+                @endforeach
+            </div>
+        </div>
+    @endif
+</div>
\ No newline at end of file
diff --git a/resources/views/comments/comment.blade.php b/resources/views/comments/comment.blade.php
index 6189c65d4..093e5a899 100644
--- a/resources/views/comments/comment.blade.php
+++ b/resources/views/comments/comment.blade.php
@@ -1,4 +1,4 @@
-<div class="comment-box mb-m" comment="{{ $comment->id }}" local-id="{{$comment->local_id}}" parent-id="{{$comment->parent_id}}" id="comment{{$comment->local_id}}">
+<div class="comment-box" comment="{{ $comment->id }}" local-id="{{$comment->local_id}}" parent-id="{{$comment->parent_id}}" id="comment{{$comment->local_id}}">
     <div class="header p-s">
         <div class="grid half left-focus no-gap v-center">
             <div class="meta text-muted text-small">
diff --git a/resources/views/comments/comments.blade.php b/resources/views/comments/comments.blade.php
index 140d0d027..f50e3a218 100644
--- a/resources/views/comments/comments.blade.php
+++ b/resources/views/comments/comments.blade.php
@@ -8,8 +8,8 @@
          aria-label="{{ trans('entities.comments') }}">
 
     <div refs="page-comments@commentCountBar" class="grid half left-focus v-center no-row-gap">
-        <h5 comments-title>{{ trans_choice('entities.comment_count', count($page->comments), ['count' => count($page->comments)]) }}</h5>
-        @if (count($page->comments) === 0 && userCan('comment-create-all'))
+        <h5 comments-title>{{ trans_choice('entities.comment_count', $commentTree->count(), ['count' => $commentTree->count()]) }}</h5>
+        @if ($commentTree->empty() && userCan('comment-create-all'))
             <div class="text-m-right" refs="page-comments@addButtonContainer">
                 <button type="button" action="addComment"
                         class="button outline">{{ trans('entities.comment_add') }}</button>
@@ -18,15 +18,15 @@
     </div>
 
     <div refs="page-comments@commentContainer" class="comment-container">
-        @foreach($page->comments as $comment)
-            @include('comments.comment', ['comment' => $comment])
+        @foreach($commentTree->get() as $branch)
+            @include('comments.comment-branch', ['branch' => $branch])
         @endforeach
     </div>
 
     @if(userCan('comment-create-all'))
         @include('comments.create')
 
-        @if (count($page->comments) > 0)
+        @if (!$commentTree->empty())
             <div refs="page-comments@addButtonContainer" class="text-right">
                 <button type="button" action="addComment"
                         class="button outline">{{ trans('entities.comment_add') }}</button>
diff --git a/resources/views/pages/show.blade.php b/resources/views/pages/show.blade.php
index 2cbc7fe47..fa6b1a2cd 100644
--- a/resources/views/pages/show.blade.php
+++ b/resources/views/pages/show.blade.php
@@ -27,7 +27,7 @@
 
     @include('entities.sibling-navigation', ['next' => $next, 'previous' => $previous])
 
-    @if ($commentsEnabled)
+    @if ($commentTree->enabled())
         @if(($previous || $next))
             <div class="px-xl">
                 <hr class="darker">
@@ -35,7 +35,7 @@
         @endif
 
         <div class="px-xl comments-container mb-l print-hidden">
-            @include('comments.comments', ['page' => $page])
+            @include('comments.comments', ['commentTree' => $commentTree, 'page' => $page])
             <div class="clearfix"></div>
         </div>
     @endif