Caching System - Lazy Builders

Lazy Builders in Drupal defer the rendering of expensive or personalized content until after the main page is built. They return placeholders that are replaced later, often using BigPipe. This allows pages to remain highly cacheable while still delivering dynamic content efficiently.

Lazy Builders are one of the most powerful performance features in Drupal.

They allow Drupal to delay building expensive or personalized content until the last possible moment.

Think of it like this:

Instead of preparing every part of the page up front, Drupal builds the easy parts first and waits to build the slow or user-specific parts only when needed.

Lazy Builders are commonly used for:

  • personalized greetings
  • shopping carts
  • notifications
  • dashboards
  • expensive API calls

They are a core building block behind:

  • BigPipe
  • placeholders
  • high-performance authenticated pages

Core Concept

A Lazy Builder is a callback that Drupal executes later.

Basic flow:

Build Page
   ↓
Insert Placeholder
   ↓
Send Cached Page
   ↓
Execute Lazy Builder Callback
   ↓
Replace Placeholder with Final HTML

This keeps the page cacheable while still allowing dynamic content.


Why Lazy Builders Matter

Without Lazy Builders:

  • personalized blocks may disable caching
  • expensive logic runs on every request
  • authenticated pages become slow

With Lazy Builders:

  • page shell remains cached
  • dynamic parts are rendered separately
  • users still receive personalized content

Real Project Example (Enterprise Dashboard)

A government dashboard displayed:

  • welcome message
  • unread notifications
  • recent submissions

The welcome block depended on the current user.

Without Lazy Builder:

  • Dynamic Page Cache became ineffective.

With Lazy Builder:

  • page shell was cached
  • personalized greeting rendered separately
  • page load improved significantly

Lazy Builder Architecture

Controller
   ↓
Render Array
   ↓
#lazy_builder callback
   ↓
Placeholder
   ↓
BigPipe / Render Pipeline
   ↓
Final HTML

Basic Example

$build['welcome'] = [
  '#lazy_builder' => [
    'my_module.lazy_builder:buildGreeting',
    [$uid],
  ],
  '#create_placeholder' => TRUE,
];

Callback method:

public function buildGreeting($uid) {
  return [
    '#markup' => 'Welcome back!',
    '#cache' => [
      'contexts' => ['user'],
    ],
  ];
}

Important Rule: Callback Must Return a Render Array

Lazy Builder callbacks should:

  • return a render array
  • include correct cache metadata
  • avoid direct HTML strings when possible

Lazy Builders and BigPipe

Lazy Builders work best with BigPipe.

Page Shell Sent Immediately
   ↓
Placeholder Appears
   ↓
Lazy Builder Executes
   ↓
BigPipe Streams Final Content

This creates a fast, progressive experience.


Lazy Builders vs Regular Rendering

ApproachWhen Content Is Built
Normal render arrayDuring page build
Lazy BuilderAfter page shell is generated

Decision Framework

Use Lazy Builders when:

  • content is user-specific
  • content is expensive to generate
  • page should remain cacheable

Avoid when:

  • content is static
  • rendering is very lightweight

Cache Metadata Still Required

Lazy Builder output still needs:

  • cache tags
  • cache contexts
  • max-age

Example:

'#cache' => [
  'contexts' => ['user.roles'],
  'max-age' => 3600,
]

If cache metadata is missing, Drupal may cache incorrectly.


Security Considerations

Only scalar arguments should be passed to lazy builders.

Good:

[$uid, $nid]

Avoid:

  • passing service objects
  • passing entity objects

This keeps placeholders serializable and secure.


Cache Context Granularity

Use the most efficient cache context possible.

ContextImpact
userone cache per user
user.rolesone cache per role set
languagesone cache per language

Senior rule:

Use user.roles when possible instead of user.


Platform / DevOps Layer

Lazy Builders integrate with:

  • Render Cache
  • Dynamic Page Cache
  • BigPipe
  • Redis
  • CDN

Flow:

Redis stores cached fragments
   ↓
BigPipe streams placeholders
   ↓
CDN delivers page globally

Debugging Lazy Builders

Check page source for:

  • data-big-pipe-placeholder-id

Useful tools:

  • Webprofiler
  • Kint / dpm()
  • browser Network tab

Common debugging steps:

  1. Confirm callback executes.
  2. Verify cache metadata.
  3. Check placeholder replacement.
  4. Inspect response headers.

Performance Considerations

Benefits:

  • preserves page caching
  • isolates expensive logic
  • improves perceived performance

Best practices:

  • keep callbacks focused
  • cache callback output
  • use placeholders only where needed

Common Production Issues

  • forgetting #create_placeholder = TRUE
  • callback returns invalid structure
  • missing cache metadata
  • passing non-serializable arguments
  • using user context unnecessarily

SEO & Accessibility Considerations

Lazy Builders do not harm SEO because final HTML is still delivered.

They improve accessibility by reducing time to first meaningful content.


AI & Future Integration

Lazy Builders are ideal for:

  • AI recommendations
  • semantic search suggestions
  • personalized insights

Example:

  • page loads instantly
  • AI recommendations stream afterward

  1. What is a Lazy Builder?
  2. How does it differ from normal rendering?
  3. Why is #create_placeholder important?
  4. How do Lazy Builders work with BigPipe?
  5. What cache metadata should the callback return?
  6. What types of arguments are safe to pass?

Memory Trick

Lazy Builder = Build Later
Placeholder = Temporary Marker
BigPipe = Stream Result
Cache Metadata = Still Required

Key Takeaway

If you need personalized or expensive content but want to keep the page cacheable:

Use a Lazy Builder.

It is one of the most important patterns in modern Drupal performance architecture.