diff --git a/tests/src/Kernel/FieldTest.php b/tests/src/Kernel/FieldTest.php
index 75dd367..90499b7 100644
--- a/tests/src/Kernel/FieldTest.php
+++ b/tests/src/Kernel/FieldTest.php
@@ -17,6 +17,7 @@ use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\filter\Entity\FilterFormat;
 use Drupal\node\Entity\Node;
 use Drupal\node\Entity\NodeType;
+use Drupal\taxonomy\Tests\TaxonomyTestTrait;
 
 /**
  * Tests field tokens.
@@ -25,17 +26,26 @@ use Drupal\node\Entity\NodeType;
  */
 class FieldTest extends KernelTestBase {
 
+  use TaxonomyTestTrait;
+
   /**
    * @var \Drupal\filter\FilterFormatInterface
    */
   protected $testFormat;
 
   /**
+   * Vocabulary for testing chained token support.
+   *
+   * @var \Drupal\taxonomy\VocabularyInterface
+   */
+  protected $vocabulary;
+
+  /**
    * Modules to enable.
    *
    * @var array
    */
-  public static $modules = ['node', 'text', 'field', 'filter'];
+  public static $modules = ['node', 'text', 'field', 'filter', 'taxonomy'];
 
   /**
    * {@inheritdoc}
@@ -45,6 +55,7 @@ class FieldTest extends KernelTestBase {
 
     $this->installEntitySchema('user');
     $this->installEntitySchema('node');
+    $this->installEntitySchema('taxonomy_term');
 
     // Create the article content type with a text field.
     $node_type = NodeType::create([
@@ -67,6 +78,64 @@ class FieldTest extends KernelTestBase {
     ]);
     $field->save();
 
+    // Add a node reference field.
+    $reference_storage = FieldStorageConfig::create([
+      'field_name' => 'test_reference',
+      'entity_type' => 'node',
+      'type' => 'entity_reference',
+    ]);
+    $reference_storage->save();
+
+    $reference = FieldConfig::create([
+      'field_name' => 'test_reference',
+      'entity_type' => 'node',
+      'bundle' => 'article',
+      'label' => 'Test reference',
+    ]);
+    $reference->save();
+
+    // Add a taxonomy term reference field.
+    $this->vocabulary = $this->createVocabulary();
+    $reference_storage = FieldStorageConfig::create([
+      'field_name' => 'test_term_reference',
+      'entity_type' => 'node',
+      'type' => 'entity_reference',
+      'settings' => [
+        'target_type' => 'taxonomy_term',
+      ],
+    ]);
+    $reference_storage->save();
+
+    $reference = FieldConfig::create([
+      'field_name' => 'test_term_reference',
+      'entity_type' => 'node',
+      'bundle' => 'article',
+      'label' => 'Test term reference',
+      'settings' => [
+        'handler' => 'default:taxonomy_term',
+        'handler_settings' => [
+          'target_bundles' => [
+            $this->vocabulary->id() => $this->vocabulary->id(),
+          ],
+        ]
+      ]
+    ]);
+    $reference->save();
+
+    // Add a field to the vocabulary.
+    $storage = FieldStorageConfig::create([
+      'field_name' => 'term_field',
+      'entity_type' => 'taxonomy_term',
+      'type' => 'text',
+    ]);
+    $storage->save();
+    $field = FieldConfig::create([
+      'field_name' => 'term_field',
+      'entity_type' => 'taxonomy_term',
+      'bundle' => $this->vocabulary->id(),
+    ]);
+    $field->save();
+
     $this->testFormat = FilterFormat::create([
       'format' => 'test',
       'weight' => 1,
@@ -201,4 +270,38 @@ class FieldTest extends KernelTestBase {
       'test_field' => Markup::create(substr($value, 0, 50)),
     ]);
   }
+
+  /**
+   * Tests chaining tokens.
+   */
+  public function testChainedTokens() {
+    $reference = Node::create([
+      'title' => 'Test node to reference',
+      'type' => 'article',
+      'test_field' => [
+        'value' => 'foo',
+        'format' => $this->testFormat->id(),
+      ]
+    ]);
+    $reference->save();
+    $term_reference_field_value = $this->randomString();
+    $term_reference = $this->createTerm($this->vocabulary, [
+      'name' => 'Term to reference',
+      'term_field' => $term_reference_field_value,
+    ]);
+    $entity = Node::create([
+      'title' => 'Test entity reference',
+      'type' => 'article',
+      'test_reference' => ['target_id' => $reference->id()],
+      'test_term_reference' => ['target_id' => $term_reference->id()],
+    ]);
+    $entity->save();
+
+    $this->assertTokens('node', ['node' => $entity], [
+      'test_reference:title' => Markup::create('Test node to reference'),
+      'test_reference:test_field' => Markup::create('foo'),
+      'test_term_reference:term_field' => Markup::create($term_reference_field_value),
+    ]);
+  }
+
 }
diff --git a/token.tokens.inc b/token.tokens.inc
index 34e6904..e12c1ff 100644
--- a/token.tokens.inc
+++ b/token.tokens.inc
@@ -1148,6 +1148,30 @@ function menu_ui_tokens($type, $tokens, array $data = array(), array $options =
  * modules may already have defined some field tokens.
  */
 function field_token_info_alter(&$info) {
+
+  // Tokens as delta for a multiple field.
+  $info['types']['multiple_field_date'] = array(
+    'name' => t('Multiple date field'),
+    'description' => t('Tokens as delta for a multiple date field.'),
+    'needs-data' => 'multiple_field_date',
+    'module' => 'token',
+  );
+  $info['tokens']['multiple_field_date']['delta'] = array(
+    'name' => t('Value'),
+    'description' => t('Value of multiple date field at position delta. Delta has to be replaced by an integer.'),
+    'type' => 'date',
+  );
+  $info['types']['multiple_field_value'] = array(
+    'name' => t('Multiple field'),
+    'description' => t('Tokens as delta for a multiple  field.'),
+    'needs-data' => 'multiple_field_value',
+    'module' => 'token',
+  );
+  $info['tokens']['multiple_field_value']['delta'] = array(
+    'name' => t('Value'),
+    'description' => t('Value of multiple field at position delta. Delta has to be replaced by an integer.'),
+  );
+
   $type_info = \Drupal::service('plugin.manager.field.field_type')->getDefinitions();
 
   // Attach field tokens to their respecitve entity tokens.
@@ -1200,6 +1224,57 @@ function field_token_info_alter(&$info) {
         'description' => $description,
         'module' => 'token',
       );
+
+      // Add type of chained tokens for fields.
+      $field_cardinality = $field->getCardinality();
+      $field_property_definition = $field->getPropertyDefinitions();
+
+      // For a single field timestamps and entity references can be chained.
+      if ($field_cardinality == 1) {
+        if (isset($field_property_definition['value']) && ($field_property_definition['value']->getDataType() == 'timestamp' )) {
+          $info['tokens'][$token_type][$field_name]['type'] = 'date';
+        }
+        elseif (isset($field_property_definition['entity'])) {
+          $entity_type = $field_property_definition['entity']->getTargetDefinition()->getEntityTypeId();
+          $info['tokens'][$token_type][$field_name]['type'] = $entity_type;
+        }
+      }
+      // For a multiple field the delta is given as chained token.
+      else {
+        if (isset($field_property_definition['value']) && ($field_property_definition['value']->getDataType() == 'timestamp' )) {
+          $info['tokens'][$token_type][$field_name]['type'] = 'multiple_field_date';
+        }
+        elseif (isset($field_property_definition['entity'])) {
+          $entity_type = $field_property_definition['entity']->getTargetDefinition()->getEntityTypeId();
+          $token_name = 'multiple_field_' . $entity_type;
+          $info['tokens'][$token_type][$field_name]['type'] = $token_name;
+
+          // Ensure general token for a multiple entity reference field is given.
+          if (!isset($info['types'][$token_name])) {
+            $info['types'][$token_name] = array(
+              'name' => t('Multiple @entity_type reference field', array('@entity_type' => $entity_type)),
+              'description' => t('Tokens as delta for a multiple @entity_type reference field.', array('@entity_type' => $entity_type)),
+              'needs-data' => $token_name,
+            );
+            if ($field_cardinality == -1) {
+              $description = t('Entity of multiple @entity_type reference field at position delta. Delta has to be replaced by an integer.', array('@entity_type' => $entity_type));
+            }
+            else{
+              $description = t('Entity of multiple @entity_type reference field at position delta. Delta has to be replaced by an integer between 0 and @max.', array('@entity_type' => $entity_type, '@max' => ($field_cardinality - 1)));
+            }
+            $info['tokens'][$token_name]['delta'] = array(
+              'name' => t('@entity_type', array('@entity_type' => $entity_type)),
+              'description' => $description,
+              'module' => 'token',
+              'type' => $entity_type,
+            );
+          }
+        }
+        elseif (isset($field_property_definition['value'])) {
+          $info['tokens'][$token_type][$field_name]['type'] = 'multiple_field_value';
+        }
+      }
+
     }
   }
 }
@@ -1259,14 +1334,17 @@ function field_tokens($type, $tokens, array $data = array(), array $options = ar
     $token_view_display = (!empty($view_display) && $view_display->status());
     /** @var \Drupal\Core\Field\FieldTypePluginManager $fieldTypeManager */
     $fieldTypeManager = \Drupal::service('plugin.manager.field.field_type');
+    $chained_field_token_candidates = array();
 
     foreach ($tokens as $name => $original) {
+      $token_name_list = explode(':', $name);
+      $field_name = $token_name_list[0];
       // Replace the [entity:field-name] token only if token module added this
       // token.
-      if ($entity->hasField($name) && _token_module($data['token_type'], $name) == 'token') {
+      if ($entity->hasField($field_name) && _token_module($data['token_type'], $field_name) == 'token') {
 
         // Do not continue if the field is empty or not a configurable field.
-        if ($entity->get($name)->isEmpty() || !($entity->getFieldDefinition($name) instanceof FieldConfigInterface)) {
+        if ($entity->get($field_name)->isEmpty() || !($entity->getFieldDefinition($field_name) instanceof FieldConfigInterface)) {
           continue;
         }
 
@@ -1276,7 +1354,7 @@ function field_tokens($type, $tokens, array $data = array(), array $options = ar
           // default formatters. If the field has specified a specific formatter
           // to be used by default with tokens, use that, otherwise use the
           // default formatter.
-          $field_type_definition = $fieldTypeManager->getDefinition($entity->getFieldDefinition($name)->getType());
+          $field_type_definition = $fieldTypeManager->getDefinition($entity->getFieldDefinition($field_name)->getType());
 
           $display_options = [
             'type' => !empty($field_type_definition['default_token_formatter']) ? $field_type_definition['default_token_formatter'] : $field_type_definition['default_formatter'],
@@ -1284,16 +1362,140 @@ function field_tokens($type, $tokens, array $data = array(), array $options = ar
           ];
         }
 
-        $field_output = $entity->$name->view($display_options);
-        $field_output['#token_options'] = $options;
-        $field_output['#pre_render'][] = 'token_pre_render_field_token';
-        $replacements[$original] = $renderer->renderPlain($field_output);
+        // Single token given.
+        if (!isset($token_name_list[1])) {
+          $field_output = $entity->$field_name->view($display_options);
+          $field_output['#token_options'] = $options;
+          $field_output['#pre_render'][] = 'token_pre_render_field_token';
+          $replacements[$original] = $renderer->renderPlain($field_output);
+        }
+        // Chained token given.
+        else {
+          $field = $entity->$field_name;
+          $field_storage_definition = $entity->getFieldDefinition($field_name)->getFieldStorageDefinition();
+          $field_cardinality = $field_storage_definition->getCardinality();
+          $field_property_definition = $field_storage_definition->getPropertyDefinitions();
+
+          // For a single field timestamps and entity references can be chained.
+          if ($field_cardinality == 1) {
+            if (isset($field_property_definition['value']) && ($field_property_definition['value']->getDataType() == 'timestamp' )) {
+              $chained_field_token_candidates[$field_name] = array(
+                'chained_token_type' => 'date',
+                'data' => array('date' => $field->value),
+              );
+            }
+            elseif (isset($field_property_definition['entity'])) {
+              $chained_token_type = $field_property_definition['entity']->getTargetDefinition()->getEntityTypeId();
+              $chained_entity = $field->entity;
+              if ($langcode && $chained_entity->hasTranslation($langcode)) {
+                $chained_entity = $chained_entity->getTranslation($langcode);
+              }
+              $bubbleable_metadata->addCacheableDependency($chained_entity);
+              $chained_field_token_candidates[$field_name] = array(
+                'chained_token_type' => $chained_token_type,
+                'data' => array($chained_token_type => $chained_entity),
+              );
+            }
+          }
+          // For a multiple field the delta is given as chained token.
+          else {
+            $bubbleable_metadata->addCacheableDependency($field);
+            if (isset($field_property_definition['value']) && ($field_property_definition['value']->getDataType() == 'timestamp' )) {
+              $chained_field_token_candidates[$field_name] = array(
+                'chained_token_type' => 'multiple_field_date',
+                'data' => array('multiple_field_date' => $field),
+              );
+            }
+            elseif (isset($field_property_definition['entity'])) {
+              $token_name = 'multiple_field_' . $field_property_definition['entity']->getTargetDefinition()->getEntityTypeId();
+              $chained_field_token_candidates[$field_name] = array(
+                'chained_token_type' => $token_name,
+                'data' => array($token_name => $field),
+              );
+            }
+            elseif (isset($field_property_definition['value'])) {
+              $chained_field_token_candidates[$field_name] = array(
+                'chained_token_type' => 'multiple_field_value',
+                'data' => array('multiple_field_value' => $field),
+              );
+            }
+
+          }
+        }
+      }
+    }
+
+    // Chained token relationships.
+    $token_service = \Drupal::token();
+    foreach ($chained_field_token_candidates as $chained_field_token_name => $chained_field_token_info) {
+      if ($chained_field_tokens = $token_service->findWithPrefix($tokens, $chained_field_token_name)) {
+        $chained_data = $chained_field_token_info['data'];
+        $chained_token_type = $chained_field_token_info['chained_token_type'];
+        $replacements += $token_service->generate($chained_token_type, $chained_field_tokens, $chained_data, $options, $bubbleable_metadata);
       }
     }
 
     // Remove the cloned object from memory.
     unset($entity);
   }
+  // Field tokens.
+  elseif ((strpos($type, 'multiple_field_') !== FALSE) && isset($data[$type])) {
+    $field = $data[$type];
+
+    $chained_field_token_candidates = array();
+
+    foreach ($tokens as $name => $original) {
+      $token_name_list = explode(':', $name);
+      $delta = $token_name_list[0];
+
+      // Replace the [field:delta] token.
+      if (is_numeric($delta) && isset($field[$delta])) {
+        // Single token given.
+        if (!isset($token_name_list[1])) {
+          $field_output = $field[$delta]->view('token');
+          $field_output['#token_options'] = $options;
+          $replacements[$original] = $renderer->renderPlain($field_output);
+        }
+        // Chained token given.
+        else {
+          // Timestamps can be chained.
+          if ($type == 'multiple_field_date') {
+            $chained_field_token_candidates[$delta] = array(
+              'chained_token_type' => 'date',
+              'data' => array('date' => $field[$delta]->value),
+            );
+          }
+          // Entity references can be chained.
+          elseif($type != 'multiple_field_value') {
+            $field_storage_definition = $field->getFieldDefinition()->getFieldStorageDefinition();
+            $field_property_definition = $field_storage_definition->getPropertyDefinitions();
+            $chained_token_type = $field_property_definition['entity']->getTargetDefinition()->getEntityTypeId();
+            $chained_entity = $field[$delta]->entity;
+            if ($langcode && $chained_entity->hasTranslation($langcode)) {
+              $chained_entity = $chained_entity->getTranslation($langcode);
+            }
+            $bubbleable_metadata->addCacheableDependency($chained_entity);
+            $chained_field_token_candidates[$delta] = array(
+              'chained_token_type' => $chained_token_type,
+              'data' => array($chained_token_type => $chained_entity),
+            );
+          }
+        }
+
+      }
+    }
+
+    // Chained token relationships.
+    $token_service = \Drupal::token();
+    foreach ($chained_field_token_candidates as $chained_field_token_name => $chained_field_token_info) {
+      if ($chained_field_tokens = $token_service->findWithPrefix($tokens, $chained_field_token_name)) {
+        $chained_data = $chained_field_token_info['data'];
+        $chained_token_type = $chained_field_token_info['chained_token_type'];
+        $replacements += $token_service->generate($chained_token_type, $chained_field_tokens, $chained_data, $options, $bubbleable_metadata);
+      }
+    }
+
+  }
 
   return $replacements;
 }
