• 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 Vanilla JS Web Components

Step-by-Step Guide to building a custom custom analytics tracker block for Gutenberg using Vanilla JS Web Components

Setting Up Your WordPress Development Environment

Before diving into custom Gutenberg blocks, ensure your local WordPress development environment is robust. A common setup involves using Docker with a WordPress image or a local server stack like LocalWP. For this guide, we’ll assume you have a WordPress installation accessible via a local domain (e.g., my-wp-site.local) and a code editor (like VS Code) configured for PHP, JavaScript, and CSS development.

We’ll be creating a simple plugin to house our custom block. Navigate to your WordPress installation’s wp-content/plugins/ directory and create a new folder named custom-analytics-tracker. Inside this folder, create a main PHP file, custom-analytics-tracker.php.

Plugin Header and Block Registration

The custom-analytics-tracker.php file needs a standard WordPress plugin header. We’ll also register our block using the WordPress Block Editor SDK. This involves hooking into the init action.

<?php
/**
 * Plugin Name: Custom Analytics Tracker
 * Description: A custom Gutenberg block to track user interactions.
 * Version: 1.0.0
 * Author: Your Name
 * License: GPL-2.0-or-later
 * Text Domain: custom-analytics-tracker
 */

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

/**
 * Register the custom block.
 */
function custom_analytics_tracker_register_block() {
    register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'custom_analytics_tracker_register_block' );
?>

The register_block_type( __DIR__ . '/build' ); line is crucial. It tells WordPress to look inside the build directory (which we’ll create) for the block’s metadata and scripts. This is the standard way to register blocks developed using the modern WordPress build process.

Setting Up the Build Process with @wordpress/scripts

To compile our JavaScript and CSS, we’ll leverage the @wordpress/scripts package. This package provides a convenient way to handle modern JavaScript (ESNext), JSX, and CSS preprocessing. First, initialize a Node.js project in your plugin directory:

cd wp-content/plugins/custom-analytics-tracker
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. Open package.json and add the following to the scripts section:

{
  "name": "custom-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/scripts": "^26.10.0"
  }
}

The build script will compile your source files into the build directory, and the start script will watch for changes and recompile automatically, which is invaluable during development.

Creating the Block’s Source Files

Inside your plugin directory, create a src folder. Within src, create three files: index.js (the main entry point for your block), editor.scss (for styles in the editor), and style.scss (for styles on the front-end).

src/index.js: Block Definition and Web Component Integration

This file defines the block’s attributes, editor interface, and front-end rendering. We’ll use a custom Web Component for the actual tracking logic.

import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
import './editor.scss';
import './style.scss';

// Define the custom Web Component
class AnalyticsTracker extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this.trackingId = this.getAttribute('data-tracking-id') || 'default-tracker';
        this.eventName = this.getAttribute('data-event-name') || 'click';
        this.eventCategory = this.getAttribute('data-event-category') || 'interaction';
        this.eventLabel = this.getAttribute('data-event-label') || 'block_interaction';
    }

    connectedCallback() {
        this.render();
        this.addEventListeners();
    }

    render() {
        this.shadowRoot.innerHTML = `
            <style>
                :host {
                    display: inline-block; /* Or block, depending on desired layout */
                    cursor: pointer;
                    padding: 10px 15px;
                    background-color: #0073aa;
                    color: white;
                    border-radius: 4px;
                    text-align: center;
                    font-weight: bold;
                }
                :host(:hover) {
                    background-color: #005177;
                }
            </style>
            <div>
                Track Event: ${this.trackingId}
            </div>
        `;
    }

    addEventListeners() {
        this.addEventListener(this.eventName, () => {
            console.log('Analytics Event Fired:', {
                id: this.trackingId,
                name: this.eventName,
                category: this.eventCategory,
                label: this.eventLabel,
                timestamp: new Date().toISOString()
            });
            // In a real-world scenario, you'd send this data to your analytics endpoint.
            // Example:
            // fetch('/wp-json/custom-analytics/v1/track', {
            //     method: 'POST',
            //     headers: { 'Content-Type': 'application/json' },
            //     body: JSON.stringify({
            //         id: this.trackingId,
            //         name: this.eventName,
            //         category: this.eventCategory,
            //         label: this.eventLabel
            //     })
            // });
        });
    }

    disconnectedCallback() {
        // Clean up event listeners if necessary, though for simple DOM events,
        // the browser often handles this.
    }
}

// Define the custom element if it hasn't been defined already
if (!customElements.get('analytics-tracker')) {
    customElements.define('analytics-tracker', AnalyticsTracker);
}

// Register the Gutenberg block
registerBlockType('custom-analytics-tracker/tracker', {
    title: 'Analytics Tracker',
    icon: 'chart-bar', // WordPress Dashicon
    category: 'widgets',
    attributes: {
        trackingId: {
            type: 'string',
            default: 'default-tracker',
        },
        eventName: {
            type: 'string',
            default: 'click',
        },
        eventCategory: {
            type: 'string',
            default: 'interaction',
        },
        eventLabel: {
            type: 'string',
            default: 'block_interaction',
        },
    },

    edit: ({ attributes, setAttributes }) => {
        const blockProps = useBlockProps();
        const { trackingId, eventName, eventCategory, eventLabel } = attributes;

        const onTrackingIdChange = (event) => {
            setAttributes({ trackingId: event.target.value });
        };
        const onEventNameChange = (event) => {
            setAttributes({ eventName: event.target.value });
        };
        const onEventCategoryChange = (event) => {
            setAttributes({ eventCategory: event.target.value });
        };
        const onEventLabelChange = (event) => {
            setAttributes({ eventLabel: event.target.value });
        };

        // We render a placeholder in the editor, the actual Web Component
        // will be rendered on the front-end. For a more interactive editor,
        // you could render the Web Component here and pass attributes.
        return (
            <div { ...blockProps }>
                <h4>Analytics Tracker Settings</h4>
                <label htmlFor="tracking-id">Tracking ID:</label>
                <input
                    type="text"
                    id="tracking-id"
                    value={ trackingId }
                    onChange={ onTrackingIdChange }
                    placeholder="e.g., homepage_cta"
                /><br /><br />

                <label htmlFor="event-name">Event Name:</label>
                <input
                    type="text"
                    id="event-name"
                    value={ eventName }
                    onChange={ onEventNameChange }
                    placeholder="e.g., click"
                /><br /><br />

                <label htmlFor="event-category">Event Category:</label>
                <input
                    type="text"
                    id="event-category"
                    value={ eventCategory }
                    onChange={ onEventCategoryChange }
                    placeholder="e.g., interaction"
                /><br /><br />

                <label htmlFor="event-label">Event Label:</label>
                <input
                    type="text"
                    id="event-label"
                    value={ eventLabel }
                    onChange={ onEventLabelChange }
                    placeholder="e.g., button_click"
                />
                <p>This block will render a clickable element on the front-end that logs analytics data.</p>
            </div>
        );
    },

    save: ({ attributes }) => {
        const blockProps = useBlockProps.save();
        const { trackingId, eventName, eventCategory, eventLabel } = attributes;

        // Render the custom Web Component using its tag name and pass attributes
        // as data-* properties.
        return (
            <div { ...blockProps }>
                <analytics-tracker
                    data-tracking-id={ trackingId }
                    data-event-name={ eventName }
                    data-event-category={ eventCategory }
                    data-event-label={ eventLabel }
                ></analytics-tracker>
            </div>
        );
    },
});

In this JavaScript file:

  • We define a standard Web Component, AnalyticsTracker, with its own shadow DOM for encapsulation.
  • The component accepts attributes like data-tracking-id, data-event-name, etc., which will be passed from the Gutenberg block.
  • connectedCallback renders the component’s UI and attaches event listeners.
  • The addEventListeners method simulates sending data to an analytics endpoint (logged to the console for demonstration).
  • We use customElements.define to register our Web Component with the browser.
  • registerBlockType from @wordpress/blocks defines our Gutenberg block.
  • The attributes define the data our block will store (tracking ID, event details).
  • The edit function provides the user interface in the Gutenberg editor, allowing users to input the tracking details. It uses React hooks provided by @wordpress/block-editor.
  • The save function defines how the block’s content is saved to the database. Crucially, it renders our custom Web Component (<analytics-tracker>) and passes the block’s attributes as data-* attributes.

src/editor.scss and src/style.scss

These files handle styling. For this example, we’ll keep them minimal. The Web Component’s internal styles are handled within its shadow DOM.

/* src/editor.scss */
.wp-block-custom-analytics-tracker-tracker {
    border: 1px dashed #ccc;
    padding: 15px;
    background-color: #f9f9f9;
}
/* src/style.scss */
.wp-block-custom-analytics-tracker-tracker {
    /* Styles for the block wrapper on the front-end */
    margin-bottom: 1.5em;
}

Building the Block Assets

Now that our source files are in place, we need to compile them. Run the build command from your plugin’s root directory:

cd wp-content/plugins/custom-analytics-tracker
npm run build

This command will execute the @wordpress/scripts build process. It will compile src/index.js into build/index.js and src/editor.scss and src/style.scss into build/index.css. The build directory is what our custom-analytics-tracker.php file references.

For development, use npm run start. This command watches your src files and automatically recompiles them whenever you make changes, significantly speeding up the development workflow.

Activating and Testing the Block

Navigate to your WordPress admin area, go to “Plugins,” and activate the “Custom Analytics Tracker” plugin.

Now, create a new post or page, or edit an existing one. Open the block inserter and search for “Analytics Tracker.” Add the block to your content.

In the editor, you should see the input fields for “Tracking ID,” “Event Name,” “Event Category,” and “Event Label.” Fill these out with some test values, for example:

  • Tracking ID: contact_form_submit
  • Event Name: submit
  • Event Category: form
  • Event Label: success

Save the post and then view it on the front-end of your website. Open your browser’s developer console (usually by pressing F12).

When you click on the rendered “Analytics Tracker” block on the front-end, you should see a log message in your browser’s console similar to this:

Analytics Event Fired: {
  id: "contact_form_submit",
  name: "submit",
  category: "form",
  label: "success",
  timestamp: "2023-10-27T10:30:00.000Z"
}

This confirms that our Web Component is correctly receiving the attributes from the Gutenberg block and is triggering the simulated analytics event.

Advanced Considerations and Next Steps

This example provides a foundational understanding. For production use, consider:

  • Actual Data Transmission: Replace the console.log with actual AJAX requests to a WordPress REST API endpoint or a third-party analytics service. You might need to create a custom REST API endpoint in PHP to handle these requests securely.
  • Server-Side Tracking: For critical events or to avoid client-side manipulation, implement server-side tracking by sending data to a PHP endpoint.
  • User Privacy: Ensure compliance with privacy regulations (like GDPR). Implement consent mechanisms and anonymization where necessary.
  • Performance: For blocks that fire many events, optimize the Web Component and data transmission to avoid performance bottlenecks.
  • Error Handling: Implement robust error handling for network requests and data processing.
  • Accessibility: Ensure your Web Component and block are accessible to users with disabilities.
  • Dynamic Content: If your analytics depend on dynamic content within the block, you’ll need to adjust the Web Component’s rendering and event listeners accordingly.

By combining the power of Gutenberg’s block editor with the reusability and encapsulation of Web Components, you can build sophisticated, custom tracking solutions directly within WordPress.

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 WooCommerce core overrides extensions utilizing modern WordPress Database Class ($wpdb) schemas
  • How to securely integrate OpenAI Completion API endpoints into WordPress custom plugins using WordPress Database Class ($wpdb)
  • How to build custom Sage Roots modern environments extensions utilizing modern Cron API (wp_schedule_event) schemas
  • Troubleshooting hook execution order overrides in production when using modern Sage Roots modern environments wrappers
  • How to securely integrate Salesforce CRM endpoints into WordPress custom plugins using WP HTTP API

Categories

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

Recent Posts

  • How to build custom WooCommerce core overrides extensions utilizing modern WordPress Database Class ($wpdb) schemas
  • How to securely integrate OpenAI Completion API endpoints into WordPress custom plugins using WordPress Database Class ($wpdb)
  • How to build custom Sage Roots modern environments extensions utilizing modern Cron API (wp_schedule_event) schemas

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (826)
  • Debugging & Troubleshooting (616)
  • Security & Compliance (594)
  • 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