Custom entries in entity view modes?
I made it work!

There are many details to consider, so here is just a rough overview.

Storing the entries.

I decided to store the entries not in config entities, but as raw config entries. This seems easier, and prevents recursion with hook_entity_extra_field_info().

For each entry, we store:


Configuration that tells us how the item should be displayed when viewing the entity.
Optionally, an administrative label and/or a label for visitor consumption.


We do NOT store the weight (order / position between other fields). Thanks to hook_entity_extra_field_info(), Drupal will do this for us!

We do NOT store an id or machine name as a value within the entry. Instead, the machine name is part of the key referencing the config entry.

Each entry is identified by a key of the format "entdisp.vme.$entity_type_id.$bundle.$view_mode.$entry_machine_name". 

entdisp.view_mode_entry.schema.yml:

entdisp.vme.*.*.*.*:
  type: ignore


Obviously this could be more sophisticated, but it works for me.

Crud forms for add + delete

Similar to field_group module, create forms to add and delete view mode entries.

E.g. admin/structure/types/manage/article/display/full will have an action link "Add EntDisP entry" leading to admin/structure/types/manage/article/display/full/entdisp/add.

hook_entity_extra_field_info()

Register extra fields per bundle, with placeholders for the label and other settings.

/**
 * Implements hook_entity_extra_field_info().
 */
function entdisp_entity_extra_field_info() {

  $ids = \Drupal::configFactory()-&gt;listAll('entdisp.vme.');

  $fill = ['', '', '', '', '', ''];

  $defaults = [
    'label' =&gt; '_',
    'visible' =&gt; FALSE,
  ];

  $extra = [];
  foreach ($ids as $id) {
    $pieces = explode('.', $id) + $fill;
    if (in_array('', $pieces, TRUE)) {
      continue;
    }
    list(,,$entity_type_id, $bundle, $view_mode, $vme_name) = $pieces;
    // $view_mode is not used!
    $extra[$entity_type_id][$bundle]['display']['entdisp_' . $vme_name] = $defaults;
  }

  return $extra;
}


hook_form_entity_view_display_edit_form_alter()

Alter the entity_view_display_edit_form.

In $form['fields'] and in $form['#extra'], we already find one entry for each extra field that we previously registered.

For each of the entry in $form['#extra'] that begins with 'entdisp_', do the following:


Check if the a corresponding entry exists in the settings.
If not, remove the entry from $form['fields'] and from $form['#extra'].
If yes, modify the element in $form['fields'][$name], to give it a label and configuration controls. Have a look at field_group_field_ui_display_form_alter() and at EntityViewDisplayEditForm in core.


This is all a bit tricky and more work than can be explained here. But it is easier than in field_group, thanks to the trick with hook_entity_extra_field_info().

hook_entity_view()

In this hook, for each item in $display->getComponents(), we


look up the corresponding config entry.
add a render element to $build.


It can look somehow like this: https://www.webomelette.com/creating-pseudo-fields-drupal-8