• 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 multi-currency switcher block for Gutenberg using Vue micro-frontends

Step-by-Step Guide to building a custom multi-currency switcher block for Gutenberg using Vue micro-frontends

Project Setup and Micro-Frontend Architecture

This guide details the construction of a custom Gutenberg block for WordPress, enabling a multi-currency switcher. We’ll leverage Vue.js as a micro-frontend, encapsulating the switcher’s logic and UI within a self-contained, independently deployable unit. This approach enhances maintainability, scalability, and allows for specialized development teams to work on distinct components without deep WordPress core knowledge.

Our architecture will involve a primary WordPress plugin that registers the Gutenberg block. This block will then load a compiled Vue.js application. The Vue app will handle the currency selection UI and communicate currency changes back to the WordPress environment, likely via AJAX or custom REST API endpoints.

WordPress Plugin Boilerplate and Block Registration

We begin by creating a standard WordPress plugin. This plugin will house our block registration logic and serve the compiled Vue.js assets.

Create a directory for your plugin, e.g., wp-content/plugins/multi-currency-switcher. Inside, create the main plugin file, multi-currency-switcher.php.

<?php
/**
 * Plugin Name: Multi-Currency Switcher Block
 * Description: A custom Gutenberg block for multi-currency switching using Vue.js micro-frontend.
 * Version: 1.0.0
 * Author: Your Name
 * License: GPL-2.0-or-later
 * Text Domain: multi-currency-switcher
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

/**
 * Register the Gutenberg block.
 */
function mcs_register_block() {
    // Automatically load dependencies and version.
    $asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php');

    wp_register_script(
        'mcs-block-editor-script',
        plugins_url( 'build/index.js', __FILE__ ),
        $asset_file['dependencies'],
        $asset_file['version']
    );

    wp_register_style(
        'mcs-block-editor-style',
        plugins_url( 'build/index.css', __FILE__ ),
        array( 'wp-edit-blocks' ),
        $asset_file['version']
    );

    wp_register_style(
        'mcs-frontend-style',
        plugins_url( 'build/style-index.css', __FILE__ ),
        array(),
        $asset_file['version']
    );

    register_block_type( 'mcs/currency-switcher', array(
        'editor_script' => 'mcs-block-editor-script',
        'editor_style'  => 'mcs-block-editor-style',
        'style'         => 'mcs-frontend-style',
    ) );
}
add_action( 'init', 'mcs_register_block' );

/**
 * Enqueue frontend assets.
 */
function mcs_enqueue_frontend_assets() {
    // Enqueue the compiled Vue.js application assets.
    // This assumes your Vue app is built into a 'public' directory within 'build'.
    wp_enqueue_script(
        'mcs-vue-app',
        plugins_url( 'build/app.js', __FILE__ ), // Path to your compiled Vue app entry point
        array(), // No dependencies from WordPress core scripts
        filemtime( plugin_dir_path( __FILE__ ) . 'build/app.js' ),
        true // Load in footer
    );

    wp_enqueue_style(
        'mcs-vue-app-style',
        plugins_url( 'build/app.css', __FILE__ ), // Path to your compiled Vue app CSS
        array(),
        filemtime( plugin_dir_path( __FILE__ ) . 'build/app.css' )
    );

    // Pass data to the Vue app, e.g., available currencies, default currency.
    wp_localize_script( 'mcs-vue-app', 'mcs_data', array(
        'available_currencies' => array( 'USD', 'EUR', 'GBP' ), // Example data
        'default_currency'     => 'USD',
        'rest_url'             => esc_url_raw( rest_url( 'mcs/v1/set-currency' ) ),
    ) );
}
add_action( 'wp_enqueue_scripts', 'mcs_enqueue_frontend_assets' );

/**
 * Register REST API endpoint for currency updates.
 */
function mcs_register_rest_route() {
    register_rest_route( 'mcs/v1', '/set-currency', array(
        'methods'             => 'POST',
        'callback'            => 'mcs_handle_set_currency',
        'permission_callback' => '__return_true', // For simplicity, allow all. Implement proper auth for production.
    ) );
}
add_action( 'rest_api_init', 'mcs_register_rest_route' );

/**
 * Callback for the REST API endpoint.
 */
function mcs_handle_set_currency( WP_REST_Request $request ) {
    $currency = $request->get_param( 'currency' );

    if ( ! $currency ) {
        return new WP_Error( 'invalid_currency', 'Currency parameter is missing.', array( 'status' => 400 ) );
    }

    // In a real application, you would store this preference.
    // For example, in user meta, a transient, or a cookie.
    // For demonstration, we'll just return success.
    // Example: setcookie( 'mcs_selected_currency', $currency, time() + ( 86400 * 30 ), '/' );

    return new WP_REST_Response( array( 'success' => true, 'message' => 'Currency set to ' . sanitize_text_field( $currency ) ), 200 );
}



Gutenberg Block Editor Integration

The register_block_type function in multi-currency-switcher.php points to build/index.asset.php and build/index.js. These files will be generated by our JavaScript build process. For the block editor, we'll use React (as is standard for Gutenberg) to define the block's attributes and its editor interface. This interface will simply render a placeholder or a basic representation of the switcher, as the actual dynamic UI will be handled by the Vue micro-frontend on the frontend.

Create a JavaScript source directory, e.g., src/, and inside it, create index.js and edit.js.

// src/index.js
import { registerBlockType } from '@wordpress/blocks';
import './style.scss'; // Styles for the block editor and frontend
import Edit from './edit';
import save from './save'; // We won't use save for dynamic content, but it's good practice

registerBlockType( 'mcs/currency-switcher', {
    title: 'Multi-Currency Switcher',
    icon: 'money', // Choose an appropriate icon
    category: 'widgets', // Or any other suitable category
    attributes: {
        // Define any attributes if needed for block settings, e.g., default currency selection in editor
    },
    edit: Edit,
    save: () => null, // Render nothing in the backend, the Vue app handles frontend rendering
} );
// src/edit.js
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import './editor.scss';

export default function Edit() {
    const blockProps = useBlockProps();

    return (
        <div { ...blockProps }>
            <p>{ __( 'Multi-Currency Switcher (Editor Preview)', 'multi-currency-switcher' ) }</p>
            <p>{ __( 'The actual switcher will appear on the frontend.', 'multi-currency-switcher' ) }</p>
        </div>
    );
}

Vue.js Micro-Frontend Development

This is where the core of our switcher logic resides. We'll set up a Vue.js project, build it into a single JavaScript file and a CSS file, and then copy these into the WordPress plugin's build/ directory.

First, set up a Vue.js project. You can use Vue CLI or Vite. For this example, let's assume a Vite setup for its speed and simplicity.

# Navigate to your plugin's root directory
cd wp-content/plugins/multi-currency-switcher

# Create a Vue project (e.g., using Vite)
npm create vite@latest vue-app --template vue
cd vue-app

# Install dependencies
npm install

# Install Axios for API requests
npm install axios

Now, structure your Vue app. Create a src/components/CurrencySwitcher.vue component.

<!-- vue-app/src/components/CurrencySwitcher.vue -->
<template>
  <div class="currency-switcher">
    <label for="currency-select">Select Currency: </label>
    <select id="currency-select" v-model="selectedCurrency" @change="updateCurrency">
      <option v-for="currency in availableCurrencies" :key="currency" :value="currency">
        {{ currency }}
      </option>
    </select>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';

// Access data passed from WordPress
const { available_currencies, default_currency, rest_url } = window.mcs_data;

const availableCurrencies = ref(available_currencies || ['USD', 'EUR', 'GBP']);
const selectedCurrency = ref(default_currency || 'USD');

// Function to update currency via API
const updateCurrency = async () => {
  try {
    const response = await axios.post(rest_url, {
      currency: selectedCurrency.value,
    });
    if (response.data.success) {
      console.log('Currency updated successfully:', selectedCurrency.value);
      // Optionally, trigger a page reload or update prices dynamically
      // window.location.reload();
    } else {
      console.error('Failed to update currency:', response.data.message);
    }
  } catch (error) {
    console.error('Error updating currency:', error);
  }
};

// Set initial currency from WordPress data
onMounted(() => {
  // If a cookie or session data exists, use that instead of default
  // For simplicity, we're using the default here.
  selectedCurrency.value = default_currency || 'USD';
});
</script>

<style scoped>
.currency-switcher {
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  display: inline-block;
}
.currency-switcher label {
  margin-right: 10px;
  font-weight: bold;
}
.currency-switcher select {
  padding: 5px;
  border: 1px solid #ddd;
  border-radius: 3px;
}
</style>

Next, create the main Vue app entry point, vue-app/src/main.js, and a corresponding CSS file, vue-app/src/style.css.

// vue-app/src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import CurrencySwitcher from './components/CurrencySwitcher.vue';

// Mount the Vue app to a specific element on the page.
// The Gutenberg block will provide this mount point.
// For simplicity, we'll assume a global mount point or a specific ID.
// In a real scenario, you might want to dynamically find the block's container.

const app = createApp(App);
app.component('CurrencySwitcher', CurrencySwitcher); // Register globally if needed
app.mount('#mcs-currency-switcher-app'); // This ID should be present in the frontend where the block is rendered.
<!-- vue-app/src/App.vue -->
<template>
  <div id="app">
    <CurrencySwitcher />
  </div>
</template>

<script setup>
// No specific logic needed in App.vue for this simple case
</script>

<style>
/* Global styles for the Vue app */
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  margin-top: 10px;
}
</style>
/* vue-app/src/style.css */
/* Global styles for the Vue app */
body {
  margin: 0;
  padding: 0;
}

Build Process and Asset Integration

We need to configure Vite to output the compiled assets into a location that our WordPress plugin can access. Specifically, we want the output to go into the plugin's build/ directory.

Modify vue-app/vite.config.js:

// vue-app/vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';

export default defineConfig({
  plugins: [vue()],
  build: {
    // Output directory relative to the plugin's root
    outDir: '../build',
    assetsDir: '.', // Place JS and CSS directly in outDir
    rollupOptions: {
      input: {
        app: 'src/main.js', // Entry point for the Vue app
      },
      output: {
        entryFileNames: `app.js`, // Name of the main JS file
        assetFileNames: (assetInfo) => {
          if (assetInfo.name.endsWith('.css')) {
            return `app.css`; // Name of the main CSS file
          }
          return `assets/[name]-[hash][extname]`;
        },
        // Ensure that the output is a single chunk for easier inclusion
        manualChunks: undefined, // Disable code splitting for a single file
      },
    },
    // Minify output
    minify: true,
  },
  // Define alias for @ to src
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
});

Now, run the build command from the vue-app/ directory:

npm run build

This will create a build/ directory inside your WordPress plugin's root. This directory should contain app.js and app.css. The WordPress plugin's PHP file is already set up to enqueue these.

Frontend Rendering and Block Placement

On the frontend, the Gutenberg block will render a placeholder element. The Vue micro-frontend will then find this element and mount itself onto it. However, our current setup mounts the Vue app to a global ID #mcs-currency-switcher-app. To make this work with Gutenberg blocks, we need to ensure that each instance of the block provides a unique mount point.

We need to adjust the Vue app's mounting logic and how the Gutenberg block renders its frontend output.

Modifying the Gutenberg Block's Save Function

Instead of returning null in the save function, we'll return a static HTML element that will serve as the mount point for our Vue app. We'll also need to pass unique IDs.

// src/save.js
import { useBlockProps } from '@wordpress/block-editor';

export default function save( { attributes } ) {
    const blockProps = useBlockProps.save();
    // Generate a unique ID for each instance of the block
    const mountId = `mcs-currency-switcher-${blockProps.id}`;

    return (
        <div { ...blockProps }>
            <div id={ mountId }></div>
        </div>
    );
}

Update src/index.js to use this new save function:

// src/index.js (updated)
import { registerBlockType } from '@wordpress/blocks';
import './style.scss';
import Edit from './edit';
import save from './save'; // Import the new save function

registerBlockType( 'mcs/currency-switcher', {
    title: 'Multi-Currency Switcher',
    icon: 'money',
    category: 'widgets',
    attributes: {
        // Add an attribute for the unique ID if needed, though blockProps.id is usually sufficient
    },
    edit: Edit,
    save: save, // Use the save function that renders the mount point
} );

Dynamically Mounting Vue App Instances

The Vue app needs to be instantiated for each block instance on the page. We'll modify the frontend enqueueing logic and the Vue app's entry point.

First, let's adjust the Vue app's entry point to handle multiple instances. Instead of a single main.js, we can create a factory function or a way to initialize the app for each detected mount point.

// vue-app/src/main.js (updated for dynamic mounting)
import { createApp } from 'vue';
import App from './App.vue';
import CurrencySwitcher from './components/CurrencySwitcher.vue';

// Function to initialize the Vue app for a specific mount point
const initializeCurrencySwitcher = (mountElementId) => {
  const mountElement = document.getElementById(mountElementId);
  if (!mountElement) {
    console.error(`Mount element with ID "${mountElementId}" not found.`);
    return;
  }

  // Create a new Vue app instance for this specific mount point
  const app = createApp(App);
  app.component('CurrencySwitcher', CurrencySwitcher);

  // Pass the unique mount ID to the Vue app instance if needed,
  // or directly use it to mount.
  // For this example, we'll mount directly.
  app.mount(mountElement);
};

// Find all potential mount points and initialize the app for each
document.addEventListener('DOMContentLoaded', () => {
  const mountPoints = document.querySelectorAll('[id^="mcs-currency-switcher-"]');
  mountPoints.forEach(mountPoint => {
    initializeCurrencySwitcher(mountPoint.id);
  });
});

// Export the function for potential programmatic use if needed
export { initializeCurrencySwitcher };

Ensure your vue-app/vite.config.js is configured to output a single app.js and app.css. The rollupOptions.output.manualChunks: undefined is crucial for this.

The WordPress PHP file multi-currency-switcher.php enqueues build/app.js and build/app.css. The wp_localize_script call passes necessary data. The DOMContentLoaded listener in the Vue app will then find all elements with IDs starting with mcs-currency-switcher- and mount a Vue instance for each.

Build Process for WordPress Block Assets

The Gutenberg block editor scripts (index.js, edit.js, save.js) and styles (style.scss, editor.scss) need to be compiled into the build/ directory as well. This is typically done using `@wordpress/scripts`.

Install the necessary development dependencies in your plugin's root directory:

# Navigate to your plugin's root directory
cd wp-content/plugins/multi-currency-switcher

# Install WordPress scripts and development dependencies
npm install @wordpress/scripts --save-dev
npm install sass --save-dev

Add build scripts to your plugin's package.json (create one if it doesn't exist):

{
  "name": "multi-currency-switcher",
  "version": "1.0.0",
  "description": "Custom Gutenberg block for multi-currency switching.",
  "main": "build/index.js",
  "scripts": {
    "build": "wp-scripts build --output-path=build",
    "start": "wp-scripts start --output-path=build"
  },
  "keywords": ["wordpress", "gutenberg", "vue", "micro-frontend"],
  "author": "Your Name",
  "license": "GPL-2.0-or-later",
  "devDependencies": {
    "@wordpress/scripts": "^27.0.0",
    "sass": "^1.69.5"
  }
}

Now you can run the build process for the Gutenberg block assets:

# From the plugin's root directory
npm run build

This command will compile your src/ files into the build/ directory, creating index.js, index.asset.php, and style-index.css. The index.asset.php file is crucial as it lists the dependencies and version for the block editor script, which is automatically generated by `@wordpress/scripts`.

Deployment and Workflow Considerations

For a production environment, you'll need a robust build pipeline. The workflow would typically involve:

  • Running npm run build within the vue-app/ directory to compile the Vue micro-frontend.
  • Running npm run build within the plugin's root directory to compile the Gutenberg block assets.
  • Committing both the compiled assets (build/app.js, build/app.css, build/index.js, etc.) and the source files (src/, vue-app/src/) to your version control system.
  • Alternatively, configure your deployment process to run these build commands automatically on deployment.

Important Note on Compiled Assets: While committing compiled assets can simplify deployment, it's often preferred to only commit source files and have the build process run on the server or CI/CD pipeline. This ensures that the assets are always built with the correct environment variables and dependencies.

Advanced Considerations and Future Enhancements

State Management: For more complex scenarios, consider a state management solution like Pinia or Vuex within the Vue micro-frontend. This would help manage currency-related state across different parts of your Vue application if it grows.

Dynamic Price Loading: Instead of relying on server-side rendering for prices, the Vue app could fetch prices dynamically via AJAX calls to a custom WordPress REST API endpoint after a currency is selected. This would require a more sophisticated API and potentially client-side caching.

User Preferences: Implement persistent storage for user currency preferences. This could be via WordPress user meta (if logged in), browser cookies, or local storage. The Vue app would read this preference on load and update the backend accordingly.

Internationalization (i18n): Ensure both the Gutenberg block editor strings and the Vue micro-frontend strings are translatable. Use WordPress's i18n functions for the block editor and Vue's i18n libraries (like Vue I18n) for the frontend.

Security: The provided REST API endpoint has 'permission_callback' => '__return_true' for simplicity. In a production environment, this must be secured. Implement nonce checks, user capability checks, or other appropriate authentication mechanisms.

Error Handling: Enhance error handling in both the JavaScript and PHP code. Provide user-friendly messages for API failures or unexpected states.

By following these steps, you can build a flexible and maintainable multi-currency switcher using a Vue.js micro-frontend integrated into a custom WordPress Gutenberg block. This architecture promotes modularity and allows for independent development and deployment of frontend components.

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

  • How to build custom Genesis child themes extensions utilizing modern Heartbeat API schemas
  • Step-by-Step Guide to building a custom XML sitemap generator block for Gutenberg using Vanilla CSS shadow DOM style layers
  • WordPress Development Recipe: Secure token-based API authentication for Zapier dynamic webhooks in custom plugins
  • Troubleshooting broken WP-Cron schedules in production when using modern ACF Pro dynamic fields wrappers
  • How to construct high-throughput import engines for large knowledge base document categories sets using custom XML/JSON parsers

Categories

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

Recent Posts

  • How to build custom Genesis child themes extensions utilizing modern Heartbeat API schemas
  • Step-by-Step Guide to building a custom XML sitemap generator block for Gutenberg using Vanilla CSS shadow DOM style layers
  • WordPress Development Recipe: Secure token-based API authentication for Zapier dynamic webhooks in custom plugins

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (836)
  • Debugging & Troubleshooting (633)
  • Security & Compliance (609)
  • 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