diff --git a/app/Http/Controllers/BookshelfController.php b/app/Http/Controllers/BookshelfController.php
index feb581c78..ccbeb6484 100644
--- a/app/Http/Controllers/BookshelfController.php
+++ b/app/Http/Controllers/BookshelfController.php
@@ -10,22 +10,19 @@ use BookStack\Entities\Tools\PermissionsUpdater;
 use BookStack\Entities\Tools\ShelfContext;
 use BookStack\Exceptions\ImageUploadException;
 use BookStack\Exceptions\NotFoundException;
-use BookStack\Uploads\ImageRepo;
 use Exception;
 use Illuminate\Http\Request;
 use Illuminate\Validation\ValidationException;
 
 class BookshelfController extends Controller
 {
-    protected $bookshelfRepo;
-    protected $entityContextManager;
-    protected $imageRepo;
+    protected BookshelfRepo $shelfRepo;
+    protected ShelfContext $shelfContext;
 
-    public function __construct(BookshelfRepo $bookshelfRepo, ShelfContext $entityContextManager, ImageRepo $imageRepo)
+    public function __construct(BookshelfRepo $shelfRepo, ShelfContext $shelfContext)
     {
-        $this->bookshelfRepo = $bookshelfRepo;
-        $this->entityContextManager = $entityContextManager;
-        $this->imageRepo = $imageRepo;
+        $this->shelfRepo = $shelfRepo;
+        $this->shelfContext = $shelfContext;
     }
 
     /**
@@ -42,12 +39,12 @@ class BookshelfController extends Controller
             'updated_at' => trans('common.sort_updated_at'),
         ];
 
-        $shelves = $this->bookshelfRepo->getAllPaginated(18, $sort, $order);
-        $recents = $this->isSignedIn() ? $this->bookshelfRepo->getRecentlyViewed(4) : false;
-        $popular = $this->bookshelfRepo->getPopular(4);
-        $new = $this->bookshelfRepo->getRecentlyCreated(4);
+        $shelves = $this->shelfRepo->getAllPaginated(18, $sort, $order);
+        $recents = $this->isSignedIn() ? $this->shelfRepo->getRecentlyViewed(4) : false;
+        $popular = $this->shelfRepo->getPopular(4);
+        $new = $this->shelfRepo->getRecentlyCreated(4);
 
-        $this->entityContextManager->clearShelfContext();
+        $this->shelfContext->clearShelfContext();
         $this->setPageTitle(trans('entities.shelves'));
 
         return view('shelves.index', [
@@ -68,7 +65,7 @@ class BookshelfController extends Controller
     public function create()
     {
         $this->checkPermission('bookshelf-create-all');
-        $books = Book::visible()->get();
+        $books = Book::visible()->orderBy('name')->get(['name', 'id', 'slug']);
         $this->setPageTitle(trans('entities.shelves_create'));
 
         return view('shelves.create', ['books' => $books]);
@@ -91,7 +88,7 @@ class BookshelfController extends Controller
         ]);
 
         $bookIds = explode(',', $request->get('books', ''));
-        $shelf = $this->bookshelfRepo->create($validated, $bookIds);
+        $shelf = $this->shelfRepo->create($validated, $bookIds);
 
         return redirect($shelf->getUrl());
     }
@@ -103,7 +100,7 @@ class BookshelfController extends Controller
      */
     public function show(ActivityQueries $activities, string $slug)
     {
-        $shelf = $this->bookshelfRepo->getBySlug($slug);
+        $shelf = $this->shelfRepo->getBySlug($slug);
         $this->checkOwnablePermission('bookshelf-view', $shelf);
 
         $sort = setting()->getForCurrentUser('shelf_books_sort', 'default');
@@ -115,7 +112,7 @@ class BookshelfController extends Controller
             ->all();
 
         View::incrementFor($shelf);
-        $this->entityContextManager->setShelfContext($shelf->id);
+        $this->shelfContext->setShelfContext($shelf->id);
         $view = setting()->getForCurrentUser('bookshelf_view_type');
 
         $this->setPageTitle($shelf->getShortName());
@@ -135,11 +132,11 @@ class BookshelfController extends Controller
      */
     public function edit(string $slug)
     {
-        $shelf = $this->bookshelfRepo->getBySlug($slug);
+        $shelf = $this->shelfRepo->getBySlug($slug);
         $this->checkOwnablePermission('bookshelf-update', $shelf);
 
         $shelfBookIds = $shelf->books()->get(['id'])->pluck('id');
-        $books = Book::visible()->whereNotIn('id', $shelfBookIds)->get();
+        $books = Book::visible()->whereNotIn('id', $shelfBookIds)->orderBy('name')->get(['name', 'id', 'slug']);
 
         $this->setPageTitle(trans('entities.shelves_edit_named', ['name' => $shelf->getShortName()]));
 
@@ -158,7 +155,7 @@ class BookshelfController extends Controller
      */
     public function update(Request $request, string $slug)
     {
-        $shelf = $this->bookshelfRepo->getBySlug($slug);
+        $shelf = $this->shelfRepo->getBySlug($slug);
         $this->checkOwnablePermission('bookshelf-update', $shelf);
         $validated = $this->validate($request, [
             'name'        => ['required', 'string', 'max:255'],
@@ -174,7 +171,7 @@ class BookshelfController extends Controller
         }
 
         $bookIds = explode(',', $request->get('books', ''));
-        $shelf = $this->bookshelfRepo->update($shelf, $validated, $bookIds);
+        $shelf = $this->shelfRepo->update($shelf, $validated, $bookIds);
 
         return redirect($shelf->getUrl());
     }
@@ -184,7 +181,7 @@ class BookshelfController extends Controller
      */
     public function showDelete(string $slug)
     {
-        $shelf = $this->bookshelfRepo->getBySlug($slug);
+        $shelf = $this->shelfRepo->getBySlug($slug);
         $this->checkOwnablePermission('bookshelf-delete', $shelf);
 
         $this->setPageTitle(trans('entities.shelves_delete_named', ['name' => $shelf->getShortName()]));
@@ -199,10 +196,10 @@ class BookshelfController extends Controller
      */
     public function destroy(string $slug)
     {
-        $shelf = $this->bookshelfRepo->getBySlug($slug);
+        $shelf = $this->shelfRepo->getBySlug($slug);
         $this->checkOwnablePermission('bookshelf-delete', $shelf);
 
-        $this->bookshelfRepo->destroy($shelf);
+        $this->shelfRepo->destroy($shelf);
 
         return redirect('/shelves');
     }
@@ -212,7 +209,7 @@ class BookshelfController extends Controller
      */
     public function showPermissions(string $slug)
     {
-        $shelf = $this->bookshelfRepo->getBySlug($slug);
+        $shelf = $this->shelfRepo->getBySlug($slug);
         $this->checkOwnablePermission('restrictions-manage', $shelf);
 
         return view('shelves.permissions', [
@@ -225,7 +222,7 @@ class BookshelfController extends Controller
      */
     public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $slug)
     {
-        $shelf = $this->bookshelfRepo->getBySlug($slug);
+        $shelf = $this->shelfRepo->getBySlug($slug);
         $this->checkOwnablePermission('restrictions-manage', $shelf);
 
         $permissionsUpdater->updateFromPermissionsForm($shelf, $request);
@@ -240,10 +237,10 @@ class BookshelfController extends Controller
      */
     public function copyPermissions(string $slug)
     {
-        $shelf = $this->bookshelfRepo->getBySlug($slug);
+        $shelf = $this->shelfRepo->getBySlug($slug);
         $this->checkOwnablePermission('restrictions-manage', $shelf);
 
-        $updateCount = $this->bookshelfRepo->copyDownPermissions($shelf);
+        $updateCount = $this->shelfRepo->copyDownPermissions($shelf);
         $this->showSuccessNotification(trans('entities.shelves_copy_permission_success', ['count' => $updateCount]));
 
         return redirect($shelf->getUrl());
diff --git a/resources/js/components/shelf-sort.js b/resources/js/components/shelf-sort.js
index 38e8ae8d3..07526716a 100644
--- a/resources/js/components/shelf-sort.js
+++ b/resources/js/components/shelf-sort.js
@@ -2,10 +2,12 @@ import Sortable from "sortablejs";
 
 class ShelfSort {
 
-    constructor(elem) {
-        this.elem = elem;
-        this.input = document.getElementById('books-input');
-        this.shelfBooksList = elem.querySelector('[shelf-sort-assigned-books]');
+    setup() {
+        this.elem = this.$el;
+        this.input = this.$refs.input;
+        this.shelfBookList = this.$refs.shelfBookList;
+        this.allBookList = this.$refs.allBookList;
+        this.bookSearchInput = this.$refs.bookSearch;
 
         this.initSortable();
         this.setupListeners();
@@ -25,12 +27,36 @@ class ShelfSort {
 
     setupListeners() {
         this.elem.addEventListener('click', event => {
-            const sortItem = event.target.closest('.scroll-box-item:not(.instruction)');
+            const sortItem = event.target.closest('.scroll-box-item');
             if (sortItem) {
                 event.preventDefault();
                 this.sortItemClick(sortItem);
             }
         });
+
+        this.bookSearchInput.addEventListener('input', event => {
+            this.filterBooksByName(this.bookSearchInput.value);
+        });
+    }
+
+    /**
+     * @param {String} filterVal
+     */
+    filterBooksByName(filterVal) {
+
+        // Set height on first search, if not already set, to prevent the distraction
+        // of the list height jumping around
+        if (!this.allBookList.style.height) {
+            this.allBookList.style.height = this.allBookList.getBoundingClientRect().height + 'px';
+        }
+
+        const books = this.allBookList.children;
+        const lowerFilter = filterVal.trim().toLowerCase();
+
+        for (const bookEl of books) {
+            const show = !filterVal || bookEl.textContent.toLowerCase().includes(lowerFilter);
+            bookEl.style.display = show ? null : 'none';
+        }
     }
 
     /**
@@ -47,7 +73,7 @@ class ShelfSort {
     }
 
     onChange() {
-        const shelfBookElems = Array.from(this.shelfBooksList.querySelectorAll('[data-id]'));
+        const shelfBookElems = Array.from(this.shelfBookList.querySelectorAll('[data-id]'));
         this.input.value = shelfBookElems.map(elem => elem.getAttribute('data-id')).join(',');
     }
 
diff --git a/resources/lang/en/entities.php b/resources/lang/en/entities.php
index aa353bdac..83e3c12d9 100644
--- a/resources/lang/en/entities.php
+++ b/resources/lang/en/entities.php
@@ -88,7 +88,7 @@ return [
     'shelves_save' => 'Save Shelf',
     'shelves_books' => 'Books on this shelf',
     'shelves_add_books' => 'Add books to this shelf',
-    'shelves_drag_books' => 'Drag books here to add them to this shelf',
+    'shelves_drag_books' => 'Drag books below to add them to this shelf',
     'shelves_empty_contents' => 'This shelf has no books assigned to it',
     'shelves_edit_and_assign' => 'Edit shelf to assign books',
     'shelves_edit_named' => 'Edit Bookshelf :name',
diff --git a/resources/sass/_forms.scss b/resources/sass/_forms.scss
index 73799f0a0..e39f5414f 100644
--- a/resources/sass/_forms.scss
+++ b/resources/sass/_forms.scss
@@ -317,7 +317,8 @@ input[type=color] {
 
 .form-group[collapsible] {
   padding: 0 $-m;
-  border: 1px solid #DDD;
+  border: 1px solid;
+  @include lightDark(border-color, #DDD, #000);
   border-radius: 4px;
   .collapse-title {
     margin-inline-start: -$-m;
diff --git a/resources/sass/styles.scss b/resources/sass/styles.scss
index ee99d7668..65eee866d 100644
--- a/resources/sass/styles.scss
+++ b/resources/sass/styles.scss
@@ -241,28 +241,54 @@ $btt-size: 40px;
 .scroll-box {
   max-height: 250px;
   overflow-y: scroll;
-  border: 1px solid #DDD;
+  border: 1px solid;
+  @include lightDark(border-color, #DDD, #000);
   border-radius: 3px;
+  min-height: 20px;
+  @include lightDark(background-color, #EEE, #000);
   .scroll-box-item {
     padding: $-xs $-m;
-    border-bottom: 1px solid #DDD;
-    border-top: 1px solid #DDD;
+    border-bottom: 1px solid;
+    border-top: 1px solid;
+    @include lightDark(border-color, #DDD, #000);
     margin-top: -1px;
+    @include lightDark(background-color, #FFF, #222);
+    display: flex;
+    gap: $-xs;
     &:last-child {
       border-bottom: 0;
     }
+    &:hover {
+      cursor: pointer;
+      @include lightDark(background-color, #f8f8f8, #333);
+    }
+    .handle {
+      color: #AAA;
+      cursor: grab;
+    }
   }
 }
 
-.scroll-box[data-instruction]:before {
-  content: attr(data-instruction);
+input.scroll-box-search, .scroll-box-header-item {
+  font-size: 0.8rem;
   padding: $-xs $-m;
-  border-bottom: 1px solid #DDD;
-  display: block;
-  font-size: 0.75rem;
+  border: 1px solid;
+  @include lightDark(border-color, #DDD, #000);
+  @include lightDark(background-color, #FFF, #222);
+  margin-bottom: -1px;
+  border-radius: 3px 3px 0 0;
+  width: 100%;
+  max-width: 100%;
+  height: auto;
+  line-height: 1.4;
   color: #666;
 }
 
+.scroll-box-search + .scroll-box,
+.scroll-box-header-item + .scroll-box {
+  border-radius: 0 0 3px 3px;
+}
+
 .fullscreen {
   border:0;
   position:fixed;
diff --git a/resources/views/shelves/parts/form.blade.php b/resources/views/shelves/parts/form.blade.php
index f29c28c81..1dcc4192f 100644
--- a/resources/views/shelves/parts/form.blade.php
+++ b/resources/views/shelves/parts/form.blade.php
@@ -10,15 +10,17 @@
     @include('form.textarea', ['name' => 'description'])
 </div>
 
-<div shelf-sort class="grid half gap-xl">
+<div component="shelf-sort" class="grid half gap-xl">
     <div class="form-group">
         <label for="books">{{ trans('entities.shelves_books') }}</label>
-        <input type="hidden" id="books-input" name="books"
+        <input refs="shelf-sort@input" type="hidden" name="books"
                value="{{ isset($shelf) ? $shelf->visibleBooks->implode('id', ',') : '' }}">
-        <div class="scroll-box" shelf-sort-assigned-books data-instruction="{{ trans('entities.shelves_drag_books') }}">
+        <div class="scroll-box-header-item">{{ trans('entities.shelves_drag_books') }}</div>
+        <div refs="shelf-sort@shelf-book-list" class="scroll-box">
             @if (count($shelf->visibleBooks ?? []) > 0)
                 @foreach ($shelf->visibleBooks as $book)
                     <div data-id="{{ $book->id }}" class="scroll-box-item">
+                        <div class="handle">@icon('grip')</div>
                         <a href="{{ $book->getUrl() }}" class="text-book">@icon('book'){{ $book->name }}</a>
                     </div>
                 @endforeach
@@ -27,9 +29,11 @@
     </div>
     <div class="form-group">
         <label for="books">{{ trans('entities.shelves_add_books') }}</label>
-        <div class="scroll-box">
+        <input type="text" refs="shelf-sort@book-search" class="scroll-box-search" placeholder="{{ trans('common.search') }}">
+        <div refs="shelf-sort@all-book-list" class="scroll-box">
             @foreach ($books as $book)
                 <div data-id="{{ $book->id }}" class="scroll-box-item">
+                    <div class="handle">@icon('grip')</div>
                     <a href="{{ $book->getUrl() }}" class="text-book">@icon('book'){{ $book->name }}</a>
                 </div>
             @endforeach