• 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 coupon generator block for Gutenberg using SolidJS high-performance reactive components

Step-by-Step Guide to building a custom automated coupon generator block for Gutenberg using SolidJS high-performance reactive components

Gutenberg Block Architecture: The Reactivity Challenge

Developing custom Gutenberg blocks often involves managing complex UI states and ensuring real-time updates. Traditional JavaScript approaches can lead to performance bottlenecks, especially when dealing with dynamic content generation like automated coupon codes. This guide details building a high-performance Gutenberg block using SolidJS, a declarative JavaScript library that compiles to efficient imperative code, leveraging its fine-grained reactivity for a superior user experience and optimized rendering.

Project Setup and Dependencies

We’ll start by setting up a WordPress development environment and installing the necessary tools. This involves Node.js, npm/yarn, and a local WordPress installation. For our block, we’ll use a modern JavaScript build process that integrates with WordPress’s asset pipeline.

First, ensure you have Node.js and npm installed. Then, navigate to your WordPress theme or plugin directory where you intend to build the block. We’ll use `@wordpress/scripts` for building our block assets, which provides a robust build pipeline out-of-the-box.

Initializing the Block Structure

WordPress provides a convenient tool for scaffolding new blocks. Run the following command in your terminal within your theme/plugin directory:

npx @wordpress/create-block coupon-generator-block

This command generates a new directory named `coupon-generator-block` with a basic block structure, including `src/index.js` (the main entry point for your block’s JavaScript) and `build/` for compiled assets. It also sets up a `package.json` with necessary scripts.

Integrating SolidJS

SolidJS is not directly supported by `@wordpress/scripts` out-of-the-box. We need to configure our build process to transpile SolidJS JSX and integrate its reactivity. The most straightforward way is to leverage Babel with the appropriate plugins. We’ll modify the Babel configuration.

First, install the necessary SolidJS Babel plugin:

cd coupon-generator-block
npm install --save-dev babel-plugin-solidjs

Next, create a `.babelrc` file in the root of your block’s directory:

{
  "plugins": [
    "babel-plugin-solidjs",
    "@babel/plugin-syntax-jsx"
  ]
}

Now, we need to tell `@wordpress/scripts` to use our custom Babel configuration. Edit your `package.json` and add a `babel` key:

{
  "name": "coupon-generator-block",
  "version": "1.0.0",
  "description": "A custom Gutenberg block for generating coupons.",
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start"
  },
  "keywords": ["gutenberg", "block", "solidjs", "coupon"],
  "author": "Your Name",
  "license": "GPL-2.0-or-later",
  "devDependencies": {
    "@wordpress/scripts": "^26.0.0",
    "babel-plugin-solidjs": "^1.8.1",
    "@babel/plugin-syntax-jsx": "^7.23.3"
  },
  "babel": {
    "plugins": [
      "babel-plugin-solidjs",
      "@babel/plugin-syntax-jsx"
    ]
  }
}

With this setup, `wp-scripts build` and `wp-scripts start` will now correctly transpile SolidJS JSX.

Developing the Coupon Generator Component with SolidJS

We’ll create a reusable SolidJS component for our coupon generator. This component will manage the state of coupon generation, including options for code length, character set, and prefix. SolidJS’s fine-grained reactivity means that only the parts of the DOM that depend on a changing signal will re-render, leading to exceptional performance.

Coupon Generation Logic

Let’s define the core logic for generating a coupon code. This will be a simple JavaScript function that takes configuration options.

function generateCouponCode(length = 10, characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', prefix = '') {
    let result = prefix;
    const charactersLength = characters.length;
    for (let i = 0; i < length; i++) {
        result += characters.charAt(Math.floor(Math.random() * charactersLength));
    }
    return result;
}

SolidJS Component Implementation

Now, let’s build the SolidJS component. We’ll use signals for managing state and event handlers for user interactions. This component will be embedded within the Gutenberg block’s `edit` and `save` functions.

Create a new file, e.g., `src/components/CouponGenerator.jsx`:

import { createSignal, Show } from 'solid-js';
import { generateCouponCode } from '../utils/couponUtils'; // Assuming couponUtils.js exists

export default function CouponGenerator(props) {
    // Signals for managing component state
    const [couponCode, setCouponCode] = createSignal('');
    const [codeLength, setCodeLength] = createSignal(props.attributes.codeLength || 10);
    const [characterSet, setCharacterSet] = createSignal(props.attributes.characterSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789');
    const [prefix, setPrefix] = createSignal(props.attributes.prefix || '');
    const [isGenerating, setIsGenerating] = createSignal(false);

    const handleGenerateClick = () => {
        setIsGenerating(true);
        const newCode = generateCouponCode(codeLength(), characterSet(), prefix());
        setCouponCode(newCode);
        props.onCodeGenerated(newCode); // Callback to parent for updating attributes
        setIsGenerating(false);
    };

    // Update signals when attributes change (for editor preview)
    createEffect(() => {
        setCodeLength(props.attributes.codeLength || 10);
        setCharacterSet(props.attributes.characterSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789');
        setPrefix(props.attributes.prefix || '');
        if (props.attributes.generatedCode) {
            setCouponCode(props.attributes.generatedCode);
        }
    });

    return (
        <div class="coupon-generator-component">
            <h4>Coupon Generator Settings</h4>
            <div>
                <label>Prefix: </label>
                <input
                    type="text"
                    value={prefix()}
                    onInput={(e) => {
                        const newPrefix = e.target.value;
                        setPrefix(newPrefix);
                        props.onAttributeChange('prefix', newPrefix);
                    }}
                />
            </div>
            <div>
                <label>Length: </label>
                <input
                    type="number"
                    min="1"
                    value={codeLength()}
                    onInput={(e) => {
                        const newLength = parseInt(e.target.value, 10);
                        setCodeLength(newLength);
                        props.onAttributeChange('codeLength', newLength);
                    }}
                />
            </div>
            <div>
                <label>Character Set: </label>
                <input
                    type="text"
                    value={characterSet()}
                    onInput={(e) => {
                        const newSet = e.target.value;
                        setCharacterSet(newSet);
                        props.onAttributeChange('characterSet', newSet);
                    }}
                />
            </div>
            <button onClick={handleGenerateClick} disabled={isGenerating()} >
                {isGenerating() ? 'Generating...' : 'Generate New Coupon'}
            </button>
            <Show when={couponCode()} fallback={<p>No coupon generated yet.</p>} >
                <p>Generated Code: <strong>{couponCode()}</strong></p>
            </Show>
        </div>
    );
}

We also need `src/utils/couponUtils.js`:

export function generateCouponCode(length = 10, characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', prefix = '') {
    let result = prefix;
    const charactersLength = characters.length;
    if (charactersLength === 0) return prefix; // Handle empty character set
    for (let i = 0; i < length; i++) {
        result += characters.charAt(Math.floor(Math.random() * charactersLength));
    }
    return result;
}

And `src/components/index.js` to export the component:

export { default as CouponGenerator } from './CouponGenerator';

Integrating the SolidJS Component into Gutenberg

Now, we’ll integrate our `CouponGenerator` component into the Gutenberg block’s `edit` and `save` functions. The `edit` function will render the interactive component in the editor, while the `save` function will render the static HTML for the frontend.

Block Attributes

First, define the attributes for our block in `coupon-generator-block.php` (or your block’s PHP file). These attributes will store the coupon generation settings and the generated code.

<?php
/**
 * Registers the block using the metadata loaded from the `block.json` file.
 * Behind the scenes, it registers also all assets so they can be enqueued
 * through the block editor in the corresponding context.
 *
 * @see https://developer.wordpress.org/reference/functions/register_block_type/
 */
function coupon_generator_block_block_init() {
    register_block_type( __DIR__ . '/build', array(
        'attributes' => array(
            'prefix' => array(
                'type' => 'string',
                'default' => '',
            ),
            'codeLength' => array(
                'type' => 'number',
                'default' => 10,
            ),
            'characterSet' => array(
                'type' => 'string',
                'default' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
            ),
            'generatedCode' => array(
                'type' => 'string',
                'default' => '',
            ),
        ),
    ) );
}
add_action( 'init', 'coupon_generator_block_block_init' );
?>

`edit` Function Implementation

Modify `src/index.js` to import and use the SolidJS component in the `edit` function. We’ll use `render` from `solid-js/web` to mount our SolidJS component into the Gutenberg block’s edit interface.

import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
import { render } from 'solid-js/web';
import { CouponGenerator } from './components';
import './style.scss'; // Import styles

// Import the PHP attributes definition (if using block.json)
// import metadata from './block.json';

registerBlockType('coupon-generator-block/coupon-generator', {
    // metadata, // Use if block.json is configured
    title: 'Coupon Generator',
    icon: 'tag',
    category: 'widgets',
    attributes: {
        prefix: {
            type: 'string',
            default: '',
        },
        codeLength: {
            type: 'number',
            default: 10,
        },
        characterSet: {
            type: 'string',
            default: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
        },
        generatedCode: {
            type: 'string',
            default: '',
        },
    },

    edit: (props) => {
        const { attributes, setAttributes } = props;
        const blockProps = useBlockProps();

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

        const handleCodeGenerated = (code) => {
            setAttributes({ generatedCode: code });
        };

        // Render the SolidJS component
        // We need a container element for SolidJS to render into.
        // Gutenberg's edit function provides a wrapper.
        // We'll render the SolidJS component inside this wrapper.
        // Note: This approach requires careful management of DOM elements.
        // A more robust solution might involve a dedicated wrapper div.

        // For simplicity, we'll render directly into the blockProps.ref element
        // This might require adjustments based on Gutenberg's internal structure.
        // A common pattern is to create a dedicated div and render into it.

        const renderSolidComponent = (element) => {
            render(() =>
                <CouponGenerator
                    attributes={attributes}
                    onAttributeChange={handleAttributeChange}
                    onCodeGenerated={handleCodeGenerated}
                />,
                element
            );
        };

        // Use useEffect to ensure rendering happens after the DOM is ready
        // and to handle cleanup.
        React.useEffect(() => {
            const container = document.createElement('div');
            element.appendChild(container);
            renderSolidComponent(container);

            // Cleanup function
            return () => {
                // Unmount SolidJS component
                // SolidJS doesn't have a direct unmount function like React's ReactDOM.unmountComponentAtNode.
                // However, rendering into a specific element and then removing that element
                // effectively unmounts it.
                element.removeChild(container);
            };
        }, [attributes]); // Re-render if attributes change

        return (
            <div {...blockProps} ref={(el) => {
                // This ref callback is called when the element is mounted.
                // We need to ensure the SolidJS component is rendered here.
                // If the SolidJS component is already rendered, we might need to update it.
                // A more declarative approach is preferred.
                // For now, let's assume the useEffect handles the initial render.
            }}>
                <p>Coupon Generator Editor</p>
                {/* The SolidJS component will be rendered into a child div managed by useEffect */}
            </div>
        );
    },

    save: (props) => {
        const { attributes } = props;
        const blockProps = useBlockProps.save();

        // The save function should return static HTML.
        // We'll display the generated code if it exists.
        return (
            <div {...blockProps}>
                {attributes.generatedCode ? (
                    <p>Your Coupon: <strong>{attributes.generatedCode}</strong></p>
                ) : (
                    <p>Coupon not generated yet.</p>
                )}
            </div>
        );
    },
});

Important Note on `edit` function rendering: Directly using `render` from `solid-js/web` within Gutenberg’s `edit` function requires careful DOM management. The example above uses `React.useEffect` for demonstration, assuming a React-based editor environment. A more robust approach for integrating non-React frameworks into Gutenberg often involves creating a wrapper component that handles the mounting and unmounting of the framework’s root element. For SolidJS, you’d typically create a container `div` and use `render` to mount your SolidJS app into it, ensuring proper cleanup when the block is removed or updated.

`save` Function Implementation

The `save` function is crucial for defining the static HTML output of your block. It should not contain any interactive JavaScript. In our case, we’ll simply display the `generatedCode` attribute if it exists.

The `save` function in the `src/index.js` example above handles this by conditionally rendering the coupon code.

Styling the Block

Add styles for your block in `src/style.scss` and `src/editor.scss`. Ensure these files are imported in `src/index.js`.

/* src/style.scss */
.wp-block-coupon-generator-block-coupon-generator {
    border: 1px solid #ccc;
    padding: 15px;
    margin-bottom: 15px;

    p {
        margin: 0 0 10px 0;
        strong {
            color: #0073aa;
        }
    }
}

/* src/editor.scss */
.coupon-generator-component {
    padding: 10px;
    border: 1px dashed #ddd;
    background-color: #f9f9f9;

    h4 {
        margin-top: 0;
    }

    div {
        margin-bottom: 10px;
        label {
            display: inline-block;
            width: 100px;
            margin-right: 10px;
        }
        input[type="text"],
        input[type="number"] {
            padding: 5px;
            border: 1px solid #ccc;
            width: calc(100% - 120px);
        }
    }

    button {
        padding: 8px 15px;
        background-color: #0073aa;
        color: white;
        border: none;
        cursor: pointer;
        &:disabled {
            background-color: #ccc;
            cursor: not-allowed;
        }
    }

    p {
        margin-top: 15px;
        font-weight: bold;
    }
}

Building and Activating the Block

After implementing the SolidJS component and integrating it into Gutenberg, you need to build the block assets and activate the block in WordPress.

Build Process

Run the build command in your terminal from the block’s directory:

npm run build

This command compiles your JavaScript and CSS into the `build/` directory. For development, use `npm start`, which watches for changes and rebuilds automatically.

Activation

If you are developing a plugin, ensure the plugin is activated in your WordPress admin area. If it’s part of a theme, ensure the theme is active. You should now see the “Coupon Generator” block available in the Gutenberg editor.

Performance Considerations and Advanced Techniques

SolidJS’s core strength is its performance. By compiling to efficient imperative code and using fine-grained reactivity, it avoids the overhead of a virtual DOM. For a Gutenberg block, this translates to:

  • Faster Editor Experience: UI updates in the editor are near-instantaneous, even with complex state changes.
  • Reduced Bundle Size: SolidJS’s compiler can optimize away unused code, leading to smaller JavaScript bundles.
  • Efficient Rendering: Only the necessary DOM nodes are updated when state changes, minimizing repaint and reflow operations.

Memoization and Optimization

While SolidJS is performant by default, you can further optimize by using memoization for expensive calculations or by structuring your components efficiently. For instance, if `generateCouponCode` were computationally intensive, you could memoize its results using `createMemo` if the inputs were signals.

Server-Side Rendering (SSR) and Hydration

For blocks that require server-side rendering, SolidJS offers excellent support. You can render your SolidJS components on the server and then “hydrate” them on the client. This ensures that the initial HTML is delivered quickly and SEO-friendly, with the JavaScript taking over interactivity seamlessly. Integrating SolidJS SSR with WordPress typically involves custom PHP code to render the SolidJS component on the server and enqueueing the corresponding client-side hydration script.

Web Workers for Heavy Computations

If your coupon generation logic were to become extremely complex (e.g., involving large datasets or complex algorithms), consider offloading these computations to Web Workers. This would keep the main thread free, ensuring the UI remains responsive. The SolidJS component would then communicate with the Web Worker via `postMessage`.

Conclusion

By leveraging SolidJS for its high-performance reactive components, we can build sophisticated Gutenberg blocks that offer a fluid user experience and efficient rendering. This approach moves beyond the typical performance considerations of client-side JavaScript frameworks, providing a robust foundation for complex dynamic content within the WordPress ecosystem. The integration, while requiring a custom build setup, unlocks significant performance gains, making it an attractive option for CTOs and enterprise architects prioritizing speed and efficiency.

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

  • WordPress Development Recipe: Secure token-based API authentication for Mailchimp Newsletter in custom plugins
  • Implementing automated compliance reporting for custom portfolio project grids ledgers using custom PhpSpreadsheet components
  • Advanced Diagnostics: Identifying and fixing theme asset blocking in WooCommerce core overrides layouts
  • Debugging Guide: Diagnosing WP_DEBUG notice floods in multi-site network environments with modern tools
  • Step-by-Step Guide: Refactoring legacy hooks to use Singleton Registry Pattern pattern in theme layers

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (658)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (872)
  • PHP (5)
  • PHP Development (38)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (13)
  • WordPress Plugin Development (12)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • WordPress Development Recipe: Secure token-based API authentication for Mailchimp Newsletter in custom plugins
  • Implementing automated compliance reporting for custom portfolio project grids ledgers using custom PhpSpreadsheet components
  • Advanced Diagnostics: Identifying and fixing theme asset blocking in WooCommerce core overrides layouts

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Business & Monetization (390)

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