Creating a Todo-List Component for the WordPress Block Editor – part 1

With this series of practical materials, I want to show you how far WordPress development has come. In 2024, a WordPress developer is primarily a frontend developer who must have a solid understanding of HTML/CSS, React, the specifics of WordPress, as well as PHP to independently write the backend for the blocks being created. Admit it, you thought it was just about clicking buttons in Elementor, didn’t you? 🙂

The WordPress Block Editor (Gutenberg) has significantly transformed the process of creating and editing content on WordPress sites, providing developers with powerful tools to create customizable and functional blocks. In this guide, we will create our own “Todo-List” component that allows users to create task lists and send data to the server when the status of tasks changes.

Basic Elements of the Block

The block we create will consist of several key elements:

  1. RichText: A component that allows users to enter and edit text within the block.
  2. InspectorControls: A panel that provides settings and controls for the block in the editor.
  3. CheckboxControl: A control element used to mark tasks as completed.
  4. useState: A hook that manages the state of tasks within the block.
  5. fetch API: Used to send task data to the server.

Each block is created as a separate plugin, which can be reused in other WordPress projects. This allows you to easily integrate the created block into any other sites running on WordPress.

Setting Up the Development Environment

Before we start building the component, it’s important to ensure that your development environment is properly set up. You’ll need the following tools to develop the block:

  1. WordPress: Make sure your WordPress version is 5.0 or higher, as the Block Editor was introduced in this version.
  2. Node.js and npm: Required for working with JavaScript and managing dependencies.
  3. WP-CLI (optional): A command-line tool for working with WordPress that can simplify development.

Installing Node.js and npm

If Node.js and npm are not yet installed on your computer, download and install them from the official Node.js website.

After installation, run the following commands to ensure everything is installed correctly:

node -v
npm -v

If the commands return versions, everything is working correctly.

Creating the Todo-List Block

To speed up the block creation process, you can use a command that will automatically generate the entire project structure, including configurations and files.

Step 1: Creating the Block via Command

Open your terminal and run the following command to automatically create the “Todo-List” block:

npx @wordpress/create-block@latest todo-list

This command will perform the following steps for you:

  1. Project Initialization: The command will create a new directory named todo-list, containing all the necessary files and structure for the block.
  2. Installing Dependencies: All necessary packages, such as @wordpress/scripts, will be automatically installed.
  3. Creating the File Structure: The structure will include folders src and build, as well as an index.js file with the basic code for the block.
  4. Setting up block.json: This file will be created with the basic configuration that defines the “Todo-List” block.

File Structure and Description

After running the command, the following project structure will be created:

todo-list/
│
├── src/
│   └── index.js
│
├── build/
│
├── block.json
├── package.json
└── README.md

1. src/index.js

The index.js file in the src folder contains all the source code for your block. This is where the block’s logic is implemented, including its appearance and interaction with the user. The main parts of the code involve registering the block using the registerBlockType function, setting up attributes, and defining the functions for editing and saving the block.

Sample content of index.js:

import { registerBlockType } from '@wordpress/blocks';
import { useState } from '@wordpress/element';
import { RichText, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, CheckboxControl } from '@wordpress/components';

registerBlockType('todo-plugin/todo-list', {
    title: 'Todo List',
    icon: 'list-view',
    category: 'widgets',
    attributes: {
        tasks: {
            type: 'array',
            default: [],
        },
    },
    edit({ attributes, setAttributes }) {
        const { tasks } = attributes;
        const [taskText, setTaskText] = useState('');

        const addTask = () => {
            if (taskText) {
                const newTasks = [...tasks, { text: taskText, done: false }];
                setAttributes({ tasks: newTasks });
                setTaskText('');
                sendTasksToServer(newTasks);
            }
        };

        const toggleTaskDone = (index) => {
            const newTasks = tasks.map((task, i) =>
                i === index ? { ...task, done: !task.done } : task
            );
            setAttributes({ tasks: newTasks });
            sendTasksToServer(newTasks);
        };

        const sendTasksToServer = (tasks) => {
            // Example of sending data to the server
            fetch('/wp-json/todo/v1/update', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ tasks }),
            });
        };

        return (
            <div>
                <InspectorControls>
                    <PanelBody title="Tasks">
                        {tasks.map((task, index) => (
                            <CheckboxControl
                                key={index}
                                label={task.text}
                                checked={task.done}
                                onChange={() => toggleTaskDone(index)}
                            />
                        ))}
                    </PanelBody>
                </InspectorControls>
                <div>
                    <RichText
                        tagName="p"
                        value={taskText}
                        onChange={(text) => setTaskText(text)}
                        placeholder="Enter new task"
                    />
                    <button onClick={addTask}>Add Task</button>
                </div>
                <ul>
                    {tasks.map((task, index) => (
                        <li key={index}>
                            <CheckboxControl
                                label={task.text}
                                checked={task.done}
                                onChange={() => toggleTaskDone(index)}
                            />
                        </li>
                    ))}
                </ul>
            </div>
        );
    },
    save({ attributes }) {
        const { tasks } = attributes;

        return (
            <ul>
                {tasks.map((task, index) => (
                    <li key={index}>
                        <RichText.Content
                            tagName="span"
                            value={task.text}
                            style={{ textDecoration: task.done ? 'line-through' : 'none' }}
                        />
                    </li>
                ))}
            </ul>
        );
    },
});

2. build

The build folder is intended to store compiled files. After building the project using the npm run build command, the ready-to-use index.js file will be placed in this folder, which will be used by WordPress to display and operate the block.

3. block.json

The block.json file contains metadata for your block. It defines the block’s name, category, icon, scripts, and styles necessary for its operation. This file is essential for registering the block in WordPress.

Sample content of block.json:

{
  "apiVersion": 2,
  "name": "todo-plugin/todo-list",
  "title": "Todo List",
  "category": "widgets",
  "icon": "list-view",
  "description": "A customizable todo list block.",
  "keywords": [ "todo", "tasks", "list" ],
  "version": "1.0.0",
  "textdomain": "todo-list",
  "editorScript": "file:./build/index.js"
}

4. package.json

The package.json file contains information about the project and its dependencies. It manages the installation and usage of packages required for developing and building the block. This file is automatically created during project initialization and is updated as new dependencies are added.

Sample content of package.json:

{
  "name": "todo-list",
  "version": "1.0.0",
  "description": "A custom block for todo list",
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start"
  },
  "devDependencies": {
    "@wordpress/scripts": "^24.0.0"
  }
}

5. README.md

The README.md file contains a description of the project, installation and usage instructions, and other important information that may be needed by developers or users. This file is useful for documenting the project.

Building the Project

After making changes to the code, build the project to get a ready-to-use file for WordPress:

npm run build

The build command will compile your code and place it in the build folder.

Registering the Block in WordPress

Now that the block is ready, it needs to be registered in WordPress. To do this, add the following code to your plugin or theme file:

function todo_plugin_register_block() {
    wp_register_script(
        'todo-list-editor-script',
        plugins_url( 'build/index.js', __FILE__ ),
        array( 'wp-blocks', 'wp-element', 'wp-editor' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'build/index.js' )
    );

    register_block_type( 'todo-plugin/todo-list', array(
        'editor_script' => 'todo-list-editor-script',
    ) );
}
add_action( 'init', 'todo_plugin_register_block' );

This code will register your block in WordPress and enqueue the compiled JavaScript file.

Conclusion

Using the npx @wordpress/create-block@latest command significantly speeds up the process of creating a new block for the WordPress Block Editor. In this guide, we explored how to create a “Todo-List” block that allows users to create task lists and manage their completion, with data being sent to the server. This component can be expanded by adding new features and improving user interaction, opening up broad possibilities for customizing your WordPress site.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *