Web Wash: Drupal Canvas vs WordPress Gutenberg: Block Editor Comparison

Learn More →

{ "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "name": "ww-blocks/hero", "title": "Hero (WW)", "category": "design", "icon": "cover-image", "description": "A simple hero with a heading, paragraph, and an image side by side.", "textdomain": "ww-blocks", "supports": { "html": false }, "attributes": { "heading": { "type": "string", "source": "html", "selector": "h2" }, "body": { "type": "string", "source": "html", "selector": "p" }, "imageUrl": { "type": "string" }, "imageAlt": { "type": "string", "default": "" } }, "editorScript": "file:./index.js", "style": "file:./style-index.css", "editorStyle": "file:./index.css" }

These three files are the most important: Enjoy an ad-free experience and access exclusive courses when you join the WebWash Premium Community.

Learn about Premium

SDCs live in web/themes/contrib/byte_theme/components/.

npm run build ddev wp plugin activate ww-blocks

Compile src/ into build/, then activate the plugin. The PHP loop from Step 2 picks up the block on its own.


Standard

Gutenberg vs Canvas

Step 8:

One thing to note is where the component lives. In the video we add the component to the byte theme, which is the theme that ships with Drupal CMS and Canvas. The same steps work in your own custom theme. Just swap the theme path for your theme folder and create the component inside its components directory.

Gutenberg interface.

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

export default function save( { attributes } ) { const blockProps = useBlockProps.save( { className: 'ww-hero' } ); const { heading, body, imageUrl, imageAlt } = attributes;

return ( <div { ...blockProps }> <div className="ww-hero__content"> <RichText.Content tagName="h2" className="ww-hero__heading" value={ heading } /> <RichText.Content tagName="p" className="ww-hero__body" value={ body } /> </div>

{ imageUrl && ( <div className="ww-hero__media"> <img className="ww-hero__image" src={ imageUrl } alt={ imageAlt } /> </div> ) } </div> ); }

.wp-block-ww-blocks-hero.ww-hero { display: flex; flex-wrap: wrap; align-items: center; gap: 2rem; padding: 2rem;

.ww-hero__content { flex: 1 1 320px; }

.ww-hero__media { flex: 1 1 320px; }

.ww-hero__image { display: block; max-width: 100%; height: auto; border-radius: 8px; } }

Both WordPress and Drupal, with Canvas, let you build pages from blocks and components instead of using just a text area. But the way they go about it is very different.

<?php /** * Plugin Name: WW Blocks * Description: A collection of custom blocks. Currently: Hero. * Version: 1.0.0 * Text Domain: ww-blocks */

if ( ! defined( 'ABSPATH' ) ) { exit; // No direct access. }

function ww_blocks_register_blocks() { $build_dir = __DIR__ . '/build';

if ( ! is_dir( $build_dir ) ) { return; }

foreach ( glob( $build_dir . '/*', GLOB_ONLYDIR ) as $block_dir ) { register_block_type( $block_dir ); } } add_action( 'init', 'ww_blocks_register_blocks' );

Create components/ww_hero/ww_hero.component.yml. The props are the editable fields like heading, content, and image. The slot lets editors drop other components inside this one, in this case buttons below the content.

Create components/ww_hero/ww_hero.component.yml. The props are the editable fields like heading, content, and image. The slot lets editors drop other components inside this one, in this case buttons below the content.

Don’t forget to subscribe to our YouTube channel to stay up-to-date.

Table of Contents

import { registerBlockType } from '@wordpress/blocks';

import metadata from './block.json'; import Edit from './edit'; import save from './save';

import './style.scss';

registerBlockType( metadata.name, { edit: Edit, save, } );

Create components/ww_hero/ww_hero.twig. The markup is Twig rendered on the server, and the classes like flex, gap-6, and md:flex-row are Tailwind utilities, which is why there’s a CSS build step next.

There’s no single winner, because the two editors are built for different people.

First we look at the main difference between the two editors. Then we build the hero as a Gutenberg block. Then we build the same hero as a Drupal SDC.

That’s the whole component. Two files in a folder, a CSS build because of Tailwind, and a cache clear. No JavaScript, no React, and no per-component build step.

The simplest way to describe the difference is that Gutenberg is content-first and Canvas is component-first.

cd web/themes/contrib/byte_theme

import { __ } from '@wordpress/i18n'; import { useBlockProps, RichText, MediaUpload, MediaUploadCheck, } from '@wordpress/block-editor'; import { Button } from '@wordpress/components';

import './editor.scss';

export default function Edit( { attributes, setAttributes } ) { const blockProps = useBlockProps( { className: 'ww-hero' } ); const { heading, body, imageUrl, imageAlt } = attributes;

return ( <div { ...blockProps }> <div className="ww-hero__content"> <RichText tagName="h2" className="ww-hero__heading" value={ heading } onChange={ ( value ) => setAttributes( { heading: value } ) } placeholder={ __( 'Hero heading…', 'ww-blocks' ) } /> <RichText tagName="p" className="ww-hero__body" value={ body } onChange={ ( value ) => setAttributes( { body: value } ) } placeholder={ __( 'Hero body text…', 'ww-blocks' ) } /> </div>

<div className="ww-hero__media"> <MediaUploadCheck> <MediaUpload onSelect={ ( media ) => setAttributes( { imageUrl: media.url, imageAlt: media.alt, } ) } allowedTypes={ [ 'image' ] } render={ ( { open } ) => ( <> { imageUrl && ( <img className="ww-hero__image" src={ imageUrl } alt={ imageAlt } /> ) } <Button variant="secondary" onClick={ open }> { imageUrl ? __( 'Change image', 'ww-blocks' ) : __( 'Select image', 'ww-blocks' ) } </Button> </> ) } /> </MediaUploadCheck> </div> </div> ); }

Go ahead and create a folder for the hero component.

Create src/hero/save.js. This is a static block, so what you return here is the front-end HTML that gets saved into the post.


We build the hero inside a custom plugin called ww-blocks. The plugin is a container for blocks. Today it holds one block called hero, but you can drop in more blocks later and they register on their own.

Canvas is built for site builders. It doesn’t come with a set of generic content blocks. Instead, the Canvas components you see in the editor are the ones defined in modules or your theme. A Canvas component can be a block, a views block, a single directory component (SDC), or a code component (JavaScript file you build with Canvas). The most common approach is to create an SDC in your theme, define your props and slots, and then build pages from those components.

Step 2:

Add an SCSS file for the side-by-side layout. It loads on both the front end and inside the editor. You can also add an editor.scss file for editor-only tweaks.

Which One Should You Use?

If you want a typed system of server-rendered components that site builders own and editors put together, Canvas and SDCs are a clean fit. The downside is that you have to define your components up front before editors can build anything.

The “WW Hero” component now shows up in the Canvas component list under the Hero group. Add it to a page, fill in the heading, content, and image, and it renders.

{
"name": "ww-blocks",
"version": "1.0.0",
"description": "A collection of custom WordPress blocks.",
"private": true,
"scripts": {
"build": "wp-scripts build",
"start": "wp-scripts start"
},
"devDependencies": {
"@wordpress/scripts": "^32.0.0"
}
}

The two editors look similar, but they work in opposite ways. The easiest way to see the difference is to build the same thing in both. In the video, we build a hero component twice: first as a custom Gutenberg block, then as a Drupal Single Directory Component (SDC).