diff --git a/app/Activity/ActivityType.php b/app/Activity/ActivityType.php
index 5ec9b9cf0..4a648da6c 100644
--- a/app/Activity/ActivityType.php
+++ b/app/Activity/ActivityType.php
@@ -71,6 +71,10 @@ class ActivityType
     const IMPORT_RUN = 'import_run';
     const IMPORT_DELETE = 'import_delete';
 
+    const SORT_SET_CREATE = 'sort_set_create';
+    const SORT_SET_UPDATE = 'sort_set_update';
+    const SORT_SET_DELETE = 'sort_set_delete';
+
     /**
      * Get all the possible values.
      */
diff --git a/app/Sorting/SortSet.php b/app/Sorting/SortSet.php
index ee45c211f..971b3e29a 100644
--- a/app/Sorting/SortSet.php
+++ b/app/Sorting/SortSet.php
@@ -2,6 +2,7 @@
 
 namespace BookStack\Sorting;
 
+use BookStack\Activity\Models\Loggable;
 use Carbon\Carbon;
 use Illuminate\Database\Eloquent\Model;
 
@@ -12,16 +13,14 @@ use Illuminate\Database\Eloquent\Model;
  * @property Carbon $created_at
  * @property Carbon $updated_at
  */
-class SortSet extends Model
+class SortSet extends Model implements Loggable
 {
     /**
      * @return SortSetOperation[]
      */
     public function getOperations(): array
     {
-        $strOptions = explode(',', $this->sequence);
-        $options = array_map(fn ($val) => SortSetOperation::tryFrom($val), $strOptions);
-        return array_filter($options);
+        return SortSetOperation::fromSequence($this->sequence);
     }
 
     /**
@@ -32,4 +31,14 @@ class SortSet extends Model
         $values = array_map(fn (SortSetOperation $opt) => $opt->value, $options);
         $this->sequence = implode(',', $values);
     }
+
+    public function logDescriptor(): string
+    {
+        return "({$this->id}) {$this->name}";
+    }
+
+    public function getUrl(): string
+    {
+        return url("/settings/sorting/sets/{$this->id}");
+    }
 }
diff --git a/app/Sorting/SortSetController.php b/app/Sorting/SortSetController.php
index 4ef148295..0d77bd88f 100644
--- a/app/Sorting/SortSetController.php
+++ b/app/Sorting/SortSetController.php
@@ -2,7 +2,9 @@
 
 namespace BookStack\Sorting;
 
+use BookStack\Activity\ActivityType;
 use BookStack\Http\Controller;
+use BookStack\Http\Request;
 
 class SortSetController extends Controller
 {
@@ -14,6 +16,73 @@ class SortSetController extends Controller
 
     public function create()
     {
+        $this->setPageTitle(trans('settings.sort_set_create'));
+
         return view('settings.sort-sets.create');
     }
+
+    public function store(Request $request)
+    {
+        $this->validate($request, [
+            'name' => ['required', 'string', 'min:1', 'max:200'],
+            'sequence' => ['required', 'string', 'min:1'],
+        ]);
+
+        $operations = SortSetOperation::fromSequence($request->input('sequence'));
+        if (count($operations) === 0) {
+            return redirect()->withInput()->withErrors(['sequence' => 'No operations set.']);
+        }
+
+        $set = new SortSet();
+        $set->name = $request->input('name');
+        $set->setOperations($operations);
+        $set->save();
+
+        $this->logActivity(ActivityType::SORT_SET_CREATE, $set);
+
+        return redirect('/settings/sorting');
+    }
+
+    public function edit(string $id)
+    {
+        $set = SortSet::query()->findOrFail($id);
+
+        $this->setPageTitle(trans('settings.sort_set_edit'));
+
+        return view('settings.sort-sets.edit', ['set' => $set]);
+    }
+
+    public function update(string $id, Request $request)
+    {
+        $this->validate($request, [
+            'name' => ['required', 'string', 'min:1', 'max:200'],
+            'sequence' => ['required', 'string', 'min:1'],
+        ]);
+
+        $set = SortSet::query()->findOrFail($id);
+        $operations = SortSetOperation::fromSequence($request->input('sequence'));
+        if (count($operations) === 0) {
+            return redirect()->withInput()->withErrors(['sequence' => 'No operations set.']);
+        }
+
+        $set->name = $request->input('name');
+        $set->setOperations($operations);
+        $set->save();
+
+        $this->logActivity(ActivityType::SORT_SET_UPDATE, $set);
+
+        return redirect('/settings/sorting');
+    }
+
+    public function destroy(string $id)
+    {
+        $set = SortSet::query()->findOrFail($id);
+
+        // TODO - Check if it's in use
+
+        $set->delete();
+        $this->logActivity(ActivityType::SORT_SET_DELETE, $set);
+
+        return redirect('/settings/sorting');
+    }
 }
diff --git a/app/Sorting/SortSetOperation.php b/app/Sorting/SortSetOperation.php
index 12fda669f..a6dd860f5 100644
--- a/app/Sorting/SortSetOperation.php
+++ b/app/Sorting/SortSetOperation.php
@@ -39,6 +39,21 @@ enum SortSetOperation: string
     public static function allExcluding(array $operations): array
     {
         $all = SortSetOperation::cases();
-        return array_diff($all, $operations);
+        $filtered = array_filter($all, function (SortSetOperation $operation) use ($operations) {
+            return !in_array($operation, $operations);
+        });
+        return array_values($filtered);
+    }
+
+    /**
+     * Create a set of operations from a string sequence representation.
+     * (values seperated by commas).
+     * @return SortSetOperation[]
+     */
+    public static function fromSequence(string $sequence): array
+    {
+        $strOptions = explode(',', $sequence);
+        $options = array_map(fn ($val) => SortSetOperation::tryFrom($val), $strOptions);
+        return array_filter($options);
     }
 }
diff --git a/lang/en/activities.php b/lang/en/activities.php
index 7c3454d41..7db872c0c 100644
--- a/lang/en/activities.php
+++ b/lang/en/activities.php
@@ -127,6 +127,14 @@ return [
     'comment_update'              => 'updated comment',
     'comment_delete'              => 'deleted comment',
 
+    // Sort Sets
+    'sort_set_create' => 'created sort set',
+    'sort_set_create_notification' => 'Sort set successfully created',
+    'sort_set_update' => 'updated sort set',
+    'sort_set_update_notification' => 'Sort set successfully update',
+    'sort_set_delete' => 'deleted sort set',
+    'sort_set_delete_notification' => 'Sort set successfully deleted',
+
     // Other
     'permissions_update'          => 'updated permissions',
 ];
diff --git a/lang/en/settings.php b/lang/en/settings.php
index 8bb2f6ef4..cda097590 100644
--- a/lang/en/settings.php
+++ b/lang/en/settings.php
@@ -82,6 +82,8 @@ return [
     'sorting_sets_desc' => 'These are predefined sorting operations which can be applied to content in the system.',
     'sort_set_create' => 'Create Sort Set',
     'sort_set_edit' => 'Edit Sort Set',
+    'sort_set_delete' => 'Delete Sort Set',
+    'sort_set_delete_desc' => 'Remove this sort set from the system. Deletion will only go ahead if the sort is not in active use.',
     'sort_set_details' => 'Sort Set Details',
     'sort_set_details_desc' => 'Set a name for this sort set, which will appear in lists when users are selecting a sort.',
     'sort_set_operations' => 'Sort Operations',
diff --git a/resources/views/settings/categories/sorting.blade.php b/resources/views/settings/categories/sorting.blade.php
index 9de11bb6f..b5d613840 100644
--- a/resources/views/settings/categories/sorting.blade.php
+++ b/resources/views/settings/categories/sorting.blade.php
@@ -52,6 +52,17 @@
             </div>
         </div>
 
-{{--        TODO--}}
+        @php
+            $sortSets = \BookStack\Sorting\SortSet::query()->orderBy('name', 'asc')->get();
+        @endphp
+        @if(empty($sortSets))
+            <p class="italic text-muted">{{ trans('common.no_items') }}</p>
+        @else
+            <div class="item-list">
+                @foreach($sortSets as $set)
+                    @include('settings.sort-sets.parts.sort-set-list-item', ['set' => $set])
+                @endforeach
+            </div>
+        @endif
     </div>
 @endsection
\ No newline at end of file
diff --git a/resources/views/settings/sort-sets/edit.blade.php b/resources/views/settings/sort-sets/edit.blade.php
new file mode 100644
index 000000000..3b88c1243
--- /dev/null
+++ b/resources/views/settings/sort-sets/edit.blade.php
@@ -0,0 +1,44 @@
+@extends('layouts.simple')
+
+@section('body')
+
+    <div class="container small">
+
+        @include('settings.parts.navbar', ['selected' => 'settings'])
+
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">{{ trans('settings.sort_set_edit') }}</h1>
+
+            <form action="{{ $set->getUrl() }}" method="POST">
+                {{ method_field('PUT') }}
+                {{ csrf_field() }}
+
+                @include('settings.sort-sets.parts.form', ['model' => $set])
+
+                <div class="form-group text-right">
+                    <a href="{{ url("/settings/sorting") }}" class="button outline">{{ trans('common.cancel') }}</a>
+                    <button type="submit" class="button">{{ trans('common.save') }}</button>
+                </div>
+            </form>
+        </div>
+
+        <div class="card content-wrap auto-height">
+            <div class="flex-container-row items-center gap-l">
+                <div>
+                    <h2 class="list-heading">{{ trans('settings.sort_set_delete') }}</h2>
+                    <p class="text-muted">{{ trans('settings.sort_set_delete_desc') }}</p>
+                </div>
+                <div class="flex">
+                    <form action="{{ $set->getUrl() }}" method="POST">
+                        {{ method_field('DELETE') }}
+                        {{ csrf_field() }}
+                        <div class="text-right">
+                            <button type="submit" class="button outline">{{ trans('common.delete') }}</button>
+                        </div>
+                    </form>
+                </div>
+            </div>
+        </div>
+    </div>
+
+@stop
diff --git a/resources/views/settings/sort-sets/parts/form.blade.php b/resources/views/settings/sort-sets/parts/form.blade.php
index 3f2220947..38db840ac 100644
--- a/resources/views/settings/sort-sets/parts/form.blade.php
+++ b/resources/views/settings/sort-sets/parts/form.blade.php
@@ -15,9 +15,14 @@
     <div component="sort-set-manager">
         <label class="setting-list-label">{{ trans('settings.sort_set_operations') }}</label>
         <p class="text-muted text-small">{{ trans('settings.sort_set_operations_desc') }}</p>
+        @include('form.errors', ['name' => 'sequence'])
 
-        <input refs="sort-set-manager@input" type="hidden" name="books"
-               value="{{ $model?->sequence ?? '' }}">
+        <input refs="sort-set-manager@input" type="hidden" name="sequence"
+               value="{{ old('sequence') ?? $model?->sequence ?? '' }}">
+
+        @php
+            $configuredOps = old('sequence') ? \BookStack\Sorting\SortSetOperation::fromSequence(old('sequence')) : ($model?->getOperations() ?? []);
+        @endphp
 
         <div class="grid half">
             <div class="form-group">
@@ -27,8 +32,9 @@
                     aria-labelledby="sort-set-configured-operations"
                     class="scroll-box configured-option-list">
                     <li class="text-muted empty-state px-m py-s italic text-small">{{ trans('settings.sort_set_configured_operations_empty') }}</li>
-                    @foreach(($model?->getOperations() ?? []) as $option)
-                        @include('settings.sort-sets.parts.operation')
+
+                    @foreach($configuredOps as $operation)
+                        @include('settings.sort-sets.parts.operation', ['operation' => $operation])
                     @endforeach
                 </ul>
             </div>
@@ -40,7 +46,7 @@
                     aria-labelledby="sort-set-available-operations"
                     class="scroll-box available-option-list">
                     <li class="text-muted empty-state px-m py-s italic text-small">{{ trans('settings.sort_set_available_operations_empty') }}</li>
-                    @foreach(\BookStack\Sorting\SortSetOperation::allExcluding($model?->getOperations() ?? []) as $operation)
+                    @foreach(\BookStack\Sorting\SortSetOperation::allExcluding($configuredOps) as $operation)
                         @include('settings.sort-sets.parts.operation', ['operation' => $operation])
                     @endforeach
                 </ul>
diff --git a/resources/views/settings/sort-sets/parts/sort-set-list-item.blade.php b/resources/views/settings/sort-sets/parts/sort-set-list-item.blade.php
new file mode 100644
index 000000000..e5ee1fb87
--- /dev/null
+++ b/resources/views/settings/sort-sets/parts/sort-set-list-item.blade.php
@@ -0,0 +1,8 @@
+<div class="item-list-row flex-container-row py-xs items-center">
+    <div class="py-xs px-m flex-2">
+        <a href="{{ $set->getUrl() }}">{{ $set->name }}</a>
+    </div>
+    <div class="px-m text-small text-muted">
+        {{ implode(', ', array_map(fn ($op) => $op->getLabel(), $set->getOperations())) }}
+    </div>
+</div>
\ No newline at end of file