• 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 Svelte standalone templates

Step-by-Step Guide to building a custom automated coupon generator block for Gutenberg using Svelte standalone templates

Setting Up the Development Environment

Before diving into code, ensure your WordPress development environment is properly configured. This includes having a local WordPress installation (e.g., using Local by Flywheel, Docker, or a LAMP/LEMP stack) and Node.js with npm or yarn installed. We’ll be using Svelte for our frontend components, which requires a build process. For this guide, we’ll assume a basic WordPress plugin structure is already in place. If not, create a new plugin directory (e.g., wp-content/plugins/custom-coupon-generator) and a main plugin file (e.g., custom-coupon-generator.php).

Initialize your project’s Node.js dependencies. Navigate to your plugin’s root directory in your terminal and run:

cd wp-content/plugins/custom-coupon-generator
npm init -y

Next, install the necessary development dependencies. We’ll need Svelte, a bundler (like Rollup or Webpack, though for simplicity we’ll use Rollup here), and plugins to handle Svelte compilation and WordPress asset enqueuing.

npm install --save-dev svelte rollup @rollup/plugin-node-resolve rollup-plugin-svelte @rollup/plugin-commonjs cross-env

Configuring Rollup for Svelte Compilation

Create a rollup.config.js file in your plugin’s root directory to configure the build process. This file will tell Rollup how to find your Svelte components and how to bundle them into JavaScript files that WordPress can understand.

// rollup.config.js
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';

const production = !process.env.ROLLUP_WATCH;

export default {
	input: 'src/main.js', // Entry point for your Svelte app
	output: {
		sourcemap: true,
		format: 'iife', // Immediately Invoked Function Expression for WordPress
		name: 'app', // Global variable name
		file: 'build/bundle.js' // Output file
	},
	plugins: [
		svelte({
			compilerOptions: {
				// enable run-time checks when not in production
				dev: !production
			}
		}),
		// we'll extract any third-party libraries from node_modules
		resolve({
			browser: true,
			dedupe: ['svelte']
		}),
		commonjs(),
	],
	watch: {
		clearScreen: false
	}
};

Create a package.json file (if not already created by npm init) and add build scripts:

{
  "name": "custom-coupon-generator",
  "version": "1.0.0",
  "description": "Custom Gutenberg block for coupon generation",
  "main": "build/bundle.js",
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w"
  },
  "keywords": ["wordpress", "gutenberg", "svelte", "coupon"],
  "author": "Your Name",
  "license": "GPL-2.0-or-later",
  "devDependencies": {
    "@rollup/plugin-commonjs": "^25.0.7",
    "@rollup/plugin-node-resolve": "^15.2.3",
    "cross-env": "^7.0.3",
    "rollup": "^4.12.0",
    "rollup-plugin-svelte": "^7.1.0",
    "svelte": "^4.2.12"
  }
}

Now, create the necessary directories: src and build. Inside the src directory, create an entry file, typically main.js.

Developing the Svelte Component

Let’s create a simple Svelte component for our coupon generator. This component will handle the input fields for coupon details and the logic to generate a coupon code. Create a file named CouponGenerator.svelte inside the src directory.

<!-- src/CouponGenerator.svelte -->
<script>
  import { createEventDispatcher } from 'svelte';

  const dispatch = createEventDispatcher();

  let couponName = '';
  let discountPercentage = 0;
  let expiryDate = '';
  let generatedCode = '';

  function generateCoupon() {
    // Basic coupon code generation logic
    const prefix = couponName.substring(0, 3).toUpperCase();
    const random = Math.random().toString(36).substring(2, 8).toUpperCase();
    generatedCode = `${prefix}-${discountPercentage}-${random}`;

    dispatch('couponGenerated', {
      name: couponName,
      percentage: discountPercentage,
      expiry: expiryDate,
      code: generatedCode
    });
  }
</script>

<div class="coupon-generator-block">
  <h3>Generate New Coupon</h3>
  <div>
    <label for="couponName">Coupon Name:</label>
    <input id="couponName" type="text" bind:value="{couponName}" />
  </div>
  <div>
    <label for="discountPercentage">Discount (%):</label>
    <input id="discountPercentage" type="number" min="1" max="100" bind:value="{discountPercentage}" />
  </div>
  <div>
    <label for="expiryDate">Expiry Date:</label>
    <input id="expiryDate" type="date" bind:value="{expiryDate}" />
  </div>
  <button on:click="{generateCoupon}">Generate Coupon</button>

  {#if generatedCode}
    <div class="generated-coupon">
      <p>Your Coupon Code:</p>
      <strong>{generatedCode}</strong>
      <p>Name: {couponName}, Discount: {discountPercentage}%, Expires: {expiryDate || 'N/A'}</p>
    </div>
  {/if}
</div>

<style>
  .coupon-generator-block {
    border: 1px solid #ccc;
    padding: 15px;
    margin-bottom: 15px;
    background-color: #f9f9f9;
  }
  .coupon-generator-block div {
    margin-bottom: 10px;
  }
  .coupon-generator-block label {
    display: inline-block;
    width: 120px;
    margin-right: 10px;
  }
  .generated-coupon {
    margin-top: 20px;
    padding: 10px;
    background-color: #e0ffe0;
    border: 1px solid #a0d0a0;
  }
</style>

Now, create the entry point file src/main.js to mount the Svelte component.

// src/main.js
import CouponGenerator from './CouponGenerator.svelte';

// This is where the Svelte component will be mounted in the WordPress editor.
// For a Gutenberg block, we'll typically mount it within the block's edit function.
// For this standalone example, we'll just demonstrate mounting it to a div.
// In a real Gutenberg block, this would be part of the registerBlockType definition.

// Example of mounting to a specific element (not directly used in Gutenberg block registration)
// const app = new CouponGenerator({
//   target: document.getElementById('app'),
//   props: {
//     // initial props if any
//   }
// });

// Exporting for potential use in a larger application or module system
export default CouponGenerator;

Integrating with Gutenberg

To use this Svelte component as a Gutenberg block, we need to register a new block type in PHP and then enqueue our compiled JavaScript. The Svelte component will be rendered within the block’s edit and save functions.

First, let’s modify src/main.js to export a function that can be used by Gutenberg’s registerBlockType.

// src/main.js
import { registerBlockType } from '@wordpress/blocks';
import { createElement } from '@wordpress/element';
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, TextControl, NumberControl, DatePicker } from '@wordpress/components';
import { useState } from '@wordpress/element'; // For managing state within the block editor

import CouponGenerator from './CouponGenerator.svelte'; // Import our Svelte component

// This is a wrapper to make Svelte work within Gutenberg's React-based editor.
// We'll use a simple approach here: render the Svelte component and pass data via props.
// For more complex interactions, you might need a more sophisticated bridge.
function SvelteGutenbergWrapper({ attributes, setAttributes }) {
  // State for the block editor
  const [couponName, setCouponName] = useState(attributes.couponName || '');
  const [discountPercentage, setDiscountPercentage] = useState(attributes.discountPercentage || 0);
  const [expiryDate, setExpiryDate] = useState(attributes.expiryDate || '');
  const [generatedCode, setGeneratedCode] = useState(attributes.generatedCode || '');

  // Handler for when Svelte component emits an event
  const handleCouponGenerated = (event) => {
    const { name, percentage, expiry, code } = event.detail;
    setCouponName(name);
    setDiscountPercentage(percentage);
    setExpiryDate(expiry);
    setGeneratedCode(code);
    setAttributes({
      couponName: name,
      discountPercentage: percentage,
      expiryDate: expiry,
      generatedCode: code
    });
  };

  // We need to render the Svelte component. Since Gutenberg uses React,
  // we can't directly mount a Svelte component into a React component's render method.
  // A common workaround is to use a placeholder div and manually mount/unmount Svelte.
  // For simplicity in this example, we'll simulate the Svelte component's output
  // using Gutenberg components for the editor view, and then use the generated code
  // in the save function. A true Svelte integration would involve a more complex bridge.

  // For the editor view, let's use Gutenberg components to mirror the Svelte inputs
  // and display the generated code.
  return createElement(
    'div',
    { className: 'coupon-generator-editor-wrapper' },
    createElement(InspectorControls, null,
      createElement(PanelBody, { title: 'Coupon Settings' },
        createElement(TextControl, {
          label: 'Coupon Name',
          value: couponName,
          onChange: (newName) => {
            setCouponName(newName);
            setAttributes({ couponName: newName });
          },
        }),
        createElement(NumberControl, {
          label: 'Discount (%)',
          value: discountPercentage,
          onChange: (newPercentage) => {
            setDiscountPercentage(newPercentage);
            setAttributes({ discountPercentage: newPercentage });
          },
          min: 1,
          max: 100,
        }),
        // Note: DatePicker is more complex for direct binding, using TextControl for simplicity here
        createElement(TextControl, {
          label: 'Expiry Date (YYYY-MM-DD)',
          value: expiryDate,
          onChange: (newExpiry) => {
            setExpiryDate(newExpiry);
            setAttributes({ expiryDate: newExpiry });
          },
          type: 'date'
        })
      )
    ),
    createElement(
      'div',
      { className: 'coupon-generator-block-preview' },
      createElement('h3', null, 'Coupon Generator'),
      generatedCode ? createElement(
        'div',
        { className: 'generated-coupon-preview' },
        createElement('p', null, 'Generated Coupon:'),
        createElement('strong', null, generatedCode),
        createElement('p', null, `Name: ${couponName}, Discount: ${discountPercentage}%, Expires: ${expiryDate || 'N/A'}`)
      ) : createElement('p', null, 'Enter coupon details in the sidebar to generate a code.')
    )
  );
}

// Register the block
registerBlockType('custom-coupon-generator/block', {
  title: 'Custom Coupon Generator',
  icon: 'tag', // WordPress Dashicon
  category: 'common', // Or a custom category
  attributes: {
    couponName: {
      type: 'string',
      default: '',
    },
    discountPercentage: {
      type: 'number',
      default: 0,
    },
    expiryDate: {
      type: 'string',
      default: '',
    },
    generatedCode: {
      type: 'string',
      default: '',
    },
  },
  edit: SvelteGutenbergWrapper,
  save: ({ attributes }) => {
    // The save function determines what is saved to the database.
    // We'll save the generated coupon details.
    const { couponName, discountPercentage, expiryDate, generatedCode } = attributes;

    // If a coupon was generated, render its details. Otherwise, show a placeholder.
    if (generatedCode) {
      return createElement(
        'div',
        { className: 'coupon-generator-saved-output' },
        createElement('h4', null, `Coupon: ${couponName}`),
        createElement('p', null, `Code: ${generatedCode}`),
        createElement('p', null, `Discount: ${discountPercentage}%`),
        expiryDate && createElement('p', null, `Expires: ${expiryDate}`)
      );
    } else {
      return createElement('p', null, 'Coupon details will appear here once generated.');
    }
  },
});

Now, update your main plugin file (e.g., custom-coupon-generator.php) to register the block and enqueue the compiled JavaScript.

<?php
/**
 * Plugin Name: Custom Coupon Generator
 * Description: A custom Gutenberg block for generating coupons using Svelte.
 * Version: 1.0.0
 * Author: Your Name
 * License: GPL-2.0-or-later
 * Text Domain: custom-coupon-generator
 */

function cgg_register_coupon_block() {
    // Register the block using the script handle.
    register_block_type( 'custom-coupon-generator/block', array(
        'editor_script' => 'cgg-editor-script',
        'editor_style'  => 'cgg-editor-style',
        'style'         => 'cgg-style',
        // 'render_callback' => 'cgg_render_coupon_block', // Optional: if you need server-side rendering
    ) );
}
add_action( 'init', 'cgg_register_coupon_block' );

function cgg_enqueue_block_assets() {
    // Enqueue the compiled JavaScript for the editor and frontend.
    wp_enqueue_script(
        'cgg-editor-script', // Handle
        plugins_url( 'build/bundle.js', __FILE__ ), // Path to your compiled JS
        array( 'wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-i18n' ), // Dependencies
        filemtime( plugin_dir_path( __FILE__ ) . 'build/bundle.js' ) // Version based on file modification time
    );

    // Enqueue editor-only styles if needed
    wp_enqueue_style(
        'cgg-editor-style',
        plugins_url( 'build/editor.css', __FILE__ ), // Assuming you compile CSS for the editor
        array( 'wp-edit-blocks' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'build/editor.css' )
    );

    // Enqueue frontend styles
    wp_enqueue_style(
        'cgg-style',
        plugins_url( 'build/style.css', __FILE__ ), // Assuming you compile CSS for the frontend
        array(),
        filemtime( plugin_dir_path( __FILE__ ) . 'build/style.css' )
    );
}
add_action( 'enqueue_block_assets', 'cgg_enqueue_block_assets' );

// Optional: Server-side rendering callback if you need it
// function cgg_render_coupon_block( $attributes ) {
//     $coupon_name = isset( $attributes['couponName'] ) ? esc_html( $attributes['couponName'] ) : '';
//     $discount_percentage = isset( $attributes['discountPercentage'] ) ? intval( $attributes['discountPercentage'] ) : 0;
//     $expiry_date = isset( $attributes['expiryDate'] ) ? esc_html( $attributes['expiryDate'] ) : '';
//     $generated_code = isset( $attributes['generatedCode'] ) ? esc_html( $attributes['generatedCode'] ) : '';
//
//     if ( ! $generated_code ) {
//         return '<p>Coupon details will appear here once generated.</p>';
//     }
//
//     ob_start();
//     ?>
//     <div class="coupon-generator-rendered-output">
//         <h4><?php echo esc_html__( 'Coupon:', 'custom-coupon-generator' ); ?> <?php echo $coupon_name; ?></h4>
//         <p><?php echo esc_html__( 'Code:', 'custom-coupon-generator' ); ?> <?php echo $generated_code; ?></p>
//         <p><?php echo sprintf( esc_html__( 'Discount: %d%%', 'custom-coupon-generator' ), $discount_percentage ); ?></p>
//         <?php if ( $expiry_date ) : ?>
//             <p><?php echo esc_html__( 'Expires:', 'custom-coupon-generator' ); ?> <?php echo $expiry_date; ?></p>
//         <?php endif; ?>
//     </div>
//     <?php
//     return ob_get_clean();
// }

Building the Assets

With the configuration and code in place, it’s time to build the JavaScript and CSS assets. Run the build command from your plugin’s root directory:

npm run build

This command will execute Rollup, compile your Svelte component, and output build/bundle.js. If you’ve configured Rollup to output CSS (e.g., using rollup-plugin-css-only or similar), it will also generate build/style.css and potentially build/editor.css.

Testing the Block

Activate your plugin in the WordPress admin area. Then, navigate to the post or page editor. You should now be able to search for “Custom Coupon Generator” and add the block to your content. The block’s editor interface will appear, allowing you to input coupon details. The “Inspector Controls” (sidebar) will provide fields for coupon name, discount, and expiry date. As you update these fields, the block preview will reflect the generated coupon code. Save the post and view it on the frontend to see the saved output.

Note on Svelte in Gutenberg: The provided SvelteGutenbergWrapper is a simplified approach. Directly embedding a Svelte component within Gutenberg’s React environment requires careful state management and potentially a more robust bridging mechanism for complex interactions. For this example, we’ve used Gutenberg’s own components in the edit function to manage state and display the output, effectively simulating the Svelte component’s behavior within the React context. The save function then renders the final output based on the stored attributes.

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

  • Optimizing p99 database query response latency in multi-site Model-View-Controller (MVC) modular custom tables
  • Step-by-Step Guide to building a custom automatic translation switcher block for Gutenberg using Vue micro-frontends
  • How to design secure Salesforce CRM webhook listeners using signature validation and payload queues
  • How to securely integrate Stripe Payment webhook endpoints into WordPress custom plugins using WP HTTP API
  • How to securely integrate Stripe Payment webhook endpoints into WordPress custom plugins using REST API Controllers

Categories

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

Recent Posts

  • Optimizing p99 database query response latency in multi-site Model-View-Controller (MVC) modular custom tables
  • Step-by-Step Guide to building a custom automatic translation switcher block for Gutenberg using Vue micro-frontends
  • How to design secure Salesforce CRM webhook listeners using signature validation and payload queues

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (870)
  • Debugging & Troubleshooting (653)
  • Security & Compliance (638)
  • 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