• 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 » Step-by-Step Guide to building a custom automated asset optimization manager block for Gutenberg using React components

Step-by-Step Guide to building a custom automated asset optimization manager block for Gutenberg using React components

Project Setup and Environment Configuration

Before diving into component development, establish a robust development environment. This involves setting up a local WordPress instance and configuring the necessary build tools for React and Gutenberg development. We’ll leverage the official WordPress `@wordpress/scripts` package, which provides a pre-configured Webpack setup for blocks.

First, ensure you have Node.js and npm (or yarn) installed. Navigate to your WordPress theme or plugin directory where you intend to build the block. Initialize a new project using npm:

npm init -y

Next, install the essential WordPress development scripts and React dependencies:

npm install --save-dev @wordpress/scripts react react-dom

In your `package.json` file, add the following scripts to manage the build process:

{
  "name": "asset-optimizer-block",
  "version": "1.0.0",
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start"
  },
  "devDependencies": {
    "@wordpress/scripts": "^26.0.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  }
}

The `build` script compiles your React components into production-ready JavaScript and CSS. The `start` script watches for changes and recompiles automatically, ideal for development.

Create a `src` directory in your project root. Inside `src`, create an `index.js` file which will be the entry point for your block’s JavaScript. Also, create a `block.json` file to define your block’s metadata.

Defining the Block Metadata (`block.json`)

The `block.json` file is crucial for registering your Gutenberg block with WordPress. It defines the block’s name, title, icon, categories, attributes, and script/style handles.

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 3,
  "name": "my-plugin/asset-optimizer-block",
  "version": "0.1.0",
  "title": "Automated Asset Optimizer",
  "category": "optimization",
  "icon": "performance",
  "description": "Manages and optimizes assets for better performance.",
  "keywords": [ "asset", "optimization", "performance", "images", "scripts" ],
  "attributes": {
    "optimizeImages": {
      "type": "boolean",
      "default": true
    },
    "minifyScripts": {
      "type": "boolean",
      "default": false
    },
    "lazyLoadImages": {
      "type": "boolean",
      "default": true
    }
  },
  "textdomain": "asset-optimizer-block",
  "editorScript": "file:./build/index.js",
  "editorStyle": "file:./build/index.css",
  "style": "file:./build/style-index.css"
}

Key fields:

  • name: A unique identifier for your block (namespace/block-name).
  • title: The human-readable name displayed in the block inserter.
  • category: The category the block belongs to.
  • icon: An icon representing the block.
  • attributes: Defines the data that your block will store. Here, we define boolean flags for image optimization, script minification, and lazy loading.
  • editorScript: Points to the compiled JavaScript file for the block editor.
  • editorStyle: Points to the compiled CSS file for the block editor.
  • style: Points to the compiled CSS file for the frontend.

Registering the Block and Entry Point (`src/index.js`)

The `src/index.js` file serves as the main entry point for your block’s JavaScript. It imports necessary Gutenberg components and registers the block using `registerBlockType`.

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import './style.scss'; // Frontend styles
import './editor.scss'; // Editor-only styles

import Edit from './Edit';
import save from './save';

registerBlockType( 'my-plugin/asset-optimizer-block', {
    edit: Edit,
    save,
} );

This file imports the `Edit` and `save` components (which we’ll define next), along with frontend and editor-specific stylesheets. `registerBlockType` takes the block name from `block.json` and maps the `edit` and `save` functions to their respective components.

Developing the Editor Component (`src/Edit.js`)

The `Edit` component is responsible for rendering the block’s interface within the Gutenberg editor. It uses React hooks and WordPress components to manage state and provide controls.

import { __ } from '@wordpress/i18n';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, ToggleControl } from '@wordpress/components';
import './editor.scss';

export default function Edit( { attributes, setAttributes } ) {
    const blockProps = useBlockProps();
    const { optimizeImages, minifyScripts, lazyLoadImages } = attributes;

    const updateAttribute = ( key, value ) => {
        setAttributes( { [ key ]: value } );
    };

    return (
        <>
            <InspectorControls>
                <PanelBody title={ __( 'Asset Optimization Settings', 'asset-optimizer-block' ) }>
                    <ToggleControl
                        label={ __( 'Optimize Images', 'asset-optimizer-block' ) }
                        checked={ optimizeImages }
                        onChange={ ( value ) => updateAttribute( 'optimizeImages', value ) }
                    />
                    <ToggleControl
                        label={ __( 'Minify Scripts', 'asset-optimizer-block' ) }
                        checked={ minifyScripts }
                        onChange={ ( value ) => updateAttribute( 'minifyScripts', value ) }
                    />
                    <ToggleControl
                        label={ __( 'Lazy Load Images', 'asset-optimizer-block' ) }
                        checked={ lazyLoadImages }
                        onChange={ ( value ) => updateAttribute( 'lazyLoadImages', value ) }
                    />
                </PanelBody>
            </InspectorControls>
            <div { ...blockProps }>
                <p>{ __( 'Asset Optimizer Settings', 'asset-optimizer-block' ) }</p>
                <p>{ __( 'Configure optimization settings in the sidebar.', 'asset-optimizer-block' ) }</p>
                <ul>
                    <li>{ __( 'Optimize Images:', 'asset-optimizer-block' ) } { optimizeImages ? __( 'Enabled', 'asset-optimizer-block' ) : __( 'Disabled', 'asset-optimizer-block' ) }</li>
                    <li>{ __( 'Minify Scripts:', 'asset-optimizer-block' ) } { minifyScripts ? __( 'Enabled', 'asset-optimizer-block' ) : __( 'Disabled', 'asset-optimizer-block' ) }</li>
                    <li>{ __( 'Lazy Load Images:', 'asset-optimizer-block' ) } { lazyLoadImages ? __( 'Enabled', 'asset-optimizer-block' ) : __( 'Disabled', 'asset-optimizer-block' ) }</li>
                </ul>
            </div>
        </>
    );
}

In this component:

  • useBlockProps: A hook that provides necessary props for the block’s root element, including classes and attributes.
  • InspectorControls: A component that renders controls in the block’s sidebar (Inspector).
  • PanelBody: A container for controls within the Inspector.
  • ToggleControl: A standard WordPress component for boolean toggles.
  • attributes: An object containing the current values of the block’s attributes.
  • setAttributes: A function to update the block’s attributes.

The `updateAttribute` helper function simplifies updating specific attributes. The `Edit` component renders the toggle controls in the sidebar and displays the current status of the settings within the block’s editable area.

Developing the Save Component (`src/save.js`)

The `save` component defines how the block’s content is rendered on the frontend. It receives the block’s attributes and should return a static representation of the block’s output.

import { useBlockProps } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';

export default function save( { attributes } ) {
    const blockProps = useBlockProps.save();
    const { optimizeImages, minifyScripts, lazyLoadImages } = attributes;

    // In a real-world scenario, this component would not directly control
    // the optimization process. It would likely output data attributes or
    // classes that a separate PHP or JS process would hook into.
    // For demonstration, we'll just display the settings.

    return (
        <div { ...blockProps } data-optimize-images={ optimizeImages } data-minify-scripts={ minifyScripts } data-lazy-load-images={ lazyLoadImages }>
            <p>{ __( 'Asset Optimizer Status:', 'asset-optimizer-block' ) }</p>
            <ul>
                <li>{ __( 'Optimize Images:', 'asset-optimizer-block' ) } { optimizeImages ? __( 'Enabled', 'asset-optimizer-block' ) : __( 'Disabled', 'asset-optimizer-block' ) }</li>
                <li>{ __( 'Minify Scripts:', 'asset-optimizer-block' ) } { minifyScripts ? __( 'Enabled', 'asset-optimizer-block' ) : __( 'Disabled', 'asset-optimizer-block' ) }</li>
                <li>{ __( 'Lazy Load Images:', 'asset-optimizer-block' ) } { lazyLoadImages ? __( 'Enabled', 'asset-optimizer-block' ) : __( 'Disabled', 'asset-optimizer-block' ) }</li>
            </ul>
        </div>
    );
}

Crucially, the `save` component should return a static HTML structure. It should not contain interactive elements that rely on JavaScript to function on the frontend, unless that JavaScript is enqueued separately and designed to enhance the static output. Here, we’re adding data attributes to the block’s wrapper element. These attributes can be read by a PHP function hooked into `the_content` or by a frontend JavaScript file to trigger actual optimization processes.

Styling the Block (`src/style.scss` and `src/editor.scss`)

Create two SCSS files for styling:

src/style.scss (for frontend styles):

.wp-block-my-plugin-asset-optimizer-block {
    border: 1px solid #ccc;
    padding: 15px;
    margin-bottom: 15px;
    background-color: #f9f9f9;

    ul {
        list-style: disc inside;
        margin-top: 10px;
    }
}

src/editor.scss (for editor-only styles):

.wp-block-my-plugin-asset-optimizer-block {
    border-style: dashed;
    background-color: #eef7ff;
}

The `@wordpress/scripts` package automatically compiles these SCSS files into CSS and links them according to the `editorStyle` and `style` properties in `block.json`.

Building the Block Assets

With the components and metadata defined, run the build script:

npm run build

This command will generate the `build` directory containing `index.js` (compiled editor script) and `index.css` (editor styles), as well as `style-index.css` (frontend styles). These files will be automatically enqueued by WordPress when the block is used, thanks to the `editorScript` and `style` paths in `block.json`.

Implementing Backend Optimization Logic (PHP)

The frontend rendering of the block is just the UI. The actual asset optimization needs to happen server-side or via a background process. This block acts as a control panel. A common approach is to hook into WordPress actions and filters.

For instance, to process images when content is saved or displayed, you might use:

post_content;

    // Regex to find our block. This is a simplified example.
    // A more robust solution would use parse_blocks().
    $pattern = '//i';
    if ( preg_match( $pattern, $content, $matches ) ) {
        $attributes_json = $matches[1];
        $attributes = json_decode( $attributes_json, true );

        if ( $attributes && isset( $attributes['optimizeImages'] ) && $attributes['optimizeImages'] ) {
            // Trigger image optimization for images within this post's content.
            // This would involve finding image URLs and processing them.
            error_log( "Asset Optimizer: Image optimization requested for post ID {$post_id}." );
            // Example: Call a function to optimize images found in $content.
            // my_optimize_images_in_content( $content );
        }
        if ( $attributes && isset( $attributes['minifyScripts'] ) && $attributes['minifyScripts'] ) {
            error_log( "Asset Optimizer: Script minification requested for post ID {$post_id}." );
            // Example: Call a function to minify scripts.
            // my_minify_scripts_in_content( $content );
        }
        if ( $attributes && isset( $attributes['lazyLoadImages'] ) && $attributes['lazyLoadImages'] ) {
            error_log( "Asset Optimizer: Lazy loading requested for post ID {$post_id}." );
            // Example: Add lazy loading attributes via a filter.
        }
    }
}
add_action( 'save_post', 'my_asset_optimizer_process_content', 10, 3 );

/**
 * Filter content to add lazy loading attributes if enabled via the block.
 */
function my_asset_optimizer_filter_content( $content ) {
    // Similar block parsing logic as above.
    $pattern = '//i';
    if ( preg_match( $pattern, $content, $matches ) ) {
        $attributes_json = $matches[1];
        $attributes = json_decode( $attributes_json, true );

        if ( $attributes && isset( $attributes['lazyLoadImages'] ) && $attributes['lazyLoadImages'] ) {
            // Use DOMDocument for safer HTML manipulation to add 'loading="lazy"'
            // to img tags that don't already have it.
            $dom = new DOMDocument();
            // Suppress warnings for malformed HTML, as we'll clean it up.
            @$dom->loadHTML( mb_convert_encoding( $content, 'HTML-ENTITIES', 'UTF-8' ), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
            $images = $dom->getElementsByTagName('img');
            foreach ( $images as $img ) {
                if ( ! $img->hasAttribute('loading') ) {
                    $img->setAttribute('loading', 'lazy');
                }
            }
            $content = $dom->saveHTML();
        }
    }
    return $content;
}
add_filter( 'the_content', 'my_asset_optimizer_filter_content', 9 ); // Run before most filters.

/**
 * Helper function to parse blocks and extract attributes for a specific block type.
 *
 * @param string $block_name The name of the block to find.
 * @param string $content The content to parse.
 * @return array|null An array of attributes if found, null otherwise.
 */
function my_asset_optimizer_get_block_attributes( $block_name, $content ) {
    $blocks = parse_blocks( $content );
    foreach ( $blocks as $block ) {
        if ( $block['blockName'] === $block_name ) {
            return $block['attrs'];
        }
    }
    return null;
}

// Example usage within the filter:
function my_asset_optimizer_filter_content_robust( $content ) {
    $optimizer_attrs = my_asset_optimizer_get_block_attributes( 'my-plugin/asset-optimizer-block', $content );

    if ( $optimizer_attrs && isset( $optimizer_attrs['lazyLoadImages'] ) && $optimizer_attrs['lazyLoadImages'] ) {
        $dom = new DOMDocument();
        @$dom->loadHTML( mb_convert_encoding( $content, 'HTML-ENTITIES', 'UTF-8' ), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
        $images = $dom->getElementsByTagName('img');
        foreach ( $images as $img ) {
            if ( ! $img->hasAttribute('loading') ) {
                $img->setAttribute('loading', 'lazy');
            }
        }
        $content = $dom->saveHTML();
    }
    return $content;
}
add_filter( 'the_content', 'my_asset_optimizer_filter_content_robust', 9 );
?>

This PHP code demonstrates:

  • Hooking into save_post to potentially trigger backend processes when content is saved.
  • Using parse_blocks() for a more reliable way to extract block attributes from content.
  • Hooking into the_content filter to modify output, such as adding loading="lazy" attributes to images if the corresponding block setting is enabled.
  • Using DOMDocument for safe HTML manipulation.

Actual image optimization (resizing, compression) and script minification would require integrating with external libraries (like Imagick or GD for images, and potentially JS minifiers) or using WordPress’s built-in media handling capabilities. The block’s role is to provide the user interface to control these backend operations.

Further Enhancements and Considerations

This guide provides a foundational structure. For a production-ready asset optimizer, consider:

  • Error Handling and Logging: Implement robust error handling for optimization processes and log outcomes.
  • Asynchronous Processing: For intensive tasks like image optimization, consider using WP-Cron or a dedicated job queue system to avoid blocking the user request.
  • User Feedback: Provide visual feedback in the editor or admin area about the status of optimization tasks.
  • Configuration Options: Allow users to specify optimization levels, formats (e.g., WebP conversion), and exclusion rules.
  • Security: Sanitize all inputs and ensure that any file operations are secure.
  • Performance Monitoring: Integrate with performance testing tools or provide basic metrics.
  • Asset Enqueuing Management: For script minification, you might need to dynamically enqueue or modify script handles based on block settings.

By combining a well-structured Gutenberg block with appropriate backend logic, you can create powerful tools for managing and optimizing website assets directly within the WordPress editor.

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

  • Leveraging PHP 8 JIT and AWS Lambda for High-Performance, Serverless WordPress REST API Backends
  • Beyond the Basics: Leveraging PHP 8.3’s JIT Compiler and Fibers for High-Concurrency Laravel Applications
  • Zero-Downtime Deployments with Docker, Laravel, and AWS ECS: A Deep Dive into Blue/Green Strategies
  • Leveraging PHP 9’s JIT and Concurrency Features for High-Performance Laravel Microservices on AWS ECS
  • Leveraging PHP 8.3 JIT and OPcache for Sub-Millisecond API Response Times: A Practical Deep Dive

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (664)
  • Desktop Applications (14)
  • DevOps (11)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (6)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (873)
  • PHP (14)
  • PHP Development (49)
  • Plugins & Themes (244)
  • Programming Languages (10)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (650)
  • SEO & Growth (492)
  • Server (118)
  • Softwares (1)
  • Ubuntu (9)
  • Uncategorized (17)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (24)
  • WordPress Plugin Development (728)
  • WordPress Theme Development (357)

Recent Posts

  • Leveraging PHP 8 JIT and AWS Lambda for High-Performance, Serverless WordPress REST API Backends
  • Beyond the Basics: Leveraging PHP 8.3's JIT Compiler and Fibers for High-Concurrency Laravel Applications
  • Zero-Downtime Deployments with Docker, Laravel, and AWS ECS: A Deep Dive into Blue/Green Strategies

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (873)
  • WordPress Plugin Development (728)
  • Debugging & Troubleshooting (664)
  • Security & Compliance (650)
  • SEO & Growth (492)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala