diff --git a/resources/js/controllers/reorder_controller.js b/resources/js/controllers/reorder_controller.js
new file mode 100644
index 0000000000..77691b383e
--- /dev/null
+++ b/resources/js/controllers/reorder_controller.js
@@ -0,0 +1,45 @@
+import ApplicationController from "./application_controller"
+import Sortable from 'sortablejs';
+
+export default class extends ApplicationController {
+
+ static values = {
+ handleSelector: String,
+ sortableSelector: String
+ }
+
+ connect() {
+ this.initSortable();
+ }
+
+ initSortable() {
+ new Sortable(this.element.closest(this.sortableSelectorValue), {
+ animation: 150,
+ handle: this.handleSelectorValue,
+ dragClass: "reorder-drag",
+ onEnd: (event) => {
+ this.reorderElement(event.item, event.newIndex - event.oldIndex);
+ },
+ });
+ }
+
+ reorderElement(item, offset) {
+ const handle = item.querySelector(this.handleSelectorValue);
+
+ if (handle === null) {
+ return;
+ }
+
+ axios
+ .post(handle.dataset.action, {
+ key: handle.dataset.key,
+ offset: offset,
+ })
+ .then(
+ () => this.alert(handle.dataset.successMessage, '', 'success')
+ )
+ .catch(
+ () => this.alert(handle.dataset.failureMessage, '', 'error')
+ );
+ }
+}
diff --git a/resources/views/partials/layouts/reorderHandle.blade.php b/resources/views/partials/layouts/reorderHandle.blade.php
new file mode 100644
index 0000000000..9aad5e21f3
--- /dev/null
+++ b/resources/views/partials/layouts/reorderHandle.blade.php
@@ -0,0 +1,27 @@
+
+
+
+
+ |
+
+@pushonce('stylesheets')
+
+@endpushonce
diff --git a/src/Screen/ReorderHandle.php b/src/Screen/ReorderHandle.php
new file mode 100644
index 0000000000..4847e3b959
--- /dev/null
+++ b/src/Screen/ReorderHandle.php
@@ -0,0 +1,71 @@
+render ? $this->handler($repository, $loop) : $repository->getContent($this->name);
+
+ return view('platform::partials.layouts.reorderHandle', [
+ 'action' => $this->action,
+ 'align' => $this->align,
+ 'colspan' => $this->colspan,
+ 'failure' => $this->failureMessage ?? __('Item move failed'),
+ 'icon' => $this->icon,
+ 'slug' => $this->sluggable(),
+ 'success' => $this->successMessage ?? __('Item successfully moved'),
+ 'value' => $value,
+ 'width' => $this->width,
+ ]);
+ }
+
+ public function action(string $url): static
+ {
+ if (! filter_var($url, FILTER_VALIDATE_URL)) {
+ throw new InvalidArgumentException('Argument #1 ($url) must be a URL');
+ }
+
+ $this->action = $url;
+
+ return $this;
+ }
+
+ public function icon(string $name): static
+ {
+ $this->icon = $name;
+
+ return $this;
+ }
+
+ public function messages(string $success, string $failure): static
+ {
+ $this->failureMessage = $failure;
+ $this->successMessage = $success;
+
+ return $this;
+ }
+
+ public function method(string $name, array $parameters = []): static
+ {
+ $url = request()->header('ORCHID-ASYNC-REFERER', url()->current());
+ $query = http_build_query($parameters);
+
+ $this->action = rtrim("{$url}/{$name}?{$query}", '/?');
+
+ return $this;
+ }
+}