• 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 Vue micro-frontends

Step-by-Step Guide to building a custom automated coupon generator block for Gutenberg using Vue micro-frontends

Project Setup: WordPress Plugin and Vue Micro-Frontend

We’ll begin by establishing the foundational structure for our custom Gutenberg block. This involves creating a standard WordPress plugin that will house our block registration and asset enqueuing. Concurrently, we’ll set up a separate Vue.js project to serve as our micro-frontend for the block’s editor interface.

First, create a new WordPress plugin directory. For this example, we’ll name it gutenberg-coupon-generator.

  • Navigate to your WordPress installation’s wp-content/plugins/ directory.
  • Create a new folder: mkdir gutenberg-coupon-generator
  • Change into the new directory: cd gutenberg-coupon-generator
  • Create the main plugin file: touch gutenberg-coupon-generator.php

Populate gutenberg-coupon-generator.php with the essential plugin header and registration logic.

 'gutenberg-coupon-generator-block-editor',
        'editor_style'  => 'gutenberg-coupon-generator-block-editor',
        'render_callback' => 'gutenberg_coupon_generator_render_block',
    ) );
}
add_action( 'init', 'gutenberg_coupon_generator_register_block' );

/**
 * Server-side rendering for the coupon block.
 *
 * @param array $attributes Block attributes.
 * @return string HTML output.
 */
function gutenberg_coupon_generator_render_block( $attributes ) {
    $coupon_code = isset( $attributes['couponCode'] ) ? sanitize_text_field( $attributes['couponCode'] ) : 'SAMPLECODE';
    $discount_value = isset( $attributes['discountValue'] ) ? sanitize_text_field( $attributes['discountValue'] ) : '10%';
    $expiry_date = isset( $attributes['expiryDate'] ) ? sanitize_text_field( $attributes['expiryDate'] ) : '';

    ob_start();
    ?>
    
Coupon Code:
Discount:
Expires On:

Next, we'll set up the Vue micro-frontend. This will be managed by a separate build process, typically using Vite or Vue CLI. For this guide, we'll assume a Vite setup.

In the root of your plugin directory (gutenberg-coupon-generator/), create a new directory for your Vue application.

  • Create directory: mkdir vue-app
  • Change into the directory: cd vue-app
  • Initialize a new Vue project (using Vite): npm create vite@latest . --template vue (or yarn/pnpm equivalent)
  • Install dependencies: npm install

Now, we need to configure Vite to build our Vue application into a format that WordPress can consume. This involves setting the correct build target and output directory.

Modify your vue-app/vite.config.js file as follows:

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  build: {
    outDir: path.resolve(__dirname, '../build'), // Output to the plugin's build directory
    emptyOutDir: true, // Clear the build directory before each build
    rollupOptions: {
      output: {
        entryFileNames: `index.js`, // Single JS entry file
        assetFileNames: (assetInfo) => {
          if (assetInfo.name.endsWith('.css')) {
            return `index.css`; // Single CSS file
          }
          return assetInfo.name;
        },
        manualChunks: undefined, // Ensure no chunking for a single file output
      },
    },
    lib: {
      entry: path.resolve(__dirname, 'src/main.js'), // Your main Vue entry point
      name: 'GutenbergCouponGenerator',
      fileName: (format) => `index.js`, // Output filename for JS
    },
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
});

The build.lib configuration is crucial for creating a library-like output suitable for WordPress. We're directing the output to the parent directory's build folder, which is where our PHP file expects to find index.js and index.css.

Gutenberg Block Registration and Editor Component

Now, let's define the Gutenberg block's attributes and register it using the WordPress Script API. This registration will point to the JavaScript file generated by our Vue build process.

The wp_register_script and register_block_type calls in gutenberg-coupon-generator.php are set up to load build/index.js and build/index.css. The $asset_file variable dynamically loads dependencies and the version number from build/index.asset.php, which is automatically generated by `@wordpress/scripts` or our Vite build if configured correctly.

The render_callback in register_block_type is essential for server-side rendering. This ensures that the coupon block displays correctly even when JavaScript is disabled or before it loads on the frontend.

Vue Editor Component Implementation

Within your Vue application (vue-app/src/), create the main entry point and the editor component.

vue-app/src/main.js:

import { registerBlockType } from '@wordpress/blocks';
import { App } from './App'; // Our main Vue component

// Import styles for the editor
import './editor.scss';

registerBlockType('gutenberg-coupon-generator/coupon-block', {
    edit: App,
    save: () => null, // We'll use a server-side render_callback for saving
});

The save: () => null is a common pattern when using server-side rendering callbacks. WordPress will not attempt to serialize the block's content from the editor; instead, it will rely entirely on the PHP callback to generate the frontend HTML.

vue-app/src/App.vue:

<template>
  <div class="coupon-block-editor">
    <Panel header="Coupon Settings">
      <TextControl
        label="Coupon Code"
        v-model="attributes.couponCode"
        @input="updateAttributes"
      />
      <TextControl
        label="Discount Value (e.g., 10%, $5)"
        v-model="attributes.discountValue"
        @input="updateAttributes"
      />
      <DateTimePicker
        label="Expiry Date"
        v-model="attributes.expiryDate"
        @input="updateAttributes"
      />
    </Panel>
    <div class="coupon-preview">
      <h4>Preview:</h4>
      <div class="coupon-code">
        <strong>Coupon Code:</strong> <span>{{ attributes.couponCode || 'N/A' }}</span>
      </div>
      <div class="discount-info">
        <strong>Discount:</strong> <span>{{ attributes.discountValue || 'N/A' }}</span>
      </div>
      <div v-if="attributes.expiryDate" class="expiry-info">
        <strong>Expires On:</strong> <span>{{ formattedExpiryDate }}</span>
      </div>
    </div>
  </div>
</template>

<script>
import { useBlockProps } from '@wordpress/block-editor';
import { Panel, TextControl, DateTimePicker } from '@wordpress/components';
import { computed } from 'vue';

export default {
  components: {
    Panel,
    TextControl,
    DateTimePicker,
  },
  props: {
    attributes: {
      type: Object,
      required: true,
    },
    setAttributes: {
      type: Function,
      required: true,
    },
  },
  setup(props) {
    const blockProps = useBlockProps();

    const updateAttributes = (value, key) => {
      props.setAttributes({ [key]: value });
    };

    const formattedExpiryDate = computed(() => {
      if (!props.attributes.expiryDate) return '';
      try {
        const date = new Date(props.attributes.expiryDate);
        return date.toLocaleDateString(); // Adjust formatting as needed
      } catch (e) {
        console.error("Invalid date format:", props.attributes.expiryDate, e);
        return 'Invalid Date';
      }
    });

    return {
      blockProps,
      updateAttributes,
      formattedExpiryDate,
    };
  },
};
</script>

<style lang="scss">
.coupon-block-editor {
  padding: 15px;
  border: 1px solid #ddd;
  background-color: #f9f9f9;

  .coupon-preview {
    margin-top: 20px;
    padding: 10px;
    border: 1px dashed #ccc;
    background-color: #fff;

    h4 {
      margin-top: 0;
    }
  }
}
</style>



In this Vue component:

  • We import necessary components from @wordpress/block-editor and @wordpress/components.
  • useBlockProps is used to get the necessary props for the block wrapper.
  • TextControl and DateTimePicker are used for input fields.
  • The attributes prop is bound to our input fields, and setAttributes is called on input to update the block's state.
  • A computed property formattedExpiryDate is used for cleaner date display in the preview.
  • The save: () => null in main.js means the editor component is responsible for rendering the preview, and the actual saved content is handled by the PHP callback.

We also need a basic editor.scss file for styling the block in the editor.

.coupon-block-editor {
  // Add any specific styles for the editor interface here
  border: 1px solid #e0e0e0;
  padding: 10px;
  background-color: #ffffff;
}

Build Process and Integration

With the Vue application structured, we need to define the build script in vue-app/package.json to compile the Vue code into the WordPress plugin's build directory.

Modify vue-app/package.json:

{
  "name": "vue-app",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "vue": "^3.3.4",
    "@wordpress/block-editor": "^12.10.0",
    "@wordpress/blocks": "^12.10.0",
    "@wordpress/components": "^26.10.0",
    "@wordpress/i18n": "^4.45.0"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.2.0",
    "sass": "^1.69.5",
    "vite": "^4.4.5"
  }
}

Note the addition of WordPress packages as dependencies. These are typically installed via npm within the Vue project's scope. You might need to run npm install @wordpress/block-editor @wordpress/blocks @wordpress/components @wordpress/i18n separately if they aren't automatically included.

To build the Vue application, navigate to the vue-app directory in your terminal and run:

cd gutenberg-coupon-generator/vue-app
npm run build

This command will execute Vite's build process, creating index.js and index.css in the parent directory's build/ folder. WordPress will then be able to enqueue these assets.

After the build, WordPress will automatically generate the build/index.asset.php file. This file contains metadata about the script's dependencies and version, which is crucial for WordPress to correctly load the script.

Finalizing and Testing

Ensure your plugin directory structure looks like this:

gutenberg-coupon-generator/
├── build/
│   ├── index.js
│   ├── index.css
│   └── index.asset.php
├── gutenberg-coupon-generator.php
└── vue-app/
    ├── src/
    │   ├── App.vue
    │   ├── main.js
    │   └── editor.scss
    ├── vite.config.js
    └── package.json
    └── ... (other Vue project files)

Activate the "Gutenberg Coupon Generator" plugin in your WordPress admin area. Then, navigate to the post or page editor. You should be able to search for "Coupon Generator" and add the block. The Vue-powered editor interface will appear, allowing you to input coupon details. Save the post, and view it on the frontend to verify the server-rendered output.

This setup provides a robust way to leverage modern JavaScript frameworks within Gutenberg blocks, offering a more dynamic and maintainable development experience for complex editor interfaces.

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

  • Step-by-Step Guide: Refactoring legacy hooks to use Singleton Registry Pattern pattern in theme layers
  • How to securely integrate Slack Webhooks integration endpoints into WordPress custom plugins using Shortcode API
  • Step-by-Step Guide: Offloading high-frequency user transaction ledgers metadata writes to a Redis KV store
  • How to design secure Firebase Realtime DB webhook listeners using signature validation and payload queues
  • How to securely integrate Shopify headless API endpoints into WordPress custom plugins using Filesystem API

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 (9)
  • WordPress Plugin Development (12)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Step-by-Step Guide: Refactoring legacy hooks to use Singleton Registry Pattern pattern in theme layers
  • How to securely integrate Slack Webhooks integration endpoints into WordPress custom plugins using Shortcode API
  • Step-by-Step Guide: Offloading high-frequency user transaction ledgers metadata writes to a Redis KV store

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