WordPress & Gutenberg

Recently, when building my WordPress site, I was deciding between using a page editor I’m already familiar with (Elementor or WP Bakery) or trying Gutenberg. Most of the time, when coding, I choose the tools I know will get the job done fastest and most efficiently. However, I had some extra time, and learning new skills is a must in the fast-paced world of web development.

Gutenberg is beneficial for future-proofing my WordPress skills and improving website performance. It’s incredibly lightweight compared to traditional page builders, enhances the content creation experience, offers design flexibility, and streamlines the content editing process.

My preferred plugin to start with is Essential Blocks. While the free version is limited, it still comes with a host of blocks to easily get you started. Combine it with the power of a custom theme, and I have almost everything I need. However, while building my site, I realized I couldn’t create a sliding picture carousel with text overlaid on top. After some research, I decided to build my own Gutenberg block — and this is my story of Gutenberg discovery.

So why go through the trouble of building a Gutenberg block? Well, it’s nice having a modular block designed specifically for my needs. It’s lightweight, and once you understand how it works, it’s completely under your control.

To start building one, open your terminal in the plugin directory and run:

npx @wordpress/create-block@latest –variant=static

This scaffolds your block. You can set the --variant flag to build either a dynamic or static block. Dynamic blocks typically interact with an API call to pull resources from elsewhere (for example, selecting photos from a third-party source). For my purposes, I built a simple static block.

Understanding the file structure

The package.json file contains your scripts. While working on the block, I run:

npm run start

This watches my changes and compiles them into the build directory while I edit files in the src directory.

Inside src, you’ll find:

  • index.js – the main entry file.
  • edit.js – the block editor instance; this is the UI you interact with in the WordPress editor.
  • save.js – generates the markup saved to the database when you hit save.
  • editor.scss – styles for the editor.
  • style.scss – styles for the front end.

If you were building a dynamic block, you’d also have a render.php file to handle front-end rendering logic.

Inside edit.js

At the top of my file (around line 14), I import the built-in block editor tools I need:

import {
useBlockProps,
MediaUpload,
MediaUploadCheck,
RichText,
URLInputButton,
} from “@wordpress/block-editor”;
import { Button } from “@wordpress/components”;
import { useEffect, useRef } from “@wordpress/element”;

WordPress provides a wide array of built-in tools worth exploring.

  • useBlockProps marks an element as a block, giving it all the core block functionality and allowing you to pass properties into it — essentially making it a functional React block.
  • The @wordpress/components package contains a library of UI elements shared across the WordPress dashboard.
  • useEffect and useRef come from React. In my case, useRef tracks which slide component I’m interacting with.

Attributes

Attributes are defined in block.json — think of it as your block’s database. For my slider, I needed:

  • a carousel of images,
  • text content,
  • a call-to-action (CTA) button.

“attributes”: {
“images”: { “type”: “array”, “default”: [] },
“content”: { “type”: “array”, “default”: [] },
“callToAction”: { “type”: “array”, “default”: [] }
}

These attributes are passed to the edit function and destructured for use:

const { images = [], content, callToAction = [] } = attributes;

I then create functions like removeImg and updateCallToAction within edit.js to update these attributes as needed.

Rendering

The return statement in edit.js contains the logic for the backend UI:

  • Iterating through images.
  • Adding text and a CTA button to each slide.
  • Adding slider navigation arrows outside the iteration.

This handles everything in the editor. The save.js file is responsible for saving the final HTML structure to the database for front-end rendering. And that’s just scratching the surface. You can check out the full block implementation on my GitHub.