Edit WooCommerce Products with Gutenberg

Introduction

Did you know that the Gutenberg Block Editor actually plays quite well with WooCommerce? Whether you did or not, I truly hope you find this tutorial useful and inspiring.

While there are numerous out there that will say that Gutenberg is not intended for this purpose, I will respectfully disagree and say that the power of Gutenberg is intended exactly for this purpose.

Word of caution: if you are initially setting up your store, I recommend you create a minimal number of products with WooCommerce 6.x before converting to Gutenberg editing as the latest updates for WooCommerce seem to auto-add a new left-side toolbar that overlays the Gutenberg editor.

Discussion

The power and simplicity of the Gutenberg Editor would greatly enhance any shop owners product editing experience, and has less of a learning curve than most third-party page builders. But even more important is that fact that it comes standard (read FREE) with all WordPress installations since Version 5.

Despite the above, opponents to Gutenberg cite a “disconnected and confusing” editing experience, which I do not agree with. Gutenberg allow the end-user flexibility on how they interact with the block editor (inline with keyboard shortcuts, or via the Gutenberg Toolbar) to insert blocks and content. In addition, the Block Element Toolbar allows for basic controls and the Block Editor Sidebar allow more advanced/granular control of the blocks and their settings. These features combine to provide a very powerful interface to the site owner.

Further, the core blocks packaged with WordPress and Gutenberg can be somewhat limiting; however, with plug-ins like Spectra, Stackable, Kadence Blocks and GetWid (to name just a few) your can add so much power and improve the impact and performance of your storefront without adding as much overhead as most Page Builders. In addition, by using an optimized and performant Theme (such as Blocksy or Kadence) or opt to use a newer “Block-Based” Theme you will take your site to new levels of interactivity and performance.

The OLD way of editing products

WordPress/WooCommerce Classic Editor
The WordPress Classic Editor

The Gutenberg way of editing products

WordPress/WooCommerce Gutenberg Editor
The WordPress Block Editor (Gutenberg)

Now, lets compare some of the Pros and Cons of using Gutenberg for editing your products vs the Classic Editor

Pros

  • Use advanced layout options
    • Kadence Layout Library
    • Stackable Layout Library
    • Many More
  • Use any block or series of nested blocks within the product content
  • Still retains the option of using custom HTML

Cons

  • Potential for ‘unused css’ notice
    • WordPress will load the default block-library.css files
  • Slightly higher server load and client overhead for product editing experience
  • Product Category editing is not supported at this time

The Code

There are two blocks of code that we will be injecting into our site to complete the conversion. You can place the following blocks of code in your child theme’s functions.php (alternatively create either a custom include, a Must Use Plugin or use a code snippet plugin).

Note: you can rename the functions to fit your needs, but just be sure to change the second attribute of the ‘add_filter’ lines as well.

// Enable Gutenberg editor for WooCommerce
function j0e_activate_gutenberg_product( $can_edit, $post_type ) {
 if ( $post_type == 'product' ) {
        $can_edit = true;
    }
    return $can_edit;
}
add_filter( 'use_block_editor_for_post_type', 'j0e_activate_gutenberg_product', 10, 2 );
// enable taxonomy fields for woocommerce with gutenberg on
function j0e_enable_taxonomy_rest( $args ) {
    $args['show_in_rest'] = true;
    return $args;
}
add_filter( 'woocommerce_taxonomy_args_product_cat', 'j0e_enable_taxonomy_rest' );
add_filter( 'woocommerce_taxonomy_args_product_tag', 'j0e_enable_taxonomy_rest' );

What do these functions/filters do? that’s a great question…. lets talk about that for a moment

The first filter function checks if the current post type is a product; if so, it sets the ‘can edit’ variable to true. This variable is a flag for the Gutenberg editor which tells it to load on the post edit page instead of the default TinyMCE.

The second and third Filter call the same function which alters the default taxonomy arguments for ‘product_cat’ and ‘product_tag’ so that they are available to the WP REST API (this is a necessity for using the Gutenberg editor).

The next piece of code enables setting the Visibility of the product (something it is missing by default, but this may soon change with future updates to WooCommerce).

/* PRODUCT VISIBILITY META BOX CALLBACK */
function product_data_visibility( $post ) {
    $thepostid          = $post->ID;
    $product_object     = $thepostid ? wc_get_product( $thepostid ) : new WC_Product();
    $current_visibility = $product_object->get_catalog_visibility();
    $current_featured   = wc_bool_to_string( $product_object->get_featured() );
    $visibility_options = wc_get_product_visibility_options();
    ?>
    <div class="misc-pub-section" id="catalog-visibility">
        <?php esc_html_e( 'Catalog visibility:', 'woocommerce' ); ?>
        <strong id="catalog-visibility-display">
        <?php
            echo isset( $visibility_options[ $current_visibility ] ) ? esc_html( $visibility_options[ $current_visibility ] ) : esc_html( $current_visibility );
            if ( 'yes' === $current_featured ) {
                echo ', ' . esc_html__( 'Featured', 'woocommerce' );
            }
        ?>
        </strong>
        <a href="#catalog-visibility" class="edit-catalog-visibility hide-if-no-js"><?php esc_html_e( 'Edit', 'woocommerce' ); ?></a>
        <div id="catalog-visibility-select" class="hide-if-js">
            <input type="hidden" name="current_visibility" id="current_visibility" value="<?php echo esc_attr( $current_visibility ); ?>" />
            <input type="hidden" name="current_featured" id="current_featured" value="<?php echo esc_attr( $current_featured ); ?>" />
        <?php
            echo '<p>' . esc_html__( 'This setting determines which shop pages products will be listed on.', 'woocommerce' ) . '</p>';
            foreach ( $visibility_options as $name => $label ) {
                echo '<input type="radio" name="_visibility" id="_visibility_' . esc_attr( $name ) . '" value="' . esc_attr( $name ) . '" ' . checked( $current_visibility, $name, false ) . ' data-label="' . esc_attr( $label ) . '" /> <label for="_visibility_' . esc_attr( $name ) . '" class="selectit">' . esc_html( $label ) . '</label><br />';
            }
            echo '<br /><input type="checkbox" name="_featured" id="_featured" ' . checked( $current_featured, 'yes', false ) . ' /> <label for="_featured">' . esc_html__( 'This is a featured product', 'woocommerce' ) . '</label><br />';
        ?>
        <p>
            <a href="#catalog-visibility"
                class="save-post-visibility hide-if-no-js button"><?php esc_html_e( 'OK', 'woocommerce' ); ?></a>
            <a href="#catalog-visibility"
                class="cancel-post-visibility hide-if-no-js"><?php esc_html_e( 'Cancel', 'woocommerce' ); ?></a>
        </p>
        </div>
    </div>
<?php
}
/* END PRODUCT VISIBILITY META BOX CALLBACK */
/* REGISTER PRODUCT VISBILITY VIA METABOX */
function register_catalog_meta_boxes() {
    global $current_screen;
    // Make sure gutenberg is loaded before adding the metabox
    if ( method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor() ) {
        add_meta_box( 'catalog-visibility', __( 'Catalog visibility', 'textdomain' ), 'product_data_visibility', 'product', 'side' );
    }
}
add_action( 'add_meta_boxes', 'register_catalog_meta_boxes' );
/* END REGISTER PRODUCT VISBILITY VIA METABOX */

The code above consists of two functions, the first is the callback function for the ‘add_meta_box’ hook. This function gets specific data about the current post/product and renders the HTML code for the input elements.

The second function is the meta-box registration/invocation for the our ‘product visibility’ metabox under the conditions that the block editor is enabled and we are editing a post of the type ‘product’.

My Personal Preference

In most of my bigger projects, I will typically create custom include files within a folder named ‘inc’ in my child theme and I will ‘require’ those files under certain conditions. For example, I would create ‘{child_theme_name}/inc/woocommerce.php’ and require that file from the child theme’s functions.php if the WooCommerce plugin is installed and active.

The main reason for this is to keep the main ‘functions.php’ as lean as possible and ensure that the hooks/filters are segregated in a way that makes it easy to tell what functions augment or extend which plugins.

if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) && is_readable( get_theme_file_path( '/inc/woocommerce.php' ) ) ):
	require_once get_theme_file_path( '/inc/woocommerce.php' );		// PLACE ALL CUSTOM WOOCOMMERCE HOOKS AND FILTERS IN THIS FILE
endif;

Below is the starting snippet for my woocommerce.php file

<?php
defined( 'ABSPATH' ) or die( 'Direct access is not authorized!' );
/* ENABLE BLOCK EDITOR FOR PRODUCTS */
function activate_gutenberg_product( $can_edit, $post_type ) {
    if ( $post_type == 'product' ) {
        $can_edit = true;
    }
    return $can_edit;
}
add_filter( 'use_block_editor_for_post_type', 'activate_gutenberg_product', 10, 2 );
/* END ENABLE BLOCK EDITOR FOR PRODUCTS */
/* ADD WOO TAXONOMIES TO REST API */
function enable_taxonomy_rest( $args ) {
    $args['show_in_rest'] = true;
    return $args;
}
add_filter( 'woocommerce_taxonomy_args_product_cat', 'enable_taxonomy_rest' );
add_filter( 'woocommerce_taxonomy_args_product_tag', 'enable_taxonomy_rest' );
/* END ADD WOO TAXONOMIES TO REST API */
/* INJECT CATALOG VISIBILITY METABOX TO BLOCK EDITOR */
function register_catalog_meta_boxes() {
    global $current_screen;
    // Make sure gutenberg is loaded before adding the metabox
    if ( method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor() ) {
        add_meta_box( 'catalog-visibility', __( 'Catalog visibility', 'textdomain' ), 'product_data_visibility', 'product', 'side' );
    }
}
add_action( 'add_meta_boxes', 'register_catalog_meta_boxes' );
function product_data_visibility( $post ) {
    $thepostid          = $post->ID;
    $product_object     = $thepostid ? wc_get_product( $thepostid ) : new WC_Product();
    $current_visibility = $product_object->get_catalog_visibility();
    $current_featured   = wc_bool_to_string( $product_object->get_featured() );
    $visibility_options = wc_get_product_visibility_options();
    ?>
    <div class="misc-pub-section" id="catalog-visibility">
        <?php esc_html_e( 'Catalog visibility:', 'woocommerce' ); ?>
        <strong id="catalog-visibility-display">
        <?php
            echo isset( $visibility_options[ $current_visibility ] ) ? esc_html( $visibility_options[ $current_visibility ] ) : esc_html( $current_visibility );
            if ( 'yes' === $current_featured ) {
                echo ', ' . esc_html__( 'Featured', 'woocommerce' );
            }
        ?>
        </strong>
        <a href="#catalog-visibility" class="edit-catalog-visibility hide-if-no-js"><?php esc_html_e( 'Edit', 'woocommerce' ); ?></a>
        <div id="catalog-visibility-select" class="hide-if-js">
            <input type="hidden" name="current_visibility" id="current_visibility" value="<?php echo esc_attr( $current_visibility ); ?>" />
            <input type="hidden" name="current_featured" id="current_featured" value="<?php echo esc_attr( $current_featured ); ?>" />
        <?php
            echo '<p>' . esc_html__( 'This setting determines which shop pages products will be listed on.', 'woocommerce' ) . '</p>';
            foreach ( $visibility_options as $name => $label ) {
                echo '<input type="radio" name="_visibility" id="_visibility_' . esc_attr( $name ) . '" value="' . esc_attr( $name ) . '" ' . checked( $current_visibility, $name, false ) . ' data-label="' . esc_attr( $label ) . '" /> <label for="_visibility_' . esc_attr( $name ) . '" class="selectit">' . esc_html( $label ) . '</label><br />';
            }
            echo '<br /><input type="checkbox" name="_featured" id="_featured" ' . checked( $current_featured, 'yes', false ) . ' /> <label for="_featured">' . esc_html__( 'This is a featured product', 'woocommerce' ) . '</label><br />';
        ?>
        <p>
            <a href="#catalog-visibility"
                class="save-post-visibility hide-if-no-js button"><?php esc_html_e( 'OK', 'woocommerce' ); ?></a>
            <a href="#catalog-visibility"
                class="cancel-post-visibility hide-if-no-js"><?php esc_html_e( 'Cancel', 'woocommerce' ); ?></a>
        </p>
        </div>
    </div>
<?php
}
/* END INJECT CATALOG VISIBILITY METABOX TO BLOCK EDITOR */

Taking it one step further

While some people like the default tab layout (product details, reviews, etc) below the product gallery and short description, others do not… so now that we have the Gutenberg editor enabled, lets take a look at how we can remove the tabs to get a fluid page.

First, lets remove the tabs from the flow of the woocommerce single product and disable the rendering of the Product Description heading

/* Remove default woocommerce tabs */
remove_action( 'woocommerce_after_single_product_summary', 'woocommerce_output_product_data_tabs', 10 );  // REMOVE DEFAULT WOO TABS
add_filter( 'woocommerce_product_description_heading', '__return_null' );  // DO NOT RENDER DEFAULT 'PRODUCT DESCRIPTION' TITLE
/* END: Remove default woocommerce tabs */

Now, lets place the product description and reviews back into the standard flow of the page

/* ENSURE WOOCOMMERCE PRODUCT DESCRIPTION CONTENT IS STILL RENDERED */
function woocommerce_template_product_description() {
    wc_get_template( 'single-product/tabs/description.php' );
    wc_get_template( 'single-product-reviews.php' );
}
add_action( 'woocommerce_after_single_product_summary', 'woocommerce_template_product_description', 10 );
/* END ENSURE WOOCOMMERCE PRODUCT DESCRIPTION CONTENT IS STILL RENDERED */

Note: if required, you may need to place a container before the “tabs/description’ template part to clear the floats set by WooCommerce or some custom css may suffice.

Bonus Thoughts

Try these Themes and Block Plug-ins to add powerful advanced features to your website & shop


If you found this article helpful, we hope you will share it and provide feedback using the comments form below…

Leave a Reply

Your email address will not be published. Required fields are marked *