Skip to content

Instantly share code, notes, and snippets.

@anova
Last active July 21, 2025 18:58
Show Gist options
  • Select an option

  • Save anova/97eb95d749ce6d24f05ab0627be4a755 to your computer and use it in GitHub Desktop.

Select an option

Save anova/97eb95d749ce6d24f05ab0627be4a755 to your computer and use it in GitHub Desktop.
Cline rule for Gutenberg creation from static file.

Gutenberg Block Creation Rules

This document provides comprehensive rules for creating WordPress Gutenberg blocks in the Example project.

Block Structure Template

Every Gutenberg block follows this basic structure:

import { registerBlockType } from '@wordpress/blocks';
import { InspectorControls, RichText } from '@wordpress/block-editor';
import { CheckboxControl, PanelBody, TextControl } from '@wordpress/components';
import { CustomMediaUpload } from '../../library/custom-media-upload.js';

registerBlockType('example/block-name', {
  title: 'Example / Category / Block Name',
  description: 'Block description',
  category: 'example',
  attributes: {
    // attributes definition
  },
  edit({attributes, setAttributes}) {
    // edit method implementation
  },
  save({attributes}) {
    // save method implementation
  },
});

Naming Conventions

  • Block ID: Use format example/block-name (kebab-case)
  • Title: Use format Example / Category / Block Name (Title Case)
  • File Name: Use kebab-case matching the block name (e.g., hero.js, text-section.js)

Attributes Definition Rules

Mandatory Fields

Every attribute must have these three fields:

  • type: Data type (string, text, boolean, number, array, object)
  • source: How to extract data (text, attribute, html, children)
  • selector: CSS selector to target the element

Optional Fields

  • attribute: Required when source is "attribute" - specifies which HTML attribute to read
  • default: Default value for the attribute

Common Attribute Patterns

Text Content

textAttribute: {
  type: "text",
  source: "text",
  selector: "h1", // or appropriate selector
}

HTML Content (for RichText)

richTextAttribute: {
  type: "string",
  source: "html",
  selector: "p", // or appropriate selector
}

Image Source

imageAttribute: {
  type: "string",
  source: "attribute",
  attribute: "src",
  selector: "img",
}

Boolean Attributes

booleanAttribute: {
  type: "boolean",
  source: "attribute",
  selector: "[data-attribute-name]",
  attribute: "data-attribute-name",
  default: false,
}

Link Attributes

linkHref: {
  type: "string",
  source: "attribute",
  selector: "a",
  attribute: "href",
},
linkText: {
  type: "string",
  source: "text",
  selector: "a",
}

Edit Method Implementation

Required Imports

Always import necessary components at the top:

import { InspectorControls, RichText } from '@wordpress/block-editor';
import { CheckboxControl, PanelBody, TextControl } from '@wordpress/components';
import { CustomMediaUpload } from '../../library/custom-media-upload.js';

Structure

The edit method should return JSX with this structure:

edit({attributes, setAttributes}) {
  return (
    <>
      <InspectorControls>
        <PanelBody>
          {/* Control components here */}
        </PanelBody>
      </InspectorControls>
      {/* Block preview HTML here */}
    </>
  )
}

Control Components

Image Upload

<CustomMediaUpload
  mediaUrl={attributes.imageAttribute}
  onMediaSelected={(url) => { setAttributes({imageAttribute: url}) }}
/>

Text Input

<TextControl
  label="Label Text"
  value={attributes.textAttribute}
  onChange={(value) => {setAttributes({textAttribute: value})}}
/>

Checkbox

<CheckboxControl
  label="Checkbox Label"
  value={attributes.booleanAttribute}
  checked={attributes.booleanAttribute}
  onChange={(value) => { setAttributes({booleanAttribute: value}) }}
/>

Rich Text (in preview area)

<RichText
  value={attributes.richTextAttribute}
  onChange={(value) => {setAttributes({richTextAttribute: value})}}
  placeholder='Placeholder text'
  tagName='h1'
  className="css-classes"
/>

Save Method Implementation

The save method should return the final HTML output without any WordPress components. It's critical to use the correct method for rendering attribute values to avoid issues with escaped HTML.

For source: "text" or source: "attribute"

You can directly render the attribute value inside the HTML tag.

save({attributes}) {
  return (
    <section className="css-classes">
      <h1>{attributes.textAttribute}</h1>
      <img src={attributes.imageAttribute} alt="" />
      {attributes.booleanAttribute && (
        <a href={attributes.linkHref}>{attributes.linkText}</a>
      )}
    </section>
  );
}

For source: "html" (RichText)

When an attribute uses source: "html", you must use the RichText.Content component to render the value in the save function. This prevents the HTML content from being escaped and preserves formatting like bold, italics, or links.

Important: You also need to import RichText from @wordpress/block-editor in your block's file if it's not already there for the edit function.

import { RichText } from '@wordpress/block-editor'; // Ensure this import exists

//...

save({ attributes }) {
  return (
    <div>
      <RichText.Content
        tagName="p"
        className="my-paragraph-class"
        value={attributes.richTextAttribute}
      />
    </div>
  );
}

CSS Classes and Styling

  • Use Tailwind CSS classes for styling
  • Follow the existing project's design system
  • Use semantic HTML elements
  • Include responsive classes (sm:, md:, lg:, xl:)

Data Attributes

Use data attributes for conditional rendering:

// In both edit and save methods
<section data-show-element={attributes.booleanAttribute}>

File Organization

  • Place block files in: wp/wp-content/plugins/example-blocks/src/blocks/
  • Use appropriate subdirectories: index/ for main blocks, layout/ for layout blocks
  • Import blocks in the main index.js file

Best Practices

  1. Always use the CustomMediaUpload component for image uploads instead of the default MediaUpload
  2. Keep edit and save methods in sync - the HTML structure should match
  3. Use semantic HTML elements in both edit and save methods
  4. Include proper accessibility attributes (alt text, aria labels)
  5. Test conditional rendering in both edit and save methods
  6. Use consistent naming for attributes and CSS classes
  7. Add meaningful descriptions to block registration
  8. Group related controls in InspectorControls PanelBody
  9. Provide placeholder text for RichText components
  10. Use default values for boolean attributes

Example Implementation

Refer to hero.js for a complete implementation example that demonstrates:

  • Multiple attribute types
  • Image upload with CustomMediaUpload
  • Conditional rendering
  • RichText usage
  • Complex HTML structure with Tailwind CSS
  • Proper InspectorControls setup

Handling Multiple Same Selectors

When the HTML markup contains multiple elements of the same type (e.g., multiple images, headings, or paragraphs), you must assign unique CSS classes to differentiate them in the selector:

Multiple Images

// For first image
decorativeImage: {
  type: "string",
  source: "attribute",
  attribute: "src",
  selector: ".decorative-image",
},
// For second image
mainImage: {
  type: "string",
  source: "attribute",
  attribute: "src",
  selector: ".main-image",
}

Multiple Headings of Same Level

// For main title
mainTitle: {
  type: "string",
  source: "html",
  selector: ".main-title",
},
// For content title
contentTitle: {
  type: "string",
  source: "html",
  selector: ".content-title",
}

Multiple Paragraphs

// For main description
mainDescription: {
  type: "string",
  source: "html",
  selector: ".main-description",
},
// For guide descriptions
guideDescription1: {
  type: "string",
  source: "html",
  selector: ".guide-description-1",
},
guideDescription2: {
  type: "string",
  source: "html",
  selector: ".guide-description-2",
}

Multiple Spans/Tags

// For first tag
tag1: {
  type: "string",
  source: "text",
  selector: ".tag-1",
},
// For second tag
tag2: {
  type: "string",
  source: "text",
  selector: ".tag-2",
}

Common Patterns

Conditional Link Rendering

{attributes.showLink && (
  <a href={attributes.linkHref}>{attributes.linkText}</a>
)}

Image with Fallback

{attributes.imageUrl && (
  <img src={attributes.imageUrl} alt="" />
)}

Multiple Text Fields

<RichText
  value={attributes.title}
  onChange={(value) => setAttributes({title: value})}
  tagName="h2"
  placeholder="Enter title"
/>
<RichText
  value={attributes.content}
  onChange={(value) => setAttributes({content: value})}
  tagName="p"
  placeholder="Enter content"
/>

This rule set ensures consistent, maintainable, and properly structured Gutenberg blocks for the Example project.

@anova
Copy link
Author

anova commented Jul 21, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment