Mahamudul Hasan Rubel
HomeBlogCoursesAboutProjectsSkillsExperiencePhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • Blog
  • Courses
  • About
  • Projects
  • Skills
  • Experience
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

Subscribe to the newsletter

Get new articles and course lessons delivered to your inbox. No spam, unsubscribe anytime.

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
Lesson 54 of the Advanced WordPress Plugin Engineering: Scale, Security & React UIs course
WordPressJune 28, 20264 min read

Dynamic Block Rendering: PHP, Performance, and Caching Strategies

Master Gutenberg dynamic block rendering in PHP. Learn to implement render_callback, optimize server-side performance, and cache output for high-traffic sites.

GutenbergPHPPerformanceWordPressBlock APICachingplugin-development

Previously in this course, we explored Block API v2 Essentials and how to manage complex editor experiences in Custom Gutenberg Block Controls. While those lessons focused on the React-based editor interface, this lesson shifts focus to the server. We will implement dynamic block rendering, ensuring your blocks remain performant even when they pull live data.

The Philosophy of Dynamic Blocks

In Gutenberg, a "static" block saves its HTML directly into the post content. A "dynamic" block, however, saves only its attributes. At render time, WordPress executes a PHP callback function to generate the HTML.

This architecture is essential for our Knowledge Base plugin. If we saved the list of "Latest Articles" as static HTML, that list would become stale the moment a new article is published. By using a render_callback, we ensure the block always displays fresh, live data while keeping the post content clean.

Implementing the render_callback

To make a block dynamic, you must define the render_callback property in your block.json. This tells WordPress to delegate the rendering to a specific PHP function instead of looking for a save() function in your JavaScript.

1. Define the block metadata

In your block.json, omit the save property and add render_callback:

JSON
{
    "name": "kb/latest-articles",
    "attributes": {
        "limit": { "type": "number", "default": 5 }
    },
    "render_callback": "KB\\Blocks\\render_latest_articles"
}

2. Implement the PHP Render Function

Your callback receives the block's attributes as an array. Use these to fetch data, but always wrap your logic in a performance-focused container.

PHP
namespace KB\Blocks;

function render_latest_articles(array $attributes): string {
    $limit = $attributes['limit'] ?? 5;
    $cache_key = 'kb_latest_articles_' . md5(serialize($attributes));
    
    #6A9955">// Attempt to retrieve from cache
    $output = wp_cache_get($cache_key, 'kb_blocks');
    
    if (false === $output) {
        $articles = get_posts([
            'post_type' => 'kb_article',
            'posts_per_page' => $limit,
        ]);

        ob_start();
        foreach ($articles as $post) {
            echo "<li><a href='" . esc_url(get_permalink($post)) . "'>" . esc_html($post->post_title) . "</a></li>";
        }
        $output = "<ul>" . ob_get_clean() . "</ul>";
        
        #6A9955">// Cache for 1 hour
        wp_cache_set($cache_key, $output, 'kb_blocks', HOUR_IN_SECONDS);
    }

    return $output;
}

Optimizing Server-Side Rendering (SSR)

Dynamic blocks execute on every page load. If your logic involves heavy database queries (like complex WP_Query arguments or custom SQL), you are essentially multiplying the load on your database.

Follow these rules for production-grade rendering:

  1. Cache the Output: As shown above, use wp_cache_set to store the generated HTML. Use a unique key based on the block attributes so that different configurations don't overwrite each other.
  2. Avoid Expensive Logic: Do not perform complex calculations inside the render function. If the data is difficult to retrieve, consider pre-fetching it into a dedicated option or a custom table during the save_post hook.
  3. Contextual Escaping: Always use wp_kses_post() or specific escaping functions on all dynamic output. Even if you trust the source data, the rendering pipeline must be bulletproof against injection.

Cache Invalidation Strategy

Caching is only useful if it eventually clears. In the Knowledge Base plugin, if an article is updated, your "Latest Articles" block should reflect that immediately.

Use the save_post hook to invalidate your specific block cache group:

PHP
add_action('save_post_kb_article', function() {
    #6A9955">// This clears all cached blocks in our group
    wp_cache_delete('kb_latest_articles', 'kb_blocks');
});

Note: If you need granular invalidation, store individual keys in a transient or an array option that you can purge selectively.

Hands-on Exercise

  1. Refactor: Take your current "Knowledge Base Article List" block. Move it from a static implementation to a dynamic one using a render_callback.
  2. Profile: Use a tool like Query Monitor to check the number of database queries triggered when rendering a page with five instances of your block.
  3. Cache: Implement the wp_cache_set pattern provided above and observe the drop in database queries on subsequent page loads.

Common Pitfalls

  • Missing render_callback: Forgetting to register the function in block.json will result in an empty block on the frontend.
  • Over-Caching: Don't cache blocks that contain user-specific information (e.g., "Welcome, [User Name]"). Always cache only the generic, public-facing parts of the HTML.
  • Ignoring esc_ functions: Since you are building the HTML string manually in PHP, it is incredibly easy to forget to escape the content. Use wp_kses_post on the final output string before returning it.
  • Infinite Loops: Be careful not to trigger get_posts or WP_Query inside the render function in a way that recurses if the post being queried contains the same block (though WordPress has internal protections, it's a structural risk).

Recap

  • Dynamic Blocks: Use render_callback to generate HTML at runtime, ensuring your plugin data is always current.
  • Performance: Treat every render as a potential performance bottleneck; minimize queries and maximize cache hits.
  • Integrity: Always escape your generated HTML. Treat the output of your render function as untrusted input.

By moving rendering to the server, we've successfully decoupled our UI from the static post content, allowing for a more modular and performant architecture.

Up next: We will dive into Advanced State Persistence, where we explore how to synchronize your block's complex internal states with local browser storage and server-side databases for a seamless UX.

Previous lessonBlock Transforms and DeprecationNext lesson Advanced State Persistence
Back to Blog

Similar Posts

WordPressJune 28, 20264 min read

Transient Caching Patterns: Tag-Based Invalidation and Multi-site Scale

Master enterprise-grade caching by implementing tag-based transient invalidation and multi-site synchronization to ensure your plugin scales under high load.

Read more
WordPressJune 27, 20264 min read

Query Caching Strategies: Scaling WordPress Plugins for High Traffic

Part of the course

Advanced WordPress Plugin Engineering: Scale, Security & React UIs

advanced · Lesson 54 of 56

  1. 1

    Modern PHP Standards for WordPress

    3 min
  2. 2

    Dependency Injection Basics

    3 min
  3. 3

    Architecting Service Providers

    3 min

Learn how to implement Transients and the Object Cache to slash database overhead in your custom WordPress tables, ensuring your plugins scale at speed.

Read more
WordPressJune 26, 20263 min read

Optimizing API Response Times: Caching for WordPress Plugins

Learn how to slash REST API response times in your WordPress plugins using object caching. Master cache invalidation and performance measurement strategies.

Read more
  • 4

    Advanced Custom Database Tables

    4 min
  • 5

    Data Access Objects Pattern

    3 min
  • 6

    Query Caching Strategies

    4 min
  • 7

    Database Indexing for Scale

    4 min
  • 8

    Sanitization Pipelines

    3 min
  • 9

    Output Escaping Patterns

    4 min
  • 10

    Nonce Management Architecture

    3 min
  • 11

    Capability and Permission Systems

    3 min
  • 12

    Preventing SQL Injection

    4 min
  • 13

    Secure REST API Endpoints

    3 min
  • 14

    Cross-Site Scripting Mitigation

    4 min
  • 15

    Auditing Plugin Security

    4 min
  • 16

    Modern Build Tooling with Vite

    3 min
  • 17

    React Component Architecture

    3 min
  • 18

    State Management with @wordpress/data

    3 min
  • 19

    Block API v2 Essentials

    3 min
  • 20

    InnerBlocks and Nested Structures

    3 min
  • 21

    Custom REST API Integration

    3 min
  • 22

    Optimizing React Rendering

    4 min
  • 23

    Code Splitting and Lazy Loading

    4 min
  • 24

    Advanced Admin Dashboards

    4 min
  • 25

    Component Library Design

    3 min
  • 26

    Linting and Code Quality

    3 min
  • 27

    Unit Testing with PHPUnit

    4 min
  • 28

    Integration Testing

    3 min
  • 29

    Test-Driven Development Workflow

    4 min
  • 30

    Automated CI/CD Pipelines

    3 min
  • 31

    Versioning and Release Management

    3 min
  • 32

    Internationalization (i18n)

    3 min
  • 33

    Licensing Infrastructure

    4 min
  • 34

    Automated Update API

    3 min
  • 35

    Documentation Systems

    4 min
  • 36

    Refactoring for Distribution

    4 min
  • 37

    Plugin Lifecycle Management

    3 min
  • 38

    Performance Monitoring

    3 min
  • 39

    Advanced Error Handling

    4 min
  • 40

    User Feedback Loops

    3 min
  • 41

    Handling Plugin Conflicts

    4 min
  • 42

    Advanced Hook Management

    4 min
  • 43

    Database Schema Evolution

    3 min
  • 44

    High-Concurrency Data Handling

    4 min
  • 45

    Object-Relational Mapping (ORM) Lite

    3 min
  • 46

    Advanced Query Filters

    4 min
  • 47

    Secure File Handling

    3 min
  • 48

    Background Processing

    4 min
  • 49

    Transient Caching Patterns

    4 min
  • 50

    Advanced Nonce Security

    3 min
  • 51

    Multi-tenancy Considerations

    3 min
  • 52

    Custom Gutenberg Block Controls

    3 min
  • 53

    Block Transforms and Deprecation

    4 min
  • 54

    Dynamic Block Rendering

    4 min
  • 55

    Advanced State Persistence

    4 min
  • 56

    Custom Hooks for React

    3 min
  • View full course