diff --git a/core/includes/entity.api.php b/core/includes/entity.api.php
index e9fb855..3da0d27 100644
--- a/core/includes/entity.api.php
+++ b/core/includes/entity.api.php
@@ -120,27 +120,6 @@
  *       - access callback: As in hook_menu(). 'user_access' will be assumed if
  *         no value is provided.
  *       - access arguments: As in hook_menu().
- *   - view modes: An array describing the view modes for the entity type. View
- *     modes let entities be displayed differently depending on the context.
- *     For instance, a node can be displayed differently on its own page
- *     ('full' mode), on the home page or taxonomy listings ('teaser' mode), or
- *     in an RSS feed ('rss' mode). Modules taking part in the display of the
- *     entity (notably the Field API) can adjust their behavior depending on
- *     the requested view mode. An additional 'default' view mode is available
- *     for all entity types. This view mode is not intended for actual entity
- *     display, but holds default display settings. For each available view
- *     mode, administrators can configure whether it should use its own set of
- *     field display settings, or just replicate the settings of the 'default'
- *     view mode, thus reducing the amount of display configurations to keep
- *     track of. Keys of the array are view mode names. Each view mode is
- *     described by an array with the following key/value pairs:
- *     - label: The human-readable name of the view mode
- *     - custom settings: A boolean specifying whether the view mode should by
- *       default use its own custom field display settings. If FALSE, entities
- *       displayed in this view mode will reuse the 'default' display settings
- *       by default (e.g. right after the module exposing the view mode is
- *       enabled), but administrators can later use the Field UI to apply custom
- *       display settings specific to the view mode.
  *
  * @see entity_load()
  * @see entity_load_multiple()
@@ -172,20 +151,6 @@ function hook_entity_info() {
         'bundle' => 'type',
       ),
       'bundles' => array(),
-      'view modes' => array(
-        'full' => array(
-          'label' => t('Full content'),
-          'custom settings' => FALSE,
-        ),
-        'teaser' => array(
-          'label' => t('Teaser'),
-          'custom settings' => TRUE,
-        ),
-        'rss' => array(
-          'label' => t('RSS'),
-          'custom settings' => FALSE,
-        ),
-      ),
     ),
   );
 
@@ -537,3 +502,91 @@ function hook_entity_field_info_alter(&$info, $entity_type) {
     $info['definitions']['mymodule_text']['class'] = '\Drupal\anothermodule\EntityComputedText';
   }
 }
+
+/**
+ * Act on view modes when loaded.
+ *
+ * @param array $entities
+ *   The view modes keyed by name.
+ */
+function hook_view_mode_load($entities) {
+  foreach ($entities as $entity) {
+    $entity->foo = mymodule_add_something($entity);
+  }
+}
+
+/**
+ * Act on an view mode before it is about to be created or updated.
+ *
+ * @param Drupal\Core\Entity\EntityInterface $entity
+ *   The view mode object.
+ */
+function hook_view_mode_presave(Drupal\Core\Entity\EntityInterface $entity) {
+  entity_info_cache_clear();
+  state()->set('menu_rebuild_needed', TRUE);
+}
+
+/**
+ * Act on view modes when inserted.
+ *
+ * @param Drupal\Core\Entity\EntityInterface $entity
+ *   The view mode object.
+ */
+function hook_view_mode_insert(Drupal\Core\Entity\EntityInterface $entity) {
+  // Insert the new view mode into a fictional table of all view modes.
+  db_insert('example_view_mode')
+    ->fields(array(
+      'type' => $entity->entityType(),
+      'id' => $entity->id(),
+      'created' => REQUEST_TIME,
+      'updated' => REQUEST_TIME,
+    ))
+    ->execute();
+}
+
+/**
+ * Act on view modes when updated.
+ *
+ * @param Drupal\Core\Entity\EntityInterface $entity
+ *   The view mode object.
+ */
+function hook_view_mode_update(Drupal\Core\Entity\EntityInterface $entity) {
+  // Update the view mode's entry in a fictional table of all view modes.
+  db_update('example_view_mode')
+    ->fields(array(
+      'updated' => REQUEST_TIME,
+    ))
+    ->condition('type', $entity->entityType())
+    ->condition('id', $entity->id())
+    ->execute();
+}
+
+/**
+ * Act before view mode deletion.
+ *
+ * @param Drupal\Core\Entity\EntityInterface $entity
+ *   The view mode object for the view mode that is about to be deleted.
+ */
+function hook_view_mode_predelete(Drupal\Core\Entity\EntityInterface $entity) {
+  // Count references to this view mode in a custom table before they are
+  // removed upon view mode deletion.
+  $id = $entity->id();
+  $type = $entity->entityType();
+  $count = db_select('example_view_mode_data')
+    ->condition('type', $type)
+    ->condition('id', $id)
+    ->countQuery()
+    ->execute()
+    ->fetchField();
+}
+
+/**
+ * Respond to view mode deletion.
+ *
+ * @param Drupal\Core\Entity\EntityInterface $entity
+ *   The view mode object for the view mode that has been deleted.
+ */
+function hook_view_mode_delete(Drupal\Core\Entity\EntityInterface $entity) {
+  entity_info_cache_clear();
+  state()->set('menu_rebuild_needed', TRUE);
+}
diff --git a/core/includes/entity.inc b/core/includes/entity.inc
index 48ee728..71dff6a 100644
--- a/core/includes/entity.inc
+++ b/core/includes/entity.inc
@@ -64,11 +64,6 @@ function entity_get_info($entity_type = NULL) {
           'revision' => '',
           'bundle' => '',
         );
-        foreach ($entity_info[$name]['view modes'] as $view_mode => $view_mode_info) {
-          $entity_info[$name]['view modes'][$view_mode] += array(
-            'custom settings' => FALSE,
-          );
-        }
         // If no bundle key is provided, assume a single bundle, named after
         // the entity type.
         if (empty($entity_info[$name]['entity keys']['bundle']) && empty($entity_info[$name]['bundles'])) {
@@ -83,6 +78,22 @@ function entity_get_info($entity_type = NULL) {
           }
         }
       }
+
+      // Add view modes.
+      $view_modes = drupal_container()->get('config.storage')->listAll('view_mode.');
+      foreach ($view_modes as $config_name) {
+        $view_mode = config($config_name)->get();
+        $definition = array(
+          'label' => check_plain($view_mode['label']),
+          'custom settings' => $view_mode['custom'],
+        );
+        $name = strtok($view_mode['name'], '.');
+        $name = strtok('.');
+        if (isset($view_mode['type']) && !empty($name) && isset($entity_info[$view_mode['type']])) {
+          $entity_info[$view_mode['type']]['view modes'][$name] = $definition;
+        }
+      }
+
       // Let other modules alter the entity info.
       drupal_alter('entity_info', $entity_info);
       cache()->set("entity_info:$langcode", $entity_info, CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE));
diff --git a/core/lib/Drupal/Core/Entity/ViewMode/EntityViewMode.php b/core/lib/Drupal/Core/Entity/ViewMode/EntityViewMode.php
new file mode 100644
index 0000000..d8f6bc9
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/ViewMode/EntityViewMode.php
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Entity\ViewMode\EntityViewMode.
+ */
+
+namespace Drupal\Core\Entity\ViewMode;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+
+/**
+ * Defines the view mode configuration entity class.
+ */
+class EntityViewMode extends ConfigEntityBase {
+
+  /**
+   * The name of the view mode.
+   *
+   * @var string
+   */
+  public $name;
+
+  /**
+   * The UUID of the view mode.
+   *
+   * @var string
+   */
+  public $uuid;
+
+  /**
+   * The view mode label.
+   *
+   * @var string
+   */
+  public $label;
+
+  /**
+   * An array of entity types that use this view mode.
+   *
+   * The array is keyed by the entity type, and the value is either the entity
+   * type if this view mode is used, or 0 if it is not.
+   *
+   * @var array
+   */
+  public $bundles = array();
+
+  /**
+   * The entity type this view mode is used for.
+   *
+   * @var string
+   */
+  public $type;
+
+  /**
+   * Whether or not this view mode has custom settings in the Field UI.
+   *
+   * @var bool
+   */
+  public $custom = FALSE;
+
+  /**
+   * Whether or not this view mode can be removed.
+   *
+   * @var bool
+   */
+  public $locked = FALSE;
+
+  /**
+   * Overrides Drupal\Core\Core\Entity\Entity::id().
+   */
+  public function id() {
+    return $this->name;
+  }
+
+  /**
+   * Returns the raw view mode name, exluding the entity type.
+   *
+   * @return string
+   *   The raw view mode name.
+   */
+  public function name() {
+    $name = strtok($this->name, '.');
+    return strtok('.');
+  }
+
+  /**
+   * Overrides Drupal\Core\Core\Entity\Entity::uri().
+   */
+  public function uri() {
+    return array(
+      'path' => 'admin/config/system/view-modes/' . $this->id(),
+      'options' => array(
+        'entity_type' => $this->entityType,
+        'entity' => $this,
+      ),
+    );
+  }
+
+  /**
+   * Returns the entity types that are used by the view mode.
+   *
+   * @return array
+   *   An array where both keys and values are entity types for the view mode.
+   */
+  public function getUsedEntityTypes() {
+    return array_filter($this->entityTypes);
+  }
+
+}
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index df0a33a..f628422 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -120,12 +120,6 @@ function comment_entity_info() {
       ),
       'bundles' => array(),
       'render controller class' => 'Drupal\comment\CommentRenderController',
-      'view modes' => array(
-        'full' => array(
-          'label' => t('Full comment'),
-          'custom settings' => FALSE,
-        ),
-      ),
       'static cache' => FALSE,
     ),
   );
diff --git a/core/modules/comment/config/view_mode.comment.full.yml b/core/modules/comment/config/view_mode.comment.full.yml
new file mode 100644
index 0000000..ae04d51
--- /dev/null
+++ b/core/modules/comment/config/view_mode.comment.full.yml
@@ -0,0 +1,5 @@
+name: comment.full
+label: Full comment
+custom: '0'
+type: comment
+locked: '1'
diff --git a/core/modules/entity_ui/entity_ui.admin.inc b/core/modules/entity_ui/entity_ui.admin.inc
new file mode 100644
index 0000000..69e505a
--- /dev/null
+++ b/core/modules/entity_ui/entity_ui.admin.inc
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * @file
+ * Administrative functions for custom view modes.
+ */
+
+/**
+ * Menu callback: list all view modes.
+ */
+function entity_ui_view_mode_list() {
+  $build = array();
+  $controller = entity_list_controller('view_mode');
+  foreach (entity_get_info() as $type => $info) {
+    if ($type == 'view_mode') {
+      continue;
+    }
+
+    $build[$type] = array(
+      '#type' => 'fieldset',
+      '#title' => $info['label'],
+    );
+    $build[$type]['view_modes'] = $controller->set($type)->render();
+  }
+  return $build;
+}
+
+/**
+ * Menu callback: view mode edit form.
+ */
+function entity_ui_view_mode_add($type) {
+  $view_mode = entity_create('view_mode', array('type' => $type));
+  return entity_get_form($view_mode);
+}
+
+/**
+ * Menu callback: view mode delete confirm form.
+ */
+function entity_ui_view_mode_delete_form($form, $form_state, $view_mode) {
+  $form['#view_mode'] = $view_mode;
+
+  $form['name'] = array(
+    '#type' => 'value',
+    '#value' => $view_mode->id(),
+  );
+
+  return confirm_form(
+    $form,
+    t('Are you sure you want to delete the %view-mode view mode?', array(
+      '%view-mode' => $view_mode->label(),
+    )),
+    'admin/config/system/view-modes',
+    t('Deleting a view mode will cause any output still requesting to use that view mode to use the default display settings.'),
+    t('Delete'),
+    t('Cancel')
+  );
+}
+
+/**
+ * Submit handler: delete a view mode.
+ */
+function entity_ui_view_mode_delete_form_submit($form, &$form_state) {
+  drupal_set_message(t('Deleted the %view-mode view mode.', array(
+    '%view-mode' => $form['#view_mode']->label(),
+  )));
+  $form['#view_mode']->delete();
+  $form_state['redirect'] = 'admin/config/system/view-modes';
+}
diff --git a/core/modules/entity_ui/entity_ui.info b/core/modules/entity_ui/entity_ui.info
new file mode 100644
index 0000000..9dcc4e5
--- /dev/null
+++ b/core/modules/entity_ui/entity_ui.info
@@ -0,0 +1,7 @@
+name = Entity UI
+description = User interface for the Entity API.
+package = Core
+version = VERSION
+core = 8.x
+recommends[] = field_ui
+configure = admin/config/system/view-modes
diff --git a/core/modules/entity_ui/entity_ui.module b/core/modules/entity_ui/entity_ui.module
new file mode 100644
index 0000000..38df289
--- /dev/null
+++ b/core/modules/entity_ui/entity_ui.module
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * @file
+ * Provides a user interface for the Entity API.
+ */
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Implements hook_permission().
+ */
+function entity_ui_permission() {
+  return array(
+    'administer view modes' => array(
+      'title' => t('Add, edit and delete custom view modes.'),
+    ),
+  );
+}
+
+/**
+ * Implements hook_entity_info_alter().
+ */
+function entity_ui_entity_info_alter(&$entity_info) {
+  // Add a list and form controller for the view mode entity type.
+  $entity_info['view_mode']['list controller class'] = 'Drupal\entity_ui\ViewModeListController';
+  $entity_info['view_mode']['form controller class'] = array(
+    'default' => 'Drupal\entity_ui\ViewModeFormController',
+  );
+}
+
+/**
+ * Implements hook_menu().
+ */
+function entity_ui_menu() {
+  $items['admin/config/system/view-modes'] = array(
+    'title' => 'View modes',
+    'description' => 'Manage custom view modes.',
+    'page callback' => 'entity_ui_view_mode_list',
+    'access arguments' => array('administer view modes'),
+    'file' => 'entity_ui.admin.inc',
+  );
+  $items['admin/config/system/view-modes/add/%'] = array(
+    'title' => 'Add view mode',
+    'page callback' => 'entity_ui_view_mode_add',
+    'page arguments' => array(5),
+    'access arguments' => array('administer view modes'),
+    'file' => 'entity_ui.admin.inc',
+  );
+  $items['admin/config/system/view-modes/%view_mode/edit'] = array(
+    'title' => 'Edit view mode',
+    'page callback' => 'entity_get_form',
+    'page arguments' => array(4),
+    'access arguments' => array('administer view modes'),
+  );
+  $items['admin/config/system/view-modes/%view_mode/delete'] = array(
+    'title' => 'Delete view mode',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('entity_ui_view_mode_delete_form', 4),
+    'access arguments' => array('administer view modes'),
+    'file' => 'entity_ui.admin.inc',
+  );
+
+  return $items;
+}
+
+/**
+ * Load a custom view mode by machine name.
+ */
+function view_mode_load($machine_name) {
+  return entity_load('view_mode', $machine_name);
+}
+
+/**
+ * Checks for an existing view mode.
+ */
+function view_mode_exists($machine_name, $element) {
+  return view_mode_load($element['#entity_type'] . ".$machine_name");
+}
+
+/**
+ * Implements hook_view_mode_presave().
+ */
+function entity_ui_view_mode_presave(EntityInterface $entity) {
+  entity_info_cache_clear();
+  state()->set('menu_rebuild_needed', TRUE);
+}
+
+/**
+ * Implements hook_view_mode_delete().
+ */
+function entity_ui_view_mode_delete(EntityInterface $entity) {
+  entity_info_cache_clear();
+  state()->set('menu_rebuild_needed', TRUE);
+}
diff --git a/core/modules/entity_ui/lib/Drupal/entity_ui/Tests/ViewModeTest.php b/core/modules/entity_ui/lib/Drupal/entity_ui/Tests/ViewModeTest.php
new file mode 100644
index 0000000..f70f4b8
--- /dev/null
+++ b/core/modules/entity_ui/lib/Drupal/entity_ui/Tests/ViewModeTest.php
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_ui\Tests\ViewModeTest.
+ */
+
+namespace Drupal\entity_ui\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Test managing of view modes.
+ */
+class ViewModeTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('field_ui', 'entity_ui');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'View modes',
+      'description' => 'Tests for managing custom view modes.',
+      'group' => 'Entity UI',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+
+    $this->drupalLogin($this->drupalCreateUser(array('administer content types', 'administer users', 'administer view modes', 'access administration pages')));
+    $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
+  }
+
+  /**
+   * Creates a view mode.
+   *
+   * @param array $edit
+   *   (optional) An array of view mode properties. Defaults to an empty array.
+   */
+  function createViewMode(&$edit = array()) {
+    $edit += array(
+      'label' => 'Testing',
+      'name' => 'testing',
+      'custom' => TRUE,
+      'bundles[article]' => TRUE,
+    );
+
+    $this->drupalPost('admin/config/system/view-modes/add/node', $edit, t('Save'));
+    $this->assertText(format_string('Saved the !name view mode.', array('!name' => $edit['label'])));
+  }
+
+  /**
+   * Test managing view modes.
+   */
+  function testManageViewModes() {
+    $edit = array();
+    $this->createViewMode($edit);
+
+    // Create the same and assert it already exists.
+    $this->drupalPost('admin/config/system/view-modes/add/node', $edit, t('Save'));
+    $this->assertText('The machine-readable name is already in use. It must be unique.', 'View mode testing already exists.');
+
+    // Assert it's found on the Field UI for article.
+    $this->drupalGet('admin/structure/types/manage/article/display');
+    $this->assertRaw('view_modes_custom[testing]', 'Testing view mode found on node article.');
+
+    // Assert it's not found on the Field UI for user.
+    $this->drupalGet('admin/config/people/accounts/display');
+    $this->assertNoRaw('view_modes_custom[testing]', 'Testing view mode not found on user.');
+
+    // Update view mode label.
+    $edit = array(
+      'label' => 'Testing 2',
+    );
+    $this->drupalPost('admin/config/system/view-modes/node.testing/edit', $edit, t('Save'));
+    $this->assertText('Saved the Testing 2 view mode.', 'Testing label updated.');
+
+    // Remove a view mode.
+    $this->drupalPost('admin/config/system/view-modes/node.testing/delete', array(), t('Delete'));
+    $this->assertText('Deleted the Testing 2 view mode', 'Deleted the Testing view mode.');
+
+    // Assert the view mode is gone at the manage display screen.
+    $this->drupalGet('admin/structure/types/manage/article/display');
+    $this->assertNoRaw('view_modes_custom[testing]', 'Testing view mode not found on node article.');
+  }
+
+}
diff --git a/core/modules/entity_ui/lib/Drupal/entity_ui/ViewModeFormController.php b/core/modules/entity_ui/lib/Drupal/entity_ui/ViewModeFormController.php
new file mode 100644
index 0000000..3f9fefc
--- /dev/null
+++ b/core/modules/entity_ui/lib/Drupal/entity_ui/ViewModeFormController.php
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_ui\ViewModeFormController.
+ */
+
+namespace Drupal\entity_ui;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityFormController;
+
+/**
+ * Form controller for the view mode forms.
+ */
+class ViewModeFormController extends EntityFormController {
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::form().
+   */
+  public function form(array $form, array &$form_state, EntityInterface $view_mode) {
+    $entity_info = entity_get_info($view_mode->type);
+    $form['type'] = array(
+      '#type' => 'item',
+      '#title' => t('Entity type'),
+      '#markup' => $entity_info['label'],
+    );
+    $form['label'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Label'),
+      '#default_value' => $view_mode->label,
+      '#required' => TRUE,
+    );
+
+    $form['name'] = array(
+      '#type' => 'machine_name',
+      '#machine_name' => array(
+        'source' => array('label'),
+        'exists' => 'view_mode_exists',
+      ),
+      '#entity_type' => $view_mode->type,
+      '#default_value' => $view_mode->name(),
+      '#disabled' => !empty($view_mode->label),
+      '#field_prefix' => "$view_mode->type.",
+    );
+
+    $form['custom'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Use custom display settings'),
+      '#description' => t('Unchecking this will delete all the display settings for this view mode.'),
+      '#default_value' => $view_mode->custom,
+    );
+
+    $form['bundles'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Enable this view mode for the following types'),
+      '#description' => t('Unchecking a type that has already been configured will delete the display settings for this view mode.'),
+      '#options' => array_map(function ($bundle) {
+          return $bundle['label'];
+        }, $entity_info['bundles']
+      ),
+      '#default_value' => array_filter(
+        array_keys($entity_info['bundles']),
+        function ($option) use ($view_mode) {
+          $settings = field_view_mode_settings($view_mode->type, $option);
+          return !empty($settings[$view_mode->name()]['custom_settings']);
+        }
+      ),
+      '#states' => array(
+        'visible' => array(
+          ':input[name="custom"]' => array('checked' => TRUE),
+        ),
+      ),
+    );
+
+    return $form;
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::actions().
+   */
+  protected function actions(array $form, array &$form_state) {
+    $view_mode = $this->getEntity($form_state);
+    $actions = parent::actions($form, $form_state);
+    $actions['delete']['#access'] = !$view_mode->locked;
+    return $actions;
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::validate().
+   */
+  public function validate(array $form, array &$form_state) {
+    $entity = clone $this->getEntity($form_state);
+    form_set_value($form['name'], $entity->type . '.' . $form_state['values']['name'], $form_state);
+    parent::validate($form, $form_state);
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::save().
+   *
+   * @todo Move this code to the base controller: http://drupal.org/node/1728786
+   */
+  public function save(array $form, array &$form_state) {
+    $view_mode = $this->getEntity($form_state);
+    $view_mode->save();
+    drupal_set_message(t('Saved the %view-mode view mode.', array(
+      '%view-mode' => $view_mode->label(),
+    )));
+
+    $form_state['redirect'] = 'admin/config/system/view-modes';
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::delete().
+   *
+   * @todo Move this code to the base controller: http://drupal.org/node/1728786
+   */
+  public function delete(array $form, array &$form_state) {
+    $destination = array();
+    if (isset($_GET['destination'])) {
+      $destination = drupal_get_destination();
+      unset($_GET['destination']);
+    }
+    $view_mode = $this->getEntity($form_state);
+    $form_state['redirect'] = array('admin/config/system/view-modes/' . $view_mode->id() . '/delete', array('query' => $destination));
+  }
+
+}
diff --git a/core/modules/entity_ui/lib/Drupal/entity_ui/ViewModeListController.php b/core/modules/entity_ui/lib/Drupal/entity_ui/ViewModeListController.php
new file mode 100644
index 0000000..ebbf75a
--- /dev/null
+++ b/core/modules/entity_ui/lib/Drupal/entity_ui/ViewModeListController.php
@@ -0,0 +1,88 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_ui\ViewModeListController.
+ */
+
+namespace Drupal\entity_ui;
+
+use Drupal\Core\Config\Entity\ConfigEntityListController;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Provides a listing of view modes.
+ */
+class ViewModeListController extends ConfigEntityListController {
+
+  protected $type;
+
+  public function set($type) {
+    $this->type = entity_get_info($type);
+    $this->type['type'] = $type;
+    return $this;
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityListController::load().
+   */
+  public function load() {
+    $type = $this->type['type'];
+    return array_filter(parent::load(), function ($entity) use ($type) {
+      return $entity->type == $type;
+    });
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityListController::buildHeader();
+   */
+  public function buildHeader() {
+    $header = parent::buildHeader();
+    $operations = array_pop($header);
+    $header['custom'] = t('Custom settings');
+    $header['operations'] = $operations;
+    return $header;
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityListController::buildRow();
+   */
+  public function buildRow(EntityInterface $view_mode) {
+    $row['label'] = $view_mode->label();
+    $row['id'] = $view_mode->name();
+    $row['custom'] = $view_mode->custom ? t('Yes') : t('No');
+    $row['operations']['data'] = $this->buildOperations($view_mode);
+    return $row;
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityListController::getOperations();
+   */
+  public function getOperations(EntityInterface $view_mode) {
+    $operations = parent::getOperations($view_mode);
+    if ($view_mode->locked) {
+      unset($operations['delete']);
+    }
+    return $operations;
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityListController::buildRow();
+   */
+  public function render() {
+    $build = parent::render();
+    $build['#rows']['_add_new'][] = array(
+      'data' => array(
+        '#type' => 'link',
+        '#href' => 'admin/config/system/view-modes/add/' . $this->type['type'],
+        '#title' => t('Add new %type view mode', array('%type' => $this->type['label'])),
+        '#options' => array(
+          'html' => TRUE,
+        ),
+      ),
+      'colspan' => count($build['#header']),
+    );
+    return $build;
+  }
+
+}
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index b3dc6f0..497ef49 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -1,9 +1,12 @@
 <?php
+
 /**
  * @file
  * Allows administrators to attach custom fields to fieldable types.
  */
 
+use Drupal\Core\Entity\EntityInterface;
+
 /**
  * Implements hook_help().
  */
@@ -412,3 +415,14 @@ function field_ui_library_info() {
 
   return $libraries;
 }
+
+/**
+ * Implements hook_view_mode_presave().
+ */
+function field_ui_view_mode_presave(EntityInterface $view_mode) {
+  foreach ($view_mode->bundles as $bundle => $custom) {
+    $bundle_settings = field_bundle_settings($view_mode->type, $bundle);
+    $bundle_settings['view_modes'][$view_mode->name()]['custom_settings'] = (bool) $custom;
+    field_bundle_settings($view_mode->type, $bundle, $bundle_settings);
+  }
+}
diff --git a/core/modules/file/config/view_mode.file.full.yml b/core/modules/file/config/view_mode.file.full.yml
new file mode 100644
index 0000000..44bea6c
--- /dev/null
+++ b/core/modules/file/config/view_mode.file.full.yml
@@ -0,0 +1,5 @@
+name: file.full
+label: File default
+custom: '0'
+type: file
+locked: '1'
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 4e6c8eb..c008cb3 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -102,12 +102,6 @@ function file_entity_info() {
         'label' => 'filename',
         'uuid' => 'uuid',
       ),
-      'view modes' => array(
-        'full' => array(
-          'label' => t('File default'),
-          'custom settings' => FALSE,
-        ),
-      ),
       'static cache' => FALSE,
     ),
   );
diff --git a/core/modules/node/config/view_mode.node.full.yml b/core/modules/node/config/view_mode.node.full.yml
new file mode 100644
index 0000000..ecd11ea
--- /dev/null
+++ b/core/modules/node/config/view_mode.node.full.yml
@@ -0,0 +1,5 @@
+name: node.full
+label: Full content
+custom: '0'
+type: node
+locked: '1'
diff --git a/core/modules/node/config/view_mode.node.rss.yml b/core/modules/node/config/view_mode.node.rss.yml
new file mode 100644
index 0000000..aec7364
--- /dev/null
+++ b/core/modules/node/config/view_mode.node.rss.yml
@@ -0,0 +1,5 @@
+name: node.rss
+label: RSS
+custom: '0'
+type: node
+locked: '1'
diff --git a/core/modules/node/config/view_mode.node.teaser.yml b/core/modules/node/config/view_mode.node.teaser.yml
new file mode 100644
index 0000000..005d8e0
--- /dev/null
+++ b/core/modules/node/config/view_mode.node.teaser.yml
@@ -0,0 +1,5 @@
+name: node.teaser
+label: Teaser
+custom: '1'
+type: node
+locked: '1'
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 30a6dd5..b947e5a 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -218,20 +218,6 @@ function node_entity_info() {
       ),
       'bundles' => array(),
       'render controller class' => 'Drupal\node\NodeRenderController',
-      'view modes' => array(
-        'full' => array(
-          'label' => t('Full content'),
-          'custom settings' => FALSE,
-        ),
-        'teaser' => array(
-          'label' => t('Teaser'),
-          'custom settings' => TRUE,
-        ),
-        'rss' => array(
-          'label' => t('RSS'),
-          'custom settings' => FALSE,
-        ),
-      ),
     ),
   );
 
@@ -240,21 +226,6 @@ function node_entity_info() {
     $return['node']['translation']['node'] = TRUE;
   }
 
-  // Search integration is provided by node.module, so search-related
-  // view modes for nodes are defined here and not in search.module.
-  if (module_exists('search')) {
-    $return['node']['view modes'] += array(
-      'search_index' => array(
-        'label' => t('Search index'),
-        'custom settings' => FALSE,
-      ),
-      'search_result' => array(
-        'label' => t('Search result'),
-        'custom settings' => FALSE,
-      ),
-    );
-  }
-
   // Bundles must provide a human readable name so we can create help and error
   // messages, and the path to attach Field admin pages to.
   node_type_cache_reset();
diff --git a/core/modules/search/config/view_mode.node.search_index.yml b/core/modules/search/config/view_mode.node.search_index.yml
new file mode 100644
index 0000000..9d9441e
--- /dev/null
+++ b/core/modules/search/config/view_mode.node.search_index.yml
@@ -0,0 +1,5 @@
+name: node.search_index
+label: Search index
+custom: '0'
+type: node
+locked: '1'
diff --git a/core/modules/search/config/view_mode.node.search_result.yml b/core/modules/search/config/view_mode.node.search_result.yml
new file mode 100644
index 0000000..5539901
--- /dev/null
+++ b/core/modules/search/config/view_mode.node.search_result.yml
@@ -0,0 +1,5 @@
+name: node.search_result
+label: Search result
+custom: '0'
+type: node
+locked: '1'
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index ea3dadc..83c767a 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -1079,6 +1079,30 @@ function system_menu() {
 }
 
 /**
+ * Implements hook_entity_info().
+ */
+function system_entity_info() {
+  // This entity type is provided on behalf of the entity system.
+  // @todo Remove from system module after http://drupal.org/node/1763974.
+  $return = array(
+    'view_mode' => array(
+      'label' => t('View mode'),
+      'entity class' => 'Drupal\Core\Entity\ViewMode\EntityViewMode',
+      'controller class' => 'Drupal\Core\Config\Entity\ConfigStorageController',
+      'config prefix' => 'view_mode',
+      'fieldable' => FALSE,
+      'entity keys' => array(
+        'id' => 'name',
+        'label' => 'label',
+        'uuid' => 'uuid',
+      ),
+    ),
+  );
+
+  return $return;
+}
+
+/**
  * Page callback; Execute cron tasks.
  *
  * @see system_cron_access().
diff --git a/core/modules/taxonomy/config/view_mode.taxonomy_term.full.yml b/core/modules/taxonomy/config/view_mode.taxonomy_term.full.yml
new file mode 100644
index 0000000..4e9e70b
--- /dev/null
+++ b/core/modules/taxonomy/config/view_mode.taxonomy_term.full.yml
@@ -0,0 +1,5 @@
+name: taxonomy_term.full
+label: Taxonomy term page
+custom: '0'
+type: taxonomy_term
+locked: '1'
diff --git a/core/modules/taxonomy/config/view_mode.taxonomy_vocabulary.full.yml b/core/modules/taxonomy/config/view_mode.taxonomy_vocabulary.full.yml
new file mode 100644
index 0000000..2545a2c
--- /dev/null
+++ b/core/modules/taxonomy/config/view_mode.taxonomy_vocabulary.full.yml
@@ -0,0 +1,5 @@
+name: taxonomy_vocabulary.full
+label: Taxonomy vocabulary default
+custom: '0'
+type: taxonomy_vocabulary
+locked: '1'
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 874c78a..3d142b5 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -131,13 +131,6 @@ function taxonomy_entity_info() {
       ),
       'bundles' => array(),
       'render controller class' => 'Drupal\taxonomy\TermRenderController',
-      'view modes' => array(
-        // @todo View mode for display as a field (when attached to nodes etc).
-        'full' => array(
-          'label' => t('Taxonomy term page'),
-          'custom settings' => FALSE,
-        ),
-      ),
     ),
   );
   foreach (taxonomy_vocabulary_get_names() as $machine_name => $vocabulary) {
@@ -164,12 +157,6 @@ function taxonomy_entity_info() {
       'label' => 'name',
     ),
     'fieldable' => FALSE,
-    'view modes' => array(
-      'full' => array(
-        'label' => t('Taxonomy vocabulary default'),
-        'custom settings' => FALSE,
-      ),
-    ),
   );
 
   return $return;
diff --git a/core/modules/user/config/view_mode.user.full.yml b/core/modules/user/config/view_mode.user.full.yml
new file mode 100644
index 0000000..0c05b19
--- /dev/null
+++ b/core/modules/user/config/view_mode.user.full.yml
@@ -0,0 +1,5 @@
+name: user.full
+label: User account
+custom: '0'
+type: user
+locked: '1'
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index fae45ad..ad13b3d 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -169,12 +169,6 @@ function user_entity_info() {
         ),
       ),
       'render controller class' => 'Drupal\user\UserRenderController',
-      'view modes' => array(
-        'full' => array(
-          'label' => t('User account'),
-          'custom settings' => FALSE,
-        ),
-      ),
     ),
   );
 }
diff --git a/core/profiles/standard/standard.info b/core/profiles/standard/standard.info
index 8b8a33b..28db9c6 100644
--- a/core/profiles/standard/standard.info
+++ b/core/profiles/standard/standard.info
@@ -22,3 +22,4 @@ dependencies[] = overlay
 dependencies[] = field_ui
 dependencies[] = file
 dependencies[] = rdf
+dependencies[] = entity_ui
