• 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 real-time audit dashboard block for Gutenberg using SolidJS high-performance reactive components

Step-by-Step Guide to building a custom real-time audit dashboard block for Gutenberg using SolidJS high-performance reactive components

Project Setup: WordPress, Node.js, and SolidJS Integration

This guide assumes a local WordPress development environment is already set up. We’ll leverage Node.js and npm/yarn for managing our JavaScript dependencies and building our Gutenberg block. The core of our real-time dashboard will be built with SolidJS, a declarative, efficient, and flexible JavaScript library for building user interfaces. Its fine-grained reactivity model makes it exceptionally well-suited for high-performance, dynamic UIs like an audit dashboard.

First, let’s initialize a new WordPress plugin to house our Gutenberg block. Navigate to your WordPress plugins directory and run:

cd wp-content/plugins/
mkdir real-time-audit-dashboard
cd real-time-audit-dashboard
touch real-time-audit-dashboard.php

Now, create the main plugin file real-time-audit-dashboard.php with the necessary plugin header:

<?php
/**
 * Plugin Name: Real-time Audit Dashboard
 * Description: A custom Gutenberg block for displaying real-time audit logs.
 * Version: 1.0.0
 * Author: Your Name
 * License: GPL-2.0-or-later
 * Text Domain: real-time-audit-dashboard
 */

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

/**
 * Registers the block using the metadata loaded from the `block.json` file.
 * Behind the scenes, it registers also all assets so they can be enqueued
 * through the block editor in the corresponding context.
 *
 * @see https://developer.wordpress.org/reference/functions/register_block_type/
 */
function real_time_audit_dashboard_block_init() {
    register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'real_time_audit_dashboard_block_init' );
?>

Next, we need to set up our JavaScript build process. Inside your plugin directory, create a package.json file. We’ll use Vite for its speed and excellent support for modern JavaScript frameworks like SolidJS.

npm init -y
npm install --save-dev vite @vitejs/plugin-react @vitejs/plugin-legacy solid-refresh @babel/core @babel/preset-env @babel/preset-react @babel/plugin-transform-react-jsx

Create a vite.config.js file to configure Vite:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react'; // Vite's React plugin can be used for JSX compilation
import legacy from '@vitejs/plugin-legacy';
import solidRefresh from 'solid-refresh'; // For SolidJS hot module replacement

export default defineConfig({
    plugins: [
        // Using react plugin for JSX compilation, which SolidJS also supports.
        react(),
        solidRefresh(),
        legacy({
            targets: ['defaults', 'not IE 11'],
        }),
    ],
    build: {
        outDir: 'build', // Output directory for compiled assets
        assetsDir: '',   // Keep assets in the root of outDir for easier WordPress enqueueing
        manifest: true,  // Generate manifest.json for asset mapping
        rollupOptions: {
            input: {
                index: 'src/index.js', // Entry point for our block's JavaScript
            },
        },
    },
    server: {
        // Configure server for local development if needed, e.g., proxying WordPress requests
        // For simplicity, we'll assume WordPress is served separately and we're building assets.
    },
});

Create a .babelrc file for Babel configuration, ensuring JSX is handled correctly for SolidJS:

{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ],
    "plugins": [
        "@babel/plugin-transform-react-jsx"
    ]
}

Now, let’s set up the directory structure for our source files:

mkdir src
touch src/index.js
touch src/edit.jsx
touch src/save.jsx
touch src/App.jsx

In your package.json, add build scripts:

{
    "name": "real-time-audit-dashboard",
    "version": "1.0.0",
    "description": "",
    "main": "build/index.js",
    "scripts": {
        "dev": "vite",
        "build": "vite build",
        "preview": "vite preview"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "@babel/core": "^7.23.7",
        "@babel/plugin-transform-react-jsx": "^7.23.3",
        "@babel/preset-env": "^7.23.8",
        "@babel/preset-react": "^7.23.3",
        "@vitejs/plugin-legacy": "^5.0.1",
        "@vitejs/plugin-react": "^4.2.1",
        "solid-refresh": "^0.2.0",
        "vite": "^5.0.10"
    },
    "dependencies": {
        "solid-js": "^1.8.12"
    }
}

Finally, we need to tell WordPress how to enqueue our compiled assets. We’ll create a block.json file in the root of our plugin directory.

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 3,
    "name": "real-time-audit-dashboard/block",
    "version": "1.0.0",
    "title": "Real-time Audit Dashboard",
    "category": "widgets",
    "icon": "chart-line",
    "description": "Displays real-time audit logs.",
    "attributes": {
        "auditLogSource": {
            "type": "string",
            "default": "wp_audit_log"
        }
    },
    "editorScript": "file:./build/index.js",
    "editorStyle": "file:./build/style-index.css",
    "style": "file:./build/style-index.css",
    "viewScript": "file:./build/index.js"
}

With the setup complete, run npm run dev in your terminal. This will start the Vite development server, watching for changes in the src directory and recompiling assets into the build folder. You can now activate the “Real-time Audit Dashboard” plugin in your WordPress admin area.

Gutenberg Block Structure and SolidJS Integration

Gutenberg blocks are defined by two main JavaScript files: edit.jsx for the editor interface and save.jsx for the frontend rendering. We’ll use these as entry points to load our SolidJS application.

First, let’s define the basic structure of our src/index.js, which will register the block with WordPress:

import { registerBlockType } from '@wordpress/blocks';
import './style.scss'; // Import global styles

// Import components for editor and frontend
import Edit from './edit';
import save from './save';

registerBlockType( 'real-time-audit-dashboard/block', {
    edit: Edit,
    save: save,
} );

Now, let’s create the src/edit.jsx file. This component will be rendered within the Gutenberg editor. We’ll use it to set up our SolidJS application and potentially provide controls for configuring the block.

import { useBlockProps } from '@wordpress/block-editor';
import { createRoot } from 'react-dom/client'; // Using react-dom/client for compatibility with Vite's React plugin
import { onMount } from 'solid-js';
import App from './App'; // Our main SolidJS application component

const Edit = ( { attributes, setAttributes } ) => {
    const blockProps = useBlockProps();

    // Mount SolidJS app into the block's wrapper element
    onMount( () => {
        const container = document.createElement( 'div' );
        blockProps.ref.appendChild( container ); // Append to the block's root element
        const root = createRoot( container );
        root.render( <App attributes={attributes} setAttributes={setAttributes} /> );

        // Cleanup on unmount
        return () => root.unmount();
    } );

    return (
        <div { ...blockProps }>
            {/* This div will be the mount point for our SolidJS app */}
            <div id="solid-app-mount-point"></div>
        </div>
    );
};

export default Edit;

The src/save.jsx file determines how the block is rendered on the frontend. For dynamic blocks that rely on JavaScript for rendering (like our real-time dashboard), it’s common to return null or a placeholder, and let the JavaScript handle the actual rendering.

const save = () => {
    // For dynamic blocks, we often return null and let the client-side JS handle rendering.
    // Alternatively, you could return a static placeholder and hydrate it.
    return null;
};

export default save;

Now, let’s create our main SolidJS application component, src/App.jsx. This is where the core logic for fetching and displaying audit data will reside.

import { createSignal, createEffect, Show } from 'solid-js';
import { For } from 'solid-js';

const App = ( { attributes, setAttributes } ) => {
    const [logs, setLogs] = createSignal([]);
    const [loading, setLoading] = createSignal(true);
    const [error, setError] = createSignal(null);

    // Fetch audit logs from a WordPress REST API endpoint
    const fetchLogs = async () => {
        setLoading(true);
        setError(null);
        try {
            // Assuming a custom REST API endpoint '/wp-json/myplugin/v1/audit-logs'
            // You'll need to register this endpoint in your PHP.
            const response = await fetch( '/wp-json/myplugin/v1/audit-logs' );
            if ( ! response.ok ) {
                throw new Error( `HTTP error! status: ${response.status}` );
            }
            const data = await response.json();
            setLogs( data );
        } catch ( e ) {
            setError( e.message );
            console.error( "Failed to fetch audit logs:", e );
        } finally {
            setLoading(false);
        }
    };

    // Fetch logs on component mount
    createEffect( () => {
        fetchLogs();

        // Set up polling for real-time updates
        const intervalId = setInterval( fetchLogs, 5000 ); // Poll every 5 seconds

        // Cleanup interval on component unmount
        return () => clearInterval( intervalId );
    } );

    return (
        <div class="audit-dashboard-container">
            <h3>Real-time Audit Log</h3>
            <Show when={loading()} fallback={
                <Show when={error()} fallback={
                    <ul class="audit-log-list">
                        <For each={logs()} fallback={<li>No logs found.</li>}>
                            {(log) => (
                                <li class="audit-log-item">
                                    <strong>{log.timestamp}:</strong> {log.message} - User: {log.user_id}
                                </li>
                            )}
                        </For>
                    </ul>
                }>
                    <p class="error-message">Error: {error()}</p>
                </Show>
            }>
                <p>Loading logs...</p>
            </Show>
        </div>
    );
};

export default App;

To make this work, we need to register the REST API endpoint in our real-time-audit-dashboard.php file. This endpoint will query your audit log data.

<?php
// ... (previous plugin code)

/**
 * Register REST API endpoint for audit logs.
 */
function register_audit_logs_rest_route() {
    register_rest_route( 'myplugin/v1', '/audit-logs', array(
        'methods'  => 'GET',
        'callback' => 'get_audit_logs_callback',
        'permission_callback' => '__return_true', // Adjust permissions as needed
    ) );
}
add_action( 'rest_api_init', 'register_audit_logs_rest_route' );

/**
 * Callback function to retrieve audit logs.
 * This is a placeholder; replace with your actual audit log retrieval logic.
 */
function get_audit_logs_callback( WP_REST_Request $request ) {
    // In a real-world scenario, you would query your audit log database or storage.
    // For demonstration, we'll return dummy data.
    $dummy_logs = array(
        array(
            'id'        => 1,
            'timestamp' => current_time( 'mysql' ),
            'message'   => 'User logged in.',
            'user_id'   => 1,
            'ip_address'=> '192.168.1.1',
        ),
        array(
            'id'        => 2,
            'timestamp' => date( 'Y-m-d H:i:s', strtotime( '-1 minute' ) ),
            'message'   => 'Post updated.',
            'user_id'   => 2,
            'ip_address'=> '10.0.0.5',
        ),
    );

    // Simulate fetching from a database or log file
    // For production, use a proper data access layer.
    // Example: $logs = MyAuditLogModel::getRecentLogs( 50 );

    return new WP_REST_Response( $dummy_logs, 200 );
}

// ... (rest of the plugin code)
?>

Finally, add some basic styling in src/style.scss (or create it if it doesn’t exist):

.audit-dashboard-container {
    border: 1px solid #ccc;
    padding: 15px;
    margin: 10px 0;
    background-color: #f9f9f9;
    border-radius: 4px;
}

.audit-log-list {
    list-style: none;
    padding: 0;
    margin: 10px 0 0 0;
    max-height: 300px; /* Limit height for scrollability */
    overflow-y: auto;
}

.audit-log-item {
    padding: 8px 0;
    border-bottom: 1px dashed #eee;
    font-size: 0.9em;
    color: #333;
}

.audit-log-item:last-child {
    border-bottom: none;
}

.error-message {
    color: red;
    font-weight: bold;
}

After running npm run dev and saving these files, you should be able to add the “Real-time Audit Dashboard” block to a post or page in the WordPress editor. The SolidJS application will mount, fetch dummy logs, and display them, refreshing every 5 seconds.

Optimizing for Performance: SolidJS Reactivity and Data Fetching

SolidJS’s core strength lies in its fine-grained reactivity. Unlike virtual DOM-based libraries, SolidJS compiles templates into highly efficient, direct DOM updates. This means that when data changes, only the specific parts of the DOM that depend on that data are re-rendered, leading to exceptional performance.

In our App.jsx component, we use SolidJS’s signals (createSignal) to manage the state of our logs, loading status, and errors. When setLogs is called, SolidJS automatically updates only the parts of the UI that display the logs.

The real-time aspect is handled by polling the REST API endpoint using setInterval within a createEffect. This effect also includes a cleanup function to clear the interval when the component unmounts, preventing memory leaks.

For production, consider these optimizations:

  • Debouncing/Throttling Updates: If log updates are extremely frequent, you might want to debounce or throttle the fetchLogs calls to avoid overwhelming the server or the client. However, for typical audit logs, polling every 5 seconds is usually acceptable.
  • WebSockets: For true real-time, low-latency updates, consider implementing a WebSocket connection instead of polling. This would involve a separate WebSocket server and a different client-side implementation in SolidJS.
  • Data Pagination/Infinite Scroll: For dashboards with potentially thousands of logs, implement pagination or infinite scrolling to load data incrementally, improving initial load times and reducing memory usage.
  • Server-Side Rendering (SSR) / Static Site Generation (SSG): While our current implementation is client-side rendered, for certain types of audit data that don’t need to be strictly real-time on initial load, SSR or SSG could improve perceived performance and SEO. However, for a dynamic dashboard, client-side rendering with efficient data fetching is often preferred.
  • API Endpoint Optimization: Ensure your WordPress REST API endpoint (get_audit_logs_callback) is highly optimized. Use efficient database queries, caching mechanisms (like object caching or transient API), and consider indexing relevant database columns.
  • Asset Optimization: Vite’s build process already performs minification and code splitting. Ensure your vite.config.js is configured correctly for production builds. For WordPress, you’ll typically enqueue the main JavaScript file and its dependencies based on the manifest.json generated by Vite.

To integrate the compiled assets correctly with WordPress, you’ll need to modify your real-time-audit-dashboard.php to read the manifest.json file generated by Vite and enqueue the correct script and style handles.

<?php
// ... (previous plugin code)

/**
 * Enqueues block assets.
 */
function real_time_audit_dashboard_enqueue_block_assets() {
    // Check if the manifest file exists.
    $manifest_file = __DIR__ . '/build/manifest.json';
    if ( ! file_exists( $manifest_file ) ) {
        // Fallback for development or if manifest is missing
        wp_enqueue_script(
            'real-time-audit-dashboard-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(
            'real-time-audit-dashboard-editor-style',
            plugin_dir_url( __FILE__ ) . 'build/style-index.css',
            array( 'wp-edit-blocks' ),
            filemtime( plugin_dir_path( __FILE__ ) . 'build/style-index.css' )
        );
        return;
    }

    // Read the manifest file.
    $manifest = json_decode( file_get_contents( $manifest_file ), true );

    // Enqueue editor script.
    if ( isset( $manifest['src/index.js'] ) ) {
        $script_asset = $manifest['src/index.js'];
        wp_enqueue_script(
            'real-time-audit-dashboard-editor-script',
            plugin_dir_url( __FILE__ ) . $script_asset['file'],
            $script_asset['imports'] ?? array(), // Dependencies from manifest
            $script_asset['version'] ?? filemtime( plugin_dir_path( __FILE__ ) . $script_asset['file'] )
        );
    }

    // Enqueue editor styles.
    if ( isset( $manifest['src/style.scss'] ) ) { // Assuming style.scss is the entry point for styles
        $style_asset = $manifest['src/style.scss'];
        wp_enqueue_style(
            'real-time-audit-dashboard-editor-style',
            plugin_dir_url( __FILE__ ) . $style_asset['file'],
            $style_asset['imports'] ?? array(),
            $style_asset['version'] ?? filemtime( plugin_dir_path( __FILE__ ) . $style_asset['file'] )
        );
    }
}
add_action( 'enqueue_block_editor_assets', 'real_time_audit_dashboard_enqueue_block_assets' );

// For frontend styles/scripts if needed separately, use 'wp_enqueue_scripts' hook
// and potentially 'viewScript' from block.json if you have a separate frontend JS.
// In our case, editorScript and viewScript point to the same file, so enqueueing once is fine.

// ... (rest of the plugin code)
?>

By leveraging SolidJS’s efficient reactivity and carefully managing data fetching, you can build a high-performance, real-time audit dashboard block for WordPress that provides a seamless user experience.

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

  • Step-by-Step Guide: Offloading high-frequency custom subscription logs metadata writes to a Redis KV store
  • How to design a modular Command Query Responsibility Segregation (CQRS) architecture for enterprise-level custom plugins
  • Troubleshooting guide: Resolving memory leak spikes caused by unclosed custom database loops in user transaction ledgers
  • Designing audit logs for enterprise WordPress setups tracking internal user modifications to affiliate click tracking logs
  • WordPress Development Recipe: Real-time custom event triggers using WebSockets and Transients 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 (41)
  • 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 (64)
  • WordPress Plugin Development (70)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Step-by-Step Guide: Offloading high-frequency custom subscription logs metadata writes to a Redis KV store
  • How to design a modular Command Query Responsibility Segregation (CQRS) architecture for enterprise-level custom plugins
  • Troubleshooting guide: Resolving memory leak spikes caused by unclosed custom database loops in user transaction ledgers

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