diff --git a/core/composer.json b/core/composer.json
index 7452a077766a06bc121de64d0f3701cbb37811ed..770631ddfd271a73e5c5d05c4c672d2d3d438c04 100644
--- a/core/composer.json
+++ b/core/composer.json
@@ -133,6 +133,7 @@
         "drupal/locale": "self.version",
         "drupal/minimal": "self.version",
         "drupal/media": "self.version",
+        "drupal/media_library": "self.version",
         "drupal/menu_link_content": "self.version",
         "drupal/menu_ui": "self.version",
         "drupal/migrate": "self.version",
diff --git a/core/modules/media_library/config/install/core.entity_view_mode.media.media_library.yml b/core/modules/media_library/config/install/core.entity_view_mode.media.media_library.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3406f026b491c2d22138287e2dbd71a2d287d543
--- /dev/null
+++ b/core/modules/media_library/config/install/core.entity_view_mode.media.media_library.yml
@@ -0,0 +1,12 @@
+langcode: en
+status: true
+dependencies:
+  enforced:
+    module:
+      - media_library
+  module:
+    - media
+id: media.media_library
+label: 'Media library'
+targetEntityType: media
+cache: true
diff --git a/core/modules/media_library/config/install/views.view.media_library.yml b/core/modules/media_library/config/install/views.view.media_library.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8c9e784e60087e537a3aa29dfd879dca0480b128
--- /dev/null
+++ b/core/modules/media_library/config/install/views.view.media_library.yml
@@ -0,0 +1,447 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - core.entity_view_mode.media.media_library
+  enforced:
+    module:
+      - media_library
+  module:
+    - media
+    - user
+id: media_library
+label: 'Media library'
+module: views
+description: ''
+tag: ''
+base_table: media_field_data
+base_field: mid
+core: 8.x
+display:
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: 0
+    display_options:
+      access:
+        type: perm
+        options:
+          perm: 'access media overview'
+      cache:
+        type: tag
+        options: {  }
+      query:
+        type: views_query
+        options:
+          disable_sql_rewrite: false
+          distinct: false
+          replica: false
+          query_comment: ''
+          query_tags: {  }
+      exposed_form:
+        type: basic
+        options:
+          submit_button: 'Apply Filters'
+          reset_button: false
+          reset_button_label: Reset
+          exposed_sorts_label: 'Sort by'
+          expose_sort_order: false
+          sort_asc_label: Asc
+          sort_desc_label: Desc
+      pager:
+        type: mini
+        options:
+          items_per_page: 25
+          offset: 0
+          id: 0
+          total_pages: null
+          expose:
+            items_per_page: false
+            items_per_page_label: 'Items per page'
+            items_per_page_options: '5, 10, 25, 50'
+            items_per_page_options_all: false
+            items_per_page_options_all_label: '- All -'
+            offset: false
+            offset_label: Offset
+          tags:
+            previous: ‹‹
+            next: ››
+      style:
+        type: default
+        options:
+          grouping: {  }
+          row_class: 'media-library-item js-click-to-select'
+          default_row_class: true
+      row:
+        type: fields
+        options:
+          default_field_elements: true
+          inline: {  }
+          separator: ''
+          hide_empty: false
+      fields:
+        media_bulk_form:
+          id: media_bulk_form
+          table: media
+          field: media_bulk_form
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: ''
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: js-click-to-select__checkbox
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          action_title: Action
+          include_exclude: exclude
+          selected_actions: {  }
+          entity_type: media
+          plugin_id: bulk_form
+        rendered_entity:
+          id: rendered_entity
+          table: media
+          field: rendered_entity
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: ''
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: media-library-item__content
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          view_mode: media_library
+          entity_type: media
+          plugin_id: rendered_entity
+      filters:
+        status:
+          id: status
+          table: media_field_data
+          field: status
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: '='
+          value: '1'
+          group: 1
+          exposed: true
+          expose:
+            operator_id: ''
+            label: 'Publishing status'
+            description: null
+            use_operator: false
+            operator: status_op
+            identifier: status
+            required: true
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+          is_grouped: true
+          group_info:
+            label: Published
+            description: ''
+            identifier: status
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items:
+              1:
+                title: Published
+                operator: '='
+                value: '1'
+              2:
+                title: Unpublished
+                operator: '='
+                value: '0'
+          plugin_id: boolean
+          entity_type: media
+          entity_field: status
+        name:
+          id: name
+          table: media_field_data
+          field: name
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: contains
+          value: ''
+          group: 1
+          exposed: true
+          expose:
+            operator_id: name_op
+            label: Name
+            description: ''
+            use_operator: false
+            operator: name_op
+            identifier: name
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator: '0'
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          entity_type: media
+          entity_field: name
+          plugin_id: string
+        bundle:
+          id: bundle
+          table: media_field_data
+          field: bundle
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: in
+          value: {  }
+          group: 1
+          exposed: true
+          expose:
+            operator_id: bundle_op
+            label: 'Media type'
+            description: ''
+            use_operator: false
+            operator: bundle_op
+            identifier: type
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator: '0'
+            reduce: false
+          is_grouped: false
+          group_info:
+            label: 'Media type'
+            description: null
+            identifier: bundle
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items:
+              1: {  }
+              2: {  }
+              3: {  }
+          entity_type: media
+          entity_field: bundle
+          plugin_id: bundle
+      sorts:
+        created:
+          id: created
+          table: media_field_data
+          field: created
+          relationship: none
+          group_type: group
+          admin_label: ''
+          order: DESC
+          exposed: true
+          expose:
+            label: 'Newest first'
+          granularity: second
+          entity_type: media
+          entity_field: created
+          plugin_id: date
+        name:
+          id: name
+          table: media_field_data
+          field: name
+          relationship: none
+          group_type: group
+          admin_label: ''
+          order: ASC
+          exposed: true
+          expose:
+            label: 'Name (A-Z)'
+          entity_type: media
+          entity_field: name
+          plugin_id: standard
+        name_1:
+          id: name_1
+          table: media_field_data
+          field: name
+          relationship: none
+          group_type: group
+          admin_label: ''
+          order: DESC
+          exposed: true
+          expose:
+            label: 'Name (Z-A)'
+          entity_type: media
+          entity_field: name
+          plugin_id: standard
+      title: Media
+      header: {  }
+      footer: {  }
+      empty: {  }
+      relationships: {  }
+      arguments:
+        bundle:
+          id: bundle
+          table: media_field_data
+          field: bundle
+          relationship: none
+          group_type: group
+          admin_label: ''
+          default_action: ignore
+          exception:
+            value: all
+            title_enable: false
+            title: All
+          title_enable: false
+          title: ''
+          default_argument_type: fixed
+          default_argument_options:
+            argument: ''
+          default_argument_skip_url: false
+          summary_options:
+            base_path: ''
+            count: true
+            items_per_page: 25
+            override: false
+          summary:
+            sort_order: asc
+            number_of_records: 0
+            format: default_summary
+          specify_validation: false
+          validate:
+            type: none
+            fail: 'not found'
+          validate_options: {  }
+          glossary: false
+          limit: 0
+          case: none
+          path_case: none
+          transform_dash: false
+          break_phrase: false
+          entity_type: media
+          entity_field: bundle
+          plugin_id: string
+      display_extenders: {  }
+      use_ajax: true
+      css_class: media-library-view
+    cache_metadata:
+      max-age: 0
+      contexts:
+        - 'languages:language_interface'
+        - url
+        - url.query_args
+        - 'url.query_args:sort_by'
+        - user.permissions
+      tags: {  }
+  page:
+    display_plugin: page
+    id: page
+    display_title: Page
+    position: 1
+    display_options:
+      display_extenders: {  }
+      path: admin/content/media
+      menu:
+        type: tab
+        title: Media
+        description: 'Allows users to browse and administer media items'
+        expanded: false
+        parent: system.admin_content
+        weight: 5
+        context: '0'
+        menu_name: admin
+    cache_metadata:
+      max-age: 0
+      contexts:
+        - 'languages:language_interface'
+        - url
+        - url.query_args
+        - 'url.query_args:sort_by'
+        - user.permissions
+      tags: {  }
diff --git a/core/modules/media_library/config/optional/core.entity_view_display.media.audio.media_library.yml b/core/modules/media_library/config/optional/core.entity_view_display.media.audio.media_library.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e74e3ebde6b206148a37d6f95a4ed86140fe98be
--- /dev/null
+++ b/core/modules/media_library/config/optional/core.entity_view_display.media.audio.media_library.yml
@@ -0,0 +1,29 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - core.entity_view_mode.media.media_library
+    - field.field.media.audio.field_media_audio_file
+    - image.style.thumbnail
+    - media.type.audio
+  module:
+    - image
+id: media.audio.media_library
+targetEntityType: media
+bundle: audio
+mode: media_library
+content:
+  thumbnail:
+    type: image
+    weight: 0
+    region: content
+    label: hidden
+    settings:
+      image_style: thumbnail
+      image_link: ''
+    third_party_settings: {  }
+hidden:
+  created: true
+  field_media_audio_file: true
+  name: true
+  uid: true
diff --git a/core/modules/media_library/config/optional/core.entity_view_display.media.file.media_library.yml b/core/modules/media_library/config/optional/core.entity_view_display.media.file.media_library.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e09e611b9550abf9f4001a7d77c488bef6388ef7
--- /dev/null
+++ b/core/modules/media_library/config/optional/core.entity_view_display.media.file.media_library.yml
@@ -0,0 +1,29 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - core.entity_view_mode.media.media_library
+    - field.field.media.file.field_media_file
+    - image.style.thumbnail
+    - media.type.file
+  module:
+    - image
+id: media.file.media_library
+targetEntityType: media
+bundle: file
+mode: media_library
+content:
+  thumbnail:
+    type: image
+    weight: 0
+    region: content
+    label: hidden
+    settings:
+      image_style: thumbnail
+      image_link: ''
+    third_party_settings: {  }
+hidden:
+  created: true
+  field_media_file: true
+  name: true
+  uid: true
diff --git a/core/modules/media_library/config/optional/core.entity_view_display.media.image.media_library.yml b/core/modules/media_library/config/optional/core.entity_view_display.media.image.media_library.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a916760ad99ba1898665fd0c0dcb9996f002f5c8
--- /dev/null
+++ b/core/modules/media_library/config/optional/core.entity_view_display.media.image.media_library.yml
@@ -0,0 +1,29 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - core.entity_view_mode.media.media_library
+    - field.field.media.image.field_media_image
+    - image.style.medium
+    - media.type.image
+  module:
+    - image
+id: media.image.media_library
+targetEntityType: media
+bundle: image
+mode: media_library
+content:
+  thumbnail:
+    type: image
+    weight: 0
+    region: content
+    label: hidden
+    settings:
+      image_style: medium
+      image_link: ''
+    third_party_settings: {  }
+hidden:
+  created: true
+  field_media_image: true
+  name: true
+  uid: true
diff --git a/core/modules/media_library/config/optional/core.entity_view_display.media.video.media_library.yml b/core/modules/media_library/config/optional/core.entity_view_display.media.video.media_library.yml
new file mode 100644
index 0000000000000000000000000000000000000000..33d4e885555354de44e2613b5ba582342074b2c0
--- /dev/null
+++ b/core/modules/media_library/config/optional/core.entity_view_display.media.video.media_library.yml
@@ -0,0 +1,29 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - core.entity_view_mode.media.media_library
+    - field.field.media.video.field_media_video_file
+    - image.style.thumbnail
+    - media.type.video
+  module:
+    - image
+id: media.video.media_library
+targetEntityType: media
+bundle: video
+mode: media_library
+content:
+  thumbnail:
+    type: image
+    weight: 0
+    region: content
+    label: hidden
+    settings:
+      image_style: thumbnail
+      image_link: ''
+    third_party_settings: {  }
+hidden:
+  created: true
+  field_media_video_file: true
+  name: true
+  uid: true
diff --git a/core/modules/media_library/css/media_library.module.css b/core/modules/media_library/css/media_library.module.css
new file mode 100644
index 0000000000000000000000000000000000000000..11ad56dd922e3fd31b30b8f5521b1df5bd47289d
--- /dev/null
+++ b/core/modules/media_library/css/media_library.module.css
@@ -0,0 +1,60 @@
+/**
+* @file media_library.module.css
+*/
+
+.media-library-page-form {
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.media-library-page-form > .form-actions {
+  flex-basis: 100%;
+}
+
+.media-library-page-form__header > div,
+.media-library-view .form--inline {
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.media-library-page-form__header {
+  flex-basis: 100%;
+}
+
+.media-library-item {
+  position: relative;
+}
+
+.media-library-item .js-click-to-select__trigger {
+  overflow: hidden;
+  cursor: pointer;
+}
+
+.media-library-view .form-actions {
+  align-self: flex-end;
+}
+
+.media-library-item .js-click-to-select__checkbox {
+  position: absolute;
+  display: block;
+  z-index: 1;
+  top: 5px;
+  right: 0;
+}
+
+.media-library-item__status {
+  position: absolute;
+  top: 10px;
+  left: 2px;
+  pointer-events: none;
+}
+
+.media-library-select-all {
+  flex-basis: 100%;
+}
+
+@media screen and (max-width: 600px) {
+  .media-library-view .form-actions {
+    flex-basis: 100%;
+  }
+}
diff --git a/core/modules/media_library/css/media_library.theme.css b/core/modules/media_library/css/media_library.theme.css
new file mode 100644
index 0000000000000000000000000000000000000000..0427143538e36cca88518bc430209ad86d519ee7
--- /dev/null
+++ b/core/modules/media_library/css/media_library.theme.css
@@ -0,0 +1,151 @@
+/**
+ * @file media_library.theme.css
+ *
+ * @todo Move into the Seven theme when this module is marked as stable.
+ * @see https://www.drupal.org/project/drupal/issues/2980769
+ */
+
+.media-library-page-form__header .form-item {
+  margin-right: 8px;
+}
+
+#drupal-modal .view-header {
+  margin: 16px 0;
+}
+
+.media-library-item {
+  justify-content: center;
+  vertical-align: top;
+  padding: 2px;
+  border: 1px solid #ebebeb;
+  margin: 16px 16px 2px 2px;
+  width: 180px;
+  background: #fff;
+  transition: border-color 0.2s, color 0.2s, background 0.2s;
+}
+
+.media-library-view .form-actions {
+  margin: 0.75em 0;
+}
+
+.media-library-item .field--name-thumbnail {
+  background-color: #ebebeb;
+  margin: 2px;
+  overflow: hidden;
+  text-align: center;
+}
+
+.media-library-item .field--name-thumbnail img {
+  height: 180px;
+  object-fit: contain;
+  object-position: center center;
+}
+
+.media-library-item.is-hover,
+.media-library-item.checked,
+.media-library-item.is-focus {
+  border-color: #40b6ff;
+  border-width: 3px;
+  border-radius: 3px;
+  margin: 14px 14px 0 0;
+}
+
+.media-library-item.checked {
+  border-color: #0076c0;
+}
+
+.media-library-item .js-click-to-select__checkbox input {
+  width: 30px;
+  height: 30px;
+}
+
+.media-library-item .js-click-to-select__checkbox .form-item {
+  margin: 0;
+}
+
+.media-library-item__preview {
+  padding-bottom: 44px;
+}
+
+.media-library-item__status {
+  color: #e4e4e4;
+  font-style: italic;
+  background: #666;
+  padding: 5px 10px;
+  font-size: 12px;
+}
+
+.media-library-item .views-field-operations {
+  height: 30px;
+}
+
+.media-library-item .views-field-operations .dropbutton-wrapper {
+  display: inline-block;
+  position: absolute;
+  right: 5px;
+  bottom: 5px;
+}
+
+.media-library-item__attributes {
+  position: absolute;
+  bottom: 0;
+  display: block;
+  padding: 10px;
+  max-width: calc(100% - 20px);
+  max-height: calc(100% - 60px);
+  overflow: hidden;
+  background: white;
+}
+
+.media-library-item__name {
+  font-size: 14px;
+}
+
+.media-library-item__name a {
+  display: block;
+  text-decoration: underline;
+  margin: 2px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.media-library-item__attributes:hover .media-library-item__name a,
+.media-library-item__name a:focus,
+.media-library-item.is-focus .media-library-item__name a,
+.media-library-item.checked .media-library-item__name a {
+  white-space: normal;
+}
+
+.media-library-item__name a:focus {
+  border: 2px solid;
+  margin: 0;
+}
+
+.media-library-item__type {
+  font-size: 12px;
+  color: #696969;
+}
+
+.media-library-select-all {
+  margin: 10px 0 10px 0;
+}
+
+.media-library-select-all input {
+  margin-right: 10px;
+}
+
+@media screen and (max-width: 600px) {
+  .media-library-item {
+    width: 150px;
+  }
+  .media-library-item .field--name-thumbnail img {
+    height: 150px;
+    width: 150px;
+  }
+  .media-library-item .views-field-operations .dropbutton-wrapper {
+    position: relative;
+    right: 0;
+    border: 0;
+  }
+}
diff --git a/core/modules/media_library/js/media_library.click_to_select.es6.js b/core/modules/media_library/js/media_library.click_to_select.es6.js
new file mode 100644
index 0000000000000000000000000000000000000000..321ffe0c73a9f9f056123d10fad095d832d2a1e4
--- /dev/null
+++ b/core/modules/media_library/js/media_library.click_to_select.es6.js
@@ -0,0 +1,31 @@
+/**
+ * @file media_library.click_to_select.es6.js
+ */
+
+(($, Drupal) => {
+  /**
+   * Allows users to select an element which checks a hidden checkbox.
+   */
+  Drupal.behaviors.ClickToSelect = {
+    attach(context) {
+      $('.js-click-to-select__trigger', context)
+        .once('media-library-click-to-select')
+        .on('click', (event) => {
+          // Links inside the trigger should not be click-able.
+          event.preventDefault();
+          // Click the hidden checkbox when the trigger is clicked.
+          const $input = $(event.currentTarget)
+            .closest('.js-click-to-select')
+            .find('.js-click-to-select__checkbox input');
+          $input.prop('checked', !$input.prop('checked')).trigger('change');
+        });
+      $('.js-click-to-select__checkbox input', context)
+        .once('media-library-click-to-select')
+        .on('change', ({ currentTarget }) => {
+          $(currentTarget)
+            .closest('.js-click-to-select')
+            .toggleClass('checked', $(currentTarget).prop('checked'));
+        });
+    },
+  };
+})(jQuery, Drupal);
diff --git a/core/modules/media_library/js/media_library.click_to_select.js b/core/modules/media_library/js/media_library.click_to_select.js
new file mode 100644
index 0000000000000000000000000000000000000000..4bc041c43e56a72a8047b1bc413d4e3d445e4cbf
--- /dev/null
+++ b/core/modules/media_library/js/media_library.click_to_select.js
@@ -0,0 +1,24 @@
+/**
+* DO NOT EDIT THIS FILE.
+* See the following change record for more information,
+* https://www.drupal.org/node/2815083
+* @preserve
+**/
+
+(function ($, Drupal) {
+  Drupal.behaviors.ClickToSelect = {
+    attach: function attach(context) {
+      $('.js-click-to-select__trigger', context).once('media-library-click-to-select').on('click', function (event) {
+        event.preventDefault();
+
+        var $input = $(event.currentTarget).closest('.js-click-to-select').find('.js-click-to-select__checkbox input');
+        $input.prop('checked', !$input.prop('checked')).trigger('change');
+      });
+      $('.js-click-to-select__checkbox input', context).once('media-library-click-to-select').on('change', function (_ref) {
+        var currentTarget = _ref.currentTarget;
+
+        $(currentTarget).closest('.js-click-to-select').toggleClass('checked', $(currentTarget).prop('checked'));
+      });
+    }
+  };
+})(jQuery, Drupal);
\ No newline at end of file
diff --git a/core/modules/media_library/js/media_library.view.es6.js b/core/modules/media_library/js/media_library.view.es6.js
new file mode 100644
index 0000000000000000000000000000000000000000..26e9a5b2635ef05e7276c636a063207d9383f290
--- /dev/null
+++ b/core/modules/media_library/js/media_library.view.es6.js
@@ -0,0 +1,58 @@
+/**
+ * @file media_library.view.es6.js
+ */
+(($, Drupal) => {
+  /**
+   * Adds hover effect to media items.
+   */
+  Drupal.behaviors.MediaLibraryHover = {
+    attach(context) {
+      $('.media-library-item .js-click-to-select__trigger,.media-library-item .js-click-to-select__checkbox', context).once('media-library-item-hover')
+        .on('mouseover mouseout', ({ currentTarget, type }) => {
+          $(currentTarget).closest('.media-library-item').toggleClass('is-hover', type === 'mouseover');
+        });
+    },
+  };
+
+  /**
+   * Adds focus effect to media items.
+   */
+  Drupal.behaviors.MediaLibraryFocus = {
+    attach(context) {
+      $('.media-library-item .js-click-to-select__checkbox input', context).once('media-library-item-focus')
+        .on('focus blur', ({ currentTarget, type }) => {
+          $(currentTarget).closest('.media-library-item').toggleClass('is-focus', type === 'focus');
+        });
+    },
+  };
+
+  /**
+   * Adds checkbox to select all items in the library.
+   */
+  Drupal.behaviors.MediaLibrarySelectAll = {
+    attach(context) {
+      const $view = $('.media-library-view', context).once('media-library-select-all');
+      if ($view.length && $view.find('.media-library-item').length) {
+        const $checkbox = $('<input type="checkbox" class="form-checkbox" />')
+          .on('click', ({ currentTarget }) => {
+            // Toggle all checkboxes.
+            const $checkboxes = $(currentTarget)
+              .closest('.media-library-view')
+              .find('.media-library-item input[type="checkbox"]');
+            $checkboxes
+              .prop('checked', $(currentTarget).prop('checked'))
+              .trigger('change');
+            // Announce the selection.
+            const announcement = $(currentTarget).prop('checked') ?
+              Drupal.t('Zero items selected') :
+              Drupal.t('All @count items selected', { '@count': $checkboxes.length });
+            Drupal.announce(announcement);
+          });
+        const $label = $('<label class="media-library-select-all"></label>')
+          .text(Drupal.t('Select all media'));
+        $label.prepend($checkbox);
+        $view.find('.media-library-item').first().before($label);
+      }
+    },
+  };
+})(jQuery, Drupal);
diff --git a/core/modules/media_library/js/media_library.view.js b/core/modules/media_library/js/media_library.view.js
new file mode 100644
index 0000000000000000000000000000000000000000..1cde60c3acfb633d4c792b2d70cba731a19d0e02
--- /dev/null
+++ b/core/modules/media_library/js/media_library.view.js
@@ -0,0 +1,50 @@
+/**
+* DO NOT EDIT THIS FILE.
+* See the following change record for more information,
+* https://www.drupal.org/node/2815083
+* @preserve
+**/
+
+(function ($, Drupal) {
+  Drupal.behaviors.MediaLibraryHover = {
+    attach: function attach(context) {
+      $('.media-library-item .js-click-to-select__trigger,.media-library-item .js-click-to-select__checkbox', context).once('media-library-item-hover').on('mouseover mouseout', function (_ref) {
+        var currentTarget = _ref.currentTarget,
+            type = _ref.type;
+
+        $(currentTarget).closest('.media-library-item').toggleClass('is-hover', type === 'mouseover');
+      });
+    }
+  };
+
+  Drupal.behaviors.MediaLibraryFocus = {
+    attach: function attach(context) {
+      $('.media-library-item .js-click-to-select__checkbox input', context).once('media-library-item-focus').on('focus blur', function (_ref2) {
+        var currentTarget = _ref2.currentTarget,
+            type = _ref2.type;
+
+        $(currentTarget).closest('.media-library-item').toggleClass('is-focus', type === 'focus');
+      });
+    }
+  };
+
+  Drupal.behaviors.MediaLibrarySelectAll = {
+    attach: function attach(context) {
+      var $view = $('.media-library-view', context).once('media-library-select-all');
+      if ($view.length && $view.find('.media-library-item').length) {
+        var $checkbox = $('<input type="checkbox" class="form-checkbox" />').on('click', function (_ref3) {
+          var currentTarget = _ref3.currentTarget;
+
+          var $checkboxes = $(currentTarget).closest('.media-library-view').find('.media-library-item input[type="checkbox"]');
+          $checkboxes.prop('checked', $(currentTarget).prop('checked')).trigger('change');
+
+          var announcement = $(currentTarget).prop('checked') ? Drupal.t('Zero items selected') : Drupal.t('All @count items selected', { '@count': $checkboxes.length });
+          Drupal.announce(announcement);
+        });
+        var $label = $('<label class="media-library-select-all"></label>').text(Drupal.t('Select all media'));
+        $label.prepend($checkbox);
+        $view.find('.media-library-item').first().before($label);
+      }
+    }
+  };
+})(jQuery, Drupal);
\ No newline at end of file
diff --git a/core/modules/media_library/media_library.info.yml b/core/modules/media_library/media_library.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9f7aa912e405165a3950bff6b0aec04b2b5b5f75
--- /dev/null
+++ b/core/modules/media_library/media_library.info.yml
@@ -0,0 +1,10 @@
+name: 'Media library'
+type: module
+description: 'Provides a library for re-using Media Items.'
+package: Core (Experimental)
+version: VERSION
+core: 8.x
+dependencies:
+  - drupal:media
+  - drupal:views
+  - drupal:user
diff --git a/core/modules/media_library/media_library.install b/core/modules/media_library/media_library.install
new file mode 100644
index 0000000000000000000000000000000000000000..79a97868b7b646c52b9de81703680165d8e9d739
--- /dev/null
+++ b/core/modules/media_library/media_library.install
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the media_library module.
+ */
+
+use Drupal\views\Entity\View;
+
+/**
+ * Implements hook_install().
+ */
+function media_library_install() {
+  // Change the path to the original media view.
+  /** @var \Drupal\views\Entity\View $view */
+  if ($view = View::load('media')) {
+    $display = &$view->getDisplay('media_page_list');
+    if (!empty($display)) {
+      $display['display_options']['path'] = 'admin/content/media-table';
+      unset($display['display_options']['menu']);
+      $view->trustData()->save();
+    }
+  }
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function media_library_uninstall() {
+  // Restore the path to the original media view.
+  /** @var \Drupal\views\Entity\View $view */
+  if ($view = View::load('media')) {
+    $display = &$view->getDisplay('media_page_list');
+    if (!empty($display)) {
+      $display['display_options']['path'] = 'admin/content/media';
+      $display['display_options']['menu'] = [
+        'type' => 'tab',
+        'title' => 'Media',
+        'description' => '',
+        'expanded' => FALSE,
+        'parent' => '',
+        'weight' => 0,
+        'context' => '0',
+        'menu_name' => 'main',
+      ];
+      $view->trustData()->save();
+    }
+  }
+}
diff --git a/core/modules/media_library/media_library.libraries.yml b/core/modules/media_library/media_library.libraries.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e32dbe074b496aa5b19cb47fede291b5d86ce54b
--- /dev/null
+++ b/core/modules/media_library/media_library.libraries.yml
@@ -0,0 +1,25 @@
+style:
+  version: VERSION
+  css:
+    component:
+      css/media_library.module.css: {}
+    theme:
+      css/media_library.theme.css: {}
+
+click_to_select:
+  version: VERSION
+  js:
+    js/media_library.click_to_select.js: {}
+  dependencies:
+    - core/drupal
+    - core/jquery.once
+
+view:
+  version: VERSION
+  js:
+    js/media_library.view.js: {}
+  dependencies:
+    - media_library/style
+    - media_library/click_to_select
+    - core/drupal.announce
+    - core/jquery.once
diff --git a/core/modules/media_library/media_library.links.action.yml b/core/modules/media_library/media_library.links.action.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6a37769cf17fa642ebc3dc5793c83229b041dbcb
--- /dev/null
+++ b/core/modules/media_library/media_library.links.action.yml
@@ -0,0 +1,5 @@
+media_library.add:
+  route_name: entity.media.add_page
+  title: 'Add media'
+  appears_on:
+    - view.media_library.page
diff --git a/core/modules/media_library/media_library.links.task.yml b/core/modules/media_library/media_library.links.task.yml
new file mode 100644
index 0000000000000000000000000000000000000000..757ecd853249e3ee513917e99ab89d92092108e0
--- /dev/null
+++ b/core/modules/media_library/media_library.links.task.yml
@@ -0,0 +1,10 @@
+media_library.grid:
+  title: 'Grid'
+  parent_id: entity.media.collection
+  route_name: entity.media.collection
+  weight: 10
+media_library.table:
+  title: 'Table'
+  parent_id: entity.media.collection
+  route_name: view.media.media_page_list
+  weight: 20
diff --git a/core/modules/media_library/media_library.module b/core/modules/media_library/media_library.module
new file mode 100644
index 0000000000000000000000000000000000000000..63b3cadb96a8e4f56030b9829f6a47f43be969ba
--- /dev/null
+++ b/core/modules/media_library/media_library.module
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * @file
+ * Contains hook implementations for the media_library module.
+ */
+
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\views\Plugin\views\cache\CachePluginBase;
+use Drupal\views\ViewExecutable;
+use Drupal\Core\Template\Attribute;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element;
+
+/**
+ * Implements hook_help().
+ *
+ * @todo Update in https://www.drupal.org/project/drupal/issues/2964789
+ */
+function media_library_help($route_name, RouteMatchInterface $route_match) {
+  switch ($route_name) {
+    case 'help.page.media_library':
+      $output = '<h3>' . t('About') . '</h3>';
+      $output .= '<p>' . t('The Media library module overrides the /admin/content/media view to provide a rich visual interface for performing administrative operations on media. For more information, see the <a href=":media">online documentation for the Media library module</a>.', [':media' => 'https://www.drupal.org/docs/8/core/modules/media']) . '</p>';
+      return $output;
+  }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function media_library_theme() {
+  return [
+    'media__media_library' => [
+      'base hook' => 'media',
+    ],
+  ];
+}
+
+/**
+ * Implements hook_views_post_render().
+ */
+function media_library_views_post_render(ViewExecutable $view, &$output, CachePluginBase $cache) {
+  if ($view->id() === 'media_library') {
+    $output['#attached']['library'][] = 'media_library/view';
+  }
+}
+
+/**
+ * Implements hook_preprocess_media().
+ */
+function media_library_preprocess_media(&$variables) {
+  if ($variables['view_mode'] === 'media_library') {
+    /** @var \Drupal\media\MediaInterface $media */
+    $media = $variables['media'];
+    $variables['#cache']['contexts'][] = 'user.permissions';
+    $rel = $media->access('edit') ? 'edit-form' : 'canonical';
+    $variables['url'] = $media->toUrl($rel, [
+      'language' => $media->language(),
+    ]);
+    $variables['preview_attributes'] = new Attribute();
+    $variables['preview_attributes']->addClass('media-library-item__preview', 'js-click-to-select__trigger');
+    $variables['metadata_attributes'] = new Attribute();
+    $variables['metadata_attributes']->addClass('media-library-item__attributes');
+    $variables['status'] = $media->isPublished();
+  }
+}
+
+/**
+ * Alter the bulk form to add a more accessible label.
+ *
+ * @param array $form
+ *   An associative array containing the structure of the form.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ *   The current state of the form.
+ *
+ * @todo Remove in https://www.drupal.org/project/drupal/issues/2969660
+ */
+function media_library_form_views_form_media_library_page_alter(array &$form, FormStateInterface $form_state) {
+  if (isset($form['media_bulk_form']) && isset($form['output'])) {
+    $form['#attributes']['class'][] = 'media-library-page-form';
+    $form['header']['#attributes']['class'][] = 'media-library-page-form__header';
+    /** @var \Drupal\views\ViewExecutable $view */
+    $view = $form['output'][0]['#view'];
+    foreach (Element::getVisibleChildren($form['media_bulk_form']) as $key) {
+      if (isset($view->result[$key])) {
+        $media = $view->field['media_bulk_form']->getEntity($view->result[$key]);
+        $form['media_bulk_form'][$key]['#title'] = t('Select @label', [
+          '@label' => $media->label(),
+        ]);
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_local_tasks_alter().
+ *
+ * Removes tasks for the Media library if the view display no longer exists.
+ */
+function media_library_local_tasks_alter(&$local_tasks) {
+  /** @var \Symfony\Component\Routing\RouteCollection $route_collection */
+  $route_collection = \Drupal::service('router')->getRouteCollection();
+  foreach (['media_library.grid', 'media_library.table'] as $key) {
+    if (isset($local_tasks[$key]) && !$route_collection->get($local_tasks[$key]['route_name'])) {
+      unset($local_tasks[$key]);
+    }
+  }
+}
diff --git a/core/modules/media_library/templates/media--media-library.html.twig b/core/modules/media_library/templates/media--media-library.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..a55024a22d2df1c3384fc71c245abbbec2e72c7b
--- /dev/null
+++ b/core/modules/media_library/templates/media--media-library.html.twig
@@ -0,0 +1,50 @@
+{#
+/**
+ * @file
+ * Default theme implementation to present a media entity in the media library.
+ *
+ * Available variables:
+ * - media: The entity with limited access to object properties and methods.
+ *   Only method names starting with "get", "has", or "is" and a few common
+ *   methods such as "id", "label", and "bundle" are available. For example:
+ *   - entity.getEntityTypeId() will return the entity type ID.
+ *   - entity.hasField('field_example') returns TRUE if the entity includes
+ *     field_example. (This does not indicate the presence of a value in this
+ *     field.)
+ *   Calling other methods, such as entity.delete(), will result in an exception.
+ *   See \Drupal\Core\Entity\EntityInterface for a full list of methods.
+ * - name: Name of the media.
+ * - content: Media content.
+ * - title_prefix: Additional output populated by modules, intended to be
+ *   displayed in front of the main title tag that appears in the template.
+ * - title_suffix: Additional output populated by modules, intended to be
+ *   displayed after the main title tag that appears in the template.
+ * - view_mode: View mode; for example, "teaser" or "full".
+ * - attributes: HTML attributes for the containing element.
+ * - title_attributes: Same as attributes, except applied to the main title
+ *   tag that appears in the template.
+ * - url: Direct URL of the media.
+ * - preview_attributes: HTML attributes for the preview wrapper.
+ * - metadata_attributes: HTML attributes for the expandable metadata area.
+ * - status: Whether or not the Media is published.
+ *
+ * @see template_preprocess_media()
+ *
+ * @ingroup themeable
+ */
+#}
+<article{{ attributes }}>
+  {% if content %}
+    <div{{ preview_attributes }}>
+      {{ content|without('name') }}
+    </div>
+    {% if not status %}
+      <div class="media-library-item__status">{{ "unpublished" | t }}</div>
+    {% endif %}
+    <div{{ metadata_attributes }}>
+      <div class="media-library-item__name">
+        <a href="{{ url }}" rel="bookmark">{{ name }}</a>
+      </div>
+    </div>
+  {% endif %}
+</article>
diff --git a/core/modules/media_library/tests/modules/media_library_test/config/install/core.entity_form_display.media.type_one.default.yml b/core/modules/media_library/tests/modules/media_library_test/config/install/core.entity_form_display.media.type_one.default.yml
new file mode 100644
index 0000000000000000000000000000000000000000..212daf81879608a79d7d404be27ec4fc0611e9ac
--- /dev/null
+++ b/core/modules/media_library/tests/modules/media_library_test/config/install/core.entity_form_display.media.type_one.default.yml
@@ -0,0 +1,43 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.field.media.type_one.field_media_test
+    - media.type.type_one
+id: media.type_one.default
+targetEntityType: media
+bundle: type_one
+mode: default
+content:
+  created:
+    type: datetime_timestamp
+    weight: 10
+    region: content
+    settings: {  }
+    third_party_settings: {  }
+  field_media_test:
+    settings:
+      size: 60
+      placeholder: ''
+    third_party_settings: {  }
+    type: string_textfield
+    weight: 11
+    region: content
+  name:
+    type: string_textfield
+    weight: -5
+    region: content
+    settings:
+      size: 60
+      placeholder: ''
+    third_party_settings: {  }
+  uid:
+    type: entity_reference_autocomplete
+    weight: 5
+    settings:
+      match_operator: CONTAINS
+      size: 60
+      placeholder: ''
+    region: content
+    third_party_settings: {  }
+hidden: {  }
diff --git a/core/modules/media_library/tests/modules/media_library_test/config/install/core.entity_form_display.media.type_two.default.yml b/core/modules/media_library/tests/modules/media_library_test/config/install/core.entity_form_display.media.type_two.default.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fabd13b0297b24082152efa1374ad150e520a509
--- /dev/null
+++ b/core/modules/media_library/tests/modules/media_library_test/config/install/core.entity_form_display.media.type_two.default.yml
@@ -0,0 +1,43 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.field.media.type_two.field_media_test_1
+    - media.type.type_two
+id: media.type_two.default
+targetEntityType: media
+bundle: type_two
+mode: default
+content:
+  created:
+    type: datetime_timestamp
+    weight: 10
+    region: content
+    settings: {  }
+    third_party_settings: {  }
+  field_media_test_1:
+    settings:
+      size: 60
+      placeholder: ''
+    third_party_settings: {  }
+    type: string_textfield
+    weight: 11
+    region: content
+  name:
+    type: string_textfield
+    weight: -5
+    region: content
+    settings:
+      size: 60
+      placeholder: ''
+    third_party_settings: {  }
+  uid:
+    type: entity_reference_autocomplete
+    weight: 5
+    settings:
+      match_operator: CONTAINS
+      size: 60
+      placeholder: ''
+    region: content
+    third_party_settings: {  }
+hidden: {  }
diff --git a/core/modules/media_library/tests/modules/media_library_test/config/install/core.entity_view_display.media.type_one.default.yml b/core/modules/media_library/tests/modules/media_library_test/config/install/core.entity_view_display.media.type_one.default.yml
new file mode 100644
index 0000000000000000000000000000000000000000..95670b3557bb170a2294d85d1b391319a1a2a36b
--- /dev/null
+++ b/core/modules/media_library/tests/modules/media_library_test/config/install/core.entity_view_display.media.type_one.default.yml
@@ -0,0 +1,50 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.field.media.type_one.field_media_test
+    - image.style.thumbnail
+    - media.type.type_one
+  module:
+    - image
+    - user
+id: media.type_one.default
+targetEntityType: media
+bundle: type_one
+mode: default
+content:
+  created:
+    label: hidden
+    type: timestamp
+    weight: 0
+    region: content
+    settings:
+      date_format: medium
+      custom_date_format: ''
+      timezone: ''
+    third_party_settings: {  }
+  field_media_test:
+    label: above
+    settings:
+      link_to_entity: true
+    third_party_settings: {  }
+    type: string
+    weight: 6
+    region: content
+  thumbnail:
+    type: image
+    weight: 5
+    label: hidden
+    settings:
+      image_style: thumbnail
+      image_link: ''
+    region: content
+    third_party_settings: {  }
+  uid:
+    label: hidden
+    type: author
+    weight: 0
+    region: content
+    settings: {  }
+    third_party_settings: {  }
+hidden: {  }
diff --git a/core/modules/media_library/tests/modules/media_library_test/config/install/core.entity_view_display.media.type_two.default.yml b/core/modules/media_library/tests/modules/media_library_test/config/install/core.entity_view_display.media.type_two.default.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a884fee4ce93cd2d3651f99f7b72af4840c6c262
--- /dev/null
+++ b/core/modules/media_library/tests/modules/media_library_test/config/install/core.entity_view_display.media.type_two.default.yml
@@ -0,0 +1,50 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.field.media.type_two.field_media_test_1
+    - image.style.thumbnail
+    - media.type.type_two
+  module:
+    - image
+    - user
+id: media.type_two.default
+targetEntityType: media
+bundle: type_two
+mode: default
+content:
+  created:
+    label: hidden
+    type: timestamp
+    weight: 0
+    region: content
+    settings:
+      date_format: medium
+      custom_date_format: ''
+      timezone: ''
+    third_party_settings: {  }
+  field_media_test_1:
+    label: above
+    settings:
+      link_to_entity: false
+    third_party_settings: {  }
+    type: string
+    weight: 6
+    region: content
+  thumbnail:
+    type: image
+    weight: 5
+    label: hidden
+    settings:
+      image_style: thumbnail
+      image_link: ''
+    region: content
+    third_party_settings: {  }
+  uid:
+    label: hidden
+    type: author
+    weight: 0
+    region: content
+    settings: {  }
+    third_party_settings: {  }
+hidden: {  }
diff --git a/core/modules/media_library/tests/modules/media_library_test/config/install/field.field.media.type_one.field_media_test.yml b/core/modules/media_library/tests/modules/media_library_test/config/install/field.field.media.type_one.field_media_test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f19207c9e1d31be1e17a9d02dc8106b3190ea849
--- /dev/null
+++ b/core/modules/media_library/tests/modules/media_library_test/config/install/field.field.media.type_one.field_media_test.yml
@@ -0,0 +1,18 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.storage.media.field_media_test
+    - media.type.type_one
+id: media.type_one.field_media_test
+field_name: field_media_test
+entity_type: media
+bundle: type_one
+label: field_media_test
+description: ''
+required: false
+translatable: true
+default_value: {  }
+default_value_callback: ''
+settings: {  }
+field_type: string
diff --git a/core/modules/media_library/tests/modules/media_library_test/config/install/field.field.media.type_two.field_media_test_1.yml b/core/modules/media_library/tests/modules/media_library_test/config/install/field.field.media.type_two.field_media_test_1.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5b093caf7bb3e50c8846bd6ec0b82a541d70b17a
--- /dev/null
+++ b/core/modules/media_library/tests/modules/media_library_test/config/install/field.field.media.type_two.field_media_test_1.yml
@@ -0,0 +1,18 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.storage.media.field_media_test_1
+    - media.type.type_two
+id: media.type_two.field_media_test_1
+field_name: field_media_test_1
+entity_type: media
+bundle: type_two
+label: field_media_test_1
+description: ''
+required: false
+translatable: true
+default_value: {  }
+default_value_callback: ''
+settings: {  }
+field_type: string
diff --git a/core/modules/media_library/tests/modules/media_library_test/config/install/field.storage.media.field_media_test.yml b/core/modules/media_library/tests/modules/media_library_test/config/install/field.storage.media.field_media_test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..40a77916ec7fbcbfa73d8c84479f5757c1630490
--- /dev/null
+++ b/core/modules/media_library/tests/modules/media_library_test/config/install/field.storage.media.field_media_test.yml
@@ -0,0 +1,20 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - media
+id: media.field_media_test
+field_name: field_media_test
+entity_type: media
+type: string
+settings:
+  max_length: 255
+  is_ascii: false
+  case_sensitive: false
+module: core
+locked: false
+cardinality: 1
+translatable: true
+indexes: {  }
+persist_with_no_fields: false
+custom_storage: false
diff --git a/core/modules/media_library/tests/modules/media_library_test/config/install/field.storage.media.field_media_test_1.yml b/core/modules/media_library/tests/modules/media_library_test/config/install/field.storage.media.field_media_test_1.yml
new file mode 100644
index 0000000000000000000000000000000000000000..73b11058e4f11f35fa00df76ef9e9a80e97176f7
--- /dev/null
+++ b/core/modules/media_library/tests/modules/media_library_test/config/install/field.storage.media.field_media_test_1.yml
@@ -0,0 +1,20 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - media
+id: media.field_media_test_1
+field_name: field_media_test_1
+entity_type: media
+type: string
+settings:
+  max_length: 255
+  is_ascii: false
+  case_sensitive: false
+module: core
+locked: false
+cardinality: 1
+translatable: true
+indexes: {  }
+persist_with_no_fields: false
+custom_storage: false
diff --git a/core/modules/media_library/tests/modules/media_library_test/config/install/media.type.type_one.yml b/core/modules/media_library/tests/modules/media_library_test/config/install/media.type.type_one.yml
new file mode 100644
index 0000000000000000000000000000000000000000..1f72b8beb6d13d8f66f53d9cf9b395420cb8d243
--- /dev/null
+++ b/core/modules/media_library/tests/modules/media_library_test/config/install/media.type.type_one.yml
@@ -0,0 +1,16 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - media
+    - media_test_source
+id: type_one
+label: 'Type One'
+description: ''
+source: test
+queue_thumbnail_downloads: false
+new_revision: false
+source_configuration:
+  source_field: field_media_test
+  test_config_value: 'This is default value.'
+field_map: {  }
diff --git a/core/modules/media_library/tests/modules/media_library_test/config/install/media.type.type_two.yml b/core/modules/media_library/tests/modules/media_library_test/config/install/media.type.type_two.yml
new file mode 100644
index 0000000000000000000000000000000000000000..13b549c6acb8d1fbe1c8d75b8111fa44ec685e9d
--- /dev/null
+++ b/core/modules/media_library/tests/modules/media_library_test/config/install/media.type.type_two.yml
@@ -0,0 +1,16 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - media
+    - media_test_source
+id: type_two
+label: 'Type Two'
+description: ''
+source: test
+queue_thumbnail_downloads: false
+new_revision: false
+source_configuration:
+  source_field: field_media_test_1
+  test_config_value: 'This is default value.'
+field_map: {  }
diff --git a/core/modules/media_library/tests/modules/media_library_test/media_library_test.info.yml b/core/modules/media_library/tests/modules/media_library_test/media_library_test.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3ad1ae5f46ea498431a760d02abfed82069c12b9
--- /dev/null
+++ b/core/modules/media_library/tests/modules/media_library_test/media_library_test.info.yml
@@ -0,0 +1,10 @@
+name: 'Media library test'
+type: module
+description: 'Test module for Media library.'
+package: Testing
+core: 8.x
+dependencies:
+  - drupal:media_library
+  - drupal:media_test_source
+  - drupal:menu_ui
+  - drupal:path
diff --git a/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..f8604e644bf997c07622f319fd7e1392f609a1ab
--- /dev/null
+++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Drupal\Tests\media_library\FunctionalJavascript;
+
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+use Drupal\media\Entity\Media;
+
+/**
+ * Contains Media library integration tests.
+ *
+ * @group media_library
+ */
+class MediaLibraryTest extends JavascriptTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['block', 'media_library_test'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Create a few example media items for use in selection.
+    $media = [
+      'type_one' => [
+        'media_1',
+        'media_2',
+      ],
+      'type_two' => [
+        'media_3',
+        'media_4',
+      ],
+    ];
+
+    foreach ($media as $type => $names) {
+      foreach ($names as $name) {
+        $entity = Media::create(['name' => $name, 'bundle' => $type]);
+        $source_field = $type === 'type_one' ? 'field_media_test' : 'field_media_test_1';
+        $entity->set($source_field, $this->randomString());
+        $entity->save();
+      }
+    }
+
+    // Create a user who can use the Media library.
+    $user = $this->drupalCreateUser([
+      'access administration pages',
+      'access media overview',
+      'create media',
+      'delete any media',
+      'view media',
+    ]);
+    $this->drupalLogin($user);
+    $this->drupalPlaceBlock('local_tasks_block');
+    $this->drupalPlaceBlock('local_actions_block');
+  }
+
+  /**
+   * Tests that the Media library's administration page works as expected.
+   */
+  public function testAdministrationPage() {
+    $assert_session = $this->assertSession();
+
+    // Visit the administration page.
+    $this->drupalGet('admin/content/media');
+
+    // Verify that the "Add media" link is present.
+    $assert_session->linkExists('Add media');
+
+    // Verify that media from two separate types is present.
+    $assert_session->pageTextContains('media_1');
+    $assert_session->pageTextContains('media_3');
+
+    // Test that users can filter by type.
+    $this->getSession()->getPage()->selectFieldOption('Media type', 'Type One');
+    $this->getSession()->getPage()->pressButton('Apply Filters');
+    $assert_session->assertWaitOnAjaxRequest();
+    $assert_session->pageTextContains('media_2');
+    $assert_session->pageTextNotContains('media_4');
+    $this->getSession()->getPage()->selectFieldOption('Media type', 'Type Two');
+    $this->getSession()->getPage()->pressButton('Apply Filters');
+    $assert_session->assertWaitOnAjaxRequest();
+    $assert_session->pageTextNotContains('media_2');
+    $assert_session->pageTextContains('media_4');
+
+    // Test that selecting elements as a part of bulk operations works.
+    $this->getSession()->getPage()->selectFieldOption('Media type', '- Any -');
+    $this->getSession()->getPage()->pressButton('Apply Filters');
+    $assert_session->assertWaitOnAjaxRequest();
+    // This tests that anchor tags clicked inside the preview are suppressed.
+    $this->getSession()->executeScript('jQuery(".js-click-to-select__trigger a")[0].click()');
+    $this->submitForm([], 'Apply to selected items');
+    $assert_session->pageTextContains('media_1');
+    $assert_session->pageTextNotContains('media_2');
+    $this->submitForm([], 'Delete');
+    $assert_session->pageTextNotContains('media_1');
+    $assert_session->pageTextContains('media_2');
+  }
+
+}