Enabling BigPipe for progressive page loadingfor Drupal 8 , 9 , 10 , and 11

Last updated :  

Imagine a restaurant that refuses to bring out the appetizer until the dessert is also fully plated — even though the appetizer's been ready for ten minutes. That's what an uncached page with one slow, personalized element looks like: the whole response waits on the slowest piece, even though 95% of the page was ready instantly. BigPipe is Drupal's fix — it sends the fast parts immediately and streams the slow parts in afterward, in the same response.

What you'll learn in this lesson

  • What problem BigPipe actually solves, and what it doesn't
  • How to mark a render array as a BigPipe placeholder
  • Why BigPipe needs JavaScript, and what happens without it

The problem: one slow, uncacheable block ruins the whole page

Picture a page that's almost entirely cacheable — a node's content, a cached menu, a cached sidebar — except for one small "5 friends are also viewing this" block that has to run a fresh, uncacheable query on every load. Without BigPipe, that one block's query time gets added to the total time-to-first-byte for the entire page, even though everything else was ready to go instantly.

Marking a placeholder

$build['viewers_now'] = [
  '#lazy_builder' => ['my_module.viewers_service:build', [$node->id()]],
  '#create_placeholder' => TRUE,
];

#lazy_builder tells Drupal "don't compute this now — call this service method later, when it's time to fill in the placeholder." #create_placeholder is what actually opts the element into BigPipe's streaming behavior rather than just deferring it within the same synchronous render. Drupal core already uses this exact mechanism for things like personalized "you are logged in as" links in the admin toolbar.

How the streaming actually works

BigPipe sends the initial HTML immediately, with an empty placeholder `<span>` where the slow content will go, then keeps the HTTP connection open and streams a small inline `<script>` tag afterward that fills the placeholder in once the slow content is ready — all within the same response, no extra round-trip. The visitor sees the fast 95% of the page instantly, and watches the slow piece pop in a moment later, rather than staring at a blank white screen while everything waits together.

BigPipe requires JavaScript to fill in the placeholders. For visitors without JS (rare, but real — some crawlers, some accessibility tools), Drupal falls back to rendering the placeholder's content synchronously, the old slow-but-complete way. This is why BigPipe is a genuine performance layer, not a correctness requirement — the page always eventually shows the right content either way.

Quick check: would BigPipe help a page whose *entire* content is slow and uncacheable, with nothing fast to show first? (Not really — BigPipe's benefit comes from separating fast, ready content from slow placeholders so the fast part isn't held hostage. If everything is slow, there's no fast part to send early, and you're better off addressing why the underlying content is slow to compute at all.)

Key takeaways

  • BigPipe solves "one slow, uncacheable element blocking an otherwise-fast page," not general slowness.
  • #lazy_builder plus #create_placeholder is the render array API that opts an element into BigPipe streaming.
  • It degrades gracefully without JavaScript, falling back to synchronous rendering rather than breaking the page.

Coming up next

Next, we look at how Drupal organizes cached data itself into separate cache bins, and how tuning where different kinds of data live can meaningfully improve performance on a busy site.