• 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 PHP block-render callbacks

Step-by-Step Guide to building a custom multi-currency switcher block for Gutenberg using PHP block-render callbacks

Leveraging PHP Block-Render Callbacks for a Custom Gutenberg Multi-Currency Switcher

For e-commerce platforms built on WordPress, offering a seamless multi-currency experience is paramount. While many plugins exist, building a custom Gutenberg block provides granular control and deeper integration. This guide details the construction of a custom multi-currency switcher block using PHP block-render callbacks, enabling dynamic currency selection directly within the WordPress editor and on the frontend.

I. Plugin Setup and Block Registration

We’ll start by creating a simple WordPress plugin to house our custom block. This involves defining the plugin’s main file and registering the block type.

A. Plugin Structure

Create a new directory in wp-content/plugins/, for example, custom-currency-switcher. Inside this directory, create a main PHP file, custom-currency-switcher.php.

B. Plugin Header and Block Registration

Add the standard WordPress plugin header to custom-currency-switcher.php and then register our block using register_block_type. We’ll specify a render callback function that will handle the dynamic rendering of the block’s content.

custom-currency-switcher.php

<?php
/**
 * Plugin Name: Custom Currency Switcher Block
 * Description: A custom Gutenberg block for multi-currency switching.
 * Version: 1.0.0
 * Author: Antigravity
 * License: GPL-2.0-or-later
 * Text Domain: custom-currency-switcher
 */

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

/**
 * Register the custom currency switcher block.
 */
function custom_currency_switcher_register_block() {
    register_block_type( 'custom-currency-switcher/block', array(
        'editor_script'    => 'custom-currency-switcher-editor-script',
        'editor_style'     => 'custom-currency-switcher-editor-style',
        'style'            => 'custom-currency-switcher-style',
        'render_callback'  => 'custom_currency_switcher_render_callback',
        'attributes'       => array(
            'defaultCurrency' => array(
                'type'    => 'string',
                'default' => 'USD',
            ),
        ),
    ) );
}
add_action( 'init', 'custom_currency_switcher_register_block' );

/**
 * Enqueue block editor assets.
 */
function custom_currency_switcher_editor_assets() {
    wp_enqueue_script(
        'custom-currency-switcher-editor-script',
        plugin_dir_url( __FILE__ ) . 'build/index.js',
        array( 'wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-i18n' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'build/index.js' )
    );

    wp_enqueue_style(
        'custom-currency-switcher-editor-style',
        plugin_dir_url( __FILE__ ) . 'build/index.css',
        array( 'wp-edit-blocks' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'build/index.css' )
    );
}
add_action( 'enqueue_block_editor_assets', 'custom_currency_switcher_editor_assets' );

/**
 * Enqueue frontend assets.
 */
function custom_currency_switcher_frontend_assets() {
    wp_enqueue_style(
        'custom-currency-switcher-style',
        plugin_dir_url( __FILE__ ) . 'build/style.css',
        array(),
        filemtime( plugin_dir_path( __FILE__ ) . 'build/style.css' )
    );
}
add_action( 'wp_enqueue_scripts', 'custom_currency_switcher_frontend_assets' );

/**
 * Render callback for the currency switcher block.
 *
 * @param array $attributes Block attributes.
 * @return string HTML for the block.
 */
function custom_currency_switcher_render_callback( $attributes ) {
    $default_currency = isset( $attributes['defaultCurrency'] ) ? $attributes['defaultCurrency'] : 'USD';

    // In a real-world scenario, you'd fetch available currencies and their rates.
    // For this example, we'll use a hardcoded list.
    $available_currencies = array(
        'USD' => array( 'name' => 'US Dollar', 'symbol' => '$' ),
        'EUR' => array( 'name' => 'Euro', 'symbol' => '€' ),
        'GBP' => array( 'name' => 'British Pound', 'symbol' => '£' ),
        'JPY' => array( 'name' => 'Japanese Yen', 'symbol' => '¥' ),
    );

    // Get the currently selected currency from session or cookie.
    // This logic would typically involve a more robust state management.
    $current_currency = isset( $_SESSION['current_currency'] ) ? $_SESSION['current_currency'] : $default_currency;
    if ( ! array_key_exists( $current_currency, $available_currencies ) ) {
        $current_currency = $default_currency;
    }

    ob_start();
    ?>
    <div class="wp-block-custom-currency-switcher">
        <label for="currency-select"><?php esc_html_e( 'Select Currency:', 'custom-currency-switcher' ); ?></label>
        <select id="currency-select" name="currency-select">
            <?php foreach ( $available_currencies as $code => $currency_data ) : ?>
                <option value="<?php echo esc_attr( $code ); ?>" <?php selected( $current_currency, $code ); ?>>
                    <?php echo esc_html( $currency_data['name'] ); ?> (<?php echo esc_html( $currency_data['symbol'] ); ?>)
                </option>
            <?php endforeach; ?>
        </select>
        <button id="update-currency-btn"><?php esc_html_e( 'Update', 'custom-currency-switcher' ); ?></button>
    </div>
    <script>
        document.getElementById('update-currency-btn').addEventListener('click', function() {
            const selectedCurrency = document.getElementById('currency-select').value;
            // In a real application, this would trigger a backend AJAX call
            // to update the session/cookie and reload the page or update prices dynamically.
            console.log('Selected currency:', selectedCurrency);
            // For demonstration, we'll simulate a page reload with a query parameter.
            // A more advanced solution would use AJAX and potentially WebSockets.
            const url = new URL(window.location.href);
            url.searchParams.set('currency', selectedCurrency);
            window.location.href = url.toString();
        });
    </script>
    <?php
    return ob_get_clean();
}

II. Block Editor Implementation (JavaScript)

The block editor experience requires JavaScript. We’ll use the WordPress Script package to define the block’s appearance and controls in the editor. This involves creating a src/index.js file and a src/edit.js file.

A. Project Setup (Node.js/npm)

If you don’t have a build process set up, you’ll need Node.js and npm. Navigate to your plugin directory in the terminal and run:

npm init -y
npm install @wordpress/scripts --save-dev

Add the following scripts to your package.json:

{
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start"
  }
}

Now you can run npm start for development (which watches for changes) or npm run build to create production-ready assets.

B. Block Definition (src/index.js)

This file registers the block type with WordPress and imports the editor and frontend components.

src/index.js

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import './style.scss'; // Frontend styles
import './editor.scss'; // Editor styles
import Edit from './edit';
import save from './save'; // We won't use save directly as we have a render_callback

registerBlockType( 'custom-currency-switcher/block', {
    title: __( 'Currency Switcher', 'custom-currency-switcher' ),
    icon: 'money-alt', // WordPress Dashicon
    category: 'ecommerce', // Or 'design', 'widgets', etc.
    keywords: [ __( 'currency', 'custom-currency-switcher' ), __( 'ecommerce', 'custom-currency-switcher' ), __( 'shop', 'custom-currency-switcher' ) ],
    attributes: {
        defaultCurrency: {
            type: 'string',
            default: 'USD',
        },
    },
    edit: Edit,
    // We are using render_callback, so save function is not strictly needed for rendering.
    // However, it's good practice to define it if you want to save static HTML or
    // if the render_callback relies on specific saved markup.
    // For this example, we'll leave it minimal.
    save: () => null, // Rendered by PHP callback
} );

C. Editor Component (src/edit.js)

This component defines how the block appears and is configured within the Gutenberg editor. It includes a dropdown for selecting the default currency.

src/edit.js

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

// Hardcoded list for editor control, should ideally be dynamic
const availableCurrencies = [
    { label: 'US Dollar (USD)', value: 'USD' },
    { label: 'Euro (EUR)', value: 'EUR' },
    { label: 'British Pound (GBP)', value: 'GBP' },
    { label: 'Japanese Yen (JPY)', value: 'JPY' },
];

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

    const onChangeDefaultCurrency = ( newCurrency ) => {
        setAttributes( { defaultCurrency: newCurrency } );
    };

    // The editor view will show a placeholder or a simplified representation.
    // The actual dynamic rendering happens via the PHP render_callback on the frontend.
    return (
        <div { ...blockProps }>
            <InspectorControls>
                <PanelBody title={ __( 'Currency Settings', 'custom-currency-switcher' ) }>
                    <SelectControl
                        label={ __( 'Default Currency', 'custom-currency-switcher' ) }
                        value={ defaultCurrency }
                        options={ availableCurrencies }
                        onChange={ onChangeDefaultCurrency }
                    />
                </PanelBody>
            </InspectorControls>
            <p>{ __( 'Currency Switcher Block (Editor Preview)', 'custom-currency-switcher' ) }</p>
            <p>{ __( 'Default Currency:', 'custom-currency-switcher' ) } { defaultCurrency }</p>
            <p>{ __( 'Frontend will display actual switcher.', 'custom-currency-switcher' ) }</p>
        </div>
    );
}

D. Stylesheets

Create src/style.scss for frontend styles and src/editor.scss for editor-specific styles.

src/style.scss

.wp-block-custom-currency-switcher {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
    background-color: #f9f9f9;
    margin-bottom: 1em;

    label {
        font-weight: bold;
    }

    select, button {
        padding: 8px 12px;
        border: 1px solid #ddd;
        border-radius: 3px;
    }

    button {
        background-color: #0073aa;
        color: white;
        cursor: pointer;
        border: none;
    }

    button:hover {
        background-color: #005177;
    }
}

src/editor.scss

.wp-block-custom-currency-switcher {
    background-color: #e0f0ff;
    padding: 15px;
    border: 1px dashed #0073aa;
    text-align: center;

    p {
        margin: 0.5em 0;
    }
}

III. Building the Assets

After creating the JavaScript and SCSS files in the src/ directory, run the build command:

npm run build

This will compile your source files into the build/ directory, creating index.js and index.css (for the editor) and style.css (for the frontend).

IV. Implementing the Render Callback Logic

The custom_currency_switcher_render_callback function in custom-currency-switcher.php is where the magic happens for the frontend. It dynamically generates the HTML for the currency switcher.

A. Fetching Available Currencies

In a production environment, you would integrate with a currency API (like Open Exchange Rates, Fixer.io, or a WooCommerce-specific API) to fetch real-time exchange rates and a comprehensive list of supported currencies. For this example, we use a hardcoded array.

B. Managing Current Currency State

Determining the user’s current currency is crucial. Common methods include:

  • Session/Cookies: Store the user’s selection in a PHP session or a cookie. This is a simple approach for logged-out users.
  • User Meta: For logged-in users, store their preference in their user meta data.
  • Geo-IP Detection: Automatically detect the user’s location and suggest a currency.
  • URL Parameters: As demonstrated in the example script, a URL parameter can be used to force a currency for testing or direct linking.

The provided example uses a basic check for $_SESSION['current_currency'] and falls back to the block’s defaultCurrency attribute. It also includes a rudimentary JavaScript snippet to simulate updating the currency via a URL parameter.

C. Generating HTML Output

The callback uses output buffering (ob_start() and ob_get_clean()) to capture the generated HTML. It constructs a <select> element populated with available currencies and a button to trigger an update. The JavaScript attached to this output handles the frontend interaction.

V. Advanced Considerations and Enhancements

A. Dynamic Currency Rates and Price Conversion

The most significant enhancement would be to integrate a robust currency conversion mechanism. This involves:

  • Backend API Integration: Regularly fetch exchange rates from a reliable API. Store these rates in the WordPress database or a transient for performance.
  • Price Conversion Logic: When a user selects a new currency, apply the relevant exchange rate to all product prices displayed on the site. This can be done via AJAX on the frontend or by re-rendering product data from the backend.
  • Caching: Cache converted prices to avoid repeated API calls and calculations.

B. AJAX for Seamless Updates

Instead of a full page reload, use AJAX to update the selected currency. This involves:

  • WordPress REST API Endpoint: Create a custom REST API endpoint to handle currency updates. This endpoint would receive the selected currency, update the user’s session/cookie, and return a success status.
  • Frontend JavaScript: Modify the update-currency-btn JavaScript to make an AJAX request to this endpoint. Upon success, dynamically update prices on the page or trigger a partial reload.

C. WooCommerce Integration

For deep WooCommerce integration:

  • Hook into WooCommerce Filters: Use WooCommerce filters (e.g., woocommerce_product_get_price, woocommerce_get_currency_symbol) to dynamically adjust prices and symbols based on the selected currency.
  • Store Currency Settings: Leverage WooCommerce’s built-in currency settings if available, or store your custom currency configurations within WooCommerce’s options.
  • Session Management: Ensure currency selection persists across WooCommerce sessions and checkout processes.

D. Internationalization (i18n) and Localization (l10n)

Ensure all user-facing strings are translatable using WordPress’s i18n functions (__(), _e(), esc_html__(), etc.) and that the block is properly text-domain registered.

VI. Conclusion

By combining PHP block-render callbacks with a well-structured JavaScript editor component, you can create a powerful and flexible custom Gutenberg block. This approach offers fine-grained control over the user experience, allowing for seamless multi-currency functionality tailored to your specific e-commerce needs. Remember to replace the hardcoded currency data and basic state management with robust solutions for a production-ready implementation.

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 securely integrate Salesforce CRM endpoints into WordPress custom plugins using Heartbeat API
  • Step-by-Step Guide to building a custom role-based access control editor block for Gutenberg using Vanilla JS Web Components
  • Implementing automated compliance reporting for custom member profile directories ledgers using dompdf library
  • How to construct high-throughput import engines for large affiliate click tracking logs sets using custom XML/JSON parsers
  • Step-by-Step Guide to building a custom dynamic lead collector block for Gutenberg using REST API custom routes

Categories

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

Recent Posts

  • How to securely integrate Salesforce CRM endpoints into WordPress custom plugins using Heartbeat API
  • Step-by-Step Guide to building a custom role-based access control editor block for Gutenberg using Vanilla JS Web Components
  • Implementing automated compliance reporting for custom member profile directories ledgers using dompdf library

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (855)
  • Debugging & Troubleshooting (647)
  • Security & Compliance (627)
  • 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