For years, mastering Gutenberg block building required an in-depth figuring out of applied sciences akin to React and Node.js, in addition to complicated construct steps and JavaScript gear.
Then again, WordPress building is evolving, and you’ll be able to now construct and organize Gutenberg blocks totally in PHP.
That is particularly really helpful for builders preferring to keep away from React and server-side JavaScript (JS) building. It lowers the educational curve, streamlines the developer enjoy, and allows upper efficiency by way of eliminating pointless front-end script overhead.
Within the following sections, you’ll discover ways to make the most of those new options to construct PHP-only Gutenberg blocks. Alongside the best way, you’ll discover ways to create leaner, sooner, and easier-to-maintain WordPress internet sites.
Slightly thrilling, proper? Let’s get began.
What are PHP-only blocks and why do they topic?
Making a Gutenberg block historically required complex server-side JavaScript and React coding talents. This acted as a barrier to the adoption of the block editor by way of longtime WordPress builders who would possibly now not have the vital React and Node.js wisdom.
Issues are converting now. Beginning with Gutenberg 21.8, you’ll be able to sign up Gutenberg blocks the usage of not anything however PHP. This avoids the complexities of putting in place a Node.js atmosphere for many who don’t paintings with server-side JavaScript.
With PHP-only block registration, you’ll be able to sign up and show blocks in each the editor and frontend the usage of the similar PHP code. This encourages websites the usage of hybrid subject matters or conventional PHP purposes and shortcodes to undertake and broaden at the block editor.
For many who need to study extra, listed below are the principle GitHub PRs devoted to PHP-only blocks.
- Permit registering PHP-only blocks: This PR implements computerized server-side block registration and renames the
auto_ssrimprove toauto_register. - PHP-only blocks: Cross all metadata from PHP registration to the buyer: PHP-only blocks with
auto_registerimprove now move all metadata to the buyer from the PHP registration. - PHP-only blocks: Generate inspector controls from attributes: This PR introduces computerized technology of the UI (Inspector Controls) in line with the attributes declared at the server.
How you can construct your first PHP-only Gutenberg block
When a block is registered solely at the server aspect — with out JS information — and the brand new auto_register improve flag is about to true, the editor mechanically makes use of the ServerSideRender part to sign up the block at the consumer aspect and show the block preview. In essence, the block’s content material is now generated at once from the PHP code in each the editor and the frontend.
As an example, right here is a straightforward PHP instance that registers a block the usage of the PHP-only way.
/**
* Render callback (frontend and editor)
*/
serve as my_php_only_block_render( $attributes ) {
go back '
🚀 PHP-only Block
This block was once created with solely PHP!
';
}
/**
* Sign in the block at the 'init' hook.
*/
add_action( 'init', serve as() {
register_block_type( 'my-plugin/php-only-test-block', array(
'identify' => 'My PHP-only Block',
'icon' => 'welcome-learn-more',
'class' => 'textual content',
'render_callback' => 'my_php_only_block_render',
'helps' => array(
// Robotically registers the block within the Editor JS (in the past auto_ssr)
'auto_register' => true,
),
) );
});
You’ll check out this code by way of copying and pasting it into the principle document of a customized plugin. After activating the plugin, you must see the “My PHP-Simplest Block” within the block inserter.

The register_block_type serve as registers a block sort at the server. It now contains the brand new auto_register improve, educating Gutenberg to move metadata from the PHP registration.
The serve as accepts two arguments:
- The identify of the block sort, together with the namespace. On this instance, the block identify is
my-plugin/php-only-test-block. - An array of arguments for the block sort. Within the code above, we set
identify,icon,class,render_callback, andhelps. Once more, for PHP-only block sorts, thehelpsarray should come with'auto_register' => true.
Along with simplifying the introduction of customized block sorts and making them simple to combine into hybrid subject matters, PHP-only blocks can be utilized as wrappers for legacy PHP purposes and shortcodes. Additionally, the usage of PHP-only blocks opens the door to new alternatives for customized integrations and server-side capability.
In keeping with Héctor Priethor,
A pure-PHP registration fashion would simplify the minimal necessities for block building, making them to be had to a much wider developer target audience, and lend a hand develop the block ecosystem past complex JavaScript utilization.
The use of attributes to construct the block settings UI
PR 74102 allows the automated technology of inspector controls from block characteristic definitions. This permits customers to configure the illusion and capability of your PHP-only blocks like several Gutenberg block registered by means of JavaScript.
Up to now, you needed to manually create an edit.js document in React and outline quite a lot of atmosphere controls the usage of React parts.
Now, Gutenberg reads the characteristic definitions and mechanically generates the corresponding enter fields within the WordPress editor.
The gadget maps the knowledge sorts outlined within the attributes array to DataForm box definitions.
'sort' => 'string'generates a textual content box.'sort' => 'quantity'generates a bunch box.'sort' => 'integer'generates an integer box.'sort' => 'boolean'generates a checkbox.'sort' => 'string'with'enum' => array()generates a make a selection box.
You’re going to notice that you’ll be able to solely use a couple of controls. If you want particular controls, akin to RichText, RangeControl, or ToggleControl, you’re going to nonetheless want to go for the JS/React method.
Then again, this addition has really extensive benefits. Boundaries to access are reduced additional, and also you gained’t want to study React, Webpack, or NPM to create customized blocks with easy editable choices.
Within the following instance, we prolong the pattern block proven within the earlier phase by way of including some attributes.
/**
* 1. Outline the block's HTML output.
*/
serve as my_php_only_block_render( $attributes ) {
// Extract attributes
$identify = esc_html( $attributes['blockTitle'] );
$depend = intval( $attributes['itemCount'] );
$enabled = $attributes['isEnabled']; // Boolean from the ToggleControl
$length = esc_attr( $attributes['displaySize'] );
// Get started construction the output
$output = sprintf( '',
$length === 'massive' ? '20px' : ($length === 'small' ? '12px' : '16px')
);
$output .= sprintf( '🚀 %s
', $identify );
// If the toggle is ON, display the record. If OFF, display a fallback message.
if ( $enabled ) {
$output .= '';
for ( $i = 1; $i <= $depend; $i++ ) {
$output .= sprintf( '- Merchandise %d
', $i );
}
$output .= '
';
} else {
$output .= 'The record is recently disabled.
';
}
$output .= '';
go back $output;
}
/**
* 2. Sign in the block on 'init'.
*/
add_action( 'init', serve as() {
register_block_type( 'my-plugin/php-only-test-block', array(
'identify' => 'My PHP-only Block',
'icon' => 'welcome-learn-more',
'class' => 'textual content',
'render_callback' => 'my_php_only_block_render',
// Attributes used to generate the Inspector UI
'attributes' => array(
'blockTitle' => array(
'sort' => 'string',
'default' => 'PHP-only Block',
),
'itemCount' => array(
'sort' => 'integer',
'default' => 3,
),
'isEnabled' => array(
'sort' => 'boolean',
'default' => true,
),
'displaySize' => array(
'sort' => 'string',
'enum' => array( 'small', 'medium', 'massive' ),
'default' => 'medium',
),
),
'helps' => array(
'auto_register' => true,
),
) );
});
A snappy take a look at this code finds how simple it’s to sign up a customized block with all its configuration settings the usage of the brand new API. Attributes at the moment are used now not solely to retailer user-entered information but in addition to outline the UI schema. The code above does the next:
- The
register_block_typeserve as registers the block sortmy-plugin/php-only-test-block. - The second one argument handed to the serve as is an array containing the next parts:
identify,icon,class,render_callback,attributes, andhelps. - The
attributesarray accommodates the block’s attributes. Within the above instance, the array contains the weatherblockTitle,itemCount,isEnabled, anddisplaySize. 'auto_register' => trueallows server-side computerized registration.
Here’s what the callback serve as my_php_only_block_render does:
- First, the serve as extracts the characteristic values from the
$attributesarray and assigns them to the$identify,$depend,$enabled, and$lengthvariables. - Then, it generates the block’s content material.
Right here’s the outcome on display:

An actual-world instance of PHP-only blocks
Whilst there are lots of situations the place JavaScript continues to be required, you’ll be able to already do so much with PHP-only blocks, particularly when used with block props.
Within the following instance, we use the get_block_wrapper_attributes() serve as, which generates a string of attributes to the present block being rendered. The block will mechanically obtain the user-set colours, borders, and shadows, and practice the corresponding types to the principle container. This manner, the block is customizable via Gutenberg’s local gear, similar to a React-based block.
To peer it at paintings, create a smart-pricing-widget folder to your laptop. On this folder, create a genre.css document with the next CSS code:
/* genre.css */
.pricing-card {
show: flex;
flex-direction: column;
align-items: heart;
text-align: heart;
box-sizing: border-box;
}
.pricing-card h3 {
margin: 0;
font-size: 1.5rem;
}
.pricing-card .price-value {
font-size: 3.5rem;
font-weight: 800;
margin: 15px 0;
}
.pricing-card ul {
list-style: none;
padding: 25px 0;
margin: 20px 0;
width: 100%;
border-top: 1px cast rgba(128,128,128,0.3);
show: flex;
flex-direction: column;
hole: 12px;
}
.pricing-card li {
show: flex;
align-items: heart;
justify-content: heart;
hole: 10px;
}
.pricing-card .cta-button {
margin-top: auto;
padding: 15px 25px;
border-radius: 8px;
text-decoration: none;
font-weight: daring;
transition: opacity 0.2s;
}
.pricing-card .cta-button:hover {
opacity: 0.8;
}
/* Theme Diversifications */
.pricing-card.theme-light { background-color: #ffffff; shade: #000000; }
.pricing-card.theme-light .cta-button { background-color: #21759b; shade: #ffffff; }
.pricing-card.theme-dark { background-color: #1a1a1a; shade: #ffffff; }
.pricing-card.theme-dark .cta-button { background-color: #ffffff; shade: #1a1a1a; }
.pricing-card.theme-blue { background-color: #21759b; shade: #ffffff; }
.pricing-card.theme-blue .cta-button { background-color: #000000; shade: #ffffff; }
/* Software Categories */
.pricing-card .is-full-width {
width: 100%;
show: block;
align-self: stretch;
}
We can now not remark in this code as this is a easy stylesheet to your widget block.
Now, create the principle document to your plugin, identify it smart-pricing-widget.php, and paste the next code:
"pricing-card theme-{$theme}",
) ) );
$output = sprintf( '', $wrapper_attributes );
$output .= sprintf( '%s
', $plan_name );
$output .= sprintf( '€%d', $charge );
if ( ! empty( $features_array ) ) {
$output .= '';
foreach ( $features_array as $function ) {
$is_checked = strpos( $function, '+' ) === 0;
$clean_text = esc_html( ltrim( $function, '+- ' ) );
$icon = $is_checked ? '✅' : '❌';
$genre = $is_checked ? '' : 'genre="opacity: 0.6;"';
$output .= sprintf( '- %s %s
', $genre, $icon, $clean_text );
}
$output .= '
';
}
$btn_class = 'cta-button' . ( $btn_size === 'complete' ? ' is-full-width' : '' );
$output .= sprintf( '%s', esc_attr( $btn_class ), $btn_text );
$output .= '';
go back $output;
}
/**
* Sign in Property and Block
*/
add_action( 'init', serve as() {
// 1. Sign in the CSS document
wp_register_style(
'smart-pricing-style',
plugins_url( 'genre.css', __FILE__ ),
array(),
'1.2.0'
);
// 2. Sign in the Block
register_block_type( 'educational/smart-pricing', array(
'api_version' => 3,
'identify' => 'Pricing Card',
'icon' => 'cart',
'class' => 'widgets',
'render_callback' => 'render_smart_pricing_block',
// Hyperlink the registered genre maintain right here
'genre' => 'smart-pricing-style',
'attributes' => array(
'planName' => array( 'sort' => 'string', 'default' => 'Skilled' ),
'charge' => array( 'sort' => 'integer', 'default' => 49 ),
'buttonText' => array( 'sort' => 'string', 'default' => 'Make a selection Plan' ),
'buttonSize' => array( 'sort' => 'string', 'enum' => array( 'auto', 'complete' ), 'default' => 'auto' ),
'blockTheme' => array( 'sort' => 'string', 'enum' => array( 'mild', 'darkish', 'blue' ), 'default' => 'mild' ),
'featuresList' => array( 'sort' => 'string', 'default' => "+ Toughen, + Updates, - Area" ),
),
'helps' => array(
'auto_register' => true,
'shade' => array( 'background' => true, 'textual content' => true ),
'spacing' => array( 'margin' => true, 'padding' => true ),
'typography' => array( 'fontSize' => true ),
'shadow' => true,
'__experimentalBorder' => array( 'shade' => true, 'radius' => true, 'genre' => true, 'width' => true ),
'border' => array( 'shade' => true, 'radius' => true, 'genre' => true, 'width' => true ),
),
) );
});
This script contains two purposes. The register_block_type() serve as is the engine of your plugin. Listed below are its major parts:
- The primary argument is the block identifier, together with the namespace (
educational/smart-pricing). - The second one argument is an array of arguments. Within the code above, now we have set the API model, identify, icon, class, render callback, genre, attributes, and helps.
- The attributes within the array generate the controls that customers will use so as to add content material and configure the block. The
sortpart specifies the kind of keep watch over so as to add to the block inspector. On this instance, those are textual content fields ('sort’ => 'string’), an integer ('sort’ => 'integer’), and a few drop-down menus ('sort’ => 'string’, 'enum’ => array()). - The
helpsarray goods upload options that make the block genre customizable. As we discussed previous, the one improve required in a PHP-only block isauto_register, which allows computerized UI technology for customized attributes. The opposite helps declared above come with shade, spacing, typography, shadow, and border.
The callback serve as render_smart_pricing_block() generates the block’s HTML. Here’s a detailed description of what this serve as does:
- It extracts and sanitizes the block attributes, then provides the CSS code that generates the block’s look within the frontend and editor.
- The options to be displayed within the block (
$attributes['featuresList'];) are treated one at a time. These days, it’s not conceivable so as to add complex controls to the block settings sidebar. To create an inventory, akin to an inventory of options, you’ll be able to solely use a easy textual content box. On this instance, you should manually input the product options, separated by way of commas. - The
$wrapper_attributesvariable is a container for the wrapper attributes supplied by way of theget_block_wrapper_attributesserve as. This serve as does now not simply upload the categories specified within the code (pricing-card theme-{$theme}), however mechanically retrieves all of the genre customizations that the consumer units within the block inspector, together with colours, borders, padding, margin, shadow, typography, and the usual block categories (wp-block-tutorial-smart-pricing). wp_kses_dataguarantees there are not any malicious tags or scripts (XSS) within the string.- The remainder of the code generates the block content material.
Turn on the plugin and create a brand new publish or web page. Open the block inserter and scroll right down to the Widgets phase. Right here, you must see your “Pricing Card” block recognized by way of a buying groceries cart icon.

The picture above displays the block with the default mild theme.
Within the symbol beneath, you’ll be able to see the darkish model of the block and the settings you declared to your plugin.

The next symbol displays the manner controls you added with the block helps.

It is usually price noting that types added by way of helps override the block theme types. This permits for better customization of the block’s look, as proven within the following symbol:

Convert legacy shortcodes to Gutenberg blocks with natural PHP
One of the vital direct makes use of of PHP blocks is as shortcode wrappers. With Gutenberg, you’ll be able to nonetheless use shortcodes to your content material, however it’s a must to manually insert your shortcode right into a Shortcode block, which isn’t probably the most relaxing enjoy.
Assume you might have the next shortcode:
serve as my_custom_alert_shortcode( $atts ) {
$choices = shortcode_atts( array(
'sort' => 'information',
'message' => 'Default alert message',
), $atts );
$types = array(
'information' => 'background: #d1ecf1; shade: #0c5460; border-color: #bee5eb;',
'caution' => 'background: #fff3cd; shade: #856404; border-color: #ffeeba;',
'error' => 'background: #f8d7da; shade: #721c24; border-color: #f5c6cb;'
);
$genre = $types[ $options['type'] ] ?? $types['info'];
go back sprintf(
'
%s: %s
',
esc_attr( $genre ),
esc_html( $choices['type'] ),
esc_html( $choices['message'] )
);
}
add_shortcode( 'sc_alert', 'my_custom_alert_shortcode' );
This code generates a easy field that you’ll be able to insert into your content material with the next shortcode:
[sc_alert type="alert" message="Hello"]
In Gutenberg, you’re going to use a Shortcode block to insert the field into your content material, as proven within the following symbol:

The situation adjustments utterly with PHP-only blocks. Now you’ll be able to wrap your shortcode in a PHP-only Gutenberg block and configure it in the course of the UI controls. Here’s the code so as to add in your plugin:
/**
* Rendering callback
*/
serve as render_shortcode_alert_wrapper_block( $attributes ) {
$sort = esc_attr( $attributes['alertType'] );
$message = esc_attr( $attributes['alertMessage'] );
$shortcode_string = sprintf( '[sc_alert type="%s" message="%s"]', $sort, $message );
$wrapper_attributes = wp_kses_data( get_block_wrapper_attributes( array(
'elegance' => 'wp-block-shortcode-alert-wrapper',
) ) );
go back sprintf(
'%s',
$wrapper_attributes,
do_shortcode( $shortcode_string )
);
}
/**
* Sign in the block sort at the server
*/
add_action( 'init', serve as() {
register_block_type( 'educational/alert-wrapper', array(
'api_version' => 3,
'identify' => 'Alert (Shortcode wrapper)',
'icon' => 'comments',
'class' => 'widgets',
'render_callback' => 'render_shortcode_alert_wrapper_block',
'attributes' => array(
'alertType' => array(
'sort' => 'string',
'enum' => array( 'information', 'caution', 'error' ),
'default' => 'information',
),
'alertMessage' => array(
'sort' => 'string',
'default' => 'Kind your alert message right here...',
),
),
'helps' => array(
'auto_register' => true,
'spacing' => array( 'margin' => true, 'padding' => true ),
'typography' => array( 'fontSize' => true ),
),
) );
});
The code above is very similar to the code within the earlier phase. What adjustments here’s the rendering callback.
$shortcode_stringshops the shortcode string ([sc_alert type="%s" message="%s"]).- The serve as returns the HTML of the block container and the integrated shortcode (
do_shortcode( $shortcode_string )).
Now, open the block inserter and search for the “Shortcode wrapper” block a few of the widgets. Insert it into your content material and configure it from the block settings bar. The block will seem similar in each the editor and the frontend.

How is WordPress building converting with PHP-only blocks?
As issues stand, natural PHP blocks are in an experimental segment and also have restricted features. Gutenberg provides extra robust options, akin to block patterns and block permutations, which offer all of the modifying options of local Gutenberg blocks and customized blocks inbuilt JavaScript. But there are situations the place PHP blocks be offering vital alternatives.
First, PHP-only blocks must inspire wider adoption of the block editor, particularly amongst WordPress builders who’re much less orientated in opposition to server-side JavaScript building.
Moreover, they’re preferrred wrappers for customized purposes and shortcodes, as proven within the instance on this article. As well as, they permit for simple integration with exterior products and services.
And we will be able to additionally moderately be expecting long term enhancements and have additions, extra configuration controls, and integrations with current Gutenberg gear.
Something is needless to say: with PHP-only blocks, construction Gutenberg blocks has turn out to be a lot simpler.
If WordPress building is your task, Kinsta supplies the developer gear you want, enabling you to concentrate on WordPress building, eliminating the will for complicated configurations and tedious upkeep duties: SSH, SFTP, Git integration, computerized updates, one-click staging, a integrated native building software, and a lot more. Test it out firsthand with your first month unfastened.
The publish How you can construct PHP-only Gutenberg blocks seemed first on Kinsta®.
WP Hosting
