diff --git a/core/lib/Drupal/Core/Form/ModalFormTrait.php b/core/lib/Drupal/Core/Form/ModalFormTrait.php
new file mode 100644
index 0000000..32d048b
--- /dev/null
+++ b/core/lib/Drupal/Core/Form/ModalFormTrait.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Drupal\Core\Form;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\CloseDialogCommand;
+use Drupal\Core\Ajax\HtmlCommand;
+
+/**
+ * Provides utilities for forms that want to be rendered in modals.
+ */
+trait ModalFormTrait {
+
+  /**
+   * Generates a form's submit and action button elemetns with the #ajax
+   * functionality set up.
+   *
+   * @return  array $actions
+   *   An form array with the submit and cancel buttons.
+   */
+  protected function modalFormActions() {
+    $actions['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Save'),
+      '#submit' => ['::submitForm', '::save'],
+      '#ajax' => [
+        'callback' => [$this, 'modalformsubmithandleerrors'],
+      ],
+    ];
+
+    $actions['cancel'] = [
+      '#type' => 'button',
+      '#value' => $this->t('Cancel'),
+      '#ajax' => [
+        'callback' => [$this, 'modalformclosedialog'],
+      ],
+    ];
+
+    return $actions;
+  }
+
+  /**
+   * Ajax callback to handle form errors.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @return \Drupal\Core\Ajax\AjaxResponse
+   *   An ajax response object.
+   */
+  public function modalFormSubmitHandleErrors(array &$form, FormStateInterface $form_state) {
+    $response = new AjaxResponse();
+
+    // Handle first any form errors.
+    $errors = $form_state->getErrors();
+    if (count($errors)) {
+      $form['status_messages'] = [
+        '#type' => 'status_messages',
+        '#weight' => -10,
+      ];
+      $response->addCommand(new HtmlCommand('#drupal-modal', $form));
+    }
+
+    return $response;
+  }
+
+  /**
+   * Ajax callback to close the modal.
+   *
+   * @return \Drupal\Core\Ajax\AjaxResponse
+   *   An ajax response object.
+   */
+  public function modalFormCloseDialog() {
+    $response = new AjaxResponse();
+    $response->addCommand(new CloseDialogCommand());
+
+    return $response;
+  }
+
+}
diff --git a/core/modules/content_moderation/tests/src/FunctionalJavascript/WorkflowTypeEditFormTest.php b/core/modules/content_moderation/tests/src/FunctionalJavascript/WorkflowTypeEditFormTest.php
new file mode 100644
index 0000000..a3ed41e
--- /dev/null
+++ b/core/modules/content_moderation/tests/src/FunctionalJavascript/WorkflowTypeEditFormTest.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Drupal\Tests\content_moderation\FunctionalJavascript;
+
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+
+/**
+ * AJAX modal tests for the workflow type edit form.
+ *
+ * @group content_moderation
+ */
+class WorkflowTypeEditFormTest extends JavascriptTestBase {
+
+/**
+   * Modules to install.
+   *
+   * @var array
+   */
+  public static $modules = [
+    'workflows',
+    'content_moderation',
+    'block_content',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+    $user = $this->drupalCreateUser(['administer workflows']);
+    $this->drupalLogin($user);
+  }
+
+  /**
+   * Test workflow type edit form uses a modal dialog.
+   */
+  public function testWorkflowTypeEditForm() {
+    $this->drupalGet('admin/config/workflow/workflows/manage/editorial');
+    $assert_session = $this->assertSession();
+    $page = $this->getSession()->getPage();
+
+    $this->click('table tr:contains(Custom block types) a');
+    $assert_session->assertWaitOnAjaxRequest();
+    $modal = $assert_session->waitForElementVisible('css', '#drupal-modal');
+    $this->assertTrue($modal->isVisible(), 'Modal window found.');
+
+
+    // @TODO change settings on the window and assert the ui changes on save
+
+    // Save the form.
+    $save_button = $assert_session->waitForElementVisible('css', '.ui-dialog button:contains(Save)');
+    $this->assertTrue($save_button->isVisible(), 'Save button found.');
+    $save_button->click();
+    $assert_session->waitForElementVisible('named', ['id', 'selected-block_content']);
+
+  }
+
+}
diff --git a/core/modules/workflows/src/Form/WorkflowEditForm.php b/core/modules/workflows/src/Form/WorkflowEditForm.php
index 764c0c9..e95062c 100644
--- a/core/modules/workflows/src/Form/WorkflowEditForm.php
+++ b/core/modules/workflows/src/Form/WorkflowEditForm.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\workflows\Form;
 
+use Drupal\Component\Serialization\Json;
 use Drupal\Core\Form\SubformState;
 use Drupal\workflows\WorkflowTypeFormInterface;
 use Drupal\workflows\Entity\Workflow;
@@ -53,6 +54,9 @@ public function form(array $form, FormStateInterface $form_state) {
       '#open' => TRUE,
       '#collapsible' => 'FALSE',
     ];
+    $form['states_container']['states-modal-messages'] = [
+      '#type' => 'container',
+    ];
     $form['states_container']['states'] = [
       '#type' => 'table',
       '#header' => $header,
@@ -85,7 +89,14 @@ public function form(array $form, FormStateInterface $form_state) {
         'edit' => [
           'title' => $this->t('Edit'),
           'url' => Url::fromRoute('entity.workflow.edit_state_form', ['workflow' => $workflow->id(), 'workflow_state' => $state->id()]),
-          'attributes' => ['aria-label' => $this->t('Edit @state state', ['@state' => $state->label()])],
+          'attributes' => [
+            'aria-label' => $this->t('Edit @state state', ['@state' => $state->label()]),
+            'class' => ['use-ajax'],
+            'data-dialog-type' => 'modal',
+            'data-dialog-options' => Json::encode([
+              'width' => 700,
+            ]),
+          ],
         ]
       ];
       if ($this->entity->access('delete-state:' . $state->id())) {
@@ -95,12 +106,22 @@ public function form(array $form, FormStateInterface $form_state) {
             'workflow' => $workflow->id(),
             'workflow_state' => $state->id()
           ]),
-          'attributes' => ['aria-label' => $this->t('Delete @state state', ['@state' => $state->label()])],
+          'attributes' => [
+            'aria-label' => $this->t('Delete @state state', ['@state' => $state->label()]),
+            'class' => ['use-ajax'],
+            'data-dialog-type' => 'modal',
+            'data-dialog-options' => Json::encode([
+              'width' => 700,
+            ]),
+          ],
         ];
       }
       $form['states_container']['states'][$state->id()] = [
         '#attributes' => ['class' => ['draggable']],
-        'state' => ['#markup' => $state->label()],
+        'state' => [
+          '#type' => 'container',
+          '#markup' => $state->label()
+        ],
         '#weight' => $state->weight(),
         'weight' => [
           '#type' => 'weight',
@@ -116,7 +137,23 @@ public function form(array $form, FormStateInterface $form_state) {
       ];
     }
     $form['states_container']['state_add'] = [
-      '#markup' => $workflow->toLink($this->t('Add a new state'), 'add-state-form')->toString(),
+      '#type' => 'link',
+      '#title' => $this->t('Add a new state'),
+      '#url' => Url::fromRoute('entity.workflow.add_state_form', ['workflow' => $workflow->id()]),
+      '#attributes' => [
+        'class' => ['use-ajax'],
+        'data-dialog-type' => 'modal',
+        'data-dialog-options' => Json::encode([
+          'width' => 700,
+        ]),
+      ],
+      '#attributes' => [
+        'class' => ['use-ajax'],
+        'data-dialog-type' => 'modal',
+        'data-dialog-options' => Json::encode([
+          'width' => 700,
+        ]),
+      ],
     ];
 
     $header = [
@@ -131,6 +168,9 @@ public function form(array $form, FormStateInterface $form_state) {
       '#title' => $this->t('Transitions'),
       '#open' => TRUE,
     ];
+    $form['transitions_container']['transitions-modal-messages'] = [
+      '#type' => 'container',
+    ];
     $form['transitions_container']['transitions'] = [
       '#type' => 'table',
       '#header' => $header,
@@ -148,16 +188,33 @@ public function form(array $form, FormStateInterface $form_state) {
       $links['edit'] = [
         'title' => $this->t('Edit'),
         'url' => Url::fromRoute('entity.workflow.edit_transition_form', ['workflow' => $workflow->id(), 'workflow_transition' => $transition->id()]),
-        'attributes' => ['aria-label' => $this->t('Edit \'@transition\' transition', ['@transition' => $transition->label()])],
+        'attributes' => [
+          'aria-label' => $this->t('Edit \'@transition\' transition', ['@transition' => $transition->label()]),
+          'class' => ['use-ajax'],
+          'data-dialog-type' => 'modal',
+          'data-dialog-options' => Json::encode([
+            'width' => 700,
+          ]),
+        ],
       ];
       $links['delete'] = [
         'title' => t('Delete'),
         'url' => Url::fromRoute('entity.workflow.delete_transition_form', ['workflow' => $workflow->id(), 'workflow_transition' => $transition->id()]),
-        'attributes' => ['aria-label' => $this->t('Delete \'@transition\' transition', ['@transition' => $transition->label()])],
+        'attributes' => [
+          'aria-label' => $this->t('Delete \'@transition\' transition', ['@transition' => $transition->label()]),
+          'class' => ['use-ajax'],
+          'data-dialog-type' => 'modal',
+          'data-dialog-options' => Json::encode([
+            'width' => 700,
+          ]),
+        ],
       ];
       $form['transitions_container']['transitions'][$transition->id()] = [
         '#attributes' => ['class' => ['draggable']],
-        'label' => ['#markup' => $transition->label()],
+        'label' => [
+          '#type' => 'container',
+          '#markup' => $transition->label(),
+        ],
         '#weight' => $transition->weight(),
         'weight' => [
           '#type' => 'weight',
@@ -167,11 +224,13 @@ public function form(array $form, FormStateInterface $form_state) {
           '#attributes' => ['class' => ['transition-weight']],
         ],
         'from' => [
-          '#theme' => 'item_list',
-          '#items' => array_map([State::class, 'labelCallback'], $transition->from()),
-          '#context' => ['list_style' => 'comma-list'],
+          '#type' => 'container',
+          '#markup' => implode(', ', array_map([State::class, 'labelCallback'], $transition->from())),
+        ],
+        'to' => [
+          '#type' => 'container',
+          '#markup' => $transition->to()->label()
         ],
-        'to' => ['#markup' => $transition->to()->label()],
         'operations' => [
           '#type' => 'operations',
           '#links' => $links,
@@ -179,7 +238,16 @@ public function form(array $form, FormStateInterface $form_state) {
       ];
     }
     $form['transitions_container']['transition_add'] = [
-      '#markup' => $workflow->toLink($this->t('Add a new transition'), 'add-transition-form')->toString(),
+      '#type' => 'link',
+      '#title' => $this->t('Add a new transition'),
+      '#url' => Url::fromRoute('entity.workflow.add_transition_form', ['workflow' => $workflow->id()]),
+      '#attributes' => [
+        'class' => ['use-ajax'],
+        'data-dialog-type' => 'modal',
+        'data-dialog-options' => Json::encode([
+          'width' => 700,
+        ]),
+      ],
     ];
 
     if ($workflow->getTypePlugin() instanceof WorkflowTypeFormInterface) {
@@ -189,6 +257,8 @@ public function form(array $form, FormStateInterface $form_state) {
       $subform_state = SubformState::createForSubform($form['type_settings'], $form, $form_state);
       $form['type_settings'] += $workflow->getTypePlugin()->buildConfigurationForm($form['type_settings'], $subform_state, $workflow);
     }
+    // Add the AJAX library to the form for dialog support.
+    $form['#attached']['library'][] = 'core/drupal.dialog.ajax';
 
     return $form;
   }
diff --git a/core/modules/workflows/src/Form/WorkflowStateAddForm.php b/core/modules/workflows/src/Form/WorkflowStateAddForm.php
index 0b36858..d7859e4 100644
--- a/core/modules/workflows/src/Form/WorkflowStateAddForm.php
+++ b/core/modules/workflows/src/Form/WorkflowStateAddForm.php
@@ -119,4 +119,12 @@ protected function actions(array $form, FormStateInterface $form_state) {
     return $actions;
   }
 
+  /**
+   * Route title callback.
+   */
+  public function getTitle($workflow = NULL) {
+    $title = $this->t('Add a new state to the @workflow', ['@workflow' => $workflow->label()]);
+    return $title;
+  }
+
 }
diff --git a/core/modules/workflows/src/Form/WorkflowStateDeleteForm.php b/core/modules/workflows/src/Form/WorkflowStateDeleteForm.php
index c60045c..507ebc8 100644
--- a/core/modules/workflows/src/Form/WorkflowStateDeleteForm.php
+++ b/core/modules/workflows/src/Form/WorkflowStateDeleteForm.php
@@ -2,16 +2,23 @@
 
 namespace Drupal\workflows\Form;
 
+use Drupal\Core\Form\ModalFormTrait;
 use Drupal\workflows\WorkflowInterface;
 use Drupal\Core\Form\ConfirmFormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\CloseDialogCommand;
+use Drupal\Core\Ajax\HtmlCommand;
+use Drupal\Core\Ajax\RemoveCommand;
 
 /**
  * Builds the form to delete states from Workflow entities.
  */
 class WorkflowStateDeleteForm extends ConfirmFormBase {
 
+  use ModalFormTrait;
+
   /**
    * The workflow entity the state being deleted belongs to.
    *
@@ -75,7 +82,14 @@ public function buildForm(array $form, FormStateInterface $form_state, WorkflowI
     }
     $this->workflow = $workflow;
     $this->stateId = $workflow_state;
-    return parent::buildForm($form, $form_state);
+
+    $form = parent::buildForm($form, $form_state);
+    $actions = $this->modalFormActions();
+
+    $form['actions']['submit']['#ajax']['callback'] = [$this, 'submitajaxcallback'];
+    $form['actions']['cancel'] = $actions['cancel'];
+
+    return $form;
   }
 
   /**
@@ -83,6 +97,8 @@ public function buildForm(array $form, FormStateInterface $form_state, WorkflowI
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
 
+    // @TODO redirect back to the previous page if not using ajax.
+
     $workflow_label = $this->workflow->getState($this->stateId)->label();
     $this->workflow
       ->deleteState($this->stateId)
@@ -96,4 +112,24 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     $form_state->setRedirectUrl($this->getCancelUrl());
   }
 
+  /**
+   * Ajax callback to close the modal and remove the deleted state.
+   *
+   * @return \Drupal\Core\Ajax\AjaxResponse
+   *   An ajax response object.
+   */
+  public function submitAjaxCallback() {
+
+    $response = $this->modalFormCloseDialog();
+
+    $data_selector = 'edit-states-' . strtolower(str_replace('_', '-', $this->stateId));
+
+    $response->addCommand(new RemoveCommand('[data-drupal-selector="' . $data_selector . '"]'));
+
+    $status_messages = ['#type' => 'status_messages'];
+    $response->addCommand(new HtmlCommand('[data-drupal-selector="edit-states-modal-messages"]', \Drupal::service('renderer')->renderRoot($status_messages)));
+
+    return $response;
+  }
+
 }
diff --git a/core/modules/workflows/src/Form/WorkflowStateEditForm.php b/core/modules/workflows/src/Form/WorkflowStateEditForm.php
index f21508f..f8fad11 100644
--- a/core/modules/workflows/src/Form/WorkflowStateEditForm.php
+++ b/core/modules/workflows/src/Form/WorkflowStateEditForm.php
@@ -2,16 +2,22 @@
 
 namespace Drupal\workflows\Form;
 
+use Drupal\Core\Form\ModalFormTrait;
 use Drupal\Core\Entity\EntityForm;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\CloseDialogCommand;
+use Drupal\Core\Ajax\HtmlCommand;
 
 /**
  * Class WorkflowStateEditForm.
  */
 class WorkflowStateEditForm extends EntityForm {
 
+  use ModalFormTrait;
+
   /**
    * The ID of the state that is being edited.
    *
@@ -110,6 +116,15 @@ public function form(array $form, FormStateInterface $form_state) {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function actions(array $form, FormStateInterface $form_state) {
+    $actions = $this->modalFormActions();
+    $actions['submit']['#ajax']['callback'] = [$this,'submitajaxcallback'];
+    return $actions;
+  }
+
+  /**
    * Copies top-level form values to entity properties
    *
    * This form can only change values for a state, which is part of workflow.
@@ -137,7 +152,7 @@ protected function copyFormValuesToEntity(EntityInterface $entity, array $form,
    */
   public function save(array $form, FormStateInterface $form_state) {
     /** @var \Drupal\workflows\WorkflowInterface $workflow */
-    $workflow = $this->entity;
+    $workflow = $this->getEntity();
     $workflow->save();
     drupal_set_message($this->t('Saved %label state.', [
       '%label' => $workflow->getState($this->stateId)->label(),
@@ -146,29 +161,40 @@ public function save(array $form, FormStateInterface $form_state) {
   }
 
   /**
-   * {@inheritdoc}
+   * Ajax callback to close the modal and update the label on the table.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @return \Drupal\Core\Ajax\AjaxResponse
+   *   An ajax response object.
    */
-  protected function actions(array $form, FormStateInterface $form_state) {
-    $actions['submit'] = [
-      '#type' => 'submit',
-      '#value' => $this->t('Save'),
-      '#submit' => ['::submitForm', '::save'],
-    ];
+  public function submitAjaxCallback(array &$form, FormStateInterface $form_state) {
+    $response = $this->modalFormSubmitHandleErrors($form, $form_state);
+    if ($commands = $response->getCommands()) {
+      return $response;
+    }
 
-    $actions['delete'] = [
-      '#type' => 'link',
-      '#title' => $this->t('Delete'),
-      '#access' => $this->entity->access('delete-state:' . $this->stateId),
-      '#attributes' => [
-        'class' => ['button', 'button--danger'],
-      ],
-      '#url' => Url::fromRoute('entity.workflow.delete_state_form', [
-        'workflow' => $this->entity->id(),
-        'workflow_state' => $this->stateId
-      ])
-    ];
+    $workflow = $this->getEntity();
+    $state = $workflow->getState($this->stateId);
+    $data_selector = 'edit-states-' . strtolower(str_replace('_', '-', $this->stateId));
 
-    return $actions;
+    $response = $this->modalFormCloseDialog();
+    $response->addCommand(new HtmlCommand('[data-drupal-selector="' . $data_selector . '-state"]', $state->label()));
+
+    $status_messages = ['#type' => 'status_messages'];
+    $response->addCommand(new HtmlCommand('[data-drupal-selector="edit-states-modal-messages"]', \Drupal::service('renderer')->renderRoot($status_messages)));
+
+    return $response;
   }
 
+  /**
+   * Route title callback.
+   */
+  public function getTitle($workflow = NULL, $workflow_state) {
+    $title = $this->t('Edit the @state state for the @workflow', ['@state' => $workflow->getState($workflow_state)->label(), '@workflow' => $workflow->label()]);
+    return $title;
+  }
 }
diff --git a/core/modules/workflows/src/Form/WorkflowTransitionAddForm.php b/core/modules/workflows/src/Form/WorkflowTransitionAddForm.php
index fe3a406..8eff238 100644
--- a/core/modules/workflows/src/Form/WorkflowTransitionAddForm.php
+++ b/core/modules/workflows/src/Form/WorkflowTransitionAddForm.php
@@ -54,7 +54,7 @@ public function form(array $form, FormStateInterface $form_state) {
       '#options' => $states,
     ];
     $form['to'] = [
-      '#type' => 'radios',
+      '#type' => 'select',
       '#title' => $this->t('To'),
       '#required' => TRUE,
       '#default_value' => [],
@@ -155,4 +155,12 @@ protected function actions(array $form, FormStateInterface $form_state) {
     return $actions;
   }
 
+  /**
+   * Route title callback.
+   */
+  public function getTitle($workflow = NULL) {
+    $title = $this->t('Add a new transition to the @workflow', ['@workflow' => $workflow->label()]);
+    return $title;
+  }
+
 }
diff --git a/core/modules/workflows/src/Form/WorkflowTransitionDeleteForm.php b/core/modules/workflows/src/Form/WorkflowTransitionDeleteForm.php
index abcb41e..b6c54e4 100644
--- a/core/modules/workflows/src/Form/WorkflowTransitionDeleteForm.php
+++ b/core/modules/workflows/src/Form/WorkflowTransitionDeleteForm.php
@@ -2,16 +2,23 @@
 
 namespace Drupal\workflows\Form;
 
+use Drupal\Core\Form\ModalFormTrait;
 use Drupal\workflows\WorkflowInterface;
 use Drupal\Core\Form\ConfirmFormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\CloseDialogCommand;
+use Drupal\Core\Ajax\HtmlCommand;
+use Drupal\Core\Ajax\RemoveCommand;
 
 /**
  * Builds the form to delete transitions from Workflow entities.
  */
 class WorkflowTransitionDeleteForm extends ConfirmFormBase {
 
+  use ModalFormTrait;
+
   /**
    * The workflow entity the transition being deleted belongs to.
    *
@@ -83,8 +90,16 @@ public function buildForm(array $form, FormStateInterface $form_state, WorkflowI
     catch (\InvalidArgumentException $e) {
       throw new NotFoundHttpException();
     }
+    $this->transitionId = $this->transition->id();
     $this->workflow = $workflow;
-    return parent::buildForm($form, $form_state);
+
+    $form = parent::buildForm($form, $form_state);
+    $actions = $this->modalFormActions();
+
+    $form['actions']['submit']['#ajax']['callback'] = [$this, 'submitajaxcallback'];
+    $form['actions']['cancel'] = $actions['cancel'];
+
+    return $form;
   }
 
   /**
@@ -92,11 +107,31 @@ public function buildForm(array $form, FormStateInterface $form_state, WorkflowI
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->workflow
-      ->deleteTransition($this->transition->id())
+      ->deleteTransition($this->transitionId)
       ->save();
 
     drupal_set_message($this->t('%transition transition deleted.', ['%transition' => $this->transition->label()]));
     $form_state->setRedirectUrl($this->getCancelUrl());
   }
 
+  /**
+   * Ajax callback to close the modal and remove the deleted transition.
+   *
+   * @return \Drupal\Core\Ajax\AjaxResponse
+   *   An ajax response object.
+   */
+  public function submitAjaxCallback() {
+
+    $response = $this->modalFormCloseDialog();
+
+    $data_selector = 'edit-transitions-' . strtolower(str_replace('_', '-', $this->transitionId));
+
+    $response->addCommand(new RemoveCommand('[data-drupal-selector="' . $data_selector . '"]'));
+
+    $status_messages = ['#type' => 'status_messages'];
+    $response->addCommand(new HtmlCommand('[data-drupal-selector="edit-transitions-modal-messages"]', \Drupal::service('renderer')->renderRoot($status_messages)));
+
+    return $response;
+  }
+
 }
diff --git a/core/modules/workflows/src/Form/WorkflowTransitionEditForm.php b/core/modules/workflows/src/Form/WorkflowTransitionEditForm.php
index 5bbabae..ea78fca 100644
--- a/core/modules/workflows/src/Form/WorkflowTransitionEditForm.php
+++ b/core/modules/workflows/src/Form/WorkflowTransitionEditForm.php
@@ -2,17 +2,23 @@
 
 namespace Drupal\workflows\Form;
 
+use Drupal\Core\Form\ModalFormTrait;
 use Drupal\Core\Entity\EntityForm;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
 use Drupal\workflows\State;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\CloseDialogCommand;
+use Drupal\Core\Ajax\HtmlCommand;
 
 /**
  * Class WorkflowTransitionEditForm.
  */
 class WorkflowTransitionEditForm extends EntityForm {
 
+  use ModalFormTrait;
+
   /**
    * The ID of the transition that is being edited.
    *
@@ -69,7 +75,7 @@ public function form(array $form, FormStateInterface $form_state) {
       '#options' => $states,
     ];
     $form['to'] = [
-      '#type' => 'radios',
+      '#type' => 'select',
       '#title' => $this->t('To'),
       '#required' => TRUE,
       '#default_value' => $transition->to()->id(),
@@ -89,6 +95,15 @@ public function form(array $form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
+  public function actions(array $form, FormStateInterface $form_state) {
+    $actions = $this->modalFormActions();
+    $actions['submit']['#ajax']['callback'] = [$this,'submitajaxcallback'];
+    return $actions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function validateForm(array &$form, FormStateInterface $form_state) {
     /** @var \Drupal\workflows\WorkflowInterface $workflow */
     $workflow = $this->getEntity();
@@ -140,7 +155,7 @@ protected function copyFormValuesToEntity(EntityInterface $entity, array $form,
    */
   public function save(array $form, FormStateInterface $form_state) {
     /** @var \Drupal\workflows\WorkflowInterface $workflow */
-    $workflow = $this->entity;
+    $workflow = $this->getEntity();
     $workflow->save();
     drupal_set_message($this->t('Saved %label transition.', [
       '%label' => $workflow->getTransition($this->transitionId)->label(),
@@ -149,30 +164,43 @@ public function save(array $form, FormStateInterface $form_state) {
   }
 
   /**
-   * {@inheritdoc}
+   * Ajax callback to update the label, from and to text.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @return \Drupal\Core\Ajax\AjaxResponse
+   *   An ajax response object.
    */
-  protected function actions(array $form, FormStateInterface $form_state) {
-    $actions['submit'] = [
-      '#type' => 'submit',
-      '#value' => $this->t('Save'),
-      '#submit' => ['::submitForm', '::save'],
-    ];
+  public function submitAjaxCallback(array &$form, FormStateInterface $form_state) {
+    $response = $this->modalFormSubmitHandleErrors($form, $form_state);
+    if ($commands = $response->getCommands()) {
+      return $response;
+    }
 
-    $actions['delete'] = [
-      '#type' => 'link',
-      '#title' => $this->t('Delete'),
-      // Deleting a transition is editing a workflow.
-      '#access' => $this->entity->access('edit'),
-      '#attributes' => [
-        'class' => ['button', 'button--danger'],
-      ],
-      '#url' => Url::fromRoute('entity.workflow.delete_transition_form', [
-        'workflow' => $this->entity->id(),
-        'workflow_transition' => $this->transitionId
-      ])
-    ];
+    $workflow = $this->getEntity();
+    $transition = $workflow->getTransition($this->transitionId);
+    $data_selector = 'edit-transitions-' . strtolower(str_replace('_', '-', $this->transitionId));
 
-    return $actions;
+    $response = $this->modalFormCloseDialog();
+    $response->addCommand(new HtmlCommand('[data-drupal-selector="' . $data_selector . '-label"]', $transition->label()));
+    $response->addCommand(new HtmlCommand('[data-drupal-selector="' . $data_selector . '-from"]', implode(', ', array_map([State::class, 'labelCallback'], $transition->from()))));
+    $response->addCommand(new HtmlCommand('[data-drupal-selector="' . $data_selector . '-to]', $transition->to()->label()));
+
+    $status_messages = ['#type' => 'status_messages'];
+    $response->addCommand(new HtmlCommand('[data-drupal-selector="edit-transitions-modal-messages"]', \Drupal::service('renderer')->renderRoot($status_messages)));
+
+    return $response;
+  }
+
+  /**
+   * Route title callback.
+   */
+  public function getTitle($workflow = NULL, $workflow_transition) {
+    $title = $this->t('Edit the @transition transition for the @workflow', ['@transition' => $workflow->getTransition($workflow_transition)->label(), '@workflow' => $workflow->label()]);
+    return $title;
   }
 
 }
diff --git a/core/modules/workflows/tests/src/Functional/WorkflowUiTest.php b/core/modules/workflows/tests/src/Functional/WorkflowUiTest.php
index 0411915..cd69cf8 100644
--- a/core/modules/workflows/tests/src/Functional/WorkflowUiTest.php
+++ b/core/modules/workflows/tests/src/Functional/WorkflowUiTest.php
@@ -156,7 +156,7 @@ public function testWorkflowCreation() {
     // Delete the transition.
     $workflow = $workflow_storage->loadUnchanged('test');
     $this->assertTrue($workflow->hasTransitionFromStateToState('published', 'published'), 'Can transition from published to published');
-    $this->clickLink('Delete');
+    $this->drupalGet('admin/config/workflow/workflows/manage/test/transition/save_and_publish/delete');
     $this->assertSession()->pageTextContains('Are you sure you want to delete Save and publish from Test?');
     $this->submitForm([], 'Delete');
     $workflow = $workflow_storage->loadUnchanged('test');
diff --git a/core/modules/workflows/tests/src/FunctionalJavascript/WorkflowEditFormTest.php b/core/modules/workflows/tests/src/FunctionalJavascript/WorkflowEditFormTest.php
new file mode 100644
index 0000000..95111fe
--- /dev/null
+++ b/core/modules/workflows/tests/src/FunctionalJavascript/WorkflowEditFormTest.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace Drupal\Tests\workflows\FunctionalJavascript;
+
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+use Drupal\workflows\Entity\Workflow;
+
+
+/**
+ * AJAX modal tests for the workflow list page.
+ *
+ * @group content_moderation
+ */
+class WorkflowEditFormTest extends JavascriptTestBase {
+
+/**
+   * Modules to install.
+   *
+   * @var array
+   */
+  public static $modules = [
+    'workflows',
+    'workflow_type_test',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+    // Create a minimal workflow for testing.
+    $workflow = Workflow::create(['id' => 'test', 'type' => 'workflow_type_test']);
+    $workflow
+      ->addState('draft', 'Draft')
+      ->addState('published', 'Published')
+      ->addTransition('publish', 'Publish', ['draft', 'published'], 'published')
+      ->save();
+
+    $user = $this->drupalCreateUser(['administer workflows']);
+    $this->drupalLogin($user);
+  }
+
+  /**
+   * Test states operations uses a modal dialog.
+   */
+  public function testWorkflowStatesForm() {
+    $this->drupalGet('admin/config/workflow/workflows/manage/test');
+    $assert_session = $this->assertSession();
+    $page = $this->getSession()->getPage();
+    $new_label = 'Murray Rothbard';
+
+    // Edit a state.
+    $this->click('#edit-states-container tr:contains(Draft) a:contains(Edit)');
+    $assert_session->assertWaitOnAjaxRequest();
+    $modal = $assert_session->waitForElementVisible('css', '#drupal-modal');
+    $this->assertTrue($modal->isVisible(), 'Modal window found.');
+    $input = $assert_session->elementExists('css', '#drupal-modal .form-item-label input[name="label"]');
+    $input->setValue($new_label);
+    $save_button = $assert_session->waitForElementVisible('css', '.ui-dialog-buttonpane .button--primary');
+    $this->assertTrue($save_button->isVisible(), 'Save button found.');
+    $save_button->click();
+    $assert_session->assertWaitOnAjaxRequest();
+    $assert_session->waitForElementVisible('css', '#edit-states-modal-messages .messages');
+    $assert_session->pageTextContains('Saved ' . $new_label . ' state.');
+    $assert_session->elementExists('css', '#edit-states-draft-state:contains(' . $new_label . ')');
+    $assert_session->elementNotExists('css', '#edit-states-draft-state:contains(Draft)');
+
+    // Delete a state.
+    $this->click('#edit-states-container tr:contains(Published) button .dropbutton-arrow');
+    $delete_button = $assert_session->waitForElementVisible('css', '#edit-states-container tr:contains(Published) a:contains(Delete)');
+    $this->assertTrue($delete_button->isVisible(), 'Delete state button found.');
+    $delete_button->click();
+    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertTrue($modal->isVisible(), 'Modal window found.');
+    $delete_button = $assert_session->waitForElementVisible('css', '.ui-dialog-buttonpane .button--primary');
+    $this->assertTrue($save_button->isVisible(), 'Delete button found.');
+    $delete_button->click();
+    $assert_session->assertWaitOnAjaxRequest();
+    $assert_session->waitForElementVisible('css', '#edit-states-modal-messages .messages');
+    $assert_session->pageTextContains('State Published deleted.');
+    $this->assertNull($page->find('css', '#edit-states-container tr:contains(Published)'));
+  }
+
+  /**
+   * Test transitions operations uses a modal dialog.
+   */
+  public function testWorkflowTransitionsForm() {
+    $this->drupalGet('admin/config/workflow/workflows/manage/test');
+    $assert_session = $this->assertSession();
+    $page = $this->getSession()->getPage();
+    $new_label = 'Murray Rothbard';
+    $screen_dir = \Drupal::root() . '/sites/default/files/simpletest/';
+
+    // Test the Transitions elements.
+    // Edit a transition.
+    $this->click('#edit-transitions-container tr:contains(Publish) a:contains(Edit)');
+    $assert_session->assertWaitOnAjaxRequest();
+    $modal = $assert_session->waitForElementVisible('css', '#drupal-modal');
+    $this->assertTrue($modal->isVisible(), 'Modal window found.');
+    $input = $assert_session->elementExists('css', '#drupal-modal .form-item-label input[name="label"]');
+    $input->setValue($new_label);
+    $page->uncheckField('from[published]');
+    $save_button = $assert_session->waitForElementVisible('css', '.ui-dialog-buttonpane .button--primary');
+    $this->assertTrue($save_button->isVisible(), 'Save button found.');
+    $save_button->click();
+    $assert_session->assertWaitOnAjaxRequest();
+    $assert_session->waitForElementVisible('css', '#edit-transitions-modal-messages .messages');
+    $assert_session->pageTextContains('Saved ' . $new_label . ' transition.');
+    // @TODO assert ui elements get updated with the newly saved values.
+    // #edit-transitions-publish-label
+    // #edit-transitions-publish-from
+
+    // @TODO Delete a transition.
+  }
+
+}
diff --git a/core/modules/workflows/workflows.routing.yml b/core/modules/workflows/workflows.routing.yml
index 329ed10..7290c5f 100644
--- a/core/modules/workflows/workflows.routing.yml
+++ b/core/modules/workflows/workflows.routing.yml
@@ -2,7 +2,7 @@ entity.workflow.add_state_form:
   path: '/admin/config/workflow/workflows/manage/{workflow}/add_state'
   defaults:
     _entity_form: 'workflow.add-state'
-    _title: 'Add state'
+    _title_callback: '\Drupal\workflows\Form\WorkflowStateAddForm::getTitle'
   requirements:
     _entity_access: 'workflow.edit'
 
@@ -10,7 +10,7 @@ entity.workflow.edit_state_form:
   path: '/admin/config/workflow/workflows/manage/{workflow}/state/{workflow_state}'
   defaults:
     _entity_form: 'workflow.edit-state'
-    _title: 'Edit state'
+    _title_callback: '\Drupal\workflows\Form\WorkflowStateEditForm::getTitle'
   requirements:
     _entity_access: 'workflow.edit'
 
@@ -26,7 +26,7 @@ entity.workflow.add_transition_form:
   path: '/admin/config/workflow/workflows/manage/{workflow}/add_transition'
   defaults:
     _entity_form: 'workflow.add-transition'
-    _title: 'Add transition'
+    _title_callback: '\Drupal\workflows\Form\WorkflowTransitionAddForm::getTitle'
   requirements:
     _entity_access: 'workflow.edit'
 
@@ -34,7 +34,7 @@ entity.workflow.edit_transition_form:
   path: '/admin/config/workflow/workflows/manage/{workflow}/transition/{workflow_transition}'
   defaults:
     _entity_form: 'workflow.edit-transition'
-    _title: 'Edit transition'
+    _title_callback: '\Drupal\workflows\Form\WorkflowTransitionEditForm::getTitle'
   requirements:
     _entity_access: 'workflow.edit'
 
diff --git a/core/themes/seven/css/components/dropbutton.component.css b/core/themes/seven/css/components/dropbutton.component.css
index 2cf0141..f6148da 100644
--- a/core/themes/seven/css/components/dropbutton.component.css
+++ b/core/themes/seven/css/components/dropbutton.component.css
@@ -203,8 +203,22 @@
   -webkit-transition: none;
   transition: none;
 }
-.dropbutton-single .dropbutton-action a.use-ajax {
-  float: left;
+.dropbutton .dropbutton-action .ajax-progress {
+  position: absolute;
+  z-index: 2;
+  top: 0.2em;
+  right: 0.2em;
+  padding: 0px 0 0 0.1em;
+}
+.dropbutton-multiple .dropbutton-action .ajax-progress {
+  right: 2.2em;
+  top: 0.15em;
+  margin-right: 0;
+}
+.dropbutton-multiple .secondary-action .ajax-progress {
+  top: auto;
+  bottom: 0.3em;
+  right: 2em;
 }
 
 /**
