• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Building a Reactive Frontend Framework inside Full Site Editing (FSE) Block Themes and theme.json for High-Traffic Content Portals

Building a Reactive Frontend Framework inside Full Site Editing (FSE) Block Themes and theme.json for High-Traffic Content Portals

Leveraging `theme.json` for Dynamic Block Rendering in FSE

Full Site Editing (FSE) and block themes offer a powerful paradigm for building WordPress sites. However, achieving truly reactive frontend experiences, akin to modern JavaScript frameworks, within this environment requires a nuanced approach. The key lies in strategically utilizing theme.json to control block behavior and data fetching, enabling dynamic content updates without full page reloads. This is particularly crucial for high-traffic content portals where performance and user engagement are paramount.

Instead of relying solely on client-side JavaScript for dynamic elements, we can pre-render and hydrate components server-side, then enhance them with targeted JavaScript. theme.json acts as our central configuration hub, defining styles, layout, and crucially, enabling custom settings that can be interpreted by our block components.

Custom Settings in theme.json for Reactivity

The settings.custom object within theme.json is an underutilized but potent feature for passing configuration data to blocks. We can define custom properties here that our custom blocks will read and act upon. For instance, consider a “Featured Posts” block that needs to fetch and display the latest articles based on a specific category and a defined number of posts. We can configure these parameters directly in theme.json.

Here’s an example of how you might structure this in your theme.json:

{
    "version": 2,
    "settings": {
        "color": {
            "palette": [
                // ... color palette definitions
            ]
        },
        "layout": {
            "contentSize": "650px",
            "wideSize": "1000px"
        },
        "custom": {
            "featuredPosts": {
                "enabled": true,
                "categorySlug": "featured",
                "numberOfPosts": 5,
                "displayStyle": "card",
                "fetchOnClient": false
            }
        }
    }
}

The fetchOnClient flag is critical. If false (as shown above), the block will attempt to fetch and render its content server-side. If true, it signals that client-side JavaScript should handle the data fetching and rendering, enabling a more traditional reactive component behavior.

Server-Side Rendering with Custom Block Settings

For blocks where fetchOnClient is false, we need to ensure they can dynamically query and render content based on the theme.json settings. This involves creating a custom block type with a server-side rendering callback.

Let’s define a custom block, say my-theme/featured-posts. The registration in PHP would look something like this:

<?php
/**
 * Registers the custom Featured Posts block.
 */
function my_theme_register_featured_posts_block() {
    register_block_type( 'my-theme/featured-posts', array(
        'editor_script'    => 'my-theme-editor-script',
        'editor_style'     => 'my-theme-editor-style',
        'render_callback'  => 'my_theme_render_featured_posts_block',
        'attributes'       => array(
            // Attributes that can be set in the editor,
            // but we'll primarily use theme.json for defaults.
            'categorySlug' => array(
                'type'    => 'string',
                'default' => '',
            ),
            'numberOfPosts' => array(
                'type'    => 'number',
                'default' => 3,
            ),
            'displayStyle' => array(
                'type'    => 'string',
                'default' => 'list',
            ),
        ),
    ) );
}
add_action( 'init', 'my_theme_register_featured_posts_block' );

/**
 * Server-side rendering callback for the Featured Posts block.
 *
 * @param array $attributes Block attributes.
 * @return string HTML output.
 */
function my_theme_render_featured_posts_block( $attributes ) {
    // Get custom settings from theme.json
    $theme_settings = get_option( 'theme_json' );
    $theme_json_config = json_decode( $theme_settings, true );

    $featured_posts_config = $theme_json_config['settings']['custom']['featuredPosts'] ?? null;

    if ( ! $featured_posts_config || ! $featured_posts_config['enabled'] ) {
        return ''; // Block is disabled in theme.json
    }

    // Prioritize attributes set in the editor, fall back to theme.json
    $category_slug = $attributes['categorySlug'] ?: $featured_posts_config['categorySlug'];
    $number_of_posts = $attributes['numberOfPosts'] ?: $featured_posts_config['numberOfPosts'];
    $display_style = $attributes['displayStyle'] ?: $featured_posts_config['displayStyle'];

    // Ensure we have a valid category slug
    if ( empty( $category_slug ) ) {
        return '<p>Error: Featured posts category not configured.</p>';
    }

    $args = array(
        'posts_per_page' => $number_of_posts,
        'category_name'  => $category_slug,
        'post_status'    => 'publish',
        'orderby'        => 'date',
        'order'          => 'DESC',
    );

    $query = new WP_Query( $args );

    if ( ! $query->have_posts() ) {
        return '<p>No featured posts found.</p>';
    }

    $output = '<div class="wp-block-my-theme-featured-posts featured-posts-display-style-' . esc_attr( $display_style ) . '">';

    while ( $query->have_posts() ) {
        $query->the_post();
        $output .= '<article>';
        $output .= '<h3><a href="' . esc_url( get_permalink() ) . '">' . esc_html( get_the_title() ) . '</a></h3>';
        // Add more content as needed, e.g., excerpt, thumbnail
        $output .= '</article>';
    }

    $output .= '</div>';

    wp_reset_postdata();

    return $output;
}
?>

In this PHP code:

  • We register a custom block type my-theme/featured-posts.
  • The render_callback points to my_theme_render_featured_posts_block, which handles the server-side generation of HTML.
  • Inside the callback, we retrieve the entire theme.json options using get_option( 'theme_json' ).
  • We then specifically extract our custom featuredPosts configuration.
  • The block respects attributes set in the editor but falls back to the theme.json settings if no editor-specific value is provided. This allows for global defaults managed in theme.json and overrides in specific instances.
  • A standard WP_Query is used to fetch posts based on the determined category and number.
  • The output is a simple HTML structure, which can be further styled using CSS.

Client-Side Hydration for Enhanced Reactivity

For scenarios where fetchOnClient is true, or to add interactive elements to server-rendered blocks, we employ client-side JavaScript. This JavaScript will “hydrate” the server-rendered HTML, attaching event listeners and enabling dynamic updates. This is where we can introduce more complex reactive patterns.

First, ensure your theme.json has fetchOnClient set to true for the desired block configuration, or that you have a separate mechanism to trigger client-side fetching.

{
    "version": 2,
    "settings": {
        // ... other settings
        "custom": {
            "featuredPosts": {
                "enabled": true,
                "categorySlug": "featured",
                "numberOfPosts": 5,
                "displayStyle": "card",
                "fetchOnClient": true // Crucial for client-side fetching
            }
        }
    }
}

Now, let’s write the JavaScript. We’ll enqueue a script that looks for specific elements and fetches data using the WordPress REST API.

<?php
/**
 * Enqueues the client-side JavaScript for dynamic blocks.
 */
function my_theme_enqueue_dynamic_blocks_script() {
    // Enqueue only if the block is likely to be used or if fetchOnClient is true
    // A more robust check might involve inspecting theme.json settings globally.
    wp_enqueue_script(
        'my-theme-dynamic-blocks',
        get_template_directory_uri() . '/assets/js/dynamic-blocks.js',
        array( 'wp-element', 'wp-api-fetch' ), // Dependencies: React, WP REST API fetch
        filemtime( get_template_directory() . '/assets/js/dynamic-blocks.js' ),
        true // Load in footer
    );

    // Pass necessary data to the script, e.g., REST API URL, nonces
    wp_localize_script( 'my-theme-dynamic-blocks', 'myThemeConfig', array(
        'restApiUrl' => esc_url_raw( rest_url() ),
        'nonce'      => wp_create_nonce( 'wp_rest' ),
        // Potentially pass theme.json custom settings here if needed for initial setup
        'themeCustomSettings' => get_option( 'theme_json' ) ? json_decode( get_option( 'theme_json' ), true )['settings']['custom'] : []
    ) );
}
add_action( 'wp_enqueue_scripts', 'my_theme_enqueue_dynamic_blocks_script' );
?>

And here’s the corresponding dynamic-blocks.js:

document.addEventListener('DOMContentLoaded', () => {
    const featuredPostsBlocks = document.querySelectorAll('.wp-block-my-theme-featured-posts[data-fetch-on-client="true"]');

    featuredPostsBlocks.forEach(blockElement => {
        const blockId = blockElement.dataset.blockId; // Assuming you add a unique ID attribute
        const categorySlug = blockElement.dataset.categorySlug;
        const numberOfPosts = parseInt(blockElement.dataset.numberOfPosts, 10);
        const displayStyle = blockElement.dataset.displayStyle;

        // If block was server-rendered, we might need to find a placeholder
        // or replace existing content. For pure client-side, we'd start with an empty container.
        // Let's assume we're replacing content for now.

        const fetchAndRenderPosts = async () => {
            if (!categorySlug || !numberOfPosts) {
                console.error('Missing configuration for featured posts:', blockElement);
                return;
            }

            try {
                // Construct REST API URL
                const endpoint = `${myThemeConfig.restApiUrl}wp/v2/posts?categories_slug=${categorySlug}&per_page=${numberOfPosts}&_wpnonce=${myThemeConfig.nonce}`;

                const response = await wp.apiFetch({
                    path: endpoint,
                    method: 'GET',
                });

                if (!response || response.length === 0) {
                    blockElement.innerHTML = '<p>No featured posts found.</p>';
                    return;
                }

                let htmlContent = '';
                response.forEach(post => {
                    htmlContent += `
                        <article class="featured-post-item">
                            <h3><a href="${post.link}">${post.title.rendered}</a></h3>
                            ${post.excerpt.rendered}
                        </article>
                    `;
                });

                blockElement.innerHTML = htmlContent;

            } catch (error) {
                console.error('Error fetching featured posts:', error);
                blockElement.innerHTML = '<p>Error loading featured posts.</p>';
            }
        };

        // Initial fetch on load
        fetchAndRenderPosts();

        // Add any event listeners for interactivity here (e.g., pagination, filtering)
        // Example: If you had a "Load More" button within the block:
        // const loadMoreButton = blockElement.querySelector('.load-more-button');
        // if (loadMoreButton) {
        //     loadMoreButton.addEventListener('click', () => {
        //         // Implement logic to fetch more posts
        //     });
        // }
    });
});

Key aspects of the JavaScript approach:

  • We use wp.apiFetch, the official WordPress JavaScript API for interacting with the REST API, ensuring compatibility and security.
  • The script looks for specific data attributes (e.g., data-fetch-on-client="true", data-category-slug) on the block’s wrapper element. These attributes should be dynamically set either by the server-side rendering callback (if fetchOnClient is true) or directly in the block’s save function in JavaScript.
  • The wp_localize_script function passes the REST API URL and a nonce for secure API requests.
  • The DOMContentLoaded event ensures the script runs after the DOM is fully loaded.
  • Error handling and fallback content are included for robustness.

Advanced Diagnostics and Performance Tuning

For high-traffic portals, performance is non-negotiable. Here’s how to diagnose and optimize:

1. Server-Side Rendering vs. Client-Side Rendering Analysis

Diagnostic Steps:

  • Browser Developer Tools (Network Tab): Observe the initial page load. If using server-side rendering, the content should be present in the HTML response. If using client-side rendering (fetchOnClient: true), you’ll see additional XHR/Fetch requests after the initial load. Monitor the size and timing of these requests.
  • WordPress Debugging Tools: Enable WP_DEBUG and WP_DEBUG_LOG. Check debug.log for any PHP errors during WP_Query or theme.json parsing.
  • Query Monitor Plugin: This invaluable plugin helps identify slow database queries, memory usage, and hooks. Analyze the queries generated by your custom blocks.

Optimization Strategy:

  • Prioritize Server-Side Rendering: For content that doesn’t need immediate user interaction, server-side rendering is generally faster for the initial user perception (Time To First Byte – TTFB). Use client-side rendering only when interactivity is essential or for content that can be loaded asynchronously without impacting the core page experience.
  • Caching: Implement robust server-side caching (e.g., Varnish, Redis Object Cache Pro) and client-side caching (browser cache headers). Ensure your dynamic blocks are cacheable where possible. For blocks that change frequently, consider cache invalidation strategies.
  • REST API Optimization: If relying heavily on the REST API, ensure your server is optimized. Consider using a plugin like WP-Optimize or custom database indexing for faster query responses.

2. `theme.json` Parsing Overhead

Diagnostic Steps:

  • Profiling PHP Execution Time: Use tools like Xdebug or the Query Monitor plugin to profile the execution time of get_option( 'theme_json' ) and subsequent JSON decoding. While generally efficient, on very large or complex theme.json files, this could become a bottleneck.

Optimization Strategy:

  • Simplify `theme.json`: Remove unused settings or deeply nested structures.
  • Cache `theme.json` Data: Although get_option is usually cached by WordPress internally, for extreme cases, you could implement a transient to cache the parsed JSON data for a short period, reducing repeated calls within a single request lifecycle if your theme logic is complex.

3. JavaScript Performance and Hydration

Diagnostic Steps:

  • Browser Developer Tools (Performance Tab): Record a performance profile while your dynamic JavaScript runs. Identify long tasks, excessive re-renders, or inefficient event handling.
  • Bundle Size Analysis: Use tools like Webpack Bundle Analyzer if you’re using a build process to check the size of your JavaScript files.

Optimization Strategy:

  • Code Splitting: If your dynamic-blocks.js grows large, implement code splitting so that only the necessary JavaScript is loaded for the blocks present on a given page.
  • Debouncing/Throttling: For event handlers that fire rapidly (e.g., on scroll or resize), use debouncing or throttling to limit the number of times the handler executes.
  • Efficient DOM Manipulation: Minimize direct DOM manipulation. Use techniques like document fragments or virtual DOM (if using React/Vue within blocks) for performance.
  • Lazy Loading: Implement lazy loading for images and potentially for entire blocks that are below the fold.

Conclusion

By strategically combining server-side rendering with targeted client-side hydration, and leveraging theme.json as a central configuration point, you can build highly dynamic and reactive frontend experiences within FSE block themes. This approach allows for the best of both worlds: the SEO and performance benefits of server-rendered content, coupled with the rich interactivity expected by modern users. Continuous monitoring and performance tuning are essential for high-traffic content portals to ensure a seamless user experience.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Top 100 Developer Tooling and Productivity SaaS Ideas to Launch in 2026 to Boost Organic Search Growth by 200%
  • Top 100 Developer-Centric Code Snippet Managers and Customization Plugins to Double User Engagement and Session Duration
  • Top 5 API Monetization Frameworks and Gateway Strategies for Developers to Minimize Server Costs and Load Overhead
  • Top 50 Automated PDF & Document Generation Tool Ideas for Developers to Minimize Server Costs and Load Overhead
  • Top 50 Premium Newsletter and Subscription Business Models for Devs for High-Traffic Technical Portals

Categories

  • apache (1)
  • Business & Monetization (386)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (538)
  • DevOps (7)
  • DevOps & Cloud Scaling (938)
  • Django (1)
  • Migration & Architecture (134)
  • MySQL (1)
  • Performance & Optimization (709)
  • PHP (5)
  • Plugins & Themes (184)
  • Security & Compliance (531)
  • SEO & Growth (468)
  • Server (23)
  • Ubuntu (9)
  • WordPress (22)
  • WordPress Plugin Development (7)
  • WordPress Theme Development (193)

Recent Posts

  • Top 100 Developer Tooling and Productivity SaaS Ideas to Launch in 2026 to Boost Organic Search Growth by 200%
  • Top 100 Developer-Centric Code Snippet Managers and Customization Plugins to Double User Engagement and Session Duration
  • Top 5 API Monetization Frameworks and Gateway Strategies for Developers to Minimize Server Costs and Load Overhead
  • Top 50 Automated PDF & Document Generation Tool Ideas for Developers to Minimize Server Costs and Load Overhead
  • Top 50 Premium Newsletter and Subscription Business Models for Devs for High-Traffic Technical Portals
  • Top 100 SEO and Schema Markup Plugins for Headless Decoupled Sites for Independent Web Developers and Indie Hackers

Top Categories

  • DevOps & Cloud Scaling (938)
  • Performance & Optimization (709)
  • Debugging & Troubleshooting (538)
  • Security & Compliance (531)
  • SEO & Growth (468)
  • Business & Monetization (386)

Our Products

  • School Management & Student Administration System
  • Integrated Hospital & Clinic Management System
  • Real Estate Directory & Agent Portal
  • Restaurant POS & Table Booking System
  • Retail Inventory POS & Billing System
  • Pharmacy Inventory & Clinic Billing System

Our Services

  • Vibe Engineering & AI Code Auditing Services
  • Prompt Engineering & "Vibe Coding" Workflow Consulting
  • AI-Augmented "Vibe Coding" & Rapid MVP Development
  • Figma to Shopify Liquid Theme Customization
  • Figma to WooCommerce Frontend Development
  • Figma to Magento 2 Theme Development

Copyright © 2026 · Vinay Vengala