To check the block.json for eg or import component in edit.js :
https://github.com/WordPress/gutenberg/tree/trunk/packages/components
https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src
Notes about WordPress Gutenberg themes development.
.htaccess (1) ACF (7) admin (1) ajax (2) api (1) API interactivity (1) block (20) block_style (2) colors (2) constante (1) context (1) conventions (2) cron (1) css (5) custom post type (1) data (1) debug (2) define (1) file_API (1) functions.php (6) git (4) hook (7) i18n (2) js (2) layout (1) loop (1) media (1) media library (1) menu (2) navigation (1) patterns (1) performance (2) post (1) query (3) readmore (1) responsive (1) rest api (1) scss (1) security (7) spacing (1) sql (1) svg (1) taxonomy (1) theme (1) theme.json (11) typo (2) URL (1) wp-config.php (6) wp cli (3) wp function (7)
To check the block.json for eg or import component in edit.js :
https://github.com/WordPress/gutenberg/tree/trunk/packages/components
https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src
$ npx @wordpress/create-block@latest blocks --template @wordpress/create-block-interactive-template
$ cd blocks/src/
$ mkdir "blockname"
$ mv * in "blockname"
Duplicate the “whatever blockname” in the src folder to add new blocks.
In functions.php:
foreach ( glob( get_stylesheet_directory() . '/blocks/build/*' ) as $block_directory ) {
register_block_type( $block_directory );
}
A convenient way to display the block in the editor :
import ServerSideRender from '@wordpress/server-side-render';
export default function Edit( { attributes, setAttributes } ) {
return (
<ServerSideRender
block="dev/some-block"
attributes={attributes}
/>
);
}
Without module : <wp.blockEditor.ServerSideRender block="custom/menu-display" attributes={attributes} />
Source: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-server-side-render/
Used to create a context of data, for example between parent / children blocks.
Example, making the post id accessible, in block.json:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
...
"usesContext": [ "postId" ]
}
In render.php :
$post_id = $block->context['postId'];
In edit.json :
export default function Edit( { attributes, setAttributes, context } ) {
// context.postId
}
How to fetch data from a block : in edit.js
import { useSelect } from '@wordpress/data';
const SomeComponent = ( { attributes, setAttributes } ) => {
const taxonomies = useSelect( select => select( 'core' ).getTaxonomies(), [] );
...
}
Sources :
In block.json : { supports: { interactivity: true }, viewScriptModule: file:./view.js }
data-wp-interactive="namespace"
data-wp-context='{"foo": "bar"}'
→ local state
data-wp-class--{class-name}
the class presence will be tied to a value
data-wp-bind--{attribute-name}
data-wp-on--{event-name}
view.js :
import { store, getContext } from '@wordpress/interactivity';
store( 'wpdsfr', {
actions: {
toggle: () => {
const context = getContext();
context.isOpen = ! context.isOpen;
},
}
} );
render.php
<section
data-wp-interactive="wpdsfr"
data-wp-context='{ "isOpen": false}'
<?php echo get_block_wrapper_attributes(); ?>>
<h3 data-wp-on--click="actions.toggle">
item title
</h3>
<div data-wp-class--is-open="context.isOpen">
<?php echo $content; ?>
</div>
</section>
Sources:
in index.js :
registerBlockType(
metadata,
{
icon: {
src: <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" enable-background="new 0 0 512 512">
<g>
<g>
<path d="...
</g>
</g>
</svg>
},
edit: Edit,
save: Save
}
)
Source: https://rudrastyh.com/gutenberg/custom-svg-icons.html
index.js:
import { InnerBlocks } from '@wordpress/block-editor';
registerBlockType( metadata.name, {
edit: Edit,
save: props => { return <InnerBlocks.Content /> }
):
render.php:
<?php echo $content; ?>
in edit.js:
return ( <InnerBlocks { ...blockProps } /> )
This allow more control on the editor site. In edit.js:
import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
export default function Edit( { attributes, setAttributes } ) {
const MC_TEMPLATE = [
[ 'core/image', {} ],
[ 'core/paragraph', { content: 'Text content' } ]
];
const blockProps = useBlockProps( { className: 'sim-slide sim-slide-single-editor' } );
const innerBlocksProps = useInnerBlocksProps(
blockProps,
{ template: MC_TEMPLATE },
);
return (
<div { ...innerBlocksProps } />
);
}
Source:
With an attribute in block.json as attributes: { foo: { type: string, default: bar } }
In edit.js :
import { useBlockProps, RichText } from '@wordpress/block-editor';
export default function Edit( { attributes, setAttributes } ) {
const blockProps = useBlockProps();
return (
<RichText
{ ...useBlockProps() }
tagName="p"
onChange={ val => setAttributes( { foo: val } ) }
value={ attributes.content }
placeholder="Enter some text here..."
/>
);
}
In render.php
echo attributes['foo'];
Sources:
in block.json:
{ ...
"attributes": {
"foo": {
"type": "string",
"default": "bar"
}
}
}
in render.php, the data is accessible simply with echo $attributes['foo'];
In edit.js
import { TextControl, Panel, PanelBody } from '@wordpress/components';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
export default function Edit( { attributes, setAttributes } ) {
const blockProps = useBlockProps();
return (
<p { ...blockProps }>
<InspectorControls key="setting">
<Panel>
<PanelBody title="Champs" initialOpen={true}>
<TextControl className="blocks-base-control__input"
label={"Foo"}
value={attributes.foo}
onChange={(val) => setAttributes({foo: val})}
/>
</PanelBody>
</Panel>
</InspectorControls>
{ attributes.heading }
</p>
);
}
Adding toggle button, for an attribute defined as { foo: { type: "boolean", default: true } }
<ToggleControl
label="Toggle Content"
checked={ showContent }
onChange={ (newValue) => setAttributes( { foo: newValue } ) }
/>
A range button :
<InspectorControls>
<PanelBody>
<RangeControl
label="Columns"
value={ columnCount }
onChange={ onChangeColumnCount }
min={ 2 }
max={ 6 }
/>
</PanelBody>
</InspectorControls>
NB: To extend useBlockProps
hooks, just send new data as parameter:
{ ...useBlockProps( { style: columnStyles } ) }
Sources:
npx @wordpress/create-block@latest blockname --template @wordpress/create-block-interactive-template
Some other options :
npx @wordpress/create-block@latest blockname --variant dynamic
npx @wordpress/create-block@latest blockname --variant dynamic --template es5
→ no node module, great for simple block, remove –variant for a static one.
To register the block : register_block_type( ${path of directory containing the block.json} );
In case of several block, optional but perfomance improving : wp_register_block_metadata_collection( $path, $manifest );
To generate a manifest : wp-scripts build-blocks-manifest
Sources:
Populate dynamically blocks (button, image, paragraph and heading), by setting the source, from meta field or php logic.
<!-- wp:paragraph {
"metadata":{
"bindings":{
"content":{
"source":"core/post-meta",
"args":{
"key":"book-genre"
}
}
}
}
} -->
<p></p>
<!-- /wp:paragraph -->
register_meta(
'post',
'book-genre',
array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'default' => 'Default text field',
)
);
$source_name = 'namespace/name';
$source_properties = array(
'label' => 'Name',
'get_value_callback' => 'fn',
'uses_context' => ['postId']
);
register_block_bindings_source(
string $source_name,
array $source_properties
);
projectslug_bindings_callback(
array $source_args,
WP_Block $block_instance,
string $attribute_name
);
Source: https://make.wordpress.org/core/2024/03/06/new-feature-the-block-bindings-api/
Block styles: create specific look variations with CSS
Block variations: preset a block configuration.
Example:
wp.blocks.registerBlockVariation(
'core/quote',
{
name: 'dev-name',
title: 'Title',
icon: 'format-status',
isDefault: true,
attributes: {
templateLock: 'all',
className: 'dev-classname'
},
innerBlocks: [
[
'core/image',
{
className: 'wp-block-image alignleft'
}
],
[
'core/paragraph',
{
className: 'dev-paragraph'
}
]
]
}
);
Source:
To override block styles for rules that can’t be changed through theme.json (complexe rules or non core blocks) , one can use dev__blocks__enqueue_styles
function:
add_action( 'init', 'dev__blocks__enqueue_styles' );
function dev__blocks__enqueue_styles() {
foreach ( glob( get_stylesheet_directory() . '/assets/css/blocks-overriding/*', GLOB_ONLYDIR ) as $directory) {
$namespace = substr( strrchr( $directory, '/' ), 1 );
foreach ( glob( $directory . '/*.css' ) as $file) {
$filename = pathinfo( $file, PATHINFO_FILENAME );
wp_enqueue_block_style(
$namespace . '/' . $filename,
array(
'handle' => 'dw-override--' . $namespace . '-' . $filename,
'src' => get_stylesheet_directory_uri() . '/assets/css/blocks-overriding/' . $namespace . '/' . $filename . '.css',
'path' => get_stylesheet_directory() . '/assets/css/blocks-overriding/' . $namespace . '/' . $filename . '.css',
'ver' => filemtime( get_stylesheet_directory() . '/assets/css/blocks-overriding/' . $namespace . '/' . $filename . '.css' )
)
);
}
}
}
Source : https://developer.wordpress.org/reference/functions/wp_enqueue_block_style/
$block = array(
'blockName' => 'dev/blockname',
'attrs' => array(
'name' => 'dev/blockname',
'data' => array(
'ACF__field' => get_field( 'ACF__field' )
)
),
);
$html_output = render_block($block);
To know what data send as parameter for the $block object, one can use the render_block
filter
add_filter( 'render_block', 'dev__paragraph_add_block', 10, 2 );
function dev__paragraph_add_block( $block_content, $block ){
if ( 'dw/icon' === $block['blockName'] ){
echo '<pre>';
var_dump($block);
echo '</pre>';
}
return $block_content;
}
To use the supports properties, first enable them in the block.json.
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
...
"supports": {
"color": {
"text": true,
"background": true
}
},
"acf": {
"mode": "preview",
"renderTemplate": "./render.php"
}
}
Then in render.php :
<?php
$classes = array();
if ( ! empty( $block['textColor'] ) ) {
// $classes = array_merge( $classes, explode( ' ', $block['className'] ) );
$classes[] = 'has-text-color';
$classes[] = 'has-' . $block['textColor'] . '-color';
}
echo '<div class="' . esc_attr( join( ' ', $classes ) ) . '"></div>';
Or simply use the function get_block_wrapper_attributes( string[] $extra_attributes = array() ): string
for e.g. to get the classes :
preg_match( '/class="(.*?)"/', get_block_wrapper_attributes(), $matches);
classes = $matches[1];
Sources:
add_filter( 'render_block', 'dev__paragraph_add_block', 10, 2 );
function dev__paragraph_add_block( $block_content, $block ){
if ( 'core/paragraph' === $block['blockName'] ){
$block_content = new WP_HTML_Tag_Processor( $block_content );
$block_content->next_tag(); /* first tag should always be ul or ol */
$block_content->add_class( 'wp-block-paragraph' );
$block_content->get_updated_html();
}
return $block_content;
}
Interesting attributes : allowedBlocks
, template
, InnerBlocks
.
Example of a devblock/render.php
<?php
$classes_container = array( 'block__container' );
$classes_innerblock = array( 'block__innerblock' );
if ( !empty( $block['className'] ) ){
$classes_root = array_merge( $classes_root, explode( ' ', $block['className'] ) );
}
$allowed_blocks = array( 'namespace/blockname' );
$template = array(
array(
'namespace/blockname',
array(
'className' => 'some-class'
)
)
);
?>
<div class="<?php echo join( ' ', $classes_container ); ?>">
<InnerBlocks
class="<?php echo join( ' ', $classes_innerblock ); ?>"
allowedBlocks="<?php echo esc_attr( wp_json_encode( $allowed_blocks ) ); ?>"
template="<?php echo esc_attr( wp_json_encode( $template ) ); ?>" />
</div>
Prototype:register_block_type( string|WP_Block_Type $block_type, array $args = array() ): WP_Block_Type|false
add_action( 'init', 'dev__register_blocks', 5 );
function dev__register_blocks() {
register_block_type( __DIR__ . '/some_block_dir' );
}
The block.json file :
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "namespace/blockname",
"title": "Notice",
"category": "text",
"parent": [ "core/group" ],
"icon": "star",
"description": "Shows warning, error or success notices...",
"keywords": [ "alert", "message" ],
"version": "1.0.3",
"textdomain": "my-plugin",
"editorScript": "file:./index.js",
"script": "file:./script.js",
"viewScript": [ "file:./view.js", "example-shared-view-script" ],
"editorStyle": "file:./index.css",
"style": [ "file:./style.css", "example-shared-style" ],
"render": "file:./render.php",
"supports": {
"className": true,
"customClassName": true,
"jsx": true // to use innerBlocks,
"inserter": true // will be accessible only programatically
},
// ACF options:
"acf": {
"mode": "preview",
"renderTemplate": "render.php"
},
"example": { "attributes": { "mode": "preview" } }
}
Sources: