Dynamic Gutenburg block output echoes in WP Dashboard

The question:

I’m building a dynamic block in a custom plugin that shows code in my blog posts. I’m struggling with this bug where the output of the render_callback function gets echoed out to the WP dashboard when editing that post.

Here’s what the HTML (just the top) of the post edit page looks like:

test (frontend)test (frontend)test (frontend)<!DOCTYPE html>
<html class="wp-toolbar"
    lang="en-US">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /

This is what my init.php file looks like (my render_code_block function at the end of the file):

<?php

/**  * Blocks Initializer  *  * Enqueue CSS/JS of all the blocks.  * 
* @since   1.0.0  * @package CGB  */

// Exit if accessed directly. if (!defined('ABSPATH')) {   exit; }

/**  * Enqueue Gutenberg block assets for both frontend + backend.  * 
* Assets enqueued:  * 1. blocks.style.build.css - Frontend + Backend.  * 2. blocks.build.js - Backend.  * 3. blocks.editor.build.css - Backend.  *  * @uses {wp-blocks} for block type registration & related functions.  * @uses {wp-element} for WP Element abstraction — structure of blocks.  * @uses {wp-i18n} to internationalize the block's text.  * @uses {wp-editor} for WP editor styles.  * @since
1.0.0  */ function fe24_post_blocks_cgb_block_assets() { // phpcs:ignore   // Register block styles for both frontend + backend.   wp_register_style(
    'fe24_post_blocks-cgb-style-css', // Handle.
    plugins_url('dist/blocks.style.build.css', dirname(__FILE__)), // Block style CSS.
    is_admin() ? array('wp-editor') : null, // Dependency to include the CSS after it.
    null // filemtime( plugin_dir_path( __DIR__ ) . 'dist/blocks.style.build.css' ) // Version: File modification time.   );

  // Register block editor script for backend.   wp_register_script(
    'fe24_post_blocks-cgb-block-js', // Handle.
    plugins_url('/dist/blocks.build.js', dirname(__FILE__)), // Block.build.js: We register the block here. Built with Webpack.
    array('wp-blocks', 'wp-i18n', 'wp-element', 'wp-editor'), // Dependencies, defined above.
    null, // filemtime( plugin_dir_path( __DIR__ ) . 'dist/blocks.build.js' ), // Version: filemtime — Gets file modification time.
    true // Enqueue the script in the footer.   );

  // Register block editor styles for backend.   wp_register_style(
    'fe24_post_blocks-cgb-block-editor-css', // Handle.
    plugins_url('dist/blocks.editor.build.css', dirname(__FILE__)), // Block editor CSS.
    array('wp-edit-blocks'), // Dependency to include the CSS after it.
    null // filemtime( plugin_dir_path( __DIR__ ) . 'dist/blocks.editor.build.css' ) // Version: File modification time.   );

  // WP Localized globals. Use dynamic PHP stuff in JavaScript via `cgbGlobal` object.   wp_localize_script(
    'fe24_post_blocks-cgb-block-js',
    'cgbGlobal', // Array containing dynamic data for a JS Global.
    [
      'pluginDirPath' => plugin_dir_path(__DIR__),
      'pluginDirUrl'  => plugin_dir_url(__DIR__),
      // Add more data here that you want to access from `cgbGlobal` object.
    ]   );

  /**    * Register Gutenberg block on server-side.    *    * Register the block on server-side to ensure that the block    * scripts and styles for both frontend and backend are    * enqueued when the editor loads.    *    * @link https://wordpress.org/gutenberg/handbook/blocks/writing-your-first-block-type#enqueuing-block-scripts
* @since 1.16.0    */   register_block_type(
    'fe24/fe24-code-block',
    array(
      // Enqueue blocks.style.build.css on both frontend & backend.
      'style'         => 'fe24_post_blocks-cgb-style-css',
      // Enqueue blocks.build.js in the editor only.
      'editor_script' => 'fe24_post_blocks-cgb-block-js',
      // Enqueue blocks.editor.build.css in the editor only.
      'editor_style'  => 'fe24_post_blocks-cgb-block-editor-css',
      'render_callback' => 'render_code_block'
    )   ); }


function render_code_block($attributes) {   
// $content = $attributes['code'];   
// echo "<pre><code>$content</pre></code>";   
echo 'test (frontend)'; }

// Hook: Block assets. 
add_action('init', 'fe24_post_blocks_cgb_block_assets');

And finally my block.js:

/**
 * BLOCK: fe24-post-blocks
 *
 * Registering a basic block with Gutenberg.
 * Simple block, renders and saves the same content without any interactivity.
 */

//  Import CSS.
import "./editor.scss";
import "./style.scss";

const { __ } = wp.i18n; // Import __() from wp.i18n
const { registerBlockType } = wp.blocks; // Import registerBlockType() from wp.blocks
const { RichText, InspectorControls } = wp.blockEditor;
const {
    ToggleControl,
    PanelBody,
    PanelRow,
    CheckboxControl,
    SelectControl,
    ColorPicker,
} = wp.components;

registerBlockType("fe24/fe24-code-block", {
    // Block name. Block names must be string that contains a namespace prefix. Example: my-plugin/my-custom-block.
    title: __("fe24 code block"), // Block title.
    icon: "editor-code", // Block icon from Dashicons → https://developer.wordpress.org/resource/dashicons/.
    category: "common", // Block category — Group blocks together based on common traits E.g. common, formatting, layout widgets, embed.
    keywords: [__("fe24 code block"), __("code"), __("fe24")],
    attributes: {
        // languages: {
        //  type: "array",
        // },
        selectedLanguage: {
            type: "string",
        },
        code: {
            type: "string",
        },
    },


    edit: (props) => {
        const languages = ["javascript", "css", "html", "php"];

        return (
            <div>
                <InspectorControls>
                    <PanelBody title="Fe24 Code Block Settings" initialOpen={true}>
                        <PanelRow>
                            <SelectControl
                                label="Language"
                                value={props.attributes.selectedLanguage}
                                options={languages.map((language) => {
                                    return { label: language, value: language };
                                })}
                                onChange={(newval) => {
                  props.setAttributes({ selectedLanguage: newval })
                  console.log(newval);
                }
                                }
                            />
                        </PanelRow>
                    </PanelBody>
                </InspectorControls>
                <RichText
                    tagName="code"
                    placeholder="Insert your code"
                    value={props.attributes.code}
                    onChange={(newval) => {
                        console.log(newval);
                        props.setAttributes({ code: newval });
                    }}
                />
            </div>
        );
    },

    save: () => {
        return null;
    },
});

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

Excerpt from “Creating dynamic blocks” in the block editor handbook:

The server-side rendering is a function taking the block and the block
inner content as arguments, and returning the markup (quite
similar to shortcodes)

So despite the “render” as in render_callback, a block render callback should not actually echo anything (and neither call any functions that echo something) and instead, return the output — which will later be automatically echoed by WordPress. Therefore in your render_code_block() function, you need to replace the echo with return, i.e. use return 'test (frontend)';..


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment