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
| Approach | When Content Is Built |
|---|---|
| Normal render array | During page build |
| Lazy Builder | After 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.
| Context | Impact |
|---|---|
| user | one cache per user |
| user.roles | one cache per role set |
| languages | one cache per language |
Senior rule:
Use
user.roleswhen possible instead ofuser.
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:
- Confirm callback executes.
- Verify cache metadata.
- Check placeholder replacement.
- 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
usercontext 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
- What is a Lazy Builder?
- How does it differ from normal rendering?
- Why is
#create_placeholderimportant? - How do Lazy Builders work with BigPipe?
- What cache metadata should the callback return?
- 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.