diff --git a/src/Plugin/Field/FieldWidget/InlineParagraphsWidget.php b/src/Plugin/Field/FieldWidget/InlineParagraphsWidget.php
index 35aac3f..b211a50 100644
--- a/src/Plugin/Field/FieldWidget/InlineParagraphsWidget.php
+++ b/src/Plugin/Field/FieldWidget/InlineParagraphsWidget.php
@@ -847,30 +847,33 @@ class InlineParagraphsWidget extends WidgetBase {
     $field_state['real_item_count'] = $this->realItemCount;
     static::setWidgetState($this->fieldParents, $field_name, $form_state, $field_state);
 
+    $elements += [
+      '#element_validate' => [[$this, 'multipleElementValidate']],
+      '#required' => $this->fieldDefinition->isRequired(),
+      '#field_name' => $field_name,
+      '#cardinality' => $cardinality,
+      '#max_delta' => $max - 1,
+    ];
+
     if ($this->realItemCount > 0) {
       $elements += array(
         '#theme' => 'field_multiple_value_form',
-        '#field_name' => $field_name,
-        '#cardinality' => $cardinality,
         '#cardinality_multiple' => $is_multiple,
-        '#required' => $this->fieldDefinition->isRequired(),
         '#title' => $title,
         '#description' => $description,
-        '#max_delta' => $max-1,
       );
     }
     else {
+      $classes = $this->fieldDefinition->isRequired() ? ['form-required'] : [];
       $elements += [
         '#type' => 'container',
         '#theme_wrappers' => ['container'],
-        '#field_name' => $field_name,
-        '#cardinality' => $cardinality,
         '#cardinality_multiple' => TRUE,
-        '#max_delta' => $max-1,
         'title' => [
           '#type' => 'html_tag',
           '#tag' => 'strong',
           '#value' => $title,
+          '#attributes' => ['class' => $classes],
         ],
         'text' => [
           '#type' => 'container',
@@ -878,7 +881,7 @@ class InlineParagraphsWidget extends WidgetBase {
             '#markup' => $this->t('No @title added yet.', ['@title' => $this->getSetting('title')]),
             '#prefix' => '<em>',
             '#suffix' => '</em>',
-          ]
+          ],
         ],
       ];
 
@@ -1232,6 +1235,20 @@ class InlineParagraphsWidget extends WidgetBase {
   /**
    * {@inheritdoc}
    */
+  public function multipleElementValidate($element, FormStateInterface $form_state, $form) {
+    $field_name = $this->fieldDefinition->getName();
+    $widget_state = static::getWidgetState($element['#field_parents'], $field_name, $form_state);
+
+    if ($element['#required'] && $widget_state['real_item_count'] < 1) {
+      $form_state->setError($element, t('@name field is required.', ['@name' => $this->fieldDefinition->getLabel()]));
+    }
+
+    static::setWidgetState($element['#field_parents'], $field_name, $form_state, $widget_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
     $entity = $form_state->getFormObject()->getEntity();
     $field_name = $this->fieldDefinition->getName();
diff --git a/src/Plugin/Field/FieldWidget/ParagraphsWidget.php b/src/Plugin/Field/FieldWidget/ParagraphsWidget.php
index 52041b4..6653962 100644
--- a/src/Plugin/Field/FieldWidget/ParagraphsWidget.php
+++ b/src/Plugin/Field/FieldWidget/ParagraphsWidget.php
@@ -820,30 +820,33 @@ class ParagraphsWidget extends WidgetBase {
     $field_state['real_item_count'] = $this->realItemCount;
     static::setWidgetState($this->fieldParents, $field_name, $form_state, $field_state);
 
+    $elements += [
+      '#element_validate' => [[$this, 'multipleElementValidate']],
+      '#required' => $this->fieldDefinition->isRequired(),
+      '#field_name' => $field_name,
+      '#cardinality' => $cardinality,
+      '#max_delta' => $max - 1,
+    ];
+
     if ($this->realItemCount > 0) {
       $elements += array(
         '#theme' => 'field_multiple_value_form',
-        '#field_name' => $field_name,
-        '#cardinality' => $cardinality,
         '#cardinality_multiple' => $is_multiple,
-        '#required' => $this->fieldDefinition->isRequired(),
         '#title' => $title,
         '#description' => $description,
-        '#max_delta' => $max-1,
       );
     }
     else {
+      $classes = $this->fieldDefinition->isRequired() ? ['form-required'] : [];
       $elements += [
         '#type' => 'container',
         '#theme_wrappers' => ['container'],
-        '#field_name' => $field_name,
-        '#cardinality' => $cardinality,
         '#cardinality_multiple' => TRUE,
-        '#max_delta' => $max-1,
         'title' => [
           '#type' => 'html_tag',
           '#tag' => 'strong',
           '#value' => $title,
+          '#attributes' => ['class' => $classes],
         ],
         'text' => [
           '#type' => 'container',
@@ -851,7 +854,7 @@ class ParagraphsWidget extends WidgetBase {
             '#markup' => $this->t('No @title added yet.', ['@title' => $this->getSetting('title')]),
             '#prefix' => '<em>',
             '#suffix' => '</em>',
-          ]
+          ],
         ],
       ];
 
@@ -1178,6 +1181,20 @@ class ParagraphsWidget extends WidgetBase {
   /**
    * {@inheritdoc}
    */
+  public function multipleElementValidate($element, FormStateInterface $form_state, $form) {
+    $field_name = $this->fieldDefinition->getName();
+    $widget_state = static::getWidgetState($element['#field_parents'], $field_name, $form_state);
+
+    if ($element['#required'] && $widget_state['real_item_count'] < 1) {
+      $form_state->setError($element, t('@name field is required.', ['@name' => $this->fieldDefinition->getLabel()]));
+    }
+
+    static::setWidgetState($element['#field_parents'], $field_name, $form_state, $widget_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
     $entity = $form_state->getFormObject()->getEntity();
     $field_name = $this->fieldDefinition->getName();
diff --git a/src/Tests/Classic/ParagraphsAccessTest.php b/src/Tests/Classic/ParagraphsAccessTest.php
index b31d6b6..8022cab 100644
--- a/src/Tests/Classic/ParagraphsAccessTest.php
+++ b/src/Tests/Classic/ParagraphsAccessTest.php
@@ -142,4 +142,38 @@ class ParagraphsAccessTest extends ParagraphsTestBase {
     $node = $this->getNodeByTitle('delete_permissions');
     $this->assertUrl('node/' . $node->id());
   }
+
+  /**
+   * Tests displaying error message on empty paragraph required field.
+   */
+  public function testEmptyRequiredField() {
+    $admin_user = $this->drupalCreateUser([
+      'administer node fields',
+      'administer paragraph form display',
+      'administer node form display',
+      'create paragraphed_content_demo content',
+      'edit any paragraphed_content_demo content',
+    ]);
+    $this->drupalLogin($admin_user);
+    // Add required field to paragraphed content type.
+    $field_title = 'Content Test';
+    $this->drupalPostForm('admin/structure/types/manage/paragraphed_content_demo/fields/add-field', [
+      'new_storage_type' => 'field_ui:entity_reference_revisions:paragraph',
+      'label' => $field_title,
+      'field_name' => 'content',
+    ], t('Save and continue'));
+    $this->drupalPostForm(NULL, [], t('Save field settings'));
+    $edit = [
+      'required' => TRUE,
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Save settings');
+    $this->assertText('Saved ' . $field_title . ' configuration.');
+
+    // Create paragraph with empty required field.
+    $title = 'Empty';
+    $this->drupalGet('node/add/paragraphed_content_demo');
+    $this->drupalPostForm(NULL, ['title[0][value]' => $title], t('Save'));
+    $this->assertText($field_title . ' field is required');
+  }
+
 }
diff --git a/src/Tests/Experimental/ParagraphsExperimentalAccessTest.php b/src/Tests/Experimental/ParagraphsExperimentalAccessTest.php
index f2d0fdf..78d29cc 100644
--- a/src/Tests/Experimental/ParagraphsExperimentalAccessTest.php
+++ b/src/Tests/Experimental/ParagraphsExperimentalAccessTest.php
@@ -140,4 +140,38 @@ class ParagraphsExperimentalAccessTest extends ParagraphsExperimentalTestBase {
     $node = $this->getNodeByTitle('delete_permissions');
     $this->assertUrl('node/' . $node->id());
   }
+
+  /**
+   * Tests displaying error message on empty paragraph required field.
+   */
+  public function testEmptyRequiredField() {
+    $admin_user = $this->drupalCreateUser([
+      'administer node fields',
+      'administer paragraph form display',
+      'administer node form display',
+      'create paragraphed_content_demo content',
+      'edit any paragraphed_content_demo content',
+    ]);
+    $this->drupalLogin($admin_user);
+    // Add required field to paragraphed content type.
+    $field_title = 'Content Test';
+    $this->drupalPostForm('admin/structure/types/manage/paragraphed_content_demo/fields/add-field', [
+      'new_storage_type' => 'field_ui:entity_reference_revisions:paragraph',
+      'label' => $field_title,
+      'field_name' => 'content',
+    ], t('Save and continue'));
+    $this->drupalPostForm(NULL, [], t('Save field settings'));
+    $edit = [
+      'required' => TRUE,
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Save settings');
+    $this->assertText('Saved ' . $field_title . ' configuration.');
+
+    // Create paragraph with empty required field.
+    $title = 'Empty';
+    $this->drupalGet('node/add/paragraphed_content_demo');
+    $this->drupalPostForm(NULL, ['title[0][value]' => $title], t('Save'));
+    $this->assertText($field_title . ' field is required');
+  }
+
 }
