diff --git a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
index 917e810..dec58d5 100644
--- a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
+++ b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
@@ -155,6 +155,12 @@ public static function validateTitleElement(&$element, FormStateInterface $form_
       // so it won't be escaped again as it's already marked safe.
       $form_state->setError($element['title'], t('@title field is required if there is @uri input.', ['@title' => $element['title']['#title'], '@uri' => $element['uri']['#title']]));
     }
+
+    if ($element['title']['#value'] !== '' && $element['uri']['#value'] === '') {
+      // We expect the field name placeholder value to be wrapped in t() here,
+      // so it won't be escaped again as it's already marked safe.
+      $form_state->setError($element['uri'], t('@uri field is required if @title is input.', ['@uri' => $element['uri']['#title'], '@title' => $element['title']['#title']]));
+    }
   }
 
   /**
@@ -234,6 +240,14 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
             ':input[name="' . $field_name . '[' . $delta . '][uri]"]' => ['filled' => TRUE]
           ];
         }
+
+        // Make uri required on the front-end when title filled-in.
+        if (!$element['uri']['#required']) {
+          $field_name = $this->fieldDefinition->get('field_name');
+          $element['uri']['#states']['required'] = [
+            ':input[name="' . $field_name . '[' . $delta . '][title]"]' => ['filled' => TRUE]
+          ];
+        }
       }
     }
 
diff --git a/core/modules/link/tests/src/Functional/LinkFieldTest.php b/core/modules/link/tests/src/Functional/LinkFieldTest.php
index bad6f77..8166f2f 100644
--- a/core/modules/link/tests/src/Functional/LinkFieldTest.php
+++ b/core/modules/link/tests/src/Functional/LinkFieldTest.php
@@ -862,6 +862,23 @@ function testLinkRequired() {
     $this->assertEquals(count($result), 1, "Link text is not marked as required when only the link text is set to required.");
     $this->assertSession()->pageTextContains('Link text field is required if there is URL input.');
 
+    // Posts all required data, except for the link URI for $field_text_only.
+    $edit = [
+      "{$field_both_required}[0][uri]" => 'http://www.example.com',
+      "{$field_both_required}[0][title]" => 'http://www.example.com',
+      "{$field_url_only}[0][uri]" => 'http://www.example.com',
+      "{$field_text_only}[0][title]" => 'http://www.example.com',
+    ];
+    $this->drupalPostForm('entity_test/add', $edit, 'Save');
+
+    // Tests if the link URI is validated as required and it does not have the
+    // required class.
+    $result = $this->xpath('//label[not(contains(@class, :class)) and contains(@for, :for)]', [':class' => 'form-required', ':for' => 'edit-' . $field_text_only . '-0-uri']);
+    $this->assertEquals(count($result), 1, "URL is not marked as required when only link text is set as required.");
+    $result = $this->xpath('//label[not(contains(@class, :class)) and contains(@for, :for)]', [':class' => 'form-required', ':for' => 'edit-' . $field_text_only . '-0-title']);
+    $this->assertEquals(count($result), 1, "Link text is not marked as required when only the link text is set to required.");
+    $this->assertSession()->pageTextContains('URL field is required if Link text is input.');
+
     // Posts all required data.
     $edit = [
       "{$field_both_required}[0][uri]" => 'http://www.example.com',
