Gutenberg supports custom blocks via PHP (server-side render) or JavaScript (React). Use PHP SSR for dynamic content, JS for interactive editing.
PHP — Server-Side Rendering
// Register block
function register_testimonial_block() {
register_block_type('myplugin/testimonial', [
'render_callback' => 'render_testimonial_block',
'attributes' => [
'quote' => ['type' => 'string', 'default' => ''],
'author' => ['type' => 'string', 'default' => ''],
'rating' => ['type' => 'integer', 'default' => 5],
],
]);
}
add_action('init', 'register_testimonial_block');
function render_testimonial_block($attributes) {
$quote = esc_html($attributes['quote'] ?? '');
$author = esc_html($attributes['author'] ?? '');
return "<blockquote class='wp-block-testimonial'><p>{$quote}</p><cite>{$author}</cite></blockquote>";
}
JavaScript — React Block
import { registerBlockType } from '@wordpress/blocks';
import { RichText, useBlockProps } from '@wordpress/block-editor';
registerBlockType('myplugin/testimonial', {
title: 'Testimonial',
category: 'content',
attributes: {
quote: { type: 'string', source: 'html', selector: 'p' },
author: { type: 'string', source: 'html', selector: 'cite' },
},
edit({ attributes, setAttributes }) {
const blockProps = useBlockProps();
return (
<blockquote {...blockProps}>
<RichText
tagName="p"
value={attributes.quote}
onChange={(quote) => setAttributes({ quote })}
placeholder="Enter quote..."
/>
<RichText
tagName="cite"
value={attributes.author}
onChange={(author) => setAttributes({ author })}
placeholder="Author name..."
/>
</blockquote>
);
},
save({ attributes }) {
const blockProps = useBlockProps.save();
return (
<blockquote {...blockProps}>
<RichText.Content tagName="p" value={attributes.quote} />
<RichText.Content tagName="cite" value={attributes.author} />
</blockquote>
);
},
});
Build Setup
npm install @wordpress/scripts --save-dev
package.json:
{
"scripts": {
"build": "wp-scripts build",
"start": "wp-scripts start"
}
}
block.json (API Version 3 — WP 5.8+)
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "myplugin/testimonial",
"title": "Testimonial",
"category": "content",
"icon": "format-quote",
"editorScript": "file:./index.js",
"style": "file:./style-index.css"
}