• 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 custom analytics tracker block for Gutenberg using HTMX dynamic attributes

Step-by-Step Guide to building a custom custom analytics tracker block for Gutenberg using HTMX dynamic attributes

Setting Up Your WordPress Development Environment

Before we dive into building the custom Gutenberg block, ensure you have a local WordPress development environment set up. This typically involves:

  • A local web server (e.g., Local by Flywheel, MAMP, XAMPP, Docker with a WordPress image).
  • PHP 7.4+ and MySQL 5.7+ (or MariaDB equivalent).
  • Node.js and npm (or yarn) for JavaScript build processes.
  • A code editor (e.g., VS Code, Sublime Text).

For this tutorial, we’ll assume you’re comfortable with basic WordPress plugin development and the command line.

Plugin Structure and Initialization

Create a new directory for your plugin in the wp-content/plugins/ folder. Let’s call it htmx-analytics-tracker. Inside this directory, create the main plugin file, htmx-analytics-tracker.php.

Add the standard WordPress plugin header to htmx-analytics-tracker.php:

<?php
/**
 * Plugin Name: HTMX Analytics Tracker
 * Description: A custom Gutenberg block to track user interactions using HTMX.
 * Version: 1.0.0
 * Author: Your Name
 * License: GPL-2.0-or-later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain: htmx-analytics-tracker
 */

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

/**
 * Enqueue block assets.
 */
function htmx_analytics_tracker_enqueue_block_assets() {
    // Enqueue the block editor script.
    wp_enqueue_script(
        'htmx-analytics-tracker-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' )
    );

    // Enqueue the block editor styles.
    wp_enqueue_style(
        'htmx-analytics-tracker-editor-style',
        plugin_dir_url( __FILE__ ) . 'build/index.css',
        array( 'wp-edit-blocks' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'build/index.css' )
    );

    // Enqueue frontend styles and scripts (including HTMX).
    wp_enqueue_script(
        'htmx-analytics-tracker-frontend-script',
        plugin_dir_url( __FILE__ ) . 'build/frontend.js',
        array(), // No dependencies for HTMX itself, but you might add others.
        filemtime( plugin_dir_path( __FILE__ ) . 'build/frontend.js' ),
        true // Load in footer
    );

    wp_enqueue_style(
        'htmx-analytics-tracker-frontend-style',
        plugin_dir_url( __FILE__ ) . 'build/frontend.css',
        array(),
        filemtime( plugin_dir_path( __FILE__ ) . 'build/frontend.css' )
    );

    // Localize script for passing data to JavaScript.
    wp_localize_script( 'htmx-analytics-tracker-frontend-script', 'htmxAnalyticsTracker', array(
        'ajax_url' => admin_url( 'admin-ajax.php' ),
        // Add any other data you need to pass to the frontend.
    ) );
}
add_action( 'enqueue_block_assets', 'htmx_analytics_tracker_enqueue_block_assets' );

/**
 * Register the custom block.
 */
function htmx_analytics_tracker_register_block() {
    register_block_type( __DIR__ );
}
add_action( 'init', 'htmx_analytics_tracker_register_block' );

/**
 * Handle AJAX requests for tracking.
 */
function htmx_analytics_tracker_handle_ajax() {
    check_ajax_referer( 'htmx_analytics_tracker_nonce', 'nonce' );

    $event_type = isset( $_POST['event_type'] ) ? sanitize_text_field( $_POST['event_type'] ) : '';
    $element_id = isset( $_POST['element_id'] ) ? sanitize_text_field( $_POST['element_id'] ) : '';
    $user_id    = get_current_user_id(); // Get logged-in user ID, or 0 if not logged in.

    if ( ! empty( $event_type ) && ! empty( $element_id ) ) {
        // In a real-world scenario, you would log this data to a database,
        // a log file, or an external analytics service.
        // For this example, we'll just log it to the PHP error log.
        error_log( sprintf(
            'HTMX Analytics Event: Type="%s", ElementID="%s", UserID="%d", Timestamp="%s"',
            $event_type,
            $element_id,
            $user_id,
            current_time( 'mysql' )
        ) );

        wp_send_json_success( array( 'message' => 'Event tracked successfully.' ) );
    } else {
        wp_send_json_error( array( 'message' => 'Invalid event data.' ) );
    }
}
add_action( 'wp_ajax_track_analytics_event', 'htmx_analytics_tracker_handle_ajax' );
add_action( 'wp_ajax_nopriv_track_analytics_event', 'htmx_analytics_tracker_handle_ajax' ); // For non-logged-in users

Frontend JavaScript and HTMX Integration

We need to enqueue HTMX and our custom frontend JavaScript. Create a src directory in your plugin folder. Inside src, create frontend.js and frontend.css. Also, create a block.json file to define your Gutenberg block.

First, let’s set up the build process. Install the necessary npm packages:

cd htmx-analytics-tracker
npm init -y
npm install @wordpress/scripts @wordpress/blocks @wordpress/element @wordpress/i18n --save-dev
npm install htmx.org --save

Add a build script to your package.json:

{
  "name": "htmx-analytics-tracker",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@wordpress/blocks": "^12.10.0",
    "@wordpress/components": "^26.7.0",
    "@wordpress/element": "^5.7.0",
    "@wordpress/i18n": "^4.40.0",
    "@wordpress/scripts": "^26.10.0"
  },
  "dependencies": {
    "htmx.org": "^1.9.10"
  }
}

Now, create the src/frontend.js file. This script will handle the HTMX requests to our AJAX handler.

// Import HTMX.
import htmx from 'htmx.org';

// Ensure HTMX is initialized.
document.addEventListener('DOMContentLoaded', () => {
    // HTMX is globally available after import, or you can access it via `htmx`.
    // No explicit initialization needed if imported this way.

    // Example: Add a global event listener for HTMX requests to track them.
    document.body.addEventListener('htmx:afterRequest', function(evt) {
        // Check if the request was successful and if it's our tracker event.
        if (evt.detail.xhr.status >= 200 && evt.detail.xhr.status < 300 && evt.target.dataset.htmxAnalyticsEvent) {
            const eventData = evt.target.dataset;
            const eventType = eventData.htmxAnalyticsEvent;
            const elementId = eventData.htmxAnalyticsElementId || evt.target.id || 'unknown';

            // Send the event to our WordPress AJAX handler.
            // We use fetch here for simplicity, but you could also use HTMX itself
            // to POST to the AJAX URL if you prefer.
            fetch(htmxAnalyticsTracker.ajax_url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: new URLSearchParams({
                    action: 'track_analytics_event',
                    nonce: document.querySelector('meta[name="csrf-token"]')?.content || '', // Example nonce, adjust as needed
                    event_type: eventType,
                    element_id: elementId,
                }),
            })
            .then(response => response.json())
            .then(data => {
                console.log('Analytics event tracked:', data);
            })
            .catch((error) => {
                console.error('Error tracking analytics event:', error);
            });
        }
    });

    // You can also add specific event listeners for elements if needed.
    // For example, if you have a button that triggers an action and you want to track clicks.
    document.querySelectorAll('[data-htmx-analytics-track-click]').forEach(element => {
        element.addEventListener('click', function() {
            const eventType = this.dataset.htmxAnalyticsTrackClick;
            const elementId = this.id || 'unknown';

            fetch(htmxAnalyticsTracker.ajax_url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: new URLSearchParams({
                    action: 'track_analytics_event',
                    nonce: document.querySelector('meta[name="csrf-token"]')?.content || '', // Example nonce, adjust as needed
                    event_type: eventType,
                    element_id: elementId,
                }),
            })
            .then(response => response.json())
            .then(data => {
                console.log('Click event tracked:', data);
            })
            .catch((error) => {
                console.error('Error tracking click event:', error);
            });
        });
    });
});

Create src/frontend.css for any frontend styling:

/* Add any frontend styles for your block here */
.wp-block-htmx-analytics-tracker-tracker-block {
    border: 1px solid #ccc;
    padding: 15px;
    margin-bottom: 15px;
    background-color: #f9f9f9;
}

Gutenberg Block Registration and Editor Interface

Create block.json in the root of your plugin directory. This file describes your block to Gutenberg.

{
    "apiVersion": 2,
    "name": "htmx-analytics-tracker/tracker-block",
    "title": "HTMX Analytics Tracker",
    "category": "widgets",
    "icon": "chart-bar",
    "description": "A custom block to track user interactions using HTMX.",
    "keywords": ["analytics", "htmx", "tracker", "tracking"],
    "attributes": {
        "trackingEventName": {
            "type": "string",
            "default": "user_interaction"
        },
        "trackedElementId": {
            "type": "string",
            "default": ""
        },
        "triggerEvent": {
            "type": "string",
            "default": "click"
        }
    },
    "editorScript": "file:./build/index.js",
    "editorStyle": "file:./build/index.css",
    "style": "file:./build/frontend.css",
    "render": "file:./render.php"
}

Now, create the JavaScript for the block editor interface. Create src/index.js:

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

import './frontend.css'; // Import frontend styles for editor preview

registerBlockType('htmx-analytics-tracker/tracker-block', {
    title: __('HTMX Analytics Tracker', 'htmx-analytics-tracker'),
    icon: 'chart-bar',
    category: 'widgets',
    attributes: {
        trackingEventName: {
            type: 'string',
            default: 'user_interaction',
        },
        trackedElementId: {
            type: 'string',
            default: '',
        },
        triggerEvent: {
            type: 'string',
            default: 'click',
        },
    },

    edit: ({ attributes, setAttributes }) => {
        const blockProps = useBlockProps();
        const { trackingEventName, trackedElementId, triggerEvent } = attributes;

        const onTrackingEventNameChange = (newValue) => {
            setAttributes({ trackingEventName: newValue });
        };

        const onTrackedElementIdChange = (newValue) => {
            setAttributes({ trackedElementId: newValue });
        };

        const onTriggerEventChange = (newValue) => {
            setAttributes({ triggerEvent: newValue });
        };

        return (
            <>
                
                    
                        
                        
                        
                    
                
                

{__('Analytics Tracker Block', 'htmx-analytics-tracker')}

{__('Event:', 'htmx-analytics-tracker')} {trackingEventName}

{trackedElementId && (

{__('Element ID:', 'htmx-analytics-tracker')} {trackedElementId}

)}

{__('Trigger:', 'htmx-analytics-tracker')} {triggerEvent}

{__('This block will trigger an analytics event when the specified trigger occurs.', 'htmx-analytics-tracker')}

); }, save: ({ attributes }) => { const blockProps = useBlockProps.save(); const { trackingEventName, trackedElementId, triggerEvent } = attributes; // Construct HTMX attributes dynamically let htmxAttributes = {}; htmxAttributes['data-htmx-analytics-event'] = trackingEventName; if (trackedElementId) { htmxAttributes['data-htmx-analytics-element-id'] = trackedElementId; } // Add the trigger event listener using HTMX's `hx-trigger` // We'll use a generic element that listens for the specified trigger. // The actual element being tracked might be a child or the block itself. // For simplicity, we'll attach it to the main block div. // A more complex setup might involve targeting specific child elements. // The actual tracking logic will be in frontend.js, which listens for htmx:afterRequest // or specific DOM events. Here we just set up the data attributes. return (
{/* Content for the frontend */}

Analytics Tracker (Frontend)

Event: {trackingEventName}

{trackedElementId &&

Element ID: {trackedElementId}

}

Trigger: {triggerEvent}

); }, });

Create src/index.css for editor-specific styles:

/* Styles for the block editor */
.wp-block-htmx-analytics-tracker-tracker-block {
    border: 1px dashed #999;
    padding: 10px;
    background-color: #e0f0ff;
    text-align: center;
}

Rendering the Block on the Frontend

Create a render.php file in your plugin’s root directory. This file defines how the block is rendered on the frontend. We’ll use the dynamic attributes to configure HTMX.

<?php
/**
 * Block render template.
 *
 * @package htmx-analytics-tracker
 */

$tracking_event_name = isset( $attributes['trackingEventName'] ) ? esc_attr( $attributes['trackingEventName'] ) : 'user_interaction';
$tracked_element_id  = isset( $attributes['trackedElementId'] ) ? esc_attr( $attributes['trackedElementId'] ) : '';
$trigger_event       = isset( $attributes['triggerEvent'] ) ? esc_attr( $attributes['triggerEvent'] ) : 'click';

// Generate a unique ID if none is provided for the element to be tracked.
$element_id_attr = ! empty( $tracked_element_id ) ? $tracked_element_id : 'tracker-block-' . uniqid();

// The block's wrapper attributes.
$wrapper_attributes = get_block_wrapper_attributes();

// Construct HTMX attributes.
// We'll use data attributes that our frontend.js will pick up.
// The actual HTMX request will be handled by frontend.js listening to DOM events.
// For demonstration, we'll add a simple placeholder element that can be targeted.

?>
<div
    <?php echo $wrapper_attributes; ?>
    id="<?php echo esc_attr( $element_id_attr ); ?>"
    data-htmx-analytics-event="<?php echo $tracking_event_name; ?>"
    data-htmx-analytics-element-id="<?php echo esc_attr( $element_id_attr ); ?>"
    data-htmx-analytics-trigger="<?php echo $trigger_event; ?>"
>
    <p>Analytics Tracker Block (Frontend)</p>
    <p>Event: <strong><?php echo $tracking_event_name; ?></strong></p>
    <?php if ( ! empty( $tracked_element_id ) ) : ?>
        <p>Element ID: <strong><?php echo $tracked_element_id; ?></strong></p>
    </?php endif; ?>
    <p>Trigger: <strong><?php echo $trigger_event; ?></strong></p>

    <!-- Example of an element that might be tracked -->
    <button
        id="example-button-<?php echo esc_attr( $element_id_attr ); ?>"
        data-htmx-analytics-track-click="<?php echo $tracking_event_name; ?>-button"
        data-htmx-analytics-element-id="example-button-<?php echo esc_attr( $element_id_attr ); ?>"
        style="padding: 10px; margin-top: 10px; cursor: pointer;"
    >
        Click Me to Track
    </button>
</div>

In frontend.js, we need to refine the event listening to use the `data-htmx-analytics-trigger` attribute. Let’s update the `frontend.js` to dynamically attach event listeners based on the `triggerEvent` attribute.

import htmx from 'htmx.org';

document.addEventListener('DOMContentLoaded', () => {
    const ajaxUrl = htmxAnalyticsTracker.ajax_url;
    const nonce = document.querySelector('meta[name="csrf-token"]')?.content || ''; // Ensure you have a nonce mechanism if needed

    // Function to send analytics event
    const sendAnalyticsEvent = (eventType, elementId) => {
        fetch(ajaxUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
                action: 'track_analytics_event',
                nonce: nonce,
                event_type: eventType,
                element_id: elementId,
            }),
        })
        .then(response => response.json())
        .then(data => {
            console.log('Analytics event tracked:', data);
        })
        .catch((error) => {
            console.error('Error tracking analytics event:', error);
        });
    };

    // Listen for HTMX requests to potentially track them
    document.body.addEventListener('htmx:afterRequest', function(evt) {
        if (evt.detail.xhr.status >= 200 && evt.detail.xhr.status < 300) {
            const targetElement = evt.target;
            if (targetElement.dataset.htmxAnalyticsEvent) {
                const eventType = targetElement.dataset.htmxAnalyticsEvent;
                const elementId = targetElement.dataset.htmxAnalyticsElementId || targetElement.id || 'unknown';
                sendAnalyticsEvent(eventType, elementId);
            }
        }
    });

    // Dynamically attach event listeners based on data-htmx-analytics-trigger
    document.querySelectorAll('[data-htmx-analytics-event]').forEach(element => {
        const trigger = element.dataset.htmxAnalyticsTrigger || 'click'; // Default to click
        const eventType = element.dataset.htmxAnalyticsEvent;
        const elementId = element.dataset.htmxAnalyticsElementId || element.id || 'unknown';

        // Remove existing listeners to prevent duplicates if the DOM is updated
        // This is a simplified approach; a more robust solution might involve
        // event delegation or a more sophisticated listener management.

        if (trigger === 'click') {
            element.addEventListener('click', function() {
                sendAnalyticsEvent(eventType, elementId);
            });
        } else if (trigger === 'hover') {
            element.addEventListener('mouseenter', function() {
                sendAnalyticsEvent(eventType + '_hover', elementId); // Append _hover for clarity
            });
        } else if (trigger === 'focus') {
            element.addEventListener('focus', function() {
                sendAnalyticsEvent(eventType + '_focus', elementId); // Append _focus for clarity
            });
        }
        // Add more trigger types as needed
    });

    // Specific listener for the example button in render.php
    document.querySelectorAll('[data-htmx-analytics-track-click]').forEach(button => {
        button.addEventListener('click', function() {
            const eventType = this.dataset.htmxAnalyticsTrackClick;
            const elementId = this.dataset.htmxAnalyticsElementId || this.id || 'unknown';
            sendAnalyticsEvent(eventType, elementId);
        });
    });
});

Building the Assets

Navigate to your plugin’s root directory in the terminal and run the build command:

cd htmx-analytics-tracker
npm run build

This command will compile your JavaScript and CSS files from the src directory into the build directory, as specified in block.json and your main plugin file.

Testing and Verification

Activate your htmx-analytics-tracker plugin in the WordPress admin area. Then, create a new post or page and add the “HTMX Analytics Tracker” block. Configure the tracking event name and element ID in the block inspector.

Publish the post/page and view it on the frontend. Interact with the block (e.g., click the “Click Me to Track” button if you added it). Check your server’s PHP error log (or wherever you’ve configured error logging) for messages like:

[timestamp] HTMX Analytics Event: Type="user_interaction-button", ElementID="example-button-...", UserID="1", Timestamp="..."

You can also use your browser’s developer console to see the network requests being made to admin-ajax.php and any console logs from your frontend.js script.

Further Enhancements

  • Data Storage: Instead of logging to the error log, implement proper database storage for analytics events. Create custom database tables or use existing WordPress options/post meta if appropriate.
  • External Services: Integrate with third-party analytics platforms (e.g., Google Analytics, Matomo) by sending data via their respective APIs.
  • More Trigger Types: Expand the supported trigger events in frontend.js (e.g., scroll, form submission).
  • Advanced HTMX Features: Leverage more of HTMX’s capabilities, such as swapping content based on events, to create more dynamic tracking scenarios.
  • Security: Ensure robust nonce verification and input sanitization for all AJAX requests.
  • User Interface: Develop a dedicated admin page to view and manage the tracked analytics data.

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 Carbon Fields custom wrappers extensions utilizing modern WordPress Options API schemas
  • WordPress Development Recipe: Staggered database writes for high-volume custom form fields using Filesystem API
  • WordPress Development Recipe: Efficient binary storage and retrieval in custom tables using Union and Intersection Types
  • How to securely integrate Slack Webhooks integration endpoints into WordPress custom plugins using Transients API
  • WordPress Development Recipe: Implementing a secure lock mechanism for multi-worker Cron tasks with WordPress Settings 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 (42)
  • 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 (93)
  • WordPress Plugin Development (97)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • How to build custom Carbon Fields custom wrappers extensions utilizing modern WordPress Options API schemas
  • WordPress Development Recipe: Staggered database writes for high-volume custom form fields using Filesystem API
  • WordPress Development Recipe: Efficient binary storage and retrieval in custom tables using Union and Intersection Types

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