diff --git a/core/core.libraries.yml b/core/core.libraries.yml
index dc714de..26a31b9 100644
--- a/core/core.libraries.yml
+++ b/core/core.libraries.yml
@@ -133,6 +133,15 @@ drupal.collapse:
     - core/drupal.form
     - core/jquery.once
 
+drupal.date:
+  version: VERSION
+  js:
+    misc/date.js: {}
+  dependencies:
+    - core/drupal
+    - core/modernizr
+    - core/jquery.ui.datepicker
+
 drupal.debounce:
   version: VERSION
   js:
diff --git a/core/lib/Drupal/Core/Datetime/Element/Datetime.php b/core/lib/Drupal/Core/Datetime/Element/Datetime.php
index b8e4b69..12dd018 100644
--- a/core/lib/Drupal/Core/Datetime/Element/Datetime.php
+++ b/core/lib/Drupal/Core/Datetime/Element/Datetime.php
@@ -267,7 +267,7 @@ public static function processDatetime(&$element, FormStateInterface $form_state
         '#attributes' => $element['#attributes'] + $extra_attributes,
         '#required' => $element['#required'],
         '#size' => max(12, strlen($element['#value']['date'])),
-        '#error_no_message' => TRUE,
+        '#date_date_format' => $element['#date_date_format'],
       );
 
       // Allows custom callbacks to alter the element.
diff --git a/core/lib/Drupal/Core/Render/Element/Date.php b/core/lib/Drupal/Core/Render/Element/Date.php
index 8585cb3..2c96fff 100644
--- a/core/lib/Drupal/Core/Render/Element/Date.php
+++ b/core/lib/Drupal/Core/Render/Element/Date.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Render\Element;
 
+use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
 
 /**
@@ -26,14 +27,38 @@ class Date extends FormElement {
    */
   public function getInfo() {
     $class = get_class($this);
-    return array(
+    return [
       '#input' => TRUE,
       '#theme' => 'input__date',
-      '#pre_render' => array(
-        array($class, 'preRenderDate'),
-      ),
-      '#theme_wrappers' => array('form_element'),
-    );
+      '#process' => [[$class, 'processDate']],
+      '#pre_render' => [[$class, 'preRenderDate']],
+      '#theme_wrappers' => ['form_element'],
+    ];
+  }
+
+  /**
+   * Processes a date form element.
+   *
+   * @param array $element
+   *   The form element to process. Properties used:
+   *   - #attributes: An associative array containing:
+   *     - type: The type of date field rendered.
+   *   - #date_date_format: The date format used in PHP formats.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   * @param array $complete_form
+   *   The complete form structure.
+   *
+   * @return array
+   *   The processed element.
+   */
+  public static function processDate(&$element, FormStateInterface $form_state, &$complete_form) {
+    // Attach JS support, if we can determine which date format should be used.
+    if ($element['#attributes']['type'] == 'date' && !empty($element['#date_date_format'])) {
+      $element['#attached']['library'][] = 'core/drupal.date';
+      $element['#attributes']['data-drupal-date-format'] = [$element['#date_date_format']];
+    }
+    return $element;
   }
 
   /**
@@ -42,6 +67,7 @@ public function getInfo() {
    * Supports HTML5 types of 'date', 'datetime', 'datetime-local', and 'time'.
    * Falls back to a plain textfield. Used as a sub-element by the datetime
    * element type.
+   * Adds JS fallback support for browsers not understanding date types.
    *
    * @param array $element
    *   An associative array containing the properties of the element.
diff --git a/core/misc/date.js b/core/misc/date.js
new file mode 100644
index 0000000..3f401d5
--- /dev/null
+++ b/core/misc/date.js
@@ -0,0 +1,55 @@
+(function ($, Modernizr, Drupal) {
+
+  "use strict";
+
+  /**
+   * Attach datepicker fallback on date elements.
+   */
+  Drupal.behaviors.date = {
+    /**
+     * Attaches the behavior.
+     *
+     * @param settings.date
+     *   A list of elements to process, keyed by the HTML ID of the form element
+     *   containing the human-readable value. Each element is an datepicker
+     *   settings object.
+     */
+    attach: function (context, settings) {
+      var $context = $(context);
+      // Skip if date are supported by the browser.
+      if (Modernizr.inputtypes.date === true) {
+        return;
+      }
+      $context.find('input[data-drupal-date-format]').not('.hasDatepicker').each(function () {
+        var $input = $(this);
+        var datepickerSettings = {};
+        var dateFormat;
+        dateFormat = $input.data('drupalDateFormat');
+        // The date format is saved in PHP style, we need to convert to jQuery
+        // datepicker.
+        dateFormat = dateFormat
+          .replace('Y', 'yy')
+          .replace('m', 'mm')
+          .replace('d', 'dd');
+        datepickerSettings.dateFormat = dateFormat;
+        // Add min and max date if set on the input.
+        if ($input.attr('min')) {
+          datepickerSettings.minDate = $input.attr('min');
+        }
+        if ($input.attr('max')) {
+          datepickerSettings.maxDate = $input.attr('max');
+        }
+        $input.datepicker(datepickerSettings);
+      });
+    },
+    /**
+     * Detach the behavior destroying datepickers on effected elements.
+     */
+    detach: function (context, settings, trigger) {
+      if (trigger === 'unload') {
+        $(context).find('input[data-drupal-date-format].hasDatepicker').datepicker("destroy");
+      }
+    }
+  };
+
+})(jQuery, Modernizr, Drupal);
