While WordPress comes with plenty of native components, real-world client projects often require custom, lightweight elements tailored to specific functionality. Many developers avoid creating custom Gutenberg blocks, believing it always requires a heavy Node.js, React, and Webpack build setup. However, for standard WordPress installations, you can build performant dynamic blocks using only core PHP and vanilla JavaScript.
In this guide, we will walk you through building a custom, lightweight Facebook Video embed block from scratch, using pure WordPress APIs without any build tools.

How to Register a Custom Dynamic Block in WordPress?
To create a custom block, we need specific WordPress hooks. First, we use the init hook to register our block on the backend using register_block_type(). We pass a render_callback parameter to handle the server-side HTML rendering. Second, we use the enqueue_block_editor_assets hook to load our JavaScript file into the Gutenberg editor. This allows our custom controls to appear in the block editor interface.
add_action( 'init', function () {
register_block_type( 'custom/fb-video', [
'render_callback' => 'child_render_fb_video_block',
] );
} );
add_action( 'enqueue_block_editor_assets', function () {
wp_enqueue_script(
'fb-video-block',
get_stylesheet_directory_uri() . '/blocks/fb-video/block.js',
[ 'wp-blocks', 'wp-element', 'wp-components', 'wp-block-editor' ],
filemtime( get_stylesheet_directory() . '/blocks/fb-video/block.js' ),
true
);
} );
Our callback function child_render_fb_video_block receives the $attributes array containing the user’s input. Inside this function, we process the Facebook video URL, generate an iframe embed link, and return the wrapper HTML.
At this stage, our backend code has this look:
add_action( 'init', function () {
register_block_type( 'custom/fb-video', [
'render_callback' => 'child_render_fb_video_block',
] );
} );
add_action( 'enqueue_block_editor_assets', function () {
wp_enqueue_script(
'fb-video-block',
get_stylesheet_directory_uri() . '/blocks/fb-video/block.js',
[ 'wp-blocks', 'wp-element', 'wp-components', 'wp-block-editor' ],
filemtime( get_stylesheet_directory() . '/blocks/fb-video/block.js' ),
true
);
} );
function child_render_fb_video_block( $attributes ) {
if ( empty( $attributes['url'] ) ) {
return '';
}
$embed = 'https://www.facebook.com/plugins/video.php?href='
. rawurlencode( $attributes['url'] )
. '&show_text=false&t=0';
return '
';
}
How to Register the Block Settings in JavaScript?
To define how our block behaves inside the Gutenberg editor, we create a block.js file. We wrap our code in an Immediately Invoked Function Expression (IIFE) to pass global WordPress objects cleanly.
The title parameter defines the block name. Gutenberg displays this title in the block inserter list.
The icon parameter sets a Dashicon for our block. The icon appears inside the Gutenberg editor toolbar and inserter panel.
The category parameter groups our block inside the editor. Setting it to embed helps organize custom components logically.
The attributes parameter defines all input data fields for the block. In our case, we define a url attribute of type string with an empty default value.
Inside the edit function, we render the editor UI. We use TextControl to create an input field for the Facebook video URL.
The label sets the field caption displayed in the editor.
The placeholder provides an example URL for the user.
The onChange function handles user input and updates block attributes using setAttributes.
The save function returns null. This tells WordPress that our block is dynamic and should be rendered using our PHP callback function instead of static HTML saved in the database.
Here is how our complete block.js file looks:
( function ( blocks, element, components, blockEditor ) {
const el = element.createElement;
const TextControl = components.TextControl;
const useBlockProps = blockEditor.useBlockProps;
blocks.registerBlockType( 'custom/fb-video', {
title: 'WG Facebook Video',
icon: 'video-alt3',
category: 'embed',
attributes: {
url: {
type: 'string',
default: ''
}
},
edit: function ( props ) {
return el(
'div',
useBlockProps(),
el( TextControl, {
label: 'Facebook video URL',
value: props.attributes.url,
placeholder: 'https://www.facebook.com/reel/...',
onChange: function ( value ) {
props.setAttributes( { url: value } );
}
} )
);
},
save: function () {
return null; // render PHP
}
});
} )(
window.wp.blocks,
window.wp.element,
window.wp.components,
window.wp.blockEditor
);
We learned how to create a custom Gutenberg block without Webpack or build tools. We covered the PHP registration hooks and JavaScript block settings. We also built a dynamic render function to embed Facebook videos safely. This workflow can be reused for other custom Gutenberg components.
If you want to learn how to build custom components for other popular page builders, check out our guides on Building a Custom Widget for Elementor: Step-by-Step Guide and Developer Guide: Custom WPBakery Widgets for WordPress.
If you have any questions, feel free to contact us. We will be happy to help with your project. We can also assist with custom Gutenberg or WordPress development. Just reach out and we will reply soon.