diff --git a/entity_embed.module b/entity_embed.module
index fbdd994..079bc82 100644
--- a/entity_embed.module
+++ b/entity_embed.module
@@ -6,6 +6,9 @@
  * format.
  */
 
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Url;
+
 /**
  * Implements hook_theme().
  */
@@ -31,6 +34,25 @@ function template_preprocess_entity_embed_container(&$variables) {
   $variables['element'] += ['#attributes' => []];
   $variables['attributes'] = $variables['element']['#attributes'];
   $variables['children'] = $variables['element']['#children'];
+  if (!empty($variables['element']['#context']['data-entity-embed-display-settings']['link_url'])) {
+    $link = UrlHelper::filterBadProtocol($variables['element']['#context']['data-entity-embed-display-settings']['link_url']);
+    if (!UrlHelper::isExternal($link)) {
+      $link = 'internal:/' . ltrim($link, '/');
+    }
+    $link = Url::fromUri($link);
+    $attributes = [];
+    if (!empty($variables['element']['#context']['data-entity-embed-display-settings']['link_url_target']) && $variables['element']['#context']['data-entity-embed-display-settings']['link_url_target'] == 1) {
+      $attributes = ['attributes' => ['target' => '_blank']];
+    }
+    $variables['children'] = [
+      [
+        '#type' => 'link',
+        '#title' => $variables['children'],
+        '#options' => $attributes,
+        '#url' => $link,
+      ]
+    ];
+  }
 }
 
 /**
diff --git a/src/Form/EntityEmbedDialog.php b/src/Form/EntityEmbedDialog.php
index ae0194b..a26cbc2 100644
--- a/src/Form/EntityEmbedDialog.php
+++ b/src/Form/EntityEmbedDialog.php
@@ -8,6 +8,7 @@ use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\CloseModalDialogCommand;
 use Drupal\Core\Ajax\HtmlCommand;
 use Drupal\Core\Ajax\SetDialogTitleCommand;
+use Drupal\Core\Entity\Element\EntityAutocomplete;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
@@ -434,6 +435,31 @@ class EntityEmbedDialog extends FormBase {
       if (is_string($entity_element['data-entity-embed-display-settings'])) {
         $entity_element['data-entity-embed-display-settings'] = Json::decode($entity_element['data-entity-embed-display-settings']);
       }
+
+      // Supress Drupal's "Link image to" dropdown when embedding an image,
+      // since the 'Link to' option provides this functionality.
+      if (isset($form['attributes']['data-entity-embed-display-settings']['image_link'])) {
+        $form['attributes']['data-entity-embed-display-settings']['image_link']['#type'] = 'hidden';
+        $form['attributes']['data-entity-embed-display-settings']['image_link']['#value'] = '';
+      }
+      $form['attributes']['data-entity-embed-display-settings']['link_url'] = [
+        '#title' => t('Link to'),
+        '#type' => 'entity_autocomplete',
+        '#target_type' => 'node',
+        '#attributes' => [
+          'data-autocomplete-first-character-blacklist' => '/#?'
+        ],
+        '#element_validate' => [[get_called_class(), 'validateUriElement']],
+        '#process_default_value' => FALSE,
+        '#description' => $this->t('Start typing the title of a piece of content to select it. You can also enter an internal path such as %add-node or an external URL such as %url. Enter %front to link to the front page.', ['%front' => '<front>', '%add-node' => '/node/add', '%url' => 'http://example.com']),
+        '#default_value' => isset($entity_element['data-entity-embed-display-settings']['link_url']) ? $this->getUriAsDisplayableString($entity_element['data-entity-embed-display-settings']['link_url']) : '',
+        '#maxlength' => 2048,
+      ];
+      $form['attributes']['data-entity-embed-display-settings']['link_url_target'] = [
+        '#title' => t('Open in a new window?'),
+        '#type' => 'checkbox',
+        '#default_value' => isset($entity_element['data-entity-embed-display-settings']['link_url_target']) ? Html::decodeEntities($entity_element['data-entity-embed-display-settings']['link_url_target']) : '',
+      ];
       $display = $this->entityEmbedDisplayManager->createInstance($plugin_id, $entity_element['data-entity-embed-display-settings']);
       $display->setContextValue('entity', $entity);
       $display->setAttributes($entity_element);
@@ -497,6 +523,118 @@ class EntityEmbedDialog extends FormBase {
     return $form;
   }
 
+  /**
+   * Gets the URI without the 'internal:' or 'entity:' scheme.
+   *
+   * The following two forms of URIs are transformed:
+   * - 'entity:' URIs: to entity autocomplete ("label (entity id)") strings;
+   * - 'internal:' URIs: the scheme is stripped.
+   *
+   * This method is the inverse of ::getUserEnteredStringAsUri().
+   *
+   * @param string $uri
+   *   The URI to get the displayable string for.
+   *
+   * @return string
+   *
+   * @see static::getUserEnteredStringAsUri()
+   */
+  protected function getUriAsDisplayableString($uri) {
+    $uri = Html::decodeEntities($uri);
+    $scheme = parse_url($uri, PHP_URL_SCHEME);
+
+    // By default, the displayable string is the URI.
+    $displayable_string = $uri;
+
+    // A different displayable string may be chosen in case of the 'internal:'
+    // or 'entity:' built-in schemes.
+    if ($scheme === 'internal') {
+      $uri_reference = explode(':', $uri, 2)[1];
+
+      // @todo '<front>' is valid input for BC reasons, may be removed by
+      //   https://www.drupal.org/node/2421941
+      $path = parse_url($uri, PHP_URL_PATH);
+      if ($path === '/') {
+        $uri_reference = '<front>' . substr($uri_reference, 1);
+      }
+
+      $displayable_string = $uri_reference;
+    }
+    elseif ($scheme === 'entity') {
+      list($entity_type, $entity_id) = explode('/', substr($uri, 7), 2);
+      // Show the 'entity:' URI as the entity autocomplete would.
+      // @todo Support entity types other than 'node'. Will be fixed in
+      //    https://www.drupal.org/node/2423093.
+      if ($entity_type == 'node' && $entity = \Drupal::entityTypeManager()->getStorage($entity_type)->load($entity_id)) {
+        $displayable_string = EntityAutocomplete::getEntityLabels([$entity]);
+      }
+    }
+
+    return $displayable_string;
+  }
+
+  /**
+   * Gets the user-entered string as a URI.
+   *
+   * The following two forms of input are mapped to URIs:
+   * - entity autocomplete ("label (entity id)") strings: to 'entity:' URIs;
+   * - strings without a detectable scheme: to 'internal:' URIs.
+   *
+   * This method is the inverse of ::getUriAsDisplayableString().
+   *
+   * @param string $string
+   *   The user-entered string.
+   *
+   * @return string
+   *   The URI, if a non-empty $uri was passed.
+   *
+   * @see static::getUriAsDisplayableString()
+   */
+  protected static function getUserEnteredStringAsUri($string) {
+    // By default, assume the entered string is an URI.
+    $uri = $string;
+
+    // Detect entity autocomplete string, map to 'entity:' URI.
+    $entity_id = EntityAutocomplete::extractEntityIdFromAutocompleteInput($string);
+    if ($entity_id !== NULL) {
+      // @todo Support entity types other than 'node'. Will be fixed in
+      //    https://www.drupal.org/node/2423093.
+      $uri = 'entity:node/' . $entity_id;
+    }
+    // Detect a schemeless string, map to 'internal:' URI.
+    elseif (!empty($string) && parse_url($string, PHP_URL_SCHEME) === NULL) {
+      // @todo '<front>' is valid input for BC reasons, may be removed by
+      //   https://www.drupal.org/node/2421941
+      // - '<front>' -> '/'
+      // - '<front>#foo' -> '/#foo'
+      if (strpos($string, '<front>') === 0) {
+        $string = '/' . substr($string, strlen('<front>'));
+      }
+      $uri = 'internal:' . $string;
+    }
+
+    return $uri;
+  }
+
+  /**
+   * Form element validation handler for the 'uri' element.
+   *
+   * Disallows saving inaccessible or untrusted URLs.
+   */
+  public static function validateUriElement($element, FormStateInterface $form_state, $form) {
+    $uri = static::getUserEnteredStringAsUri($element['#value']);
+    $form_state->setValueForElement($element, $uri);
+
+    // If getUserEnteredStringAsUri() mapped the entered value to a 'internal:'
+    // URI , ensure the raw value begins with '/', '?' or '#'.
+    // @todo '<front>' is valid input for BC reasons, may be removed by
+    //   https://www.drupal.org/node/2421941
+    if (parse_url($uri, PHP_URL_SCHEME) === 'internal' && !in_array($element['#value'][0], ['/', '?', '#'], TRUE) && substr($element['#value'], 0, 7) !== '<front>') {
+      $form_state->setError($element, t('Manually entered paths should start with /, ? or #.'));
+      return;
+    }
+  }
+
   /**
    * {@inheritdoc}
    */