Show preview image for custom Gutenberg blocks

13k views Asked by At

I've created a batch of custom ACF gutenberg blocks and now trying to assign a preview image.

Issue: Getting the preview image to show

The below image here shows a paragraph component which is a default block.

enter image description here

You can see on the right hand side, that the paragraph block has an image and description alongside it. Below is how my component is currently appearing (full code will be at the end)

enter image description here

As you can see, it says "no preview available" and no description is added, even though I've defined both in the code.

Approach:

acf-blocks/blocks.php

<?php
$img_root = "../../src/components";

$hero = array(
    'name' => 'hero',
    'title' => __('Hero') ,
    'description' => __('Hero section') ,
    'render_callback' => 'block_render',
    'category' => 'formatting',
    'icon' => 'admin-comments',
    'image' => $img_root . '/hero/hero.png',
    'mode' => 'edit',
    'keywords' => array(
        'hero'
    ) ,
);

$blocks = [$hero];

return $blocks;

?>

acf-blocks/functions.php

<?php

function block_acf_init(){
  $path = get_template_directory().'/inc/acf-blocks/blocks.php';
  $blocks = require($path);
  foreach($blocks as $block) {
    acf_register_block_type($block);
  }
}

if( function_exists('acf_register_block_type') ) {
  add_action('acf/init', 'block_acf_init');
}


?>

My folder structure is as follows:

theme
  inc
    acf-blocks
      blocks.php
      functions.php
  src
    components
      hero
        hero.js
        hero.scss
        hero.png

Unsure why my preview image doesn't show?

Edit:

I've added the block_render function but still no success. Here is my current functions.php file:

<?php

$component_path = "../../src/components" . strtolower($block['title']) . strtolower($block['title']).".js";

function block_render( $block, $content = '', $is_preview = false ) {
  $context = get_context();
    $context['block'] = $block; // store block values
    $context['fields'] = get_fields(); // store field values
    $context['is_preview'] = $is_preview;
    render($component_path, $context ); // render the block
}

function block_acf_init(){
  $path = get_template_directory().'/inc/acf-blocks/blocks.php';
  $blocks = require($path);
  foreach($blocks as $block) {
    acf_register_block_type($block);
  }
}

if( function_exists('acf_register_block_type') ) {
  add_action('acf/init', 'block_acf_init');
}

?>

Edit 2:

<?php

$hero = array(
    'name' => 'hero',
    'title' => __('Hero'),
    'description' => __('Add hero section'),
    'render_callback' => 'block_render',
    'category' => 'formatting',
    'icon' => 'admin-comments',
    'mode' => 'edit',
    'category' => 'custom',
    'post_types' => array(
        'page'
    ),
    'keywords' => array(
        'hero'
    ),
    'example' => array(
        'mode' => 'preview',
        'data' => array(
            'field' => 'value' // sample data
        )
    )
);


function block_render($block, $content = '', $is_preview = false)
{
    if ($is_preview && !empty($block['data'])) {
        echo '<img src="https://i.picsum.photos/id/1021/536/354.jpg?hmac=XeUbyCXoxX2IrSELemo2mRl4zVXzhjFyxtj3GTVZ8xo">';
        return;
    } elseif ($is_preview) {
        echo 'A Hero block using ACF';
        return;
    }
    
    echo 'A Hero block using ACF.';
}


?>

Have even tried:

<?php

function block_render( $block, $content = '', $is_preview = false ) {
  if($is_preview):
    echo '<img src="https://i.picsum.photos/id/1021/536/354.jpg?hmac=XeUbyCXoxX2IrSELemo2mRl4zVXzhjFyxtj3GTVZ8xo">';
  else:
    echo '<img src="https://i.picsum.photos/id/1021/536/354.jpg?hmac=XeUbyCXoxX2IrSELemo2mRl4zVXzhjFyxtj3GTVZ8xo">';
  endif;
}


?>

In both cases, when trying to show the image (not the block preview), I see the ACF fields for the block, not the dummy image defined:

enter image description here

4

There are 4 answers

5
Sally CJ On BEST ANSWER

it says "no preview available" and no description is added, even though I've defined both in the code

It says "no preview available" because it's indeed so, and what you defined is not something that ACF or even WordPress supports, i.e. the image arg ('image' => $img_root . '/hero/hero.png') will do nothing out-of-the-box.

You can see on the right hand side, that the paragraph block has an image and description alongside it.

No, that is not an image.

It is instead a preview of the block output as returned by the block's render callback (which is the block_render() function in your case) or template in the case of an ACF block type.

And that (core) Paragraph block, it actually defines the example property which then enables the block preview even if the property is empty (i.e. no attributes defined). Working example using registerBlockType():

registerBlockType( 'my-blocks/foo-bar', {
    title: 'My Foo Bar block',
    category: 'formatting',
    description: 'Sample description.',

    // Just define this property and there'll be a preview.
    example: {},

    // And the preview is the one coming from this callback.
    edit: () => <p>just testing the block previews :)</p>,

    save: () => null,
} );

And with that, you'd get this preview:

Sample block preview

But I provided that example just to let you know how would you add a preview to a non-dynamic block.

So how to add the block preview via ACF

Simple, and as shown in the other answer as well as the acf_register_block_type() documentation, use the example arg:

example
(Array) (Optional) An array of structured data used to construct a preview shown within the block-inserter. All values entered into the ‘data’ attribute array will become available within the block render template/callback via $block['data'] or get_field().

So in your case, you would add that arg to your $hero array:

$hero = array(
    'name'            => 'hero',
    'title'           => __( 'Hero' ),
    'description'     => __( 'Hero section' ),
    'render_callback' => 'block_render',
    'category'        => 'formatting',
    'icon'            => 'admin-comments',
    'keywords'        => array( 'hero' ),
    // Just add this and you'll get the block preview:
    'example'         => array(
        'attributes' => array(
            'mode' => 'preview',
        ),
    ),
);

And basically, whatever the render callback/template outputs, would be what you see in the preview when adding the block via the block inserter UI.

But if you want a different output when on preview mode — both when inserting the block via the UI and after the block is added into the editor, then you can make use of the $is_preview parameter passed to the render callback:

function block_render( $block, $content = '', $is_preview = false ) {
    // back-end preview
    if ( $is_preview ) {
        echo 'A Hero block using ACF — In preview mode.';
        return;
    }

    // front-end output
    echo 'A Hero block using ACF.';
}

And if you want a different preview in the inserter UI and the editor, then set the data arg in the example arg, and in the render callback, just check if the data is not empty:

/* In $hero, add example.attributes.data:
$hero = array(
    'name'            => 'hero',
    ...
    'example'         => array(
        'attributes' => array(
            'mode' => 'preview',
            'data' => array(
                'my_field' => 'Sample value',
            ),
        ),
    ),
);
 */

function block_render( $block, $content = '', $is_preview = false ) {
    // back-end previews
    if ( $is_preview && ! empty( $block['data'] ) ) {
        echo 'A Hero block using ACF — In preview mode — In the block inserter UI.';
        return;
    } elseif ( $is_preview ) {
        echo 'A Hero block using ACF — In preview mode — In the editor.';
        return;
    }

    // front-end output
    echo 'A Hero block using ACF.';
}

And actually, you could show an actual image via those if:

if ( $is_preview && ! empty( $block['data'] ) ) {
    echo '<img src="https://example.com/path/to/image-file-name.png">';
    return;
}

which then gives you a preview that looks like:

Sample block preview 2

So I hope that helps? :)

And note that the examples have been tried & tested working with ACF Pro 5.9.4 and WordPress 5.6, which both are the latest releases as of writing.

0
paulgagu On

After reading a zillion of posts on all over internet about how to add an image as preview in custom ACF Blocks and getting nowhere I found myself a solution:

  1. You need to add ‘example’ when you register your ACF block, add “mode” attribute as “preview” and any specific data you want to pass.
acf_register_block_type(
        array(
            'name' => 'name-of-block',
            'title' => __('Your Title'),
            'description' => __('Your description'),
            'post_types' => array('page'),
            'category' => 'common',
            'render_template' => '../your/path/block_template.php', // your template
            'icon' => '', // svg code for icon
            'keywords' => array('Keyword', 'another'),
            'example'  => array(
                'attributes' => array(
                    'mode' => 'preview',
                    'data' => array(
                        'preview_image_help' => '../your/dir/block-image-preview.jpg',
                    )
                )
            )
        )
    );

WARNING: The field “preview_image_help” needs to be a unique name, and CAN NOT EXIST in the block fields.

The block will be render in preview mode in 2 places, the editor and the block inserter preview, (when you hover on the name of the block). When renders in the editor it will have all the fields you define in the block. When renders in the inserter preview will have only the field “preview_image_help”.

  1. Now the render part(in your template), check for the unique field and you will know where the render is taking place:
<?       
        if( isset( $block['data']['preview_image_help'] )  ) :    /* rendering in inserter preview  */

            echo '<img src="'. $block['data']['preview_image_help'] .'" style="width:100%; height:auto;">';
    
        
        else : /* rendering in editor body */
            
            $text   = get_field('text');
            $author = get_field('author');
            $title  = get_field('title');
            ?>
            <div class="my-class">
                <span class="class-title"><?=$title?></span>
                <span class="class-text"><?=$text?></span>
                <span class="class-author"><?=$author?></span>
            </div>
            <?
            
        endif;
3
rank On

I did not find anything about 'image' to be used in the acf_register_block_type() function in the documentation (https://www.advancedcustomfields.com/resources/acf_register_block_type/).

If you use this function and set mode to be preview, you will get a preview of your rendered template if you hover the block with the mouse in the block editor.

functions.php

acf_register_block_type(array(
    'name' => 'name-of-block',
    'title' => __('Your Title'),
    'description' => __('Your description'),
    'post_types' => array('page'),
    'category' => 'common',
    'render_template' => '../your/path/block_template.php', // your template file
    'enqueue_style' => get_template_directory_uri() . '/style.css', // path to css file
    'icon' => '', // svg code for icon
    'keywords' => array('Keyword', 'another'),
    'example'  => array(
        'attributes' => array(
            'mode' => 'preview' // this needs to be set to preview (!)
        )
    ),
));

If you are using custom fields for your blocks, you need to make sure that there will be a value to be displayed in the preview of your gutenberg block template file.

block_template.php

$myfield = get_field( 'my_field' );

<p><?php if ($myfield) { echo $myfield; } else { echo "my fallback paragraph"; } ?></p>

This way the preview will be your rendered block template. It is not an image as you would like it to have, but it shows how your block will look like. This is what the preview is meant to be.

1
grbav On
  1. Add callback function

    'render_callback' => 'block_render',
  2. Add example and image

     'example' => array(
     'attributes' => array(
         'mode' => 'preview', // Important!
         'data' => array(
             'image' => '<img src="' . get_template_directory_uri() . '/gutenberg-preview/cta.png' . '" style="display: block; margin: 0 auto;">'
         ),
     ),
    

    ),

  3. Make callback function

     /**
      * Callback block render,
      * return preview image
      */
     function block_render( $block, $content = '', $is_preview = false ) {
         /**
          * Back-end preview
          */
          if ( $is_preview && ! empty( $block['data'] ) ) {
               echo $block['data']['image'];
               return;
          } else {
               if ( $block ) :
                    $template = $block['render_template'];
                    $template = str_replace( '.php', '', $template );
                    get_template_part( '/' . $template );
               endif;
          }
     }
    

Source https://www.grbav.com/acf-custom-block-shows-preview-image-in-gutenberg/