How to prevent duplicate entity reference items from being entered on a field?
Using form alter, you could add an #element_validate function to the field.

function mymodule_form_alter(&amp;$form, FormStateInterface $form_state, $form_id) {

  if (isset($form['field_articles'])) {
    if (empty($form['field_articles']['widget']['#element_validate'])) {
      $form['field_articles']['widget']['#element_validate'] = ['mymodule_prevent_duplicate_entity_reference'];
    }
    else {
      $form['field_articles']['widget']['#element_validate'][] = 'mymodule_prevent_duplicate_entity_reference';
    }
  }
}


Then in your custom validation function, if there are duplicate entity reference items, set a form error.

function mymodule_prevent_duplicate_entity_reference(array &amp;$element, FormStateInterface $form_state, array &amp;$complete_form) {

  $input = NestedArray::getValue($form_state-&gt;getValues(), $element['#parents']);

  $ids = array_filter(array_column($input, 'target_id'));

  // Check that there aren't duplicate entity_id values.
  if (count($ids) !== count(array_flip($ids))) {
    $form_state-&gt;setError($element, 'Field "' . $element['#title'] . '" doesn\'t allow duplicates.');
  }

}


If you would like to create a reusable widget, you could extend one of the entity reference widgets, for example,  EntityReferenceAutocompleteWidget:

namespace Drupal\mymodule\Plugin\Field\FieldWidget;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget;
use Drupal\Component\Utility\NestedArray;

/**
 * Plugin implementation of the 'entity_reference_autocomplete' widget.
 *
 * @FieldWidget(
 *   id = "no_duplicate_entity_reference_autocomplete",
 *   label = @Translation("Autocomplete - Disallow Duplicates"),
 *   description = @Translation("An autocomplete text field that validates against duplicates."),
 *   field_types = {
 *     "entity_reference"
 *   }
 * )
 */
class NoDuplicateEntityAutocomplete extends EntityReferenceAutocompleteWidget {


  /**
   * {@inheritdoc}
   */
  public static function afterBuild(array $element, FormStateInterface $form_state) {
    parent::afterBuild($element, $form_state);

    $class = get_class();
    $element['#element_validate'][] = [$class, 'validateNoDuplicates'];

    return $element;
  }

  /**
   * Set a form error if there are duplicate entity ids.
   */
  public static function validateNoDuplicates(array &amp;$element, FormStateInterface $form_state, array &amp;$complete_form) {

    $input = NestedArray::getValue($form_state-&gt;getValues(), $element['#parents']);

    $ids = array_filter(array_column($input, 'target_id'));

    // Check that there aren't duplicate entity_id values.
    if (count($ids) !== count(array_flip($ids))) {
      $form_state-&gt;setError($element, 'Field "' . $element['#title'] . '" doesn\'t allow duplicates.');
    }

  }
}