Most Gutenberg block tutorials start with npx @wordpress/create-block, which sets up React, JSX, and a webpack build pipeline. That’s the right call for a block you’re publishing or maintaining long-term. For a one-off custom block on a client site, it’s often more setup than the block itself needs.
A block using wp.blocks directly, no build step
// blocks/testimonial.js — loaded directly, no compilation
(function(blocks, element, blockEditor) {
const { registerBlockType } = blocks;
const { createElement: el } = element;
const { RichText, InspectorControls } = blockEditor;
registerBlockType('myplugin/testimonial', {
title: 'Testimonial',
icon: 'format-quote',
category: 'common',
attributes: {
quote: { type: 'string', source: 'html', selector: 'blockquote' },
author: { type: 'string', source: 'html', selector: 'cite' },
},
edit: function(props) {
const { attributes, setAttributes } = props;
return el('div', { className: 'testimonial-block' },
el(RichText, {
tagName: 'blockquote',
value: attributes.quote,
onChange: (quote) => setAttributes({ quote }),
placeholder: 'Enter quote...',
}),
el(RichText, {
tagName: 'cite',
value: attributes.author,
onChange: (author) => setAttributes({ author }),
placeholder: 'Author name',
})
);
},
save: function(props) {
const { attributes } = props;
return el('div', { className: 'testimonial-block' },
el(RichText.Content, { tagName: 'blockquote', value: attributes.quote }),
el(RichText.Content, { tagName: 'cite', value: attributes.author })
);
},
});
})(window.wp.blocks, window.wp.element, window.wp.blockEditor);
Registering it in PHP
add_action('enqueue_block_editor_assets', function() {
wp_enqueue_script(
'myplugin-testimonial-block',
plugin_dir_url(__FILE__) . 'blocks/testimonial.js',
['wp-blocks', 'wp-element', 'wp-block-editor'],
filemtime(plugin_dir_path(__FILE__) . 'blocks/testimonial.js')
);
});
Where this stops being the right call
No JSX means every element is a nested el() call, which gets genuinely hard to read past 3-4 nesting levels. For a simple two-field block like this, it’s fine. For a block with variations, dynamic previews, or complex conditional UI, the readability cost outweighs the build-step savings — that’s when it’s worth switching to the standard tooling.