diff --git a/tests/src/Kernel/FieldTest.php b/tests/src/Kernel/FieldTest.php
index 75dd367..a26a3d7 100644
--- a/tests/src/Kernel/FieldTest.php
+++ b/tests/src/Kernel/FieldTest.php
@@ -67,6 +67,21 @@ class FieldTest extends KernelTestBase {
     ]);
     $field->save();
 
+    $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();
+
     $this->testFormat = FilterFormat::create([
       'format' => 'test',
       'weight' => 1,
@@ -201,4 +216,31 @@ 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();
+    $entity = Node::create([
+      'title' => 'Test entity reference',
+      'type' => 'article',
+      'test_reference' => ['target_id' => $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'),
+    ]);
+  }
+
 }
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;
 }
