diff --git a/core/misc/icons/000000/chevron-left.svg b/core/misc/icons/000000/chevron-left.svg
new file mode 100644
index 0000000000000000000000000000000000000000..19ba5807048b8f95f841b5d3f2ce0852ad3631af
--- /dev/null
+++ b/core/misc/icons/000000/chevron-left.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#000000" d="M7.951 7.645c-.193.196-.193.516 0 .71l3.258 3.29c.193.193.191.519-.002.709l-1.371 1.371c-.193.192-.512.191-.707 0l-5.335-5.371c-.194-.194-.194-.514 0-.708l5.335-5.369c.195-.195.514-.195.707-.001l1.371 1.371c.193.194.195.513.002.709l-3.258 3.289z"/></svg>
diff --git a/core/modules/block/src/Tests/BlockConfigSchemaTest.php b/core/modules/block/src/Tests/BlockConfigSchemaTest.php
index 9065fa4839ac6cf25e2febcdaccddf156f9fd8fb..47e242144802e2870288c3b0090a2810374df5db 100644
--- a/core/modules/block/src/Tests/BlockConfigSchemaTest.php
+++ b/core/modules/block/src/Tests/BlockConfigSchemaTest.php
@@ -35,6 +35,7 @@ class BlockConfigSchemaTest extends KernelTestBase {
     // BlockManager->getModuleName() calls system_get_info().
     'system',
     'taxonomy',
+    'user',
   );
 
   /**
diff --git a/core/modules/file/src/Tests/FileFieldDisplayTest.php b/core/modules/file/src/Tests/FileFieldDisplayTest.php
index 281ad344515649c183231c3de2d313939fac3a01..6a1798d7d3653da276b5028a8c4fe17b11d5391c 100644
--- a/core/modules/file/src/Tests/FileFieldDisplayTest.php
+++ b/core/modules/file/src/Tests/FileFieldDisplayTest.php
@@ -87,6 +87,7 @@ function testNodeDisplay() {
     $edit[$field_name . '[0][display]'] = FALSE;
     $edit[$field_name . '[1][display]'] = FALSE;
     $this->drupalPostForm("node/$nid/edit", $edit, t('Preview'));
+    $this->clickLink(t('Back to content editing'));
     $this->assertRaw($field_name . '[0][display]', 'First file appears as expected.');
     $this->assertRaw($field_name . '[1][display]', 'Second file appears as expected.');
   }
diff --git a/core/modules/filter/src/Tests/FilterFormatAccessTest.php b/core/modules/filter/src/Tests/FilterFormatAccessTest.php
index dc247b5e587fdb2d06cb53091e4b06175af93dee..1882a265170b6128cf1b3a17cbb97eb4163fc6a1 100644
--- a/core/modules/filter/src/Tests/FilterFormatAccessTest.php
+++ b/core/modules/filter/src/Tests/FilterFormatAccessTest.php
@@ -234,7 +234,7 @@ function testFormatWidgetPermissions() {
     $this->assertText($edit[$body_value_key], 'Old body found in preview.');
 
     // Save and verify that only the title was changed.
-    $this->drupalPostForm(NULL, $new_edit, t('Save'));
+    $this->drupalPostForm('node/' . $node->id() . '/edit', $new_edit, t('Save'));
     $this->assertNoText($edit['title[0][value]'], 'Old title not found.');
     $this->assertText($new_edit['title[0][value]'], 'New title found.');
     $this->assertText($edit[$body_value_key], 'Old body found.');
diff --git a/core/modules/node/css/node.preview.css b/core/modules/node/css/node.preview.css
new file mode 100644
index 0000000000000000000000000000000000000000..9aca68a961753654f2741dc12b46bb1bc9d34ab2
--- /dev/null
+++ b/core/modules/node/css/node.preview.css
@@ -0,0 +1,17 @@
+/**
+ * @file
+ * Styles for node preview page.
+ */
+
+.node-preview-container {
+  position: fixed;
+  z-index: 499;
+  width: 100%;
+  padding: 10px;
+}
+
+@media only screen and (min-width: 36em) {
+  .node-preview-container .form-type-select {
+    margin-left: 25%;
+  }
+}
diff --git a/core/modules/node/node.libraries.yml b/core/modules/node/node.libraries.yml
index ab1f9357b7e074d6c19e12f29bc8b2e029c1a0ac..231687e30644bb293110cb80c8d86bc455883030 100644
--- a/core/modules/node/node.libraries.yml
+++ b/core/modules/node/node.libraries.yml
@@ -10,12 +10,16 @@ drupal.node:
 
 drupal.node.preview:
   version: VERSION
+  css:
+    theme:
+      css/node.preview.css: {}
   js:
     node.preview.js: {}
   dependencies:
     - core/jquery
     - core/jquery.once
     - core/drupal
+    - core/drupal.form
 
 drupal.content_types:
   version: VERSION
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index fefd6d5c9a13fb93d0a7393906ef4c2c79ed4a62..d96052d3200fcea8d770f47b41abfb66ea20c4ec 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -169,11 +169,6 @@ function node_theme() {
       'file' => 'node.pages.inc',
       'template' => 'node-add-list',
     ),
-    'node_preview' => array(
-      'variables' => array('node' => NULL),
-      'file' => 'node.pages.inc',
-      'template' => 'node-preview',
-    ),
     'node_edit_form' => array(
       'render element' => 'form',
       'template' => 'node-edit-form',
@@ -627,7 +622,10 @@ function template_preprocess_node(&$variables) {
   ));
   $variables['label'] = $variables['elements']['title'];
   unset($variables['elements']['title']);
-  $variables['page'] = $variables['view_mode'] == 'full' && node_is_page($node);
+  // The 'page' variable is set to TRUE in two occasions:
+  //   - The view mode is 'full' and we are on the 'node.view' route.
+  //   - The node is in preview and view mode is either 'full' or 'default'.
+  $variables['page'] = ($variables['view_mode'] == 'full' && (node_is_page($node)) || (isset($node->in_preview) && in_array($node->preview_view_mode, array('full', 'default'))));
 
   // Helpful $content variable for templates.
   $variables += array('content' => array());
@@ -669,7 +667,7 @@ function template_preprocess_node(&$variables) {
   if ($variables['view_mode']) {
     $variables['attributes']['class'][] = drupal_html_class('node--view-mode-' . $variables['view_mode']);
   }
-  if (isset($variables['preview'])) {
+  if (isset($node->preview)) {
     $variables['attributes']['class'][] = 'node--preview';
   }
 }
@@ -1044,6 +1042,25 @@ function node_view_multiple($nodes, $view_mode = 'teaser', $langcode = NULL) {
   return entity_view_multiple($nodes, $view_mode, $langcode);
 }
 
+/**
+ * Implements hook_page_build().
+ */
+function node_page_build(&$page) {
+  // Add 'Back to content editing' link on preview page.
+  $route_match = \Drupal::routeMatch();
+  if ($route_match->getRouteName() == 'entity.node.preview') {
+    $page['page_top']['node_preview'] = array(
+      '#type' => 'container',
+      '#attributes' => array(
+        'class' => array('node-preview-container', 'container-inline')
+      ),
+    );
+
+    $form = \Drupal::formBuilder()->getForm('\Drupal\node\Form\NodePreviewForm', $route_match->getParameter('node_preview'));
+    $page['page_top']['node_preview']['view_mode'] = $form;
+  }
+}
+
 /**
  * Implements hook_form_FORM_ID_alter().
  *
diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc
index e848f3e56f3c6d05a00a292c6209344f61cf56d9..517a5d5e9faab6eb3f12c02da182542bb7212978 100644
--- a/core/modules/node/node.pages.inc
+++ b/core/modules/node/node.pages.inc
@@ -37,68 +37,3 @@ function template_preprocess_node_add_list(&$variables) {
     }
   }
 }
-
-/**
- * Generates a node preview.
- *
- * @param \Drupal\node\NodeInterface $node
- *   The node to preview.
- *
- * @return
- *   An HTML-formatted string of a node preview.
- *
- * @see node_form_build_preview()
- */
-function node_preview(NodeInterface $node, FormStateInterface $form_state) {
-  if ($node->access('create') || $node->access('update')) {
-
-    $node->changed = REQUEST_TIME;
-
-    // Display a preview of the node.
-    if (!form_get_errors($form_state)) {
-      $node->in_preview = TRUE;
-      $node_preview = array(
-        '#theme' => 'node_preview',
-        '#node' => $node,
-      );
-      $output = drupal_render($node_preview);
-      unset($node->in_preview);
-    }
-
-    return $output;
-  }
-}
-
-/**
- * Prepares variables for node preview templates.
- *
- * Default template: node-preview.html.twig.
- *
- * @param array $variables
- *   An associative array containing:
- *   - node: The node entity which is being previewed.
- *
- * @see NodeForm::preview()
- * @see node_preview()
- */
-function template_preprocess_node_preview(&$variables) {
-  $node = $variables['node'];
-
-  // Render trimmed teaser version of the post.
-  $node_teaser = node_view($node, 'teaser');
-  $node_teaser['#attached']['library'][] = 'node/drupal.node.preview';
-  $variables['teaser'] = $node_teaser;
-  // Render full version of the post.
-  $node_full = node_view($node, 'full');
-  $variables['full'] = $node_full;
-
-  // Display a preview of the teaser only if the content of the teaser is
-  // different to the full post.
-  if ($variables['teaser'] != $variables['full']) {
-    drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication.<span class="no-js"> You can insert the delimiter "&lt;!--break--&gt;" (without the quotes) to fine-tune where your post gets split.</span>'));
-    $variables['preview_teaser'] = TRUE;
-  }
-  else {
-    $variables['preview_teaser'] = FALSE;
-  }
-}
diff --git a/core/modules/node/node.preview.js b/core/modules/node/node.preview.js
index f27cbba21f2cdae348327a26498d56fe376a381c..9041f24512c51b249ae3eccb1f939e88ca2361d5 100644
--- a/core/modules/node/node.preview.js
+++ b/core/modules/node/node.preview.js
@@ -8,16 +8,16 @@
    */
   Drupal.behaviors.nodePreviewDestroyLinks = {
     attach: function (context) {
-      var $preview = $(context).find('.node').once('node-preview');
+      var $preview = $(context).find('.page-node-preview').once('node-preview');
       if ($preview.length) {
-        $preview.on('click.preview', 'a:not([href^=#])', function (e) {
+        $preview.on('click.preview', 'a:not([href^=#], #edit-backlink, #toolbar-administration a)', function (e) {
           e.preventDefault();
         });
       }
     },
     detach: function (context, settings, trigger) {
       if (trigger === 'unload') {
-        var $preview = $(context).find('.node').removeOnce('node-preview');
+        var $preview = $(context).find('.page-node-preview').removeOnce('node-preview');
         if ($preview.length) {
           $preview.off('click.preview');
         }
@@ -25,4 +25,18 @@
     }
   };
 
+  /**
+   * Switch view mode.
+   */
+  Drupal.behaviors.nodePreviewSwitchViewMode = {
+    attach: function (context) {
+      var $autosubmit = $(context).find('[data-drupal-autosubmit]').once('autosubmit');
+      if ($autosubmit.length) {
+        $autosubmit.on('formUpdated.preview', function() {
+          $(this.form).trigger('submit');
+        });
+      }
+    }
+  };
+
 })(jQuery, Drupal);
diff --git a/core/modules/node/node.routing.yml b/core/modules/node/node.routing.yml
index f9523d4c89ae4949a99322a81722e48fe815719a..26a4d07b9c6b58bbf2a6c20c85054dd554e5e940 100644
--- a/core/modules/node/node.routing.yml
+++ b/core/modules/node/node.routing.yml
@@ -1,4 +1,3 @@
-
 node.multiple_delete_confirm:
   path: '/admin/content/node/delete'
   defaults:
@@ -37,6 +36,18 @@ node.add:
   options:
     _node_operation_route: TRUE
 
+entity.node.preview:
+  path: '/node/preview/{node_preview}/{view_mode_id}'
+  defaults:
+    _content: '\Drupal\node\Controller\NodePreviewController::view'
+    _title_callback: '\Drupal\node\Controller\NodePreviewController::title'
+  requirements:
+    _node_preview_access: '{node_preview}'
+  options:
+    parameters:
+      node_preview:
+        type: 'node_preview'
+
 entity.node.canonical:
   path: '/node/{node}'
   defaults:
diff --git a/core/modules/node/node.services.yml b/core/modules/node/node.services.yml
index 85d998a6bf40e484217de65c0e40965f2f8438a9..546f47f4358c1c713af06d26ea54bf9309741b1e 100644
--- a/core/modules/node/node.services.yml
+++ b/core/modules/node/node.services.yml
@@ -19,8 +19,18 @@ services:
     arguments: ['@entity.manager']
     tags:
       - { name: access_check, applies_to: _node_add_access }
+  access_check.node.preview:
+    class: Drupal\node\Access\NodePreviewAccessCheck
+    arguments: ['@entity.manager']
+    tags:
+      - { name: access_check, applies_to: _node_preview_access }
   node.admin_path.route_subscriber:
     class: Drupal\node\EventSubscriber\NodeAdminRouteSubscriber
     arguments: ['@config.factory']
     tags:
       - { name: event_subscriber }
+  node_preview:
+    class: Drupal\node\ParamConverter\NodePreviewConverter
+    arguments: ['@user.tempstore']
+    tags:
+      - { name: paramconverter }
diff --git a/core/modules/node/src/Access/NodePreviewAccessCheck.php b/core/modules/node/src/Access/NodePreviewAccessCheck.php
new file mode 100644
index 0000000000000000000000000000000000000000..18bbcd185e52a0eaf1d2962ae4e68c5252dd06cc
--- /dev/null
+++ b/core/modules/node/src/Access/NodePreviewAccessCheck.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Access\NodePreviewAccessCheck.
+ */
+
+namespace Drupal\node\Access;
+
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Routing\Access\AccessInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\node\NodeInterface;
+
+/**
+ * Determines access to node previews.
+ */
+class NodePreviewAccessCheck implements AccessInterface {
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * Constructs a EntityCreateAccessCheck object.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   */
+  public function __construct(EntityManagerInterface $entity_manager) {
+    $this->entityManager = $entity_manager;
+  }
+
+  /**
+   * Checks access to the node preview page.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The currently logged in account.
+   * @param \Drupal\node\NodeInterface $node_preview
+   *   The node that is being previewed.
+   *
+   * @return string
+   *   A \Drupal\Core\Access\AccessInterface constant value.
+   */
+  public function access(AccountInterface $account, NodeInterface $node_preview) {
+    if ($node_preview->isNew()) {
+      $access_controller = $this->entityManager->getAccessControlHandler('node');
+      return $access_controller->createAccess($node_preview->bundle(), $account) ? static::ALLOW : static::DENY;
+    }
+    else {
+      return $node_preview->access('update', $account) ? static::ALLOW : static::DENY;
+    }
+  }
+
+}
diff --git a/core/modules/node/src/Controller/NodePreviewController.php b/core/modules/node/src/Controller/NodePreviewController.php
new file mode 100644
index 0000000000000000000000000000000000000000..9fb8d13114069a05a9e89627ea7cff2a82a3f321
--- /dev/null
+++ b/core/modules/node/src/Controller/NodePreviewController.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Controller\NodePreviewController.
+ */
+
+namespace Drupal\node\Controller;
+
+use Drupal\Component\Utility\String;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\Controller\EntityViewController;
+
+/**
+ * Defines a controller to render a single node in preview.
+ */
+class NodePreviewController extends EntityViewController {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function view(EntityInterface $node_preview, $view_mode_id = 'full', $langcode = NULL) {
+    // Do not cache this page.
+    drupal_page_is_cacheable(FALSE);
+
+    $node_preview->preview_view_mode = $view_mode_id;
+    $build = array('nodes' => parent::view($node_preview, $view_mode_id));
+
+    $build['#attached']['library'][] = 'node/drupal.node.preview';
+
+    $build['#title'] = $build['nodes']['#title'];
+    unset($build['nodes']['#title']);
+
+    // Don't render cache previews.
+    unset($build['nodes']['#cache']);
+
+    foreach ($node_preview->uriRelationships() as $rel) {
+      // Set the node path as the canonical URL to prevent duplicate content.
+      $build['#attached']['drupal_add_html_head_link'][] = array(
+        array(
+        'rel' => $rel,
+        'href' => $node_preview->url($rel),
+        )
+        , TRUE);
+
+      if ($rel == 'canonical') {
+        // Set the non-aliased canonical path as a default shortlink.
+        $build['#attached']['drupal_add_html_head_link'][] = array(
+          array(
+            'rel' => 'shortlink',
+            'href' => $node_preview->url($rel, array('alias' => TRUE)),
+          )
+        , TRUE);
+      }
+    }
+
+    return $build;
+  }
+
+  /**
+   * The _title_callback for the page that renders a single node in preview.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $node_preview
+   *   The current node.
+   *
+   * @return string
+   *   The page title.
+   */
+  public function title(EntityInterface $node_preview) {
+    return String::checkPlain($this->entityManager->getTranslationFromContext($node_preview)->label());
+  }
+
+}
diff --git a/core/modules/node/src/Form/NodePreviewForm.php b/core/modules/node/src/Form/NodePreviewForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..4c5bb913892adbc3c5bae0ff319e2217c11f9a8c
--- /dev/null
+++ b/core/modules/node/src/Form/NodePreviewForm.php
@@ -0,0 +1,169 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Form\NodePreviewForm.
+ */
+
+namespace Drupal\node\Form;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Contains a form for switching the view mode of a node during preview.
+ */
+class NodePreviewForm extends FormBase implements ContainerInjectionInterface {
+
+  /**
+   * The entity manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('entity.manager'), $container->get('config.factory'));
+  }
+
+  /**
+   * Constructs a new NodePreviewForm.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager service.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The configuration factory.
+   */
+  public function __construct(EntityManagerInterface $entity_manager, ConfigFactoryInterface $config_factory) {
+    $this->entityManager = $entity_manager;
+    $this->configFactory = $config_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'node_preview_form_select';
+  }
+
+  /**
+   * Form constructor.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   * @param \Drupal\Core\Entity\EntityInterface $node
+   *   The node being previews
+   *
+   * @return array
+   *   The form structure.
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, EntityInterface $node = NULL) {
+    $view_mode = $node->preview_view_mode;
+
+    $query_options = $node->isNew() ? array('query' => array('uuid' => $node->uuid())) : array();
+    $form['backlink'] = array(
+      '#type' => 'link',
+      '#title' => $this->t('Back to content editing'),
+      '#href' => $node->isNew() ? 'node/add/' . $node->bundle() :  'node/' . $node->id() . '/edit',
+      '#options' => array('attributes' => array('class' => array('node-preview-backlink'))) + $query_options,
+    );
+
+    $view_mode_options = $this->getViewModeOptions($node);
+
+    $form['uuid'] = array(
+      '#type' => 'value',
+      '#value' => $node->uuid(),
+    );
+
+    $form['view_mode'] = array(
+      '#type' => 'select',
+      '#title' => $this->t('View mode'),
+      '#options' => $view_mode_options,
+      '#default_value' => $view_mode,
+      '#attributes' => array(
+        'data-drupal-autosubmit' => TRUE,
+      )
+    );
+
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => $this->t('Switch'),
+      '#attributes' => array(
+        'class' => array('js-hide'),
+      ),
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $form_state->setRedirect('entity.node.preview', array(
+      'node_preview' => $form_state['values']['uuid'],
+      'view_mode_id' => $form_state['values']['view_mode'],
+    ));
+  }
+
+  /**
+   * Retrieves the list of available view modes for the current node.
+   *
+   * @param EntityInterface $node
+   *   The node being previewed.
+   *
+   * @return array
+   *   List of available view modes for the current node.
+   */
+  protected function getViewModeOptions(EntityInterface $node) {
+    $load_ids = array();
+    $view_mode_options = array();
+
+    // Load all the node's view modes.
+    $view_modes = $this->entityManager->getViewModes('node');
+
+    // Get the list of available view modes for the current node's bundle.
+    $ids = $this->configFactory->listAll('entity.view_display.node.' . $node->bundle());
+    foreach ($ids as $id) {
+      $config_id = str_replace('entity.view_display' . '.', '', $id);
+      $load_ids[] = $config_id;
+    }
+    $displays = entity_load_multiple('entity_view_display', $load_ids);
+
+    // Generate the display options array.
+    foreach ($displays as $display) {
+
+      $view_mode_name = $display->get('mode');
+
+      // Skip view modes that are not used in the front end.
+      if (in_array($view_mode_name, array('rss', 'search_index'))) {
+        continue;
+      }
+
+      if ($display->status()) {
+        $view_mode_options[$view_mode_name] = ($view_mode_name == 'default') ? t('Default') : $view_modes[$view_mode_name]['label'];
+      }
+    }
+
+    return $view_mode_options;
+  }
+
+}
diff --git a/core/modules/node/src/NodeForm.php b/core/modules/node/src/NodeForm.php
index a2ec826928969dade2cfc4d497633633489f1826..327aee5d18ae067e4082e81ef0691c63bf0e03e2 100644
--- a/core/modules/node/src/NodeForm.php
+++ b/core/modules/node/src/NodeForm.php
@@ -13,6 +13,9 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Component\Utility\String;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\user\TempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Form controller for the node edit forms.
@@ -26,6 +29,36 @@ class NodeForm extends ContentEntityForm {
    */
   protected $settings;
 
+  /**
+   * The tempstore factory.
+   *
+   * @var \Drupal\user\TempStoreFactory
+   */
+  protected $tempStoreFactory;
+
+  /**
+   * Constructs a ContentEntityForm object.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\user\TempStoreFactory $temp_store_factory
+   *   The factory for the temp store object.
+   */
+  public function __construct(EntityManagerInterface $entity_manager, TempStoreFactory $temp_store_factory) {
+    parent::__construct($entity_manager);
+    $this->tempStoreFactory = $temp_store_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity.manager'),
+      $container->get('user.tempstore')
+    );
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -47,6 +80,28 @@ protected function prepareEntity() {
    * {@inheritdoc}
    */
   public function form(array $form, FormStateInterface $form_state) {
+
+    // Try to restore from temp store.
+    $uuid = $this->entity->uuid();
+    $store = $this->tempStoreFactory->get('node_preview');
+
+    // If the user is creating a new node, the UUID is passed in the request.
+    if ($request_uuid = \Drupal::request()->query->get('uuid')) {
+      $uuid = $request_uuid;
+    }
+
+    if ($preview = $store->get($uuid)) {
+      $form_state = $preview;
+
+      // Rebuild the form.
+      $form_state['rebuild'] = TRUE;
+      $this->entity = $preview['controller']->getEntity();
+      unset($this->entity->in_preview);
+
+      // Remove the entry from the temp store.
+      $store->delete($uuid);
+    }
+
     /** @var \Drupal\node\NodeInterface $node */
     $node = $this->entity;
 
@@ -56,15 +111,6 @@ public function form(array $form, FormStateInterface $form_state) {
 
     $current_user = \Drupal::currentUser();
     $user_config = \Drupal::config('user.settings');
-    // Some special stuff when previewing a node.
-    if (isset($form_state['node_preview'])) {
-      $form['#prefix'] = $form_state['node_preview'];
-      $node->in_preview = TRUE;
-      $form['#title'] = $this->t('Preview');
-    }
-    else {
-      unset($node->in_preview);
-    }
 
     // Override the default CSS class name, since the user-defined node type
     // name in 'TYPE-node-form' potentially clashes with third-party class
@@ -366,11 +412,13 @@ public function submit(array $form, FormStateInterface $form_state) {
    *   The current state of the form.
    */
   public function preview(array $form, FormStateInterface $form_state) {
-    // @todo Remove this: we should not have explicit includes in autoloaded
-    //   classes.
-    module_load_include('inc', 'node', 'node.pages');
-    $form_state['node_preview'] = node_preview($this->entity, $form_state);
-    $form_state['rebuild'] = TRUE;
+    $store = $this->tempStoreFactory->get('node_preview');
+    $this->entity->in_preview = TRUE;
+    $store->set($this->entity->uuid(), $form_state);
+    $form_state->setRedirect('entity.node.preview', array(
+      'node_preview' => $this->entity->uuid(),
+      'view_mode_id' => 'default',
+    ));
   }
 
   /**
diff --git a/core/modules/node/src/ParamConverter/NodePreviewConverter.php b/core/modules/node/src/ParamConverter/NodePreviewConverter.php
new file mode 100644
index 0000000000000000000000000000000000000000..1b0f1181025e501fa9a1f1cb0c682293a4c32366
--- /dev/null
+++ b/core/modules/node/src/ParamConverter/NodePreviewConverter.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\ParamConverter\NodePreviewConverter.
+ */
+
+namespace Drupal\node\ParamConverter;
+
+use Drupal\Core\Entity\EntityManagerInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+use Drupal\Core\ParamConverter\ParamConverterInterface;
+use Drupal\user\TempStoreFactory;
+
+/**
+ * Provides upcasting for a node entity in preview.
+ */
+class NodePreviewConverter implements ParamConverterInterface {
+
+  /**
+   * Stores the tempstore factory.
+   *
+   * @var \Drupal\user\TempStoreFactory
+   */
+  protected $tempStoreFactory;
+
+  /**
+   * Constructs a new NodePreviewConverter.
+   *
+   * @param \Drupal\user\TempStoreFactory $temp_store_factory
+   *   The factory for the temp store object.
+   */
+  public function __construct(TempStoreFactory $temp_store_factory) {
+    $this->tempStoreFactory = $temp_store_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function convert($value, $definition, $name, array $defaults, Request $request) {
+    $store = $this->tempStoreFactory->get('node_preview');
+    if ($form_state = $store->get($value)) {
+      return $form_state['controller']->getEntity();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function applies($definition, $name, Route $route) {
+    if (!empty($definition['type']) && $definition['type'] == 'node_preview') {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+}
diff --git a/core/modules/node/src/Tests/PagePreviewTest.php b/core/modules/node/src/Tests/PagePreviewTest.php
index e387c64801388531b6ef20552f15f46217052681..fa2a32f68159319517eb091a28c6df829f9ffef3 100644
--- a/core/modules/node/src/Tests/PagePreviewTest.php
+++ b/core/modules/node/src/Tests/PagePreviewTest.php
@@ -116,13 +116,31 @@ function testPagePreview() {
     $this->drupalPostForm('node/add/page', $edit, t('Preview'));
 
     // Check that the preview is displaying the title, body and term.
-    $this->assertTitle(t('Preview | Drupal'), 'Basic page title is preview.');
+    $this->assertTitle(t('@title | Drupal', array('@title' => $edit[$title_key])), 'Basic page title is preview.');
     $this->assertText($edit[$title_key], 'Title displayed.');
     $this->assertText($edit[$body_key], 'Body displayed.');
     $this->assertText($edit[$term_key], 'Term displayed.');
+    $this->assertLink(t('Back to content editing'));
+
+    // Get the UUID.
+    $url = parse_url($this->getUrl());
+    $paths = explode('/', $url['path']);
+    $view_mode = array_pop($paths);
+    $uuid = array_pop($paths);
+
+    // Switch view mode. We'll remove the body from the teaser view mode.
+    entity_get_display('node', 'page', 'teaser')
+      ->removeComponent('body')
+      ->save();
+
+    $view_mode_edit = array('view_mode' => 'teaser');
+    $this->drupalPostForm('node/preview/' . $uuid . '/default', $view_mode_edit, t('Switch'));
+    $this->assertRaw('view-mode-teaser', 'View mode teaser class found.');
+    $this->assertNoText($edit[$body_key], 'Body not displayed.');
 
     // Check that the title, body and term fields are displayed with the
-    // correct values.
+    // values after going back to the content edit page.
+    $this->clickLink(t('Back to content editing'));
     $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
     $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
     $this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
@@ -153,7 +171,7 @@ function testPagePreview() {
     $this->assertNoLink($newterm1);
     $this->assertNoLink($newterm2);
 
-    $this->drupalPostForm(NULL, $edit, t('Save'));
+    $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
 
     // Check with one more new term, keeping old terms, removing the existing
     // one.
@@ -168,7 +186,6 @@ function testPagePreview() {
     $this->assertLink($newterm1);
     $this->assertLink($newterm2);
     $this->assertNoLink($newterm3);
-    $this->drupalPostForm(NULL, $edit, t('Save'));
   }
 
   /**
@@ -190,13 +207,14 @@ function testPagePreviewWithRevisions() {
     $this->drupalPostForm('node/add/page', $edit, t('Preview'));
 
     // Check that the preview is displaying the title, body and term.
-    $this->assertTitle(t('Preview | Drupal'), 'Basic page title is preview.');
+    $this->assertTitle(t('@title | Drupal', array('@title' => $edit[$title_key])), 'Basic page title is preview.');
     $this->assertText($edit[$title_key], 'Title displayed.');
     $this->assertText($edit[$body_key], 'Body displayed.');
     $this->assertText($edit[$term_key], 'Term displayed.');
 
-    // Check that the title, body and term fields are displayed with the correct values.
-    $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
+    // Check that the title and body fields are displayed with the correct
+    // values after going back to the content edit page.
+    $this->clickLink(t('Back to content editing'));    $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
     $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
     $this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
 
diff --git a/core/modules/node/templates/node-preview.html.twig b/core/modules/node/templates/node-preview.html.twig
deleted file mode 100644
index 1bc3441944e83b2767f028842b22c93fe0947661..0000000000000000000000000000000000000000
--- a/core/modules/node/templates/node-preview.html.twig
+++ /dev/null
@@ -1,23 +0,0 @@
-{#
-/**
- * @file
- * Default theme implementation for a node preview.
- *
- * This display may be used during node creation and editing.
- *
- * Available variables:
- * - preview_teaser: Flag indicating whether to show a trimmed teaser version.
- * - teaser: Trimmed teaser version of the node.
- * - full: Full version of the node.
- *
- * @see template_preprocess_node_preview()
- *
- * @ingroup themeable
- */
-#}
-{% if preview_teaser %}
-  <h3>{{ "Preview trimmed version"|t }}</h3>
-  {{ teaser }}
-  <h3>{{ "Preview full version"|t }}</h3>
-{% endif %}
-{{ full }}
diff --git a/core/modules/node/templates/node.html.twig b/core/modules/node/templates/node.html.twig
index c80eecba6d7c10c9fcab428101882a81f7193058..a08b0643f6342019ad2359f0b538bd61ebde1a39 100644
--- a/core/modules/node/templates/node.html.twig
+++ b/core/modules/node/templates/node.html.twig
@@ -39,7 +39,6 @@
  *   - node--view-mode-[view_mode]: The View Mode of the node; for example, a
  *     teaser would result in: "node--view-mode-teaser", and
  *     full: "node--view-mode-full".
- *   - node--preview: Whether a node is in preview mode.
  *   The following are controlled through the node publishing options.
  *   - node--promoted: Appears on nodes promoted to the front page.
  *   - node--sticky: Appears on nodes ordered above other non-sticky nodes in
diff --git a/core/modules/system/css/system.theme.css b/core/modules/system/css/system.theme.css
index f3a74903595344a1554ef9367967d1b09bc3ac9f..7023b4fd02bf764c33aceb59e861cb0348d3bbc2 100644
--- a/core/modules/system/css/system.theme.css
+++ b/core/modules/system/css/system.theme.css
@@ -9,9 +9,6 @@
 .node--unpublished {
   background-color: #fff4f4;
 }
-.node--preview {
-  background-color: #ffffea;
-}
 
 /**
  * Markup generated by theme_tablesort_indicator().
diff --git a/core/modules/system/src/Tests/Database/RegressionTest.php b/core/modules/system/src/Tests/Database/RegressionTest.php
index 0836572d1ef690dccd1a0163c36d1bc5157a3e37..81f962d0da1e29c635bc1661a37c704f7c434cf0 100644
--- a/core/modules/system/src/Tests/Database/RegressionTest.php
+++ b/core/modules/system/src/Tests/Database/RegressionTest.php
@@ -19,7 +19,7 @@ class RegressionTest extends DatabaseTestBase {
    *
    * @var array
    */
-  public static $modules = array('node');
+  public static $modules = array('node', 'user');
 
   /**
    * Ensures that non-ASCII UTF-8 data is stored in the database properly.
diff --git a/core/modules/system/src/Tests/Entity/EntityTypedDataDefinitionTest.php b/core/modules/system/src/Tests/Entity/EntityTypedDataDefinitionTest.php
index 82848ac8fcb39d12bf98f608ba5fe9784b6dc608..23318a0d26b3cc3679ef458c45c4b26638fbde49 100644
--- a/core/modules/system/src/Tests/Entity/EntityTypedDataDefinitionTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityTypedDataDefinitionTest.php
@@ -36,7 +36,7 @@ class EntityTypedDataDefinitionTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('filter', 'text', 'node');
+  public static $modules = array('filter', 'text', 'node', 'user');
 
   protected function setUp() {
     parent::setup();
diff --git a/core/modules/taxonomy/src/Tests/TermTest.php b/core/modules/taxonomy/src/Tests/TermTest.php
index 4f7c58dcfd1b8205c439d7619f3940dcabe31eca..b2e9757a2c6bfecd1fd7c9d7c15db1c91b3854f1 100644
--- a/core/modules/taxonomy/src/Tests/TermTest.php
+++ b/core/modules/taxonomy/src/Tests/TermTest.php
@@ -184,9 +184,9 @@ function testTaxonomyNode() {
 
     // Preview the node.
     $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
-    $this->assertNoUniqueText($term2->getName(), 'Term is displayed when previewing the node.');
-    $this->drupalPostForm(NULL, NULL, t('Preview'));
-    $this->assertNoUniqueText($term2->getName(), 'Term is displayed when previewing the node again.');
+    $this->assertUniqueText($term2->getName(), 'Term is displayed when previewing the node.');
+    $this->drupalPostForm('node/' . $node->id() . '/edit', NULL, t('Preview'));
+    $this->assertUniqueText($term2->getName(), 'Term is displayed when previewing the node again.');
   }
 
   /**
diff --git a/core/themes/bartik/css/style.css b/core/themes/bartik/css/style.css
index c866079977f17f194cc03c7b3cd35e178c3aa4c0..71772b367083751f823f2001cbe310f7ffbeae72 100644
--- a/core/themes/bartik/css/style.css
+++ b/core/themes/bartik/css/style.css
@@ -847,6 +847,48 @@ ul.links {
   border-left: 1px solid #fff4f4;
   border-right: 1px solid #fff4f4;
 }
+.node-preview-container {
+  background: #d1e8f5;
+  background-image: linear-gradient(to bottom, #d1e8f5, #d3e8f4);
+  font-family: Arial, sans-serif;
+  box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.3333);
+  position: fixed;
+  z-index: 499;
+  width: 100%;
+  padding: 10px;
+}
+.node-preview-backlink {
+  background-color: #419ff1;
+  background: url(../../../misc/icons/000000/chevron-left.svg) left no-repeat, linear-gradient(to bottom, #419ff1, #1076d5);
+  border: 1px solid #0048c8;
+  border-radius: .4em;
+  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .4);
+  color: #fff;
+  font-size: 0.9em;
+  line-height: normal;
+  margin: 0;
+  padding: 4px 1em 4px 0.6em;
+  text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.5);
+}
+.node-preview-backlink:focus,
+.node-preview-backlink:hover {
+  background-color: #419cf1;
+  background: url(../../../misc/icons/000000/chevron-left.svg) left no-repeat, linear-gradient(to bottom, #59abf3, #2a90ef);
+  border: 1px solid #0048c8;
+  text-decoration: none;
+  color: #fff;
+}
+.node-preview-backlink:active {
+  background-color: #0e69be;
+  background: url(../../../misc/icons/000000/chevron-left.svg) left no-repeat, linear-gradient(to bottom, #0e69be, #2a93ef);
+  border: 1px solid #0048c8;
+  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .25);
+}
+.node-preview-backlink::before {
+  content: '';
+  width: 10px;
+  display: inline-block;
+}
 
 /* ----------------- Comments ----------------- */