Edit and Save

When registering a block with JavaScript on the client, the edit and save functions provide the interface for how a block is going to be rendered within the editor, how it will operate and be manipulated, and how it will be saved.

Edit

The edit function describes the structure of your block in the context of the editor. This represents what the editor will render when the block is used.

import { useBlockProps } from '@wordpress/block-editor';

// ...
const blockSettings = {
    apiVersion: 3,

    // ...

    edit: () => {
        const blockProps = useBlockProps();

        return <div { ...blockProps }>Your block.</div>;
    },
};

Block wrapper props

The first thing to notice here is the use of the useBlockProps React hook on the block wrapper element. In the example above, the block wrapper renders a “div” in the editor, but in order for the Gutenberg editor to know how to manipulate the block, add any extra classNames that are needed for the block… the block wrapper element should apply props retrieved from the useBlockProps react hook call. The block wrapper element should be a native DOM element, like <div> and <table>, or a React component that forwards any additional props to native DOM elements. Using a <Fragment> or <ServerSideRender> component, for instance, would be invalid.

If the element wrapper needs any extra custom HTML attributes, these need to be passed as an argument to the useBlockProps hook. For example to add a my-random-classname className to the wrapper, you can use the following code:

import { useBlockProps } from '@wordpress/block-editor';

// ...
const blockSettings = {
    apiVersion: 3,

    // ...

    edit: () => {
        const blockProps = useBlockProps( {
            className: 'my-random-classname',
        } );

        return <div { ...blockProps }>Your block.</div>;
    },
};

attributes

The edit function also receives a number of properties through an object argument. You can use these properties to adapt the behavior of your block.

The attributes property surfaces all the available attributes and their corresponding values, as described by the attributes property when the block type was registered. See attributes documentation for how to specify attribute sources.

In this case, assuming we had defined an attribute of content during block registration, we would receive and use that value in our edit function:

edit: ( { attributes } ) => {
    const blockProps = useBlockProps();

    return <div { ...blockProps }>{ attributes.content }</div>;
};

The value of attributes.content will be displayed inside the div when inserting the block in the editor.

isSelected

The isSelected property is a boolean that communicates whether the block is currently selected.

edit: ( { attributes, isSelected } ) => {
    const blockProps = useBlockProps();

    return (
        <div { ...blockProps }>
            Your block.
            { isSelected && (
                <span>Shows only when the block is selected.</span>
            ) }
        </div>
    );
};

setAttributes

This function allows the block to update individual attributes based on user interactions.

edit: ( { attributes, setAttributes, isSelected } ) => {
    const blockProps = useBlockProps();

    // Simplify access to attributes
    const { content, mySetting } = attributes;

    // Toggle a setting when the user clicks the button
    const toggleSetting = () => setAttributes( { mySetting: ! mySetting } );
    return (
        <div { ...blockProps }>
            { content }
            { isSelected && (
                <button onClick={ toggleSetting }>Toggle setting</button>
            ) }
        </div>
    );
};

When using attributes that are objects or arrays it’s a good idea to copy or clone the attribute prior to updating it:

// Good - a new array is created from the old list attribute and a new list item:
const { list } = attributes;
const addListItem = ( newListItem ) =>
    setAttributes( { list: [ ...list, newListItem ] } );

// Bad - the list from the existing attribute is modified directly to add the new list item:
const { list } = attributes;
const addListItem = ( newListItem ) => {
    list.push( newListItem );
    setAttributes( { list } );
};

Why do this? In JavaScript, arrays and objects are passed by reference, so this practice ensures changes won’t affect other code that might hold references to the same data. Furthermore, the Gutenberg project follows the philosophy of the Redux library that state should be immutable—data should not be changed directly, but instead a new version of the data created containing the changes.

The setAttributes also supports an updater function as an argument. It must be a pure function, which takes current attributes as its only argument and returns updated attributes. This method is helpful when you want to update a value based on a previous state or when working with objects and arrays.

Note: Since WordPress 6.9.

// Toggle a setting when the user clicks the button.
const toggleSetting = () =>
    setAttributes( ( currentAttr ) => ( {
        mySetting: ! currentAttr.mySetting,
    } ) );

// Add item to the list.
const addListItem = ( newListItem ) =>
    setAttributes( ( currentAttr ) => ( {
        list: [ ...currentAttr.list, newListItem ],
    } ) );

Save

The save function defines the way in which the different attributes should be combined into the final markup, which is then serialized into post_content.

save: () => {
    const blockProps = useBlockProps.save();

    return <div { ...blockProps }> Your block. </div>;
};

For most blocks, the return value of save should be an instance of WordPress Element representing how the block is to appear on the front of the site.

Note: While it is possible to return a string value from save, it will be escaped. If the string includes HTML markup, the markup will be shown on the front of the site verbatim, not as the equivalent HTML node content. If you must return raw HTML from save, use wp.element.RawHTML. As the name implies, this is prone to cross-site scripting and therefore is discouraged in favor of a WordPress Element hierarchy whenever possible.

Note: The save function should be a pure and stateless function that depends only on the attributes used to invoke it. It shouldn’t use any APIs such as useState or useEffect, nor retrieve information from another source; for example, it is not possible to use the data module inside – select( store ).selector( ... ).
This is because if the external information changes, the block may be flagged as invalid when the post is later edited (read more about Validation).