diff --git a/modules/base/src/Plugin/Action/ListSort.php b/modules/base/src/Plugin/Action/ListSort.php
new file mode 100644
index 0000000000000000000000000000000000000000..a2ff9a13a8e0ddfbd365e3525bde0054f1debaab
--- /dev/null
+++ b/modules/base/src/Plugin/Action/ListSort.php
@@ -0,0 +1,124 @@
+<?php
+
+namespace Drupal\eca_base\Plugin\Action;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\eca\Plugin\Action\ListOperationBase;
+use Drupal\eca\Plugin\DataType\DataTransferObject;
+use Drupal\eca\Plugin\ECA\PluginFormTrait;
+
+/**
+ * Sorts a list.
+ *
+ * @Action(
+ *   id = "eca_list_sort",
+ *   label = @Translation("List: sort items"),
+ *   description = @Translation("Sorts a list."),
+ *   eca_version_introduced = "2.2.0"
+ * )
+ */
+class ListSort extends ListOperationBase {
+
+  use PluginFormTrait;
+
+  /**
+   * Gets the supported sorting methods.
+   *
+   * @return array
+   *   The supported sorting methods.
+   */
+  public function getSupportedSortMethods() {
+    return [
+      'asort' => $this->t('Ascending by value'),
+      'arsort' => $this->t('Descending by value'),
+      'ksort' => $this->t('Ascending by key'),
+      'krsort' => $this->t('Descending by key'),
+      'natsort' => $this->t('Natural'),
+      'natcasesort' => $this->t('Natural (case insensitive)'),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration(): array {
+    return [
+      'sort_method' => 'sort',
+      'token_name' => '',
+    ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
+    $form['sort_method'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Sort method'),
+      '#options' => $this->getSupportedSortMethods(),
+      '#default_value' => $this->configuration['sort_method'],
+      '#weight' => 0,
+    ];
+
+    $form['token_name'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Name of token'),
+      '#description' => $this->t('The sorted list will be loaded into this specified token. If this is not provided, the list will be sorted in-place.'),
+      '#default_value' => $this->configuration['token_name'],
+      '#weight' => 4,
+    ];
+
+    return parent::buildConfigurationForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void {
+    parent::submitConfigurationForm($form, $form_state);
+    $this->configuration['sort_method'] = $form_state->getValue('sort_method');
+    $this->configuration['token_name'] = $form_state->getValue('token_name');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute(): void {
+    $list = $this->getItemList();
+    $items = ($list instanceof DataTransferObject) ? $list->toArray() : \iterator_to_array($list);
+
+    // Note - verbosity is intended to aid in static analysis.
+    switch ($this->configuration['sort_method']) {
+      case 'asort':
+        \asort($items);
+        break;
+
+      case 'arsort':
+        \arsort($items);
+        break;
+
+      case 'ksort':
+        \ksort($items);
+        break;
+
+      case 'krsort':
+        \krsort($items);
+        break;
+
+      case 'natsort':
+        \natsort($items);
+        break;
+
+      case 'natcasesort':
+        \natcasesort($items);
+        break;
+    }
+
+    $dest_token = trim((string) $this->configuration['token_name']);
+    if (!$dest_token) {
+      $dest_token = trim((string) $this->configuration['list_token']);
+    }
+    $this->tokenService->addTokenData($dest_token, DataTransferObject::create($items));
+  }
+
+}
diff --git a/modules/base/tests/src/Kernel/ListOperationTest.php b/modules/base/tests/src/Kernel/ListOperationTest.php
index 07ddc06b9b57a6f2fae5b8bf339b94beda4e6c78..1ab1cf51c55a25756264048f526854a6e321713d 100644
--- a/modules/base/tests/src/Kernel/ListOperationTest.php
+++ b/modules/base/tests/src/Kernel/ListOperationTest.php
@@ -379,4 +379,75 @@ class ListOperationTest extends KernelTestBase {
     $this->assertNull(User::load(2));
   }
 
+  /**
+   * Data provider for the list sort test case.
+   *
+   * @return array[]
+   *   Test case data for in-place and copy based sorting.
+   */
+  public function listSortProvider(): array {
+    return [
+      'without dest token' => [''],
+      'with dest token' => ['dest_token'],
+    ];
+  }
+
+  /**
+   * Tests the "eca_list_sort" action plugin.
+   *
+   * @dataProvider listSortProvider
+   */
+  public function testListSortDestToken(string $dest_token): void {
+
+    $chaos = [
+      'Img12.png' => 'Img12.png',
+      'img1.png' => 'img1.png',
+      'img10.png' => 'img10.png',
+      'Img10.png' => 'Img10.png',
+      'Img2.png' => 'Img2.png',
+      'img12.png' => 'img12.png',
+      'img2.png' => 'img2.png',
+      'Img1.png' => 'Img1.png',
+    ];
+
+    /** @var \Drupal\Core\Action\ActionManager $action_manager */
+    $action_manager = \Drupal::service('plugin.manager.action');
+    /** @var \Drupal\eca\Token\TokenInterface $token_services */
+    $token_services = \Drupal::service('eca.token_services');
+
+    /** @var \Drupal\eca\Plugin\Action\ListSortBase $action */
+    $action = $action_manager->createInstance('eca_list_sort', []);
+
+    foreach (array_keys($action->getSupportedSortMethods()) as $method) {
+
+      $action->setConfiguration([
+        'list_token' => 'list',
+        'sort_method' => $method,
+        'token_name' => $dest_token,
+      ]);
+      $token_services->addTokenData(
+        $action->getConfiguration()['list_token'],
+        DataTransferObject::create($chaos)
+      );
+
+      // Copy the unsorted list into a new variable, then sort it.
+      $expected = $chaos;
+      $method($expected);
+
+      $action->execute();
+
+      $dest_token = trim((string) $action->getConfiguration()['token_name']);
+      if (!$dest_token) {
+        $dest_token = trim((string) $action->getConfiguration()['list_token']);
+      }
+
+      $list = $token_services->getTokenData($dest_token);
+
+      $this->assertSame($expected, $list->toArray());
+
+      // Reset the input token data.
+      $token_services->clearTokenData();
+    }
+  }
+
 }