0

I'm trying to make a simple block with an image from media library. It looks like this in the editor:

enter image description here

After select the image from library, the image are not displayed in the editor. the interface remains unchanged, as if no image had been selected. How do I make the image I have selected appear in the editor?

block.json

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 2,
    "name": "giraldez-blocks/image-with-text",
    "version": "0.1.0",
    "title": "Imagen con texto",
    "category": "giraldez",
    "icon": "format-image",
    "description": "Imagen y texto con el diseño inclinado de giraldez",
    "supports": {
        "html": false
    },
    "textdomain": "giraldez-blocks",
    "editorScript": "file:./index.js",
    "editorStyle": "file:./index.css",
    "style": "file:./style-index.css",
    "attributes": {
        "imageUrl": {
            "type": "string"
        },
        "imageId": {
            "type": "number"
        },
        "imageAlt": {
            "type": "string",
            "source": "attribute",
            "selector": "img",
            "attribute": "alt",
            "default": ""
        }
    }
}

edit function

import { MediaPlaceholder } from "@wordpress/block-editor";
import { BlockIcon } from "@wordpress/block-editor";

export default function Edit({ attributes, setAttributes }) {
    return (
            <div {...useBlockProps()}>
                <MediaPlaceholder
                    onSelect = {
                        ( el ) => {
                            setAttributes( { 
                                imageUrl: el.url,
                                imageId: el.id,
                                imageAlt: el.alt 
                            } );
                        }
                    }
                    accept="image/*"
                    icon={<BlockIcon icon="format-image" />}
                    allowedTypes = { [ 'image' ] }
                    multiple = { false }
                    labels = { { title: 'The Image' } }
                >
                </MediaPlaceholder>
            </div>
    );
}
1
  • 2
    I would study how the core image block uses the component. That block conditionally renders an img when the url has been set. It might also be important to provide the value prop to MediaPlaceholder too.
    – stokesman
    Jul 18 at 22:13

1 Answer 1

1

the interface remains unchanged, as if no image had been selected. How do I make the image I have selected appear in the editor?

It remains unchanged because we actually need to manually add a preview of the image, just like the core Image block does, and you can check the source on GitHub, but basically, you could just add a simple <img> tag above, below or even inside the MediaPlaceholder element/area.

And here's an example for the 3rd option, where we need to pass a React component to MediaPlaceholder via the mediaPreview property:

const mediaPreview = !! attributes.imageUrl && (
    <img src={ attributes.imageUrl } />
);

return (
    <div { ...useBlockProps() }>
        <MediaPlaceholder
            ... your other props
            mediaPreview={ mediaPreview }
        />
    </div>
);

Or for images uploaded to the media library, if you want, you can instead display a thumbnail of the image like so, which uses useSelect and getMedia to fetch the image details (from the REST API), and then I'm displaying the medium-sized thumbnail image:

Preview (WordPress v6.0.1):

Preview image

  1. Load useSelect, e.g. add at the top in your file:

    import { useSelect } from '@wordpress/data';
    
  2. Replace the const mediaPreview = ... part with:

    const { imageUrl, imageId, imageAlt } = attributes;
    
    const thumbnailUrl = useSelect( select => {
      const image = imageId && select( 'core' ).getMedia( imageId );
      return image && image?.media_details?.sizes?.medium?.source_url || imageUrl;
    }, [ imageId ] );
    
    const mediaPreview = !! thumbnailUrl && (
        <p>
            Image URL: { imageUrl }<br />
            Thumbnail URL: { thumbnailUrl }<br />
    
            <a href={ imageUrl } title={ imageAlt }>
                <img src={ thumbnailUrl } />
            </a>
        </p>
    );
    

    Note: The above [ imageId ] means if the attachment/image ID is changed (e.g. a new image was selected from the media library), then the thumbnailUrl's value will also change. Also, if a medium-sized thumbnail isn't available, then I'm displaying the full-sized image, i.e. imageUrl.

Additional Notes

  1. I'd pass a value to MediaPlaceholder so that the selected image will be pre-selected in the media modal. E.g.

    <MediaPlaceholder
        ... your other props
        value={ { // pass an object!
            id: attributes.imageId,
        } }
    />
    
  2. You can use a single import declaration to import multiple "module exports" from a module. For example, this imports 3 exports (a hook and 2 components) from the @wordpress/block-editor package:

    import {
        useBlockProps,
        MediaPlaceholder,
        BlockIcon,
    } from '@wordpress/block-editor';
    
  3. For elements with no content (or no inner text), you can just use the self-closing format like <MediaPlaceholder ... /> and no need for <MediaPlaceholder ...></MediaPlaceholder>.

    You know, just like the img tag where we don't use <img ...></img> .. :) And thus if you noticed it, my examples actually used that self-closing format.

1
  • 1
    You're my hero! Thank you very much for the explanation so detailed and perfectly adapted to my level.
    – aitor
    Jul 19 at 6:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.