diff --git a/core/core.libraries.yml b/core/core.libraries.yml
index 3a6906e..6d71ff0 100644
--- a/core/core.libraries.yml
+++ b/core/core.libraries.yml
@@ -121,6 +121,15 @@ drupal.batch:
     - core/drupal.progress
     - core/jquery.once
 
+drupal.big_pipe:
+  version: VERSION
+  js:
+    misc/big_pipe.js: {}
+  drupalSettings:
+    bigPipePlaceholders: []
+  dependencies:
+    - core/drupal.ajax
+
 drupal.collapse:
   version: VERSION
   js:
diff --git a/core/core.services.yml b/core/core.services.yml
index 0fe0a7f..e9d4742 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -869,7 +869,7 @@ services:
       - { name: event_subscriber }
   main_content_renderer.html:
     class: Drupal\Core\Render\MainContent\HtmlRenderer
-    arguments: ['@title_resolver', '@plugin.manager.display_variant', '@event_dispatcher', '@module_handler', '@renderer', '@render_cache']
+    arguments: ['@title_resolver', '@plugin.manager.display_variant', '@event_dispatcher', '@module_handler', '@renderer', '@render_cache', '@render_strategy_manager']
     tags:
       - { name: render.main_content_renderer, format: html }
   main_content_renderer.ajax:
@@ -1392,6 +1392,26 @@ services:
   renderer:
     class: Drupal\Core\Render\Renderer
     arguments: ['@controller_resolver', '@theme.manager', '@plugin.manager.element_info', '@render_cache', '%renderer.config%']
+  # Render strategies for rendering placeholders.
+  render_strategy_manager:
+    class: Drupal\Core\Render\Strategy\RenderStrategyManager
+    arguments: ['@renderer']
+    tags:
+      - { name: service_collector, tag: render_strategy, call: addRenderStrategy }
+    lazy: true
+  render_strategy.single_flush:
+    class: Drupal\Core\Render\Strategy\SingleFlushRenderStrategy
+    tags:
+      - { name: render_strategy, priority: -1000 }
+  render_strategy.big_pipe:
+    class: Drupal\Core\Render\Strategy\BigPipeRenderStrategy
+    arguments: ['@big_pipe']
+    tags:
+      - { name: render_strategy, priority: 0 }
+  big_pipe:
+    class: Drupal\Core\Render\BigPipe
+    arguments: ['@renderer', '@ajax_response.attachments_processor']
+    lazy: true
   email.validator:
     class: Egulias\EmailValidator\EmailValidator
 
diff --git a/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php b/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php
index 7d36b51..ee7e827 100644
--- a/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php
+++ b/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php
@@ -105,16 +105,20 @@ public function __construct(AssetResolverInterface $asset_resolver, ConfigFactor
   /**
    * {@inheritdoc}
    */
-  public function processAttachments(AttachmentsInterface $response) {
+  public function processAttachments(AttachmentsInterface $response, $ajax_page_state = NULL) {
     // @todo Convert to assertion once https://www.drupal.org/node/2408013 lands
     if (!$response instanceof AjaxResponse) {
       throw new \InvalidArgumentException('\Drupal\Core\Ajax\AjaxResponse instance expected.');
     }
 
-    $request = $this->requestStack->getCurrentRequest();
-
     if ($response->getContent() == '{}') {
-      $response->setData($this->buildAttachmentsCommands($response, $request));
+      if (!isset($ajax_page_state)) {
+        $request = $this->requestStack->getCurrentRequest();
+        // @todo below line is broken, one ->request too much.
+        $ajax_page_state = $request->request->get('ajax_page_state');
+      }
+
+      $response->setData($this->buildAttachmentsCommands($response, $ajax_page_state));
     }
 
     return $response;
@@ -125,14 +129,13 @@ public function processAttachments(AttachmentsInterface $response) {
    *
    * @param \Drupal\Core\Ajax\AjaxResponse $response
    *   The AJAX response to update.
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The request object that the AJAX is responding to.
+   * @param array $ajax_page_state
+   *   The ajax page state of the current state.
    *
    * @return array
    *   An array of commands ready to be returned as JSON.
    */
-  protected function buildAttachmentsCommands(AjaxResponse $response, Request $request) {
-    $ajax_page_state = $request->request->get('ajax_page_state');
+  protected function buildAttachmentsCommands(AjaxResponse $response, array $ajax_page_state) {
 
     // Aggregate CSS/JS if necessary, but only during normal site operation.
     $optimize_css = !defined('MAINTENANCE_MODE') && $this->config->get('css.preprocess');
diff --git a/core/lib/Drupal/Core/Render/BigPipe.php b/core/lib/Drupal/Core/Render/BigPipe.php
new file mode 100644
index 0000000..8115869
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/BigPipe.php
@@ -0,0 +1,130 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Render\BigPipe.
+ */
+
+namespace Drupal\Core\Render;
+
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\ReplaceCommand;
+
+/**
+ * A class that allows sending the main content first, then replace
+ * placeholders to send the rest using Javascript replacements.
+ */
+class BigPipe implements BigPipeInterface {
+
+  /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * The AJAX response attachments processor service.
+   *
+   * @var \Drupal\Core\Render\AttachmentsResponseProcessorInterface
+   */
+  protected $ajaxResponseAttachmentsProcessor;
+
+  /**
+   * Constructs a new BigPipe class.
+   *
+   * @param \Drupal\Core\Render\RendererInterface
+   *   The renderer.
+   * @param \Drupal\Core\Render\AttachmentsResponseProcessorInterface $ajax_response_attachments_processor
+   *   The AJAX response attachments processor service.
+   */
+  public function __construct(RendererInterface $renderer, AttachmentsResponseProcessorInterface $ajax_response_attachments_processor) {
+    $this->renderer = $renderer;
+    $this->ajaxResponseAttachmentsProcessor = $ajax_response_attachments_processor;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function sendContent($content, array $placeholders) {
+    // Split it up in various chunks.
+    $split = '<!-- X-RENDER-CACHE-BIG-PIPE-SPLIT -->';
+    if (strpos($content, $split) === FALSE) {
+      $split = '</body>';
+    }
+    $page_parts = explode($split, $content);
+
+    if (count($page_parts) !== 2) {
+      throw new \LogicException("You need to have only one body or one <!-- X-RENDER-CACHE-BIG-PIPE-SPLIT --> tag in your html.html.twig template file.");
+    }
+
+    // Support streaming on NGINX + php-fpm (nginx >= 1.5.6).
+    header('X-Accel-Buffering: no');
+
+    print $page_parts[0];
+
+    // Print the start signal.
+    print '  <script type="application/json" data-big-pipe-start="1"></script>' . "\n";
+
+    ob_end_flush();
+    flush();
+
+    ksort($placeholders);
+
+    foreach ($placeholders as $placeholder => $placeholder_elements) {
+      // Check if the placeholder is present at all.
+      if (strpos($content, $placeholder) === FALSE) {
+        continue;
+      }
+
+      // Create elements to process in right format.
+      $elements = [
+        '#markup' => $placeholder,
+        '#attached' => [
+          'placeholders' => [
+            $placeholder => $placeholder_elements,
+          ],
+        ],
+      ];
+
+      $elements = $this->renderer->renderPlaceholder($placeholder, $elements);
+
+      // @todo Get ajax page state of the response.
+      $ajax_page_state = [
+       'libraries' => 'bartik/global-styling,classy/base,core/drupal.big_pipe,core/html5shiv,system/base',
+      ];
+
+      // Create a new AjaxResponse.
+      $response = new AjaxResponse();
+      $response->addCommand(new ReplaceCommand(sprintf('[data-big-pipe-selector="%s"]', $placeholder), $elements['#markup']));
+      $response->setAttachments($elements['#attached']);
+
+      $this->ajaxResponseAttachmentsProcessor->processAttachments($response, $ajax_page_state);
+
+      // @todo Filter response.
+      $json = $response->getContent();
+
+      $output = <<<EOF
+  <script type="application/json" data-big-pipe-placeholder="$placeholder" data-drupal-ajax-processor="big_pipe">
+  $json
+  </script>
+
+EOF;
+      print $output;
+
+      flush();
+    }
+
+    // Send the stop signal.
+    print '  <script type="application/json" data-big-pipe-stop="1"></script>' . "\n";
+
+    // Now that we have processed all the placeholders, attach the behaviors
+    // on the page again.
+    print $behaviors;
+
+    print $split;
+    print $page_parts[1];
+
+    return $this;
+  }
+}
diff --git a/core/lib/Drupal/Core/Render/BigPipeInterface.php b/core/lib/Drupal/Core/Render/BigPipeInterface.php
new file mode 100644
index 0000000..884b294
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/BigPipeInterface.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Render\BigPipeInterface.
+ */
+
+namespace Drupal\Core\Render;
+
+/**
+ * An interface that allows sending the main content first, then replace
+ * placeholders to send the rest using Javascript replacements.
+ */
+interface BigPipeInterface {
+
+  /**
+   * Sends the content to the browser, splitting before the closing </body> tag
+   * and afterwards processes placeholders to send when they have been rendered.
+   *
+   * The output buffers are flushed in between.
+   *
+   * @param array $placeholders
+   *   The placeholders to process.
+   * @param string $content
+   *   The content to send.
+   */
+  public function sendContent($content, array $placeholders);
+
+}
diff --git a/core/lib/Drupal/Core/Render/BigPipeResponse.php b/core/lib/Drupal/Core/Render/BigPipeResponse.php
new file mode 100644
index 0000000..bf82bef
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/BigPipeResponse.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Render\BigPipeResponse.
+ */
+
+namespace Drupal\Core\Render;
+
+/**
+ * A response that allows to send placeholders after the main content has been
+ * send.
+ */
+class BigPipeResponse extends HtmlResponse {
+
+  /**
+   * An array of placeholders to process.
+   *
+   * @var array
+   */
+  protected $bigPipePlaceholders;
+
+  /**
+   * The BigPipe service.
+   *
+   * @var \Drupal\Core\Render\BigPipeInterface
+   */
+  protected $bigPipe;
+
+  /**
+   * Sets the big pipe placeholders to process.
+   *
+   * @param array $placeholders
+   *   The placeholders to process.
+   */
+  public function setBigPipePlaceholders(array $placeholders) {
+    $this->bigPipePlaceholders = $placeholders;
+  }
+
+  /**
+   * Sets the big pipe service to use.
+   *
+   * @param \Drupal\Core\Render\BigPipeInterface $big_pipe
+   *   The BigPipe service.
+   */
+  public function setBigPipeService(BigPipeInterface $big_pipe) {
+    $this->bigPipe = $big_pipe;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function sendContent() {
+    $this->bigPipe->sendContent($this->content, $this->bigPipePlaceholders);
+
+    return $this;
+  }
+}
diff --git a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
index cb987f4..12d5a2a 100644
--- a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
+++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
@@ -11,11 +11,13 @@
 use Drupal\Core\Controller\TitleResolverInterface;
 use Drupal\Core\Display\PageVariantInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Render\BigPipeResponse;
 use Drupal\Core\Render\HtmlResponse;
 use Drupal\Core\Render\PageDisplayVariantSelectionEvent;
 use Drupal\Core\Render\RenderCacheInterface;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Render\RenderEvents;
+use Drupal\Core\Render\Strategy\RenderStrategyManagerInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -74,6 +76,13 @@ class HtmlRenderer implements MainContentRendererInterface {
   protected $renderCache;
 
   /**
+   * The render strategy manager service.
+   *
+   * @var \Drupal\Core\Render\Strategy\RenderStrategyManager
+   */
+  protected $renderStrategyManager;
+
+  /**
    * Constructs a new HtmlRenderer.
    *
    * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver
@@ -89,13 +98,14 @@ class HtmlRenderer implements MainContentRendererInterface {
    * @param \Drupal\Core\Render\RenderCacheInterface $render_cache
    *   The render cache service.
    */
-  public function __construct(TitleResolverInterface $title_resolver, PluginManagerInterface $display_variant_manager, EventDispatcherInterface $event_dispatcher, ModuleHandlerInterface $module_handler, RendererInterface $renderer, RenderCacheInterface $render_cache) {
+  public function __construct(TitleResolverInterface $title_resolver, PluginManagerInterface $display_variant_manager, EventDispatcherInterface $event_dispatcher, ModuleHandlerInterface $module_handler, RendererInterface $renderer, RenderCacheInterface $render_cache, RenderStrategyManagerInterface $render_strategy_manager) {
     $this->titleResolver = $title_resolver;
     $this->displayVariantManager = $display_variant_manager;
     $this->eventDispatcher = $event_dispatcher;
     $this->moduleHandler = $module_handler;
     $this->renderer = $renderer;
     $this->renderCache = $render_cache;
+    $this->renderStrategyManager = $render_strategy_manager;
   }
 
   /**
@@ -124,18 +134,38 @@ public function renderResponse(array $main_content, Request $request, RouteMatch
     // page.html.twig, hence add them here, just before rendering html.html.twig.
     $this->buildPageTopAndBottom($html);
 
-    // @todo https://www.drupal.org/node/2495001 Make renderRoot return a
-    //       cacheable render array directly.
-    $this->renderer->renderRoot($html);
+    // This uses render() instead of renderRoot() to ensure that placeholders
+    // are not replaced.
+    $this->renderer->render($html);
     $content = $this->renderCache->getCacheableRenderArray($html);
 
+    $this->renderStrategyManager->renderPlaceholders($content);
+
     // Also associate the "rendered" cache tag. This allows us to invalidate the
     // entire render cache, regardless of the cache bin.
     $content['#cache']['tags'][] = 'rendered';
 
-    $response = new HtmlResponse($content, 200, [
-      'Content-Type' => 'text/html; charset=UTF-8',
-    ]);
+
+    if (!empty($content['#attached']['big_pipe_placeholders'])) {
+      $response = new BigPipeResponse('', 200, [
+        'Content-Type' => 'text/html; charset=UTF-8',
+      ]);
+
+      // Inject the placeholders and service into the response.
+      $response->setBigPipePlaceholders($content['#attached']['big_pipe_placeholders']);
+      $response->setBigPipeService(reset($content['#attached']['big_pipe_service']));
+
+      unset($content['#attached']['big_pipe_placeholders']);
+      unset($content['#attached']['big_pipe_service']);
+
+      // Now after all pre-processing finally set the content.
+      $response->setContent($content);
+    }
+    else {
+      $response = new HtmlResponse($content, 200, [
+        'Content-Type' => 'text/html; charset=UTF-8',
+      ]);
+    }
 
     return $response;
   }
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index d24b7d7..5499919 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -118,10 +118,8 @@ public function renderPlain(&$elements) {
    *   The updated $elements.
    *
    * @see ::replacePlaceholders()
-   *
-   * @todo Make public as part of https://www.drupal.org/node/2469431
    */
-  protected function renderPlaceholder($placeholder, array $elements) {
+  public function renderPlaceholder($placeholder, array $elements) {
     // Get the render array for the given placeholder
     $placeholder_elements = $elements['#attached']['placeholders'][$placeholder];
 
@@ -211,12 +209,6 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
       $cached_element = $this->renderCache->get($elements);
       if ($cached_element !== FALSE) {
         $elements = $cached_element;
-        // Only when we're in a root (non-recursive) Renderer::render() call,
-        // placeholders must be processed, to prevent breaking the render cache
-        // in case of nested elements with #cache set.
-        if ($is_root_call) {
-          $this->replacePlaceholders($elements);
-        }
         // Mark the element markup as safe. If we have cached children, we need
         // to mark them as safe too. The parent markup contains the child
         // markup, so if the parent markup is safe, then the markup of the
@@ -233,6 +225,14 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
         // Render cache hit, so rendering is finished, all necessary info
         // collected!
         $this->bubbleStack();
+
+        // Only when we're in a root (non-recursive) Renderer::render() call,
+        // placeholders must be processed, to prevent breaking the render cache
+        // in case of nested elements with #cache set.
+        if ($is_root_call) {
+          $this->replacePlaceholders($elements);
+        }
+
         return $elements['#markup'];
       }
     }
@@ -470,6 +470,15 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
       $this->renderCache->set($elements, $pre_bubbling_elements);
     }
 
+    if ($is_root_call) {
+      if (static::$stack->count() !== 1) {
+        throw new \LogicException('A stray drupal_render() invocation with $is_root_call = TRUE is causing bubbling of attached assets to break.');
+      }
+    }
+
+    // Rendering is finished, all necessary info collected!
+    $this->bubbleStack();
+
     // Only when we're in a root (non-recursive) Renderer::render() call,
     // placeholders must be processed, to prevent breaking the render cache in
     // case of nested elements with #cache set.
@@ -481,14 +490,8 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
     // that is handled earlier in Renderer::render().
     if ($is_root_call) {
       $this->replacePlaceholders($elements);
-      if (static::$stack->count() !== 1) {
-        throw new \LogicException('A stray drupal_render() invocation with $is_root_call = TRUE is causing bubbling of attached assets to break.');
-      }
     }
 
-    // Rendering is finished, all necessary info collected!
-    $this->bubbleStack();
-
     $elements['#printed'] = TRUE;
     $elements['#markup'] = SafeMarkup::set($elements['#markup']);
     return $elements['#markup'];
diff --git a/core/lib/Drupal/Core/Render/Strategy/BigPipeRenderStrategy.php b/core/lib/Drupal/Core/Render/Strategy/BigPipeRenderStrategy.php
new file mode 100644
index 0000000..67608c2
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Strategy/BigPipeRenderStrategy.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Render\Strategy\BigPipeRenderStrategy
+ */
+
+namespace Drupal\Core\Render\Strategy;
+
+use Drupal\Component\Utility\Crypt;
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Render\BigPipeInterface;
+
+/**
+ * Defines the 'big_pipe' render strategy.
+ *
+ * This is the last strategy that always replaces all remaining placeholders.
+ */
+class BigPipeRenderStrategy implements RenderStrategyInterface {
+
+  /**
+   * The BigPipe service.
+   *
+   * @var object
+   */
+  protected $bigPipe;
+
+  /**
+   * Constructs a BigPipeRenderStrategy class.
+   *
+   * @param \Drupal\Core\Render\BigPipeInterface $big_pipe
+   *   The BigPipe service.
+   */
+  public function __construct(BigPipeInterface $big_pipe) {
+    $this->bigPipe = $big_pipe;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processPlaceholders(array $placeholders) {
+    $return = [];
+
+    // @todo Only do this for authorized users.
+
+    // Ensure placeholders are unique per page.
+    $token = Crypt::randomBytesBase64(55);
+
+    foreach ($placeholders as $placeholder => $placeholder_elements) {
+      $html_placeholder = Html::getId($placeholder . '-' . $token);
+      $return[$placeholder] = [
+         '#markup' => '<div data-big-pipe-selector="' . $html_placeholder . '"></div>',
+         // Big Pipe placeholders are not cacheable.
+         '#cache' => [
+           'max-age' => 0,
+         ],
+         '#attached' => [
+           // Use the big_pipe library.
+           'library' => [
+             'core/drupal.big_pipe',
+           ],
+           // Add the placeholder to a white list of JS processed placeholders.
+           'drupalSettings' => [
+             'bigPipePlaceholders' => [ $html_placeholder => TRUE ],
+           ],
+           'big_pipe_placeholders' => [
+             $html_placeholder => $placeholder_elements,
+           ],
+           'big_pipe_service' => [ $this->bigPipe ],
+         ],
+      ];
+    }
+
+    return $return;
+  }
+}
diff --git a/core/lib/Drupal/Core/Render/Strategy/RenderStrategyInterface.php b/core/lib/Drupal/Core/Render/Strategy/RenderStrategyInterface.php
new file mode 100644
index 0000000..e6abbd5
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Strategy/RenderStrategyInterface.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Render\Strategy\RenderStrategyInterface.
+ */
+
+namespace Drupal\Core\Render\Strategy;
+
+/**
+ * Provides an interface for defining a render strategy service.
+ */
+interface RenderStrategyInterface {
+
+  /**
+   * Processes placeholders to render them with different strategies.
+   *
+   * @param array $placeholders
+   *   The placeholders to process keyed by $placeholder_id with the render
+   *   cache array as value.
+   *
+   * @return array $placeholders
+   *   The new values for the placeholders, keyed by $placeholder_id => $render_array.
+   */
+  public function processPlaceholders(array $placeholders);
+
+}
diff --git a/core/lib/Drupal/Core/Render/Strategy/RenderStrategyManager.php b/core/lib/Drupal/Core/Render/Strategy/RenderStrategyManager.php
new file mode 100644
index 0000000..4bc05e0
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Strategy/RenderStrategyManager.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Render\Strategy\RenderStrategyManager.
+ */
+
+namespace Drupal\Core\Render\Strategy;
+
+use Drupal\Core\Render\RendererInterface;
+
+/**
+ * Provides a class which allows to render placeholders.
+ */
+class RenderStrategyManager implements RenderStrategyManagerInterface {
+
+  /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * The RenderStrategy services.
+   *
+   * @var Drupal\Core\Render\Strategy\RenderStrategyInterface[]
+   */
+  protected $renderStrategies;
+
+  /**
+   * Constructs a new RenderStrategyManager class.
+   *
+   * @param \Drupal\Core\Render\RendererInterface
+   *   The renderer.
+   */
+  public function __construct(RendererInterface $renderer) {
+    $this->renderer = $renderer;
+  }
+
+  /**
+   * Adds a render strategy to process.
+   *
+   * @param RenderStrategyInterface $strategy
+   *   The strategy to add to the render strategies.
+   */
+  public function addRenderStrategy(RenderStrategyInterface $strategy) {
+    $this->renderStrategies[] = $strategy;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function renderPlaceholders(&$elements) {
+    if (!isset($elements['#attached']['placeholders']) || empty($elements['#attached']['placeholders'])) {
+      return FALSE;
+    }
+
+    $new_placeholders = [];
+    $placeholders = $elements['#attached']['placeholders'];
+
+    // Give each render strategy a chance to replace all not-yet replaced
+    // placeholders.
+    foreach ($this->renderStrategies as $strategy) {
+      $processed_placeholders = $strategy->processPlaceholders($placeholders);
+      $placeholders = array_diff_key($placeholders, $processed_placeholders);
+      $new_placeholders += $processed_placeholders;
+
+      if (empty($placeholders)) {
+        break;
+      }
+    }
+
+    // Now render the new placeholders.
+    $elements['#attached']['placeholders'] = $new_placeholders;
+    foreach (array_keys($elements['#attached']['placeholders']) as $placeholder) {
+      $elements = $this->renderer->renderPlaceholder($placeholder, $elements);
+    }
+    unset($elements['#attached']['placeholders']);
+
+    return TRUE;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Render/Strategy/RenderStrategyManagerInterface.php b/core/lib/Drupal/Core/Render/Strategy/RenderStrategyManagerInterface.php
new file mode 100644
index 0000000..e7c0385
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Strategy/RenderStrategyManagerInterface.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Render\Strategy\RenderStrategyManagerInterface.
+ */
+
+namespace Drupal\Core\Render\Strategy;
+
+/**
+ * Provides an interface which allows to render placeholders.
+ */
+interface RenderStrategyManagerInterface {
+
+  /**
+   * Renders placeholders with different strategies.
+   *
+   * @param array $elements
+   *   The render cache array containing #markup and #attached placeholders.
+   *
+   * @return bool
+   *   TRUE when placeholders have been replaced, FALSE otherwise.
+   */
+  public function renderPlaceholders(&$elements);
+
+}
diff --git a/core/lib/Drupal/Core/Render/Strategy/SingleFlushRenderStrategy.php b/core/lib/Drupal/Core/Render/Strategy/SingleFlushRenderStrategy.php
new file mode 100644
index 0000000..7bed91e
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Strategy/SingleFlushRenderStrategy.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Render\Strategy\SingleFlushRenderStrategy
+ */
+
+namespace Drupal\Core\Render\Strategy;
+
+/**
+ * Defines the 'single_flush' render strategy.
+ *
+ * This is the last strategy that always replaces all remaining placeholders.
+ */
+class SingleFlushRenderStrategy implements RenderStrategyInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processPlaceholders(array $placeholders) {
+    // Return all placeholders as is; they should be rendered directly.
+    return $placeholders;
+  }
+}
diff --git a/core/misc/big_pipe.js b/core/misc/big_pipe.js
new file mode 100644
index 0000000..65be516
--- /dev/null
+++ b/core/misc/big_pipe.js
@@ -0,0 +1,84 @@
+/**
+ * @file
+ * Provides Ajax page updating via BigPipe.
+ */
+
+(function ($, window, Drupal, drupalSettings) {
+
+  "use strict";
+
+  var interval = 100; // Check every 100 ms.
+  var maxWait = 10; // Wait for a maximum of 10 seconds.
+
+  // The internal ID to contain the watcher service.
+  var intervalID;
+
+  function BigPipeClearInterval() {
+    if (intervalID) {
+      clearInterval(intervalID);
+      intervalID = undefined;
+    }
+  }
+
+  function BigPipeProcessPlaceholders(context) {
+    // Process BigPipe inlined ajax responses.
+    $('[data-drupal-ajax-processor="big_pipe"]', context).each(function() {
+      var placeholder = $(this).data('big-pipe-placeholder');
+
+      // Ignore any placeholders that are not in the known placeholder list.
+      // This is used to avoid someone trying to XSS the site via the
+      // placeholdering mechanism.
+      if (typeof(drupalSettings.bigPipePlaceholders[placeholder]) !== 'undefined') {
+        var response = JSON.parse(this.textContent);
+        // Use a dummy url.
+        var ajaxObject = Drupal.ajax({url: 'big-pipe/placeholder.json'});
+        ajaxObject.success(response);
+      }
+      $(this).remove();
+    });
+
+    // Check for start signal to attachBehaviors.
+    $('[data-big-pipe-start="1"]', context).each(function() {
+      $(this).remove();
+      Drupal.attachBehaviors();
+    });
+
+    // Check for stop signal to stop checking.
+    $('[data-big-pipe-stop="1"]', context).each(function() {
+      $(this).remove();
+      BigPipeClearInterval();
+    });
+  }
+
+  $('body').each(function() {
+    if ($(this).data('big-pipe-processed') == true) {
+      return;
+    }
+
+    $(this).data('big-pipe-processed', true);
+
+    // Check for new BigPipe elements until we get the stop signal or the timeout occurs.
+    intervalID = setInterval(function() {
+      BigPipeProcessPlaceholders(document);
+    }, 100);
+
+    // Wait for a maxium of maxWait seconds.
+    setTimeout(BigPipeClearInterval, maxWait * 1000);
+  });
+
+  /**
+   * Defines the BigPipe behavior.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.BigPipe = {
+    attach: function (context, settings) {
+      if (!intervalID) {
+        // If we timeout before all data is loaded, try again once
+        // the regular ready() event fires.
+        BigPipeProcessPlaceholders(context);
+      }
+    }
+  };
+
+})(jQuery, this, Drupal, drupalSettings);
