Step-by-Step Guide to building a custom multi-currency switcher block for Gutenberg using REST API custom routes
Setting Up the WordPress Environment and Plugin Structure
Before diving into the custom Gutenberg block and REST API routes, we need a solid foundation. This involves creating a new WordPress plugin and setting up the necessary file structure. For this guide, we’ll assume you have a local WordPress development environment (e.g., using LocalWP, Docker, or a LAMP/LEMP stack) with a recent version of WordPress installed.
Create a new directory for your plugin within the wp-content/plugins/ directory of your WordPress installation. Let’s name it custom-currency-switcher.
Plugin Initialization and Main File
Inside the custom-currency-switcher directory, create the main plugin file, typically named after the plugin directory: custom-currency-switcher.php. This file will contain the plugin header and hooks to register our Gutenberg block and REST API routes.
<?php
/**
* Plugin Name: Custom Currency Switcher
* Description: A custom Gutenberg block for switching currencies via REST API.
* Version: 1.0.0
* Author: Your Name
* Author URI: https://yourwebsite.com
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Text Domain: custom-currency-switcher
* Domain Path: /languages
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Enqueue Gutenberg block assets.
*/
function ccs_enqueue_block_assets() {
// Register the block.
register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'ccs_enqueue_block_assets' );
/**
* Register custom REST API routes.
*/
function ccs_register_api_routes() {
require_once plugin_dir_path( __FILE__ ) . 'inc/rest-api.php';
ccs_register_currency_routes();
}
add_action( 'rest_api_init', 'ccs_register_api_routes' );
/**
* Include plugin functionality.
*/
require_once plugin_dir_path( __FILE__ ) . 'inc/plugin-functions.php';
This file includes the standard plugin header, a security check, and hooks for registering our block and REST API routes. We’re also including separate files for better organization: inc/rest-api.php for our API logic and inc/plugin-functions.php for any general plugin functions we might need later.
Gutenberg Block Registration and Structure
Gutenberg blocks are typically built using JavaScript (React). We’ll use the modern `@wordpress/scripts` package to handle the build process. First, navigate to your plugin directory in your terminal and initialize a Node.js project:
cd wp-content/plugins/custom-currency-switcher npm init -y
Next, install the necessary development dependencies:
npm install @wordpress/scripts --save-dev
Now, update your package.json file to include build scripts. Add the following lines to the scripts section:
"scripts": {
"build": "wp-scripts build",
"start": "wp-scripts start"
},
Create a src directory within your plugin folder. Inside src, create index.js and block.json. The block.json file is crucial for defining your block’s metadata.
block.json Configuration
This file describes your block to WordPress. It specifies the block’s name, title, category, attributes, and where to find its JavaScript and CSS files.
{
"apiVersion": 2,
"name": "custom-currency-switcher/currency-switcher-block",
"version": "0.1.0",
"title": "Currency Switcher",
"category": "widgets",
"icon": "money-alt",
"description": "A custom block to switch between different currencies.",
"keywords": [ "currency", "switcher", "exchange" ],
"attributes": {
"selectedCurrency": {
"type": "string",
"default": "USD"
}
},
"textdomain": "custom-currency-switcher",
"editorScript": "file:./build/index.js",
"editorStyle": "file:./build/index.css",
"style": "file:./build/style-index.css"
}
Key fields here:
name: A unique identifier for your block (namespace/block-name).title: The human-readable name shown in the block inserter.category: Where the block appears in the inserter.icon: The icon for the block.attributes: Defines the data your block will store. Here, we haveselectedCurrency.editorScript: Path to the JavaScript file for the block editor.editorStyle: Path to the CSS file for the block editor.style: Path to the CSS file for the frontend.
src/index.js – Block Editor Logic
This is the main JavaScript file for your block. It uses React and WordPress components to define the block’s appearance and behavior in the editor.
import { registerBlockType } from '@wordpress/blocks';
import { useSelect } from '@wordpress/data';
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, SelectControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import './style.scss'; // For frontend styles
import './editor.scss'; // For editor styles
const ALLOWED_CURRENCIES = [
{ label: 'US Dollar', value: 'USD' },
{ label: 'Euro', value: 'EUR' },
{ label: 'British Pound', value: 'GBP' },
{ label: 'Japanese Yen', value: 'JPY' },
];
registerBlockType( 'custom-currency-switcher/currency-switcher-block', {
apiVersion: [ 2, 0 ],
title: __( 'Currency Switcher', 'custom-currency-switcher' ),
icon: 'money-alt',
category: 'widgets',
attributes: {
selectedCurrency: {
type: 'string',
default: 'USD',
},
},
edit: ( { attributes, setAttributes } ) => {
const { selectedCurrency } = attributes;
// Fetch available currencies from REST API (example, actual fetch logic would be here)
// For now, we use a static list. In a real scenario, you'd fetch this.
const availableCurrencies = ALLOWED_CURRENCIES;
const onChangeCurrency = ( newValue ) => {
setAttributes( { selectedCurrency: newValue } );
};
return (
<>
<InspectorControls>
<PanelBody title={ __( 'Currency Settings', 'custom-currency-switcher' ) }>
<SelectControl
label={ __( 'Select Currency', 'custom-currency-switcher' ) }
value={ selectedCurrency }
options={ availableCurrencies }
onChange={ onChangeCurrency }
/>
</PanelBody>
</InspectorControls>
<div className="currency-switcher-editor">
<p>{ __( 'Current Currency:', 'custom-currency-switcher' ) } { selectedCurrency }</p>
<p>{ __( 'This is the frontend view. Select currency in the sidebar.', 'custom-currency-switcher' ) }</p>
</div>
</>
);
},
save: ( { attributes } ) => {
const { selectedCurrency } = attributes;
// The save function should return the markup that will be saved to the database.
// For dynamic blocks, this is often just a placeholder or a function that
// renders the block on the frontend using PHP or a REST API call.
// For simplicity here, we'll render a static representation.
// In a real-world scenario, you might use a server-side rendering approach
// or a client-side fetch on page load.
return (
<div className="currency-switcher-frontend" data-selected-currency={ selectedCurrency }>
<p>{ __( 'Displaying prices in:', 'custom-currency-switcher' ) } { selectedCurrency }</p>
<!-- This block will dynamically update prices based on the selected currency -->
</div>
);
},
} );
In the edit function:
- We import necessary components from
@wordpress/blocks,@wordpress/data,@wordpress/block-editor, and@wordpress/components. registerBlockTypeis used to define our block.InspectorControlsprovides a sidebar panel for block settings.SelectControlallows users to choose a currency from a dropdown.- The
attributesare managed usinguseAttributesandsetAttributes. - The
savefunction defines the static HTML that gets saved to the post content. For dynamic blocks, this is often a placeholder, and the actual rendering happens on the frontend via PHP or JavaScript.
Styling the Block
Create src/editor.scss for editor-specific styles and src/style.scss for styles that apply to both the editor and the frontend.
/* src/editor.scss */
.currency-switcher-editor {
border: 1px dashed #ccc;
padding: 15px;
background-color: #f9f9f9;
text-align: center;
}
/* src/style.scss */
.currency-switcher-frontend {
padding: 10px;
border: 1px solid #eee;
background-color: #f0f0f0;
text-align: center;
font-weight: bold;
}
Building the Block Assets
Now, run the build script to compile your JavaScript and SCSS files into the build directory:
npm run build
This command will create build/index.js, build/index.css, and build/style-index.css. The registerBlockType function in custom-currency-switcher.php points to __DIR__ . '/build', which tells WordPress to look for the block’s assets in this directory.
Implementing the REST API Routes
We need REST API endpoints to fetch available currencies and potentially to get exchange rates or convert prices. Create the inc directory and inside it, create rest-api.php.
Fetching Available Currencies
This endpoint will return a list of currencies that the switcher can use. In a real-world scenario, this list might come from a third-party API, a database table, or a configuration setting.
<?php
// inc/rest-api.php
function ccs_register_currency_routes() {
// Endpoint to get available currencies.
register_rest_route( 'custom-currency-switcher/v1', '/currencies', array(
'methods' => 'GET',
'callback' => 'ccs_get_available_currencies',
'permission_callback' => '__return_true', // Or implement proper permissions
) );
// Endpoint to get current exchange rates (example).
register_rest_route( 'custom-currency-switcher/v1', '/rates', array(
'methods' => 'GET',
'callback' => 'ccs_get_exchange_rates',
'permission_callback' => '__return_true', // Or implement proper permissions
) );
// Endpoint to convert a price (example).
register_rest_route( 'custom-currency-switcher/v1', '/convert', array(
'methods' => 'POST', // Use POST for requests with parameters
'callback' => 'ccs_convert_price',
'permission_callback' => '__return_true', // Or implement proper permissions
) );
}
/**
* Callback for the /currencies endpoint.
*
* @return WP_REST_Response|\WP_Error
*/
function ccs_get_available_currencies() {
// In a real application, fetch this from settings, a database, or an external API.
$currencies = array(
array( 'value' => 'USD', 'label' => 'US Dollar' ),
array( 'value' => 'EUR', 'label' => 'Euro' ),
array( 'value' => 'GBP', 'label' => 'British Pound' ),
array( 'value' => 'JPY', 'label' => 'Japanese Yen' ),
// Add more currencies as needed
);
return new WP_REST_Response( $currencies, 200 );
}
/**
* Callback for the /rates endpoint.
* This is a placeholder. Real implementation would fetch live rates.
*
* @return WP_REST_Response|\WP_Error
*/
function ccs_get_exchange_rates() {
// Placeholder for exchange rates.
// In a real app, you'd integrate with a service like Open Exchange Rates, Fixer.io, etc.
$rates = array(
'USD' => array( 'EUR' => 0.92, 'GBP' => 0.79, 'JPY' => 155.00 ),
'EUR' => array( 'USD' => 1.09, 'GBP' => 0.86, 'JPY' => 168.00 ),
'GBP' => array( 'USD' => 1.27, 'EUR' => 1.16, 'JPY' => 195.00 ),
'JPY' => array( 'USD' => 0.0065, 'EUR' => 0.0060, 'GBP' => 0.0051 ),
);
return new WP_REST_Response( $rates, 200 );
}
/**
* Callback for the /convert endpoint.
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_REST_Response|\WP_Error
*/
function ccs_convert_price( WP_REST_Request $request ) {
$amount = (float) $request->get_param( 'amount' );
$from_currency = sanitize_text_field( $request->get_param( 'from_currency' ) );
$to_currency = sanitize_text_field( $request->get_param( 'to_currency' ) );
if ( ! $amount || ! $from_currency || ! $to_currency ) {
return new WP_Error( 'invalid_request', __( 'Amount, from_currency, and to_currency are required.', 'custom-currency-switcher' ), array( 'status' => 400 ) );
}
// Fetch rates (using the placeholder function for now)
$rates_response = ccs_get_exchange_rates();
$rates = $rates_response->get_data();
if ( ! isset( $rates[ $from_currency ][ $to_currency ] ) ) {
return new WP_Error( 'conversion_error', __( 'Could not find exchange rate for the specified currencies.', 'custom-currency-switcher' ), array( 'status' => 400 ) );
}
$exchange_rate = $rates[ $from_currency ][ $to_currency ];
$converted_amount = $amount * $exchange_rate;
return new WP_REST_Response( array(
'original_amount' => $amount,
'from_currency' => $from_currency,
'to_currency' => $to_currency,
'exchange_rate' => $exchange_rate,
'converted_amount' => round( $converted_amount, 2 ),
), 200 );
}
In custom-currency-switcher.php, we hook into rest_api_init to call ccs_register_currency_routes(), which registers these endpoints.
inc/plugin-functions.php – Frontend Rendering and Logic
For dynamic blocks, WordPress often uses server-side rendering (SSR) or a client-side JavaScript approach to render the block on the frontend. For this example, we’ll use a simple client-side approach where the block saves a placeholder, and JavaScript fetches data to render the actual content.
Create inc/plugin-functions.php. We’ll add a function here to enqueue frontend scripts if needed, though for this client-side dynamic rendering, the block’s editorScript and style handle most of it.
<?php
// inc/plugin-functions.php
/**
* Enqueue frontend scripts for the currency switcher block.
* This is an example; often, the block's 'style' and 'editorScript' handle frontend assets.
* If you need a separate JS file for frontend dynamic updates, enqueue it here.
*/
function ccs_enqueue_frontend_scripts() {
// Only enqueue if the block is present on the page.
// A more robust check would involve checking post content for the block's name.
if ( has_block( 'custom-currency-switcher/currency-switcher-block' ) ) {
wp_enqueue_script(
'custom-currency-switcher-frontend',
plugin_dir_url( __FILE__ ) . '../build/frontend.js', // Assuming you'll create this
array( 'wp-element', 'wp-api-fetch' ), // Dependencies
filemtime( plugin_dir_path( __FILE__ ) . '../build/frontend.js' ),
true // Load in footer
);
// Localize script with REST API URL if needed
wp_localize_script( 'custom-currency-switcher-frontend', 'ccs_rest_api_settings', array(
'root' => esc_url_raw( rest_url() ),
'nonce' => wp_create_nonce( 'wp_rest' ),
'currencies_endpoint' => 'custom-currency-switcher/v1/currencies',
'rates_endpoint' => 'custom-currency-switcher/v1/rates',
'convert_endpoint' => 'custom-currency-switcher/v1/convert',
) );
}
}
// add_action( 'wp_enqueue_scripts', 'ccs_enqueue_frontend_scripts' ); // Uncomment if you create frontend.js
// Note: For simplicity in this guide, we'll rely on the block's saved HTML
// and potentially client-side JS that runs on page load to interact with the API.
// If you need dynamic price updates on the frontend based on the selected currency,
// you would typically enqueue a separate frontend.js file and add logic there.
If you were to create a src/frontend.js file and build it (you’d need to add it to block.json‘s script or viewScript), you could use wp.apiFetch to interact with your REST API endpoints on the frontend.
Testing the Block and API
1. **Activate the Plugin:** Go to your WordPress admin area, navigate to “Plugins,” and activate “Custom Currency Switcher.”
2. **Build Assets:** Ensure you run npm run build in your plugin directory after making changes to JS/SCSS files.
3. **Add the Block:** Create or edit a post/page. Click the “+” icon to add a block and search for “Currency Switcher.” Add it to your content.
4. **Configure in Editor:** Select the block. In the right-hand sidebar (Inspector Controls), you should see the “Currency Settings” panel with a dropdown to select a currency. Changing this will update the block’s preview in the editor and save the selected currency as an attribute.
5. **Test REST API:** You can test your REST API endpoints using tools like Postman, Insomnia, or `curl`:
# Get available currencies
curl http://your-local-site.test/wp-json/custom-currency-switcher/v1/currencies
# Get exchange rates
curl http://your-local-site.test/wp-json/custom-currency-switcher/v1/rates
# Convert a price (using POST)
curl -X POST http://your-local-site.test/wp-json/custom-currency-switcher/v1/convert \
-H "Content-Type: application/json" \
-d '{"amount": 100, "from_currency": "USD", "to_currency": "EUR"}'
Remember to replace http://your-local-site.test with your actual WordPress site URL.
Advanced Considerations and Next Steps
This guide provides a foundational implementation. For a production-ready solution, consider the following:
- Dynamic Frontend Rendering: Implement
frontend.jsto fetch rates and dynamically update prices on the frontend based on the user’s selected currency or a user preference. Usewp.apiFetchfor this. - Real Exchange Rates: Integrate with a reliable third-party exchange rate API (e.g., Open Exchange Rates, Fixer.io) and handle API keys securely.
- Caching: Cache exchange rates to avoid excessive API calls and improve performance. Use WordPress Transients API for this.
- User Preferences: Store the user’s selected currency in cookies or local storage so it persists across sessions.
- Error Handling: Implement robust error handling for API requests and display user-friendly messages.
- Permissions: Refine the
permission_callbackfor your REST API routes to ensure only authorized users can access or modify data if necessary. - Internationalization (i18n): Ensure all user-facing strings are translatable using WordPress’s i18n functions (e.g.,
__(),_e()). - Block Variations: If you need different layouts or functionalities for the switcher, explore Gutenberg Block Variations.
- Server-Side Rendering (SSR): For blocks that require complex server-side logic or initial data fetching, consider using PHP to render the block’s initial HTML on the server. This can improve perceived performance.
By following these steps, you’ve built a custom Gutenberg block that leverages WordPress’s REST API for dynamic data, providing a flexible foundation for a multi-currency switcher.