diff --git a/config/schema/custom_field.schema.yml b/config/schema/custom_field.schema.yml
index 4c865df179859aa43b6e5b54ca9a0d50c89fee5d..20c9abb4fad8352c629b903ee2c86a9bc95d62b1 100644
--- a/config/schema/custom_field.schema.yml
+++ b/config/schema/custom_field.schema.yml
@@ -158,6 +158,21 @@ custom_field.formatter.*:
         link:
           type: boolean
           label: 'Link label to the referenced entity'
+        hierarchy_display:
+          type: string
+          label: 'Terms to display'
+        hierarchy_link:
+          type: boolean
+          label: 'Link each term'
+        hierarchy_wrap:
+          type: string
+          label: 'Wrap each term'
+        hierarchy_separator:
+          type: string
+          label: 'Separator'
+        hierarchy_reverse:
+          type: boolean
+          label: 'Reverse order'
 
 field.formatter.settings.custom_formatter:
   type: custom_field_formatter_base
diff --git a/custom_field.module b/custom_field.module
index 78ca342f18875abfaff4ab645d0e3563db80b078..de6fcf9f71dde4df3f48011dc7e6fb23668f7db7 100755
--- a/custom_field.module
+++ b/custom_field.module
@@ -34,6 +34,15 @@ function custom_field_theme(): array {
   return [
     'custom_field' => $item,
     'custom_field_item' => $item,
+    'custom_field_hierarchical_formatter' => [
+      'variables' => [
+        'terms' => [],
+        'wrapper' => '',
+        'separator' => ' » ',
+        'link' => FALSE,
+      ],
+      'file' => 'custom_field_hierarchical_formatter.theme.inc',
+    ],
   ];
 }
 
diff --git a/custom_field_hierarchical_formatter.theme.inc b/custom_field_hierarchical_formatter.theme.inc
new file mode 100644
index 0000000000000000000000000000000000000000..d16fb8ad42a98e8896c1b6b339491f135ca1d146
--- /dev/null
+++ b/custom_field_hierarchical_formatter.theme.inc
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Theme preprocess used to prepare Twig variables.
+ */
+
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Link;
+use Drupal\Core\Render\Markup;
+
+/**
+ * Prepares term objects for Twig template.
+ *
+ * @param array $variables
+ *   An associative array with preprocess variables for this theme.
+ *   by theme_preprocess.
+ */
+function template_preprocess_custom_field_hierarchical_formatter(array &$variables) {
+  $terms = [];
+  $variables['terms_objects'] = $variables['terms'];
+
+  /** @var \Drupal\taxonomy\TermInterface|[] $item */
+  foreach ($variables['terms'] as $item) {
+    if (is_array($item)) {
+      $group = [];
+      foreach ($item as $value) {
+        if ($variables['link']) {
+          $link = Link::fromTextAndUrl($value->label(), $value->toUrl())->toRenderable();
+          $group[] = \Drupal::service('renderer')->render($link);
+        }
+        else {
+          $group[] = $value->label();
+        }
+      }
+
+      $terms[] = Markup::create(implode('<span class="child-separator">, </span>', $group));
+    }
+    else {
+      if ($variables['link']) {
+        $link = Link::fromTextAndUrl($item->label(), $item->toUrl())->toRenderable();
+        $terms[] = \Drupal::service('renderer')->render($link);
+      }
+      else {
+        $terms[] = $item->label();
+      }
+    }
+  }
+
+  if ($variables['wrapper'] != 'none') {
+    $count = 0;
+    foreach ($terms as &$term) {
+      $count++;
+      $term = [
+        '#type' => 'html_tag',
+        '#tag' => in_array($variables['wrapper'], ['ol', 'ul']) ? 'li' : $variables['wrapper'],
+        '#value' => $term,
+        '#attributes' => [
+          'class' => [
+            Html::cleanCssIdentifier('taxonomy-term'),
+            Html::cleanCssIdentifier("count {$count}"),
+          ],
+        ],
+      ];
+    }
+  }
+
+  unset($variables['link']);
+  $variables['terms'] = $terms;
+}
diff --git a/src/Plugin/CustomField/FieldFormatter/HierarchicalFormatter.php b/src/Plugin/CustomField/FieldFormatter/HierarchicalFormatter.php
new file mode 100644
index 0000000000000000000000000000000000000000..45ad11020d362cb73020671504635466064fc603
--- /dev/null
+++ b/src/Plugin/CustomField/FieldFormatter/HierarchicalFormatter.php
@@ -0,0 +1,220 @@
+<?php
+
+namespace Drupal\custom_field\Plugin\CustomField\FieldFormatter;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Field\FieldItemInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\custom_field\Plugin\CustomFieldTypeInterface;
+
+/**
+ * Plugin implementation of the 'hierarchical_term_formatter' formatter.
+ *
+ * @FieldFormatter(
+ *   id = "hierarchical_term_formatter",
+ *   label = @Translation("Hierarchical term"),
+ *   description = @Translation("Display the term hierarchy."),
+ *   field_types = {
+ *     "entity_reference",
+ *   }
+ * )
+ */
+class HierarchicalFormatter extends EntityReferenceFormatterBase {
+
+  /**
+   * Returns a list of supported display options.
+   *
+   * @return array
+   *   An array whose keys are display machine names
+   *   and whose values are their labels.
+   */
+  private function displayOptions(): array {
+    return [
+      'all' => $this->t('The selected term and all of its parents'),
+      'parents' => $this->t('Just the parent terms'),
+      'root' => $this->t('Just the topmost/root term'),
+      'nonroot' => $this->t('Any non-topmost/root terms'),
+      'leaf' => $this->t('Just the selected term'),
+    ];
+  }
+
+  /**
+   * Returns a list of supported wrapping options.
+   *
+   * @return array
+   *   An array whose keys are wrapper machine names
+   *   and whose values are their labels.
+   */
+  private function wrapOptions(): array {
+    return [
+      'none' => $this->t('None'),
+      'span' => $this->t('@tag elements', ['@tag' => '<span>']),
+      'div' => $this->t('@tag elements', ['@tag' => '<div>']),
+      'ul' => $this->t('@tag elements surrounded by a @parent_tag', [
+        '@tag' => '<li>',
+        '@parent_tag' => '<ul>',
+      ]),
+      'ol' => $this->t('@tag elements surrounded by a @parent_tag', [
+        '@tag' => '<li>',
+        '@parent_tag' => '<ol>',
+      ]),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultSettings(): array {
+    return [
+      'hierarchy_display' => 'all',
+      'hierarchy_link' => FALSE,
+      'hierarchy_wrap' => 'none',
+      'hierarchy_separator' => ' » ',
+      'hierarchy_reverse' => FALSE,
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, FormStateInterface $form_state, array $settings) {
+    $settings += static::defaultSettings();
+    $element['hierarchy_display'] = [
+      '#title' => $this->t('Terms to display'),
+      '#description' => $this->t('Choose what terms to display.'),
+      '#type' => 'select',
+      '#options' => $this->displayOptions(),
+      '#default_value' => $settings['hierarchy_display'],
+    ];
+    $element['hierarchy_link'] = [
+      '#title' => $this->t('Link each term'),
+      '#description' => $this->t('If checked, the terms will link to their corresponding term pages.'),
+      '#type' => 'checkbox',
+      '#default_value' => $settings['hierarchy_link'],
+    ];
+    $element['hierarchy_reverse'] = [
+      '#title' => $this->t('Reverse order'),
+      '#description' => $this->t('If checked, children display first, parents last.'),
+      '#type' => 'checkbox',
+      '#default_value' => $settings['hierarchy_reverse'],
+    ];
+    $element['hierarchy_wrap'] = [
+      '#title' => $this->t('Wrap each term'),
+      '#description' => $this->t('Choose what type of html elements you would like to wrap the terms in, if any.'),
+      '#type' => 'select',
+      '#options' => $this->wrapOptions(),
+      '#default_value' => $settings['hierarchy_wrap'],
+    ];
+    $element['hierarchy_separator'] = [
+      '#title' => $this->t('Separator'),
+      '#description' => $this->t('Enter some text or markup that will separate each term in the hierarchy. Leave blank for no separator. Example: <em>»</em>'),
+      '#type' => 'textfield',
+      '#size' => 20,
+      '#default_value' => $settings['hierarchy_separator'],
+    ];
+
+    return $element;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formatValue(FieldItemInterface $item, CustomFieldTypeInterface $field, array $settings) {
+    /** @var \Drupal\taxonomy\Entity\Term $entity */
+    $entity = $settings['value'];
+
+    if (!$entity instanceof EntityInterface) {
+      return NULL;
+    }
+
+    /** @var \Drupal\taxonomy\TermStorageInterface $storage */
+    $storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId());
+    $access = $this->checkAccess($entity);
+    if (!$access->isAllowed()) {
+      return NULL;
+    }
+
+    $formatter_settings = $settings['formatter_settings'] + static::defaultSettings();
+    $tid = $entity->id();
+    $term_tree = [];
+
+    switch ($formatter_settings['hierarchy_display']) {
+      case 'leaf':
+        $term_tree = [$entity];
+        break;
+
+      case 'root':
+        $parents = $storage->loadAllParents($tid);
+        if (!empty($parents)) {
+          $term_tree = [array_pop($parents)];
+        }
+        break;
+
+      case 'parents':
+        $term_tree = array_reverse($storage->loadAllParents($tid));
+        array_pop($term_tree);
+        break;
+
+      case 'nonroot':
+        $parents = $storage->loadAllParents($tid);
+        if (count($parents) > 1) {
+          $term_tree = array_reverse($parents);
+          // This gets rid of the first topmost term.
+          array_shift($term_tree);
+          // Terms can have multiple parents. Now remove any remaining topmost
+          // terms.
+          foreach ($term_tree as $key => $term) {
+            $has_parents = $storage->loadAllParents($term->id());
+            // This has no parents and is topmost.
+            if (empty($has_parents)) {
+              unset($term_tree[$key]);
+            }
+          }
+        }
+        break;
+
+      default:
+        $term_tree = array_reverse($storage->loadAllParents($tid));
+        break;
+    }
+
+    // Change output order if Reverse order is checked.
+    if ($formatter_settings['hierarchy_reverse'] && count($term_tree)) {
+      $term_tree = array_reverse($term_tree);
+    }
+
+    // Remove empty elements caused by discarded items.
+    $term_tree = array_filter($term_tree);
+
+    foreach ($term_tree as $index => $term) {
+      if ($term->hasTranslation($item->getLangcode())) {
+        $term_tree[$index] = $term->getTranslation($item->getLangcode());
+      }
+    }
+
+    $build = [
+      '#theme' => 'custom_field_hierarchical_formatter',
+      '#terms' => $term_tree,
+      '#wrapper' => $formatter_settings['hierarchy_wrap'],
+      '#separator' => $formatter_settings['hierarchy_separator'],
+      '#link' => $formatter_settings['hierarchy_link'],
+    ];
+
+    return $build;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkAccess(EntityInterface $entity) {
+    return $entity->access('view label', NULL, TRUE);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function isApplicable(CustomFieldTypeInterface $custom_item): bool {
+    return $custom_item->getTargetType() === 'taxonomy_term';
+  }
+
+}
diff --git a/src/Plugin/CustomFieldFormatterBase.php b/src/Plugin/CustomFieldFormatterBase.php
index dce9e6db4b25e383ff2befe1d9ff00d7f3d6e083..8a513f636fc81a8a15f9be1dcaa90b57916eb5d4 100755
--- a/src/Plugin/CustomFieldFormatterBase.php
+++ b/src/Plugin/CustomFieldFormatterBase.php
@@ -83,4 +83,12 @@ abstract class CustomFieldFormatterBase extends PluginSettingsBase implements Cu
     return [];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function isApplicable(CustomFieldTypeInterface $custom_item): bool {
+    // By default, formatters are available for all fields.
+    return TRUE;
+  }
+
 }
diff --git a/src/Plugin/CustomFieldFormatterInterface.php b/src/Plugin/CustomFieldFormatterInterface.php
index 9af83dd6cba7480d4ae672597ea96b8f571c1431..428d60d3dff80b5e7929251db6193684d501cf27 100644
--- a/src/Plugin/CustomFieldFormatterInterface.php
+++ b/src/Plugin/CustomFieldFormatterInterface.php
@@ -86,4 +86,15 @@ interface CustomFieldFormatterInterface {
    */
   public function onFormatterDependencyRemoval(array $dependencies, array $settings): array;
 
+  /**
+   * Returns if the formatter can be used for the provided field.
+   *
+   * @param \Drupal\custom_field\Plugin\CustomFieldTypeInterface $custom_item
+   *   The custom field type.
+   *
+   * @return bool
+   *   TRUE if the formatter can be used, FALSE otherwise.
+   */
+  public static function isApplicable(CustomFieldTypeInterface $custom_item): bool;
+
 }
diff --git a/templates/custom-field-hierarchical-formatter.html.twig b/templates/custom-field-hierarchical-formatter.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..11e1e2c7fe1f0ff4f409a74c26027a59160322f3
--- /dev/null
+++ b/templates/custom-field-hierarchical-formatter.html.twig
@@ -0,0 +1,27 @@
+{#
+/**
+ * @file
+ * Default twig file for Hierarchical Term Formatter.
+ *
+ * Available variables:
+ * - terms: The processed taxonomy terms to display.
+ * - wrapper: The HTML tag that should wrap each of the terms.
+ * - separator: The text used to separate each of the terms.
+ *
+ * @see template_preprocess_hierarchical_term_formatter()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if wrapper == 'ol' or wrapper == 'ul' %}
+    <{{ wrapper }} class="terms-hierarchy">
+{% endif %}
+{% for term in terms -%}
+    {{ term }}
+    {% if loop.last == false %}
+        <span class="separator">{{ separator }}</span>
+    {% endif %}
+{%- endfor %}
+{% if wrapper == 'ol' or wrapper == 'ul' %}
+    </{{ wrapper }}>
+{% endif %}