diff --git a/core/lib/Drupal/Core/Form/ModalFormTrait.php b/core/lib/Drupal/Core/Form/ModalFormTrait.php
new file mode 100644
index 0000000..db4fc40
--- /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 cancel button elements with #ajax callbacks
+   *
+   * @return array
+   *   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'],
+      ],
+      '#limit_validation_errors' => [],
+    ];
+
+    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..82537ca
--- /dev/null
+++ b/core/modules/content_moderation/tests/src/FunctionalJavascript/WorkflowTypeEditFormTest.php
@@ -0,0 +1,56 @@
+<?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');
+
+    $this->click('table tr:contains(Custom block types) a');
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    $modal = $this->assertSession()->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 = $this->assertSession()->waitForElementVisible('css', '.ui-dialog button:contains(Save)');
+    $this->assertTrue($save_button->isVisible(), 'Save button found.');
+    $save_button->click();
+    $this->assertSession()->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..8e5b9f3 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;
@@ -85,7 +86,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 +103,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 +134,16 @@ 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,
+        ]),
+      ],
     ];
 
     $header = [
@@ -148,16 +175,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 +211,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 +225,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 +244,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 1827dd3..db8386b 100644
--- a/core/modules/workflows/src/Form/WorkflowStateAddForm.php
+++ b/core/modules/workflows/src/Form/WorkflowStateAddForm.php
@@ -5,12 +5,17 @@
 use Drupal\Core\Entity\EntityForm;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\ModalFormTrait;
+use Drupal\Core\Ajax\RedirectCommand;
+use Drupal\Core\Ajax\AjaxResponse;
 
 /**
  * Class WorkflowStateAddForm.
  */
 class WorkflowStateAddForm extends EntityForm {
 
+  use ModalFormTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -111,12 +116,40 @@ public function save(array $form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   protected function actions(array $form, FormStateInterface $form_state) {
-    $actions['submit'] = [
-      '#type' => 'submit',
-      '#value' => $this->t('Save'),
-      '#submit' => ['::submitForm', '::save'],
-    ];
+    $actions = $this->modalFormActions();
+    $actions['submit']['#ajax']['callback'] = [$this, 'submitajaxcallback'];
     return $actions;
   }
 
+  /**
+   * Ajax callback display any errors in the modal and reload the page.
+   *
+   * @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 submitAjaxCallback(array &$form, FormStateInterface $form_state) {
+    $errors = $form_state->getErrors();
+    if (count($errors)) {
+      return $this->modalFormSubmitHandleErrors($form, $form_state);
+    }
+
+    $response = new AjaxResponse();
+    $response->addCommand(new RedirectCommand($this->getEntity()->toUrl('edit-form')->toString()));
+
+    return $response;
+  }
+
+  /**
+   * 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..c1354f0 100644
--- a/core/modules/workflows/src/Form/WorkflowStateDeleteForm.php
+++ b/core/modules/workflows/src/Form/WorkflowStateDeleteForm.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\workflows\Form;
 
+use Drupal\Core\Form\ModalFormTrait;
 use Drupal\workflows\WorkflowInterface;
 use Drupal\Core\Form\ConfirmFormBase;
 use Drupal\Core\Form\FormStateInterface;
@@ -12,6 +13,8 @@
  */
 class WorkflowStateDeleteForm extends ConfirmFormBase {
 
+  use ModalFormTrait;
+
   /**
    * The workflow entity the state being deleted belongs to.
    *
@@ -75,7 +78,13 @@ 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']['cancel'] = $actions['cancel'];
+
+    return $form;
   }
 
   /**
@@ -83,6 +92,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)
diff --git a/core/modules/workflows/src/Form/WorkflowStateEditForm.php b/core/modules/workflows/src/Form/WorkflowStateEditForm.php
index f21508f..e7e6264 100644
--- a/core/modules/workflows/src/Form/WorkflowStateEditForm.php
+++ b/core/modules/workflows/src/Form/WorkflowStateEditForm.php
@@ -5,13 +5,18 @@
 use Drupal\Core\Entity\EntityForm;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\ModalFormTrait;
 use Drupal\Core\Url;
+use Drupal\Core\Ajax\RedirectCommand;
+use Drupal\Core\Ajax\AjaxResponse;
 
 /**
  * Class WorkflowStateEditForm.
  */
 class WorkflowStateEditForm extends EntityForm {
 
+  use ModalFormTrait;
+
   /**
    * The ID of the state that is being edited.
    *
@@ -67,49 +72,19 @@ public function form(array $form, FormStateInterface $form_state) {
       '#tree' => TRUE,
     ];
 
-    $header = [
-      'label' => $this->t('Transition'),
-      'state' => $this->t('To'),
-      'operations' => $this->t('Operations'),
-    ];
-    $form['transitions'] = [
-      '#type' => 'table',
-      '#header' => $header,
-      '#empty' => $this->t('There are no transitions to or from this state yet.'),
-    ];
-    foreach ($state->getTransitions() as $transition) {
-      $links['edit'] = [
-        'title' => $this->t('Edit'),
-        'url' => Url::fromRoute('entity.workflow.edit_transition_form', [
-          'workflow' => $workflow->id(),
-          'workflow_transition' => $transition->id()
-        ]),
-      ];
-      $links['delete'] = [
-        'title' => t('Delete'),
-        'url' => Url::fromRoute('entity.workflow.delete_transition_form', [
-          'workflow' => $workflow->id(),
-          'workflow_transition' => $transition->id()
-        ]),
-      ];
-      $form['transitions'][$transition->id()] = [
-        'label' => [
-          '#markup' => $transition->label(),
-        ],
-        'state' => [
-          '#markup' => $transition->to()->label(),
-        ],
-        'operations' => [
-          '#type' => 'operations',
-          '#links' => $links,
-        ],
-      ];
-    }
-
     return $form;
   }
 
   /**
+   * {@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 +112,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 +121,34 @@ 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) {
+    $errors = $form_state->getErrors();
+    if (count($errors)) {
+      return $this->modalFormSubmitHandleErrors($form, $form_state);
+    }
 
-    $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
-      ])
-    ];
+    $response = new AjaxResponse();
+    $response->addCommand(new RedirectCommand($this->getEntity()->toUrl('edit-form')->toString()));
 
-    return $actions;
+    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..925bcb8 100644
--- a/core/modules/workflows/src/Form/WorkflowTransitionAddForm.php
+++ b/core/modules/workflows/src/Form/WorkflowTransitionAddForm.php
@@ -6,12 +6,17 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\workflows\State;
+use Drupal\Core\Form\ModalFormTrait;
+use Drupal\Core\Ajax\RedirectCommand;
+use Drupal\Core\Ajax\AjaxResponse;
 
 /**
  * Class WorkflowTransitionAddForm.
  */
 class WorkflowTransitionAddForm extends EntityForm {
 
+  use ModalFormTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -54,7 +59,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' => [],
@@ -147,12 +152,40 @@ public function save(array $form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   protected function actions(array $form, FormStateInterface $form_state) {
-    $actions['submit'] = [
-      '#type' => 'submit',
-      '#value' => $this->t('Save'),
-      '#submit' => ['::submitForm', '::save'],
-    ];
+    $actions = $this->modalFormActions();
+    $actions['submit']['#ajax']['callback'] = [$this, 'submitajaxcallback'];
     return $actions;
   }
 
+  /**
+   * Ajax callback display any errors in the modal and reload the page.
+   *
+   * @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 submitAjaxCallback(array &$form, FormStateInterface $form_state) {
+    $errors = $form_state->getErrors();
+    if (count($errors)) {
+      return $this->modalFormSubmitHandleErrors($form, $form_state);
+    }
+
+    $response = new AjaxResponse();
+    $response->addCommand(new RedirectCommand($this->getEntity()->toUrl('edit-form')->toString()));
+
+    return $response;
+  }
+
+  /**
+   * 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..0181576 100644
--- a/core/modules/workflows/src/Form/WorkflowTransitionDeleteForm.php
+++ b/core/modules/workflows/src/Form/WorkflowTransitionDeleteForm.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\workflows\Form;
 
+use Drupal\Core\Form\ModalFormTrait;
 use Drupal\workflows\WorkflowInterface;
 use Drupal\Core\Form\ConfirmFormBase;
 use Drupal\Core\Form\FormStateInterface;
@@ -12,6 +13,8 @@
  */
 class WorkflowTransitionDeleteForm extends ConfirmFormBase {
 
+  use ModalFormTrait;
+
   /**
    * The workflow entity the transition being deleted belongs to.
    *
@@ -83,8 +86,15 @@ 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']['cancel'] = $actions['cancel'];
+
+    return $form;
   }
 
   /**
@@ -92,7 +102,7 @@ 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()]));
diff --git a/core/modules/workflows/src/Form/WorkflowTransitionEditForm.php b/core/modules/workflows/src/Form/WorkflowTransitionEditForm.php
index 5bbabae..ecc4a81 100644
--- a/core/modules/workflows/src/Form/WorkflowTransitionEditForm.php
+++ b/core/modules/workflows/src/Form/WorkflowTransitionEditForm.php
@@ -5,14 +5,18 @@
 use Drupal\Core\Entity\EntityForm;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Url;
+use Drupal\Core\Form\ModalFormTrait;
 use Drupal\workflows\State;
+use Drupal\Core\Ajax\RedirectCommand;
+use Drupal\Core\Ajax\AjaxResponse;
 
 /**
  * Class WorkflowTransitionEditForm.
  */
 class WorkflowTransitionEditForm extends EntityForm {
 
+  use ModalFormTrait;
+
   /**
    * The ID of the transition that is being edited.
    *
@@ -69,7 +73,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 +93,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 +153,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 +162,34 @@ 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) {
+    $errors = $form_state->getErrors();
+    if (count($errors)) {
+      return $this->modalFormSubmitHandleErrors($form, $form_state);
+    }
 
-    $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
-      ])
-    ];
+    $response = new AjaxResponse();
+    $response->addCommand(new RedirectCommand($this->getEntity()->toUrl('edit-form')->toString()));
 
-    return $actions;
+    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 7db8780..1afe1dd 100644
--- a/core/modules/workflows/tests/src/Functional/WorkflowUiTest.php
+++ b/core/modules/workflows/tests/src/Functional/WorkflowUiTest.php
@@ -174,7 +174,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..4603697
--- /dev/null
+++ b/core/modules/workflows/tests/src/FunctionalJavascript/WorkflowEditFormTest.php
@@ -0,0 +1,106 @@
+<?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')
+      ->addTransition('create_new_draft', 'Create New Draft', ['published'], 'draft')
+      ->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 that the state edit form is displayed in a modal.
+    $this->click('[data-drupal-selector="edit-states-draft-operations"] a:contains(Edit)');
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    $modal = $this->assertSession()->waitForElementVisible('css', '#drupal-modal');
+    $this->assertTrue($modal->isVisible(), 'Modal window found.');
+    // Assert that the form errors are displayed in the modal.
+    $input = $this->assertSession()->elementExists('css', '#drupal-modal .form-item-label input[name="label"]');
+    $input->setValue("");
+    $save_button = $this->assertSession()->waitForElementVisible('css', '.ui-dialog-buttonpane .button--primary');
+    $this->assertTrue($save_button->isVisible(), 'Save button found.');
+    $save_button->click();
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    $this->assertSession()->pageTextContains('Label field is required.');
+
+    // Assert that the state delete form is displayed in a modal.
+    $this->click('#edit-states-container tr:contains(Published) button .dropbutton-arrow');
+    $delete_button = $this->assertSession()->waitForElementVisible('css', '#edit-states-container tr:contains(Published) a:contains(Delete)');
+    $this->assertTrue($delete_button->isVisible(), 'Delete state button found.');
+    $delete_button->click();
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    $this->assertTrue($modal->isVisible(), 'Modal window found.');
+    $delete_button = $this->assertSession()->waitForElementVisible('css', '.ui-dialog-buttonpane .button--primary');
+    $this->assertTrue($save_button->isVisible(), 'Delete button found.');
+  }
+
+  /**
+   * Test transitions operations uses a modal dialog.
+   */
+  public function testWorkflowTransitionsForm() {
+    $this->drupalGet('admin/config/workflow/workflows/manage/test');
+
+    // Assert that the state edit form is displayed in a modal.
+    $this->click('[data-drupal-selector="edit-transitions-publish-operations"] a:contains(Edit)');
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    $modal = $this->assertSession()->waitForElementVisible('css', '#drupal-modal');
+    $this->assertTrue($modal->isVisible(), 'Modal window found.');
+    // Assert that the form errors are displayed in the modal.
+    $input = $this->assertSession()->elementExists('css', '#drupal-modal .form-item-label input[name="label"]');
+    $input->setValue("");
+    $save_button = $this->assertSession()->waitForElementVisible('css', '.ui-dialog-buttonpane .button--primary');
+    $this->assertTrue($save_button->isVisible(), 'Save button found.');
+    $save_button->click();
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    $this->assertSession()->pageTextContains('Label field is required.');
+
+    // Assert that the transition delete form is displayed in a modal.
+    $this->click('#edit-transitions-container tr:contains(Create New Draft) button .dropbutton-arrow');
+    $delete_button = $this->assertSession()->waitForElementVisible('css', '#edit-transitions-container tr:contains(Create New Draft) a:contains(Delete)');
+    $this->assertTrue($delete_button->isVisible(), 'Delete transition button found.');
+    $delete_button->click();
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    $this->assertTrue($modal->isVisible(), 'Modal window found.');
+    $delete_button = $this->assertSession()->waitForElementVisible('css', '.ui-dialog-buttonpane .button--primary');
+    $this->assertTrue($save_button->isVisible(), 'Delete button found.');
+  }
+
+}
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..ef369ce 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: 0 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;
 }
 
 /**
