SponsoredHosinger Special Offer

WP+ WordPress Plugin Development

Build WordPress plugins from scratch: actions and filters, shortcodes, settings pages, custom post types, meta boxes, AJAX, REST endpoints, custom tables, cron and blocks.

Building a Block

WordPress Plugin Development Lesson 23 of 27 ~6 min read

Overview

A block is the modern unit of content in WordPress — defined by a block.json file, edited with a React component in the editor and, for dynamic blocks, rendered on the front end by a PHP template.

Scaffold the block

You need Node.js installed. From your plugin's blocks folder:

cd wp-content/plugins/gdc-books
npx @wordpress/create-block@latest book-list --variant=dynamic --no-plugin
cd book-list
npm start   # Rebuilds automatically while you edit.

--variant=dynamic creates a block whose output is rendered by PHP on every page view, which is right for a list of books that changes as books are added. --no-plugin adds the block to your existing plugin instead of creating a new one.

block.json: the block's identity

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 3,
    "name": "gdc-books/book-list",
    "title": "Book List",
    "category": "widgets",
    "icon": "book",
    "description": "Shows the latest books from the catalogue.",
    "textdomain": "gdc-books",
    "attributes": {
        "count": { "type": "number", "default": 3 },
        "showIsbn": { "type": "boolean", "default": false }
    },
    "supports": {
        "html": false,
        "align": [ "wide", "full" ],
        "color": { "background": true, "text": true },
        "spacing": { "padding": true }
    },
    "editorScript": "file:./index.js",
    "style": "file:./style-index.css",
    "render": "file:./render.php"
}

attributes are the block's settings, stored in the post. supports switches on core design tools — colour, alignment, spacing — without you writing any UI for them.

Register it in PHP

<?php
function gdc_books_register_blocks() {
    register_block_type( GDC_BOOKS_PATH . 'blocks/book-list/build' );
}
add_action( 'init', 'gdc_books_register_blocks' );

One call. WordPress reads block.json from the build folder and registers the scripts, styles and render template it lists.

The editor side (edit.js)

import { __ } from '@wordpress/i18n';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, RangeControl, ToggleControl } from '@wordpress/components';
import ServerSideRender from '@wordpress/server-side-render';

export default function Edit( { attributes, setAttributes } ) {
    const { count, showIsbn } = attributes;

    return (
        <div { ...useBlockProps() }>
            <InspectorControls>
                <PanelBody title={ __( 'Settings', 'gdc-books' ) }>
                    <RangeControl
                        label={ __( 'Number of books', 'gdc-books' ) }
                        value={ count }
                        onChange={ ( value ) => setAttributes( { count: value } ) }
                        min={ 1 }
                        max={ 12 }
                    />
                    <ToggleControl
                        label={ __( 'Show ISBN', 'gdc-books' ) }
                        checked={ showIsbn }
                        onChange={ ( value ) => setAttributes( { showIsbn: value } ) }
                    />
                </PanelBody>
            </InspectorControls>

            <ServerSideRender block="gdc-books/book-list" attributes={ attributes } />
        </div>
    );
}

The settings appear in the block sidebar. ServerSideRender shows a live preview produced by the same PHP that renders the front end.

The front-end side (render.php)

<?php
/**
 * Renders the Book List block.
 *
 * @var array    $attributes Block attributes.
 * @var string   $content    Inner block content.
 * @var WP_Block $block      The block instance.
 */
$books = get_posts(
    array(
        'post_type'   => 'gdc_book',
        'numberposts' => min( 12, max( 1, (int) $attributes['count'] ) ),
    )
);
?>
<div <?php echo get_block_wrapper_attributes(); ?>>
    <?php if ( $books ) : ?>
        <ul class="gdc-book-list">
            <?php foreach ( $books as $book ) : ?>
                <li>
                    <a href="<?php echo esc_url( get_permalink( $book ) ); ?>"><?php echo esc_html( get_the_title( $book ) ); ?></a>
                    <?php if ( ! empty( $attributes['showIsbn'] ) ) : ?>
                        <small><?php echo esc_html( get_post_meta( $book->ID, '_gdc_books_isbn', true ) ); ?></small>
                    <?php endif; ?>
                </li>
            <?php endforeach; ?>
        </ul>
    <?php else : ?>
        <p><?php esc_html_e( 'No books yet.', 'gdc-books' ); ?></p>
    <?php endif; ?>
</div>

get_block_wrapper_attributes() outputs the classes and inline styles for every design tool you enabled in supports — colour, alignment, padding — so the block respects the user's choices automatically.

Static or dynamic?

Static blockDynamic block
Saves its final HTML into the postSaves only attributes; PHP renders on each view
Fastest; no PHP at view timeAlways up to date with current data
Good for: callouts, buttons, layoutsGood for: latest posts, book lists, anything from the database

Practice

  • Scaffold the dynamic Book List block, register it, and insert it into a page.
  • Change the count in the sidebar and watch the preview update.
  • Enable color support, set a background, and confirm it reaches the front end through get_block_wrapper_attributes().
Create a free account to save which lessons you've finished. Save my progress