diff --git a/config/schema/flag.schema.yml b/config/schema/flag.schema.yml
index 926b798..7de26bc 100644
--- a/config/schema/flag.schema.yml
+++ b/config/schema/flag.schema.yml
@@ -123,6 +123,9 @@ flag.link_type.plugin.confirm:
     unflag_confirmation:
        type: label
        label: 'Unflag confirmation'
+    modal:
+      type: boolean
+      label: 'Use modal dialog'
 
 flag.link_type.plugin.field_entry:
   type: mapping
@@ -137,3 +140,6 @@ flag.link_type.plugin.field_entry:
     unflag_confirmation:
       type: label
       label: 'Unflag confirmation'
+    modal:
+      type: boolean
+      label: 'Use modal dialog'
diff --git a/src/Plugin/ActionLink/ConfirmForm.php b/src/Plugin/ActionLink/ConfirmForm.php
index fc741f5..1dd3188 100644
--- a/src/Plugin/ActionLink/ConfirmForm.php
+++ b/src/Plugin/ActionLink/ConfirmForm.php
@@ -16,6 +16,8 @@ use Drupal\Core\Form\FormStateInterface;
  */
 class ConfirmForm extends ActionLinkTypeBase {
 
+  use ModalFormTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -36,6 +38,7 @@ class ConfirmForm extends ActionLinkTypeBase {
     $options += [
       'flag_confirmation' => 'Flag this content?',
       'unflag_confirmation' => 'Unflag this content?',
+      'modal' => FALSE,
     ];
 
     return $options;
@@ -76,6 +79,8 @@ class ConfirmForm extends ActionLinkTypeBase {
       '#required' => TRUE,
     ];
 
+    $this->buildConfigurationFormModal($form['display']['settings']['link_options_confirm'], $form_state);
+
     return $form;
   }
 
@@ -102,6 +107,7 @@ class ConfirmForm extends ActionLinkTypeBase {
     parent::submitConfigurationForm($form, $form_state);
     $this->configuration['flag_confirmation'] = $form_state->getValue('flag_confirmation');
     $this->configuration['unflag_confirmation'] = $form_state->getValue('unflag_confirmation');
+    $this->configuration['modal'] = $form_state->getValue('modal');
   }
 
   /**
diff --git a/src/Plugin/ActionLink/FieldEntry.php b/src/Plugin/ActionLink/FieldEntry.php
index 579b842..73fb2a0 100644
--- a/src/Plugin/ActionLink/FieldEntry.php
+++ b/src/Plugin/ActionLink/FieldEntry.php
@@ -16,6 +16,8 @@ use Drupal\Core\Form\FormStateInterface;
  */
 class FieldEntry extends ActionLinkTypeBase {
 
+  use ModalFormTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -36,6 +38,7 @@ class FieldEntry extends ActionLinkTypeBase {
     $options['flag_confirmation'] = 'Enter flagging details';
     $options['edit_flagging'] = 'Edit flagging details';
     $options['unflag_confirmation'] = 'Unflag this content?';
+    $options['modal'] = FALSE;
 
     return $options;
   }
@@ -84,6 +87,8 @@ class FieldEntry extends ActionLinkTypeBase {
       '#required' => TRUE,
     ];
 
+    $this->buildConfigurationFormModal($form['display']['settings']['link_options_field'], $form_state);
+
     return $form;
   }
 
@@ -115,6 +120,7 @@ class FieldEntry extends ActionLinkTypeBase {
     $this->configuration['flag_confirmation'] = $form_state->getValue('flag_confirmation');
     $this->configuration['edit_flagging'] = $form_state->getValue('flagging_edit_title');
     $this->configuration['unflag_confirmation'] = $form_state->getValue('unflag_confirmation');
+    $this->configuration['modal'] = $form_state->getValue('modal');
   }
 
   /**
@@ -152,4 +158,5 @@ class FieldEntry extends ActionLinkTypeBase {
   public function getUnflagQuestion() {
     return $this->configuration['unflag_confirmation'];
   }
+
 }
diff --git a/src/Plugin/ActionLink/ModalFormTrait.php b/src/Plugin/ActionLink/ModalFormTrait.php
new file mode 100644
index 0000000..1295443
--- /dev/null
+++ b/src/Plugin/ActionLink/ModalFormTrait.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Drupal\flag\Plugin\ActionLink;
+
+use Drupal\Component\Serialization\Json;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\flag\FlagInterface;
+
+/**
+ * A trait for managing forms in modal dialogs.
+ */
+trait ModalFormTrait {
+
+  /**
+   * Adds the configuration form option.
+   *
+   * @param array $form
+   *   The form array.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The form state.
+   */
+  protected function buildConfigurationFormModal(array &$form, FormStateInterface $form_state) {
+    $form['modal'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Use a modal dialog'),
+      '#description' => $this->t('The confirmation form will be displayed in a modal dialog instead of going to a separate page.'),
+      '#default_value' => $this->configuration['modal'],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildLink($action, FlagInterface $flag, EntityInterface $entity) {
+    $render = parent::buildLink($action, $flag, $entity);
+    if ($this->configuration['modal']) {
+      $render['#attached']['library'][] = 'core/drupal.ajax';
+      $render['#attributes']['class'][] = 'use-ajax';
+      $render['#attributes']['data-dialog-type'] = 'modal';
+      $render['#attributes']['data-dialog-options'] = Json::encode([
+        'width' => 'auto',
+      ]);
+    }
+    return $render;
+  }
+
+}
diff --git a/tests/src/FunctionalJavascript/ModalFormTest.php b/tests/src/FunctionalJavascript/ModalFormTest.php
new file mode 100644
index 0000000..022b201
--- /dev/null
+++ b/tests/src/FunctionalJavascript/ModalFormTest.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace Drupal\Tests\flag\FunctionalJavascript;
+
+use Drupal\Core\Url;
+use Drupal\flag\Tests\FlagCreateTrait;
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+
+/**
+ * Tests modal form options for action link plugins.
+ *
+ * @group flag
+ */
+class ModalFormTest extends JavascriptTestBase {
+
+  use FlagCreateTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['flag', 'node', 'user'];
+
+  /**
+   * Flag to test with.
+   *
+   * @var \Drupal\flag\FlagInterface
+   */
+  protected $flag;
+
+  /**
+   * The flag service.
+   *
+   * @var \Drupal\flag\FlagServiceInterface
+   */
+  protected $flagService;
+
+  /**
+   * Test node.
+   *
+   * @var \Drupal\node\NodeInterface
+   */
+  protected $node;
+
+  /**
+   * Admin user.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $admin;
+
+  /**
+   * Normal user.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $webUser;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // A test flag.
+    $this->flag = $this->createFlag('node', [], 'confirm');
+    $this->flagService = $this->container->get('flag');
+
+    // A node to test with.
+    $this->admin = $this->createUser([], NULL, TRUE);
+    $type = $this->createContentType();
+    $this->node = $this->createNode([
+      'type' => $type->id(),
+      'uid' => $this->admin->id(),
+      ]);
+
+    $this->webUser = $this->createUser(array_keys($this->flag->actionPermissions()));
+    $this->drupalLogin($this->webUser);
+  }
+
+  /**
+   * Tests the modal form option for confirm and field entry link types.
+   */
+  public function testModalOption() {
+    // Verify default, non-modal behavior.
+    $this->drupalGet($this->node->toUrl());
+    $this->clickLink($this->flag->getFlagShortText());
+
+    // Should be on the confirm form page, since this isn't using a modal.
+    $expected = Url::fromRoute('flag.confirm_flag', ['flag' => $this->flag->id(), 'entity_id' => $this->node->id()]);
+    $this->assertSession()->addressEquals($expected->toString());
+    $this->assertSession()->buttonExists(t('Flag'))->press();
+    $this->assertSession()->addressEquals($this->node->toUrl());
+
+    // Unflag.
+    $this->clickLink($this->flag->getUnflagShortText());
+    $expected = Url::fromRoute('flag.confirm_unflag', ['flag' => $this->flag->id(), 'entity_id' => $this->node->id()]);
+    $this->assertSession()->addressEquals($expected->toString());
+    $this->assertSession()->buttonExists(t('Unflag'))->press();
+    $this->assertSession()->addressEquals($this->node->toUrl());
+
+    // Set the modal option for the 'confirm' link.
+    $configuration = $this->flag->getLinkTypePlugin()->getConfiguration();
+    $configuration['modal'] = TRUE;
+    $this->flag->getLinkTypePlugin()->setConfiguration($configuration);
+    $this->flag->save();
+
+    $this->drupalGet($this->node->toUrl());
+    $this->clickLink($this->flag->getFlagShortText());
+    $this->assertSession()->assertWaitOnAjaxRequest();
+
+    // Should still be on the node url, as this is using a modal.
+    $this->assertSession()->addressEquals($this->node->toUrl()->toString());
+    // Note, there is some odd behavior calling the `press()` method on the
+    // button, so after asserting it exists, click via this method.
+    $this->assertSession()->buttonExists(t('Flag'));
+    $this->click('button:contains("' . t('Flag') . '")');
+    $this->assertSession()->addressEquals($this->node->toUrl()->toString());
+
+    // Unflag.
+    $this->clickLink($this->flag->getUnflagShortText());
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    $this->assertSession()->addressEquals($this->node->toUrl()->toString());
+    $this->assertSession()->buttonExists(t('Unflag'));
+    $this->click('button:contains("' . t('Unflag') . '")');
+    $this->assertSession()->addressEquals($this->node->toUrl()->toString());
+  }
+
+}
