• 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 CSV bulk exporter block for Gutenberg using SolidJS high-performance reactive components

Step-by-Step Guide to building a custom CSV bulk exporter block for Gutenberg using SolidJS high-performance reactive components

Leveraging SolidJS for High-Performance Gutenberg CSV Export

For e-commerce platforms, the ability to efficiently export large datasets in CSV format is paramount for analytics, reporting, and third-party integrations. Traditional WordPress approaches often involve server-side PHP loops that can become bottlenecks, especially with substantial data volumes. This guide details building a custom Gutenberg block for CSV export, powered by SolidJS, to deliver a high-performance, client-side reactive experience.

Project Setup and Dependencies

We’ll be using Node.js and npm for managing our project. The core dependencies include SolidJS for the UI components and a build tool like Vite for efficient compilation and development server. Ensure you have Node.js installed. If not, download it from nodejs.org.

First, let’s set up a new WordPress plugin directory and initialize our project with npm:

  • Create a new plugin folder: mkdir my-csv-exporter
  • Navigate into the folder: cd my-csv-exporter
  • Initialize npm: npm init -y
  • Install necessary development dependencies: npm install --save-dev vite @vitejs/plugin-react solid-refresh @babel/core @babel/preset-env @babel/preset-react babel-loader css-loader mini-css-extract-plugin sass sass-loader webpack webpack-cli

While Vite is generally preferred for its speed, for WordPress plugin development, integrating with the existing build process or using a Webpack configuration tailored for WordPress plugins is often more practical. For this example, we’ll outline a conceptual Vite setup, which can be adapted to Webpack or other bundlers. A typical vite.config.js might look like this:

Vite Configuration (Conceptual)

import { defineConfig } from 'vite';
import solidPlugin from 'vite-plugin-solid';
import path from 'path';

export default defineConfig({
  plugins: [solidPlugin()],
  build: {
    outDir: 'build', // Output directory for compiled assets
    emptyOutDir: true,
    rollupOptions: {
      input: {
        editor: path.resolve(__dirname, 'src/editor.js'), // Entry point for Gutenberg editor
        script: path.resolve(__dirname, 'src/script.js'), // Entry point for frontend script
      },
      output: {
        entryFileNames: '[name].js',
        chunkFileNames: 'chunks/[name]-[hash].js',
        assetFileNames: 'assets/[name]-[hash].[ext]',
      },
    },
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
});

Gutenberg Block Registration

We need to register our custom block with WordPress. This involves creating a PHP file (e.g., my-csv-exporter.php) in the plugin’s root directory to enqueue our compiled JavaScript and define the block’s attributes.

<?php
/**
 * Plugin Name: My CSV Exporter
 * Description: A custom Gutenberg block for high-performance CSV export.
 * Version: 1.0.0
 * Author: Your Name
 */

function my_csv_exporter_register_block() {
    // Automatically load dependencies and version
    $asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php'); // Assuming build/index.asset.php is generated by Vite/Webpack

    wp_register_script(
        'my-csv-exporter-block-editor-script',
        plugins_url( 'build/editor.js', __FILE__ ),
        $asset_file['dependencies'],
        $asset_file['version']
    );

    wp_register_script(
        'my-csv-exporter-block-frontend-script',
        plugins_url( 'build/script.js', __FILE__ ),
        array(), // No specific dependencies for frontend script if logic is client-side
        $asset_file['version']
    );

    register_block_type( 'my-csv-exporter/csv-exporter', array(
        'editor_script' => 'my-csv-exporter-block-editor-script',
        'script'        => 'my-csv-exporter-block-frontend-script',
        'attributes'    => array(
            'data' => array(
                'type' => 'string',
                'default' => '',
            ),
            'fileName' => array(
                'type' => 'string',
                'default' => 'export.csv',
            ),
        ),
    ) );
}
add_action( 'init', 'my_csv_exporter_register_block' );
?>

The index.asset.php file is crucial for WordPress to correctly manage script dependencies and versions. If you’re using Vite, you’ll need a plugin or a custom script to generate this file based on your package.json and Vite’s output. For Webpack, this is often handled by the @wordpress/scripts package.

SolidJS Component for the Gutenberg Editor

This is where the high-performance aspect comes into play. We’ll create a SolidJS component that handles the data fetching (or selection) and the CSV generation logic client-side. This component will be rendered within the Gutenberg editor.

src/components/CsvExporter.jsx

import { createSignal, For } from 'solid-js';
import { createStore } from 'solid-js/store';

// Helper function to convert JSON data to CSV string
function convertToCSV(data, headers) {
    const rows = data.map(row =>
        headers.map(header => {
            const value = row[header] === null || row[header] === undefined ? '' : String(row[header]);
            // Escape double quotes and wrap in double quotes if necessary
            return `"${value.replace(/"/g, '""')}"`;
        }).join(',')
    );
    return [headers.join(','), ...rows].join('\n');
}

// Helper function to download the CSV
function downloadCSV(csvString, fileName) {
    const blob = new Blob([csvString], { type: 'text/csv;charset=utf-8;' });
    const link = document.createElement('a');
    if (link.download) {
        link.setAttribute('href', URL.createObjectURL(blob));
        link.setAttribute('download', fileName);
        link.style.visibility = 'hidden';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    }
}

function CsvExporter(props) {
    const [isLoading, setIsLoading] = createSignal(false);
    const [selectedData, setSelectedData] = createStore([]);
    const [availableColumns, setAvailableColumns] = createSignal([]);
    const [selectedColumns, setSelectedColumns] = createSignal([]);
    const [fileName, setFileName] = createSignal(props.fileName || 'export.csv');

    // Simulate fetching data (replace with actual API call or WordPress REST API)
    const fetchData = async () => {
        setIsLoading(true);
        // Example: Fetching product data from a custom endpoint
        try {
            const response = await fetch('/wp-json/my-plugin/v1/products'); // Replace with your actual endpoint
            const data = await response.json();

            if (data && data.length > 0) {
                const columns = Object.keys(data[0]);
                setAvailableColumns(columns);
                setSelectedColumns(columns); // Select all by default
                setSelectedData(data);
            } else {
                setSelectedData([]);
                setAvailableColumns([]);
                setSelectedColumns([]);
            }
        } catch (error) {
            console.error("Error fetching data:", error);
            setSelectedData([]);
            setAvailableColumns([]);
            setSelectedColumns([]);
        } finally {
            setIsLoading(false);
        }
    };

    const handleExport = () => {
        if (selectedData.length === 0 || selectedColumns.length === 0) {
            alert('No data or columns selected for export.');
            return;
        }
        const csvString = convertToCSV(selectedData, selectedColumns);
        downloadCSV(csvString, fileName());
    };

    // Effect to fetch data when component mounts (or when a prop changes)
    // In a real Gutenberg block, you might fetch data based on block attributes or context
    // For simplicity, we'll call it directly here.
    // Consider using useEffect from 'solid-js/web' for side effects in a React-like way if needed.
    // For this example, we'll trigger it via a button.

    return (
        <div>
            <h3>CSV Exporter</h3>
            <button onClick={fetchData} disabled={isLoading()}>{isLoading() ? 'Loading...' : 'Load Data'}</button>

            {availableColumns().length > 0 && (
                <div>
                    <h4>Select Columns to Export:</h4>
                    <div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px', marginBottom: '20px' }}>
                        <For each={availableColumns()}>
                            {(column) => (
                                <label>
                                    <input
                                        type="checkbox"
                                        checked={selectedColumns().includes(column)}
                                        onChange={(e) => {
                                            if (e.target.checked) {
                                                setSelectedColumns([...selectedColumns(), column]);
                                            } else {
                                                setSelectedColumns(selectedColumns().filter(c => c !== column));
                                            }
                                        }}
                                    />
                                    {column}
                                </label>
                            )}
                        </For>
                    </div>

                    <div style={{ marginBottom: '20px' }}>
                        <label htmlFor="fileName">File Name: </label>
                        <input
                            type="text"
                            id="fileName"
                            value={fileName()}
                            onInput={(e) => setFileName(e.target.value)}
                            style={{ padding: '8px', border: '1px solid #ccc', borderRadius: '4px' }}
                        />
                    </div>

                    <button onClick={handleExport} disabled={selectedData.length === 0 || selectedColumns().length === 0}>
                        Export Selected CSV
                    </button>
                </div>
            )}

            {selectedData.length === 0 && !isLoading() && (
                <p>Click "Load Data" to fetch records for export.</p>
            )}
        </div>
    );
}

export default CsvExporter;

Integrating SolidJS with Gutenberg

To render the SolidJS component within the Gutenberg editor, we’ll use the @wordpress/element package, which provides a compatibility layer for React-like frameworks. We’ll create an editor.js file that imports our SolidJS component and uses wp.element.createElement to render it.

src/editor.js

import { registerBlockType } from '@wordpress/blocks';
import { createElement } from '@wordpress/element';
import CsvExporter from './components/CsvExporter'; // Assuming CsvExporter.jsx is in src/components/

// Import SolidJS runtime if not globally available or handled by bundler
// import { render } from 'solid-js/web'; // This might be needed depending on your setup

registerBlockType('my-csv-exporter/csv-exporter', {
    title: 'High-Performance CSV Exporter',
    icon: 'download',
    category: 'widgets',
    attributes: {
        data: {
            type: 'string',
            default: '',
        },
        fileName: {
            type: 'string',
            default: 'export.csv',
        },
    },
    edit: (props) => {
        // Use createElement to render the SolidJS component.
        // The 'props' object contains block attributes and other Gutenberg context.
        // We pass relevant props to our SolidJS component.
        return createElement(CsvExporter, {
            fileName: props.attributes.fileName,
            // You might pass a function to update attributes if needed
            // onFileNameChange: (newFileName) => props.setAttributes({ fileName: newFileName }),
        });
    },
    save: (props) => {
        // The save function should return null for dynamic blocks or
        // static HTML. For a block that primarily performs client-side actions
        // and doesn't render persistent UI in the frontend, returning null is common.
        // If you need to save attributes like fileName, you can render them here.
        return null; // Or return a placeholder if needed
    },
});

In the edit function, we use createElement from @wordpress/element. This function acts as a bridge, allowing us to instantiate our SolidJS component within the WordPress editor’s React-based virtual DOM. We pass the block’s attributes (like fileName) as props to our CsvExporter component.

The save function returning null signifies a dynamic block. This means the block’s rendering on the frontend will be handled by PHP (or a REST API endpoint), which can fetch data and generate the CSV on demand, or it might rely entirely on the script.js for frontend interactivity if the export is triggered directly by the user on the live site.

Frontend Script (Optional but Recommended)

While the editor component handles the UI and export logic, you might want a similar or simplified export trigger on the frontend. The script.js file is enqueued for the frontend and can contain logic to initialize the block or provide frontend-specific functionality.

src/script.js

// This script runs on the frontend.
// If your block is dynamic, this might not be strictly necessary for the export itself,
// but it's good practice to have it for potential frontend interactions or
// if you decide to render the export button directly on the page.

console.log('My CSV Exporter frontend script loaded.');

// Example: If you wanted to initialize the component on the frontend
// document.addEventListener('DOMContentLoaded', () => {
//     const exporterElements = document.querySelectorAll('.wp-block-my-csv-exporter-csv-exporter');
//     exporterElements.forEach(element => {
//         // You would need a way to render SolidJS components outside of Gutenberg's context here.
//         // This typically involves a separate SolidJS entry point and rendering logic.
//         // For this example, we assume the export is primarily an editor function or
//         // handled by a dynamic block rendering.
//     });
// });

Backend API Endpoint (Example)

The SolidJS component’s fetchData function uses a placeholder REST API endpoint (/wp-json/my-plugin/v1/products). You’ll need to create this endpoint in your WordPress plugin’s PHP file to serve the data. This endpoint should return a JSON array of objects, where each object represents a row and keys are column names.

<?php
// Add this to your main plugin file (my-csv-exporter.php)

add_action( 'rest_api_init', function () {
    register_rest_route( 'my-plugin/v1', '/products', array(
        'methods' => 'GET',
        'callback' => 'my_csv_exporter_get_products_data',
        'permission_callback' => '__return_true', // Adjust permissions as needed
    ) );
} );

function my_csv_exporter_get_products_data( WP_REST_Request $request ) {
    // --- IMPORTANT ---
    // Replace this with your actual data fetching logic.
    // This could involve querying the 'posts' table, WooCommerce products,
    // custom post types, or any other data source.
    // Ensure the data is structured as an array of associative arrays.

    // Example: Fetching published posts (as a simple demonstration)
    $args = array(
        'post_type'      => 'post',
        'post_status'    => 'publish',
        'posts_per_page' => -1, // Fetch all posts
    );
    $posts = get_posts( $args );

    $data = array();
    if ( ! empty( $posts ) ) {
        foreach ( $posts as $post ) {
            $data[] = array(
                'ID'          => $post->ID,
                'Title'       => $post->post_title,
                'Date'        => $post->post_date,
                'Author ID'   => $post->post_author,
                'Status'      => $post->post_status,
                // Add more fields as needed
            );
        }
    }

    return new WP_REST_Response( $data, 200 );
}
?>

This REST API endpoint provides the data in JSON format, which our SolidJS component can easily consume. For large datasets, consider implementing pagination or server-side filtering within this endpoint to avoid overwhelming the browser.

Build Process and Deployment

After setting up your files, you’ll need to run the build command defined in your package.json. If using Vite, you might have a script like:

"scripts": {
  "dev": "vite",
  "build": "vite build"
}

Run npm run build to compile your SolidJS components and JavaScript into the build directory. This directory should then be included in your plugin’s deployment package. Ensure the index.asset.php file is also generated and placed correctly if your build process doesn’t handle it automatically.

Performance Considerations and Enhancements

SolidJS’s fine-grained reactivity minimizes unnecessary re-renders, making it performant for complex UIs. However, for extremely large datasets:

  • Data Fetching: Optimize the backend API endpoint. Use efficient database queries, consider caching, and implement server-side pagination or lazy loading if the dataset is massive.
  • CSV Generation: While client-side generation is fast for moderate data, for millions of rows, consider a hybrid approach where the initial data is fetched client-side, but for very large exports, a server-side PHP process is triggered (perhaps via an AJAX call to a different endpoint) to generate the CSV directly on the server and offer it for download.
  • Column Selection: For hundreds of columns, the checkbox list can become unwieldy. Implement search functionality or a multi-select component for better usability.
  • Memory Management: Be mindful of browser memory limits when processing very large datasets client-side. If the JSON payload becomes too large, it can crash the browser tab.

By adopting SolidJS and a well-structured Gutenberg block architecture, you can create a powerful and performant CSV export tool that significantly enhances the workflow for e-commerce managers and technical teams.

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

  • Designing audit logs for enterprise WordPress setups tracking internal user modifications to member profile directories
  • How to build custom Elementor custom widgets extensions utilizing modern WordPress Settings API schemas
  • Step-by-Step Guide to building a custom automated performance diagnostic log block for Gutenberg using Alpine.js lightweight states
  • Troubleshooting namespace class loading collisions in production when using modern Classic Core PHP wrappers
  • How to securely integrate GitHub API repositories endpoints into WordPress custom plugins using Transients API

Categories

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

Recent Posts

  • Designing audit logs for enterprise WordPress setups tracking internal user modifications to member profile directories
  • How to build custom Elementor custom widgets extensions utilizing modern WordPress Settings API schemas
  • Step-by-Step Guide to building a custom automated performance diagnostic log block for Gutenberg using Alpine.js lightweight states

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (841)
  • Debugging & Troubleshooting (636)
  • Security & Compliance (613)
  • 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