Claude-skill-registry block-editor-components

React editor components for WordPress blocks in Oh My Brand! theme. useBlockProps, InspectorControls, MediaUpload, and attributes. Use when writing edit.tsx files.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/block-editor-components" ~/.claude/skills/majiayu000-claude-skill-registry-block-editor-components && rm -rf "$T"
manifest: skills/data/block-editor-components/SKILL.md
source content

Block Editor Components

React editor components for WordPress blocks in the Oh My Brand! FSE theme.


When to Use

  • Creating editor UI for WordPress blocks
  • Adding sidebar controls (InspectorControls)
  • Implementing media selection (MediaUpload)
  • Handling block attributes and state

Reference Files

FilePurpose
edit-gallery-example.tsxFull gallery edit component (~155 lines)
edit.tsxEdit component scaffold

Basic Edit Component

import { __ } from '@wordpress/i18n';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, RangeControl, ToggleControl } from '@wordpress/components';
import type { BlockEditProps } from '@wordpress/blocks';

interface BlockAttributes {
    count: number;
    isEnabled: boolean;
}

export default function Edit({
    attributes,
    setAttributes,
}: BlockEditProps<BlockAttributes>): JSX.Element {
    const { count, isEnabled } = attributes;
    const blockProps = useBlockProps();

    return (
        <>
            <InspectorControls>
                <PanelBody title={__('Settings', 'theme-oh-my-brand')}>
                    <RangeControl
                        label={__('Count', 'theme-oh-my-brand')}
                        value={count}
                        onChange={(value) => value !== undefined && setAttributes({ count: value })}
                        min={1}
                        max={10}
                    />
                    <ToggleControl
                        label={__('Enable Feature', 'theme-oh-my-brand')}
                        checked={isEnabled}
                        onChange={(value) => setAttributes({ isEnabled: value })}
                    />
                </PanelBody>
            </InspectorControls>
            <div {...blockProps}>
                {/* Block content */}
            </div>
        </>
    );
}

See edit-gallery-example.tsx for a complete implementation with MediaUpload.


useBlockProps

// Basic usage
const blockProps = useBlockProps();

// With additional classes
const blockProps = useBlockProps({ className: 'custom-class' });

// With data attributes
const blockProps = useBlockProps({
    className: 'wp-block-theme-oh-my-brand-gallery',
    'data-visible': visibleImages,
});

// Apply to wrapper
return <div {...blockProps}>Content</div>;

InspectorControls

Add sidebar panel controls:

<InspectorControls>
    <PanelBody title={__('Settings', 'theme-oh-my-brand')} initialOpen={true}>
        <RangeControl
            label={__('Visible Items', 'theme-oh-my-brand')}
            value={visibleItems}
            onChange={(value) => setAttributes({ visibleItems: value })}
            min={1}
            max={6}
        />
        <ToggleControl
            label={__('Enable Feature', 'theme-oh-my-brand')}
            checked={enableFeature}
            onChange={(value) => setAttributes({ enableFeature: value })}
        />
        <SelectControl
            label={__('Layout', 'theme-oh-my-brand')}
            value={layout}
            onChange={(value) => setAttributes({ layout: value })}
            options={[
                { label: __('Grid', 'theme-oh-my-brand'), value: 'grid' },
                { label: __('Carousel', 'theme-oh-my-brand'), value: 'carousel' },
            ]}
        />
        <TextControl
            label={__('Caption', 'theme-oh-my-brand')}
            value={caption}
            onChange={(value) => setAttributes({ caption: value })}
        />
    </PanelBody>
</InspectorControls>

MediaUpload

Single Image

<MediaUploadCheck>
    <MediaUpload
        onSelect={(media) => setAttributes({
            imageId: media.id,
            imageUrl: media.sizes?.large?.url || media.url,
            imageAlt: media.alt || '',
        })}
        allowedTypes={['image']}
        value={imageId}
        render={({ open }) => (
            <Button variant="primary" onClick={open}>
                {imageUrl ? __('Replace', 'theme-oh-my-brand') : __('Select', 'theme-oh-my-brand')}
            </Button>
        )}
    />
</MediaUploadCheck>

Multiple Images (Gallery)

<MediaUploadCheck>
    <MediaUpload
        onSelect={(media) => {
            const selected = media.map((item) => ({
                id: item.id,
                url: item.sizes?.large?.url || item.url,
                alt: item.alt || '',
            }));
            setAttributes({ images: selected });
        }}
        allowedTypes={['image']}
        multiple={true}
        gallery={true}
        value={images.map((img) => img.id)}
        render={({ open }) => (
            <Button variant="primary" onClick={open}>
                {hasImages ? __('Edit Gallery', 'theme-oh-my-brand') : __('Select', 'theme-oh-my-brand')}
            </Button>
        )}
    />
</MediaUploadCheck>

Block Attributes

Type Definitions

interface BlockAttributes {
    title: string;
    count: number;
    isEnabled: boolean;
    images: ImageData[];
}

interface ImageData {
    id: number;
    url: string;
    alt: string;
}

Setting Attributes

// Single attribute
setAttributes({ title: 'New Title' });

// Multiple attributes
setAttributes({ title: 'New Title', count: 5 });

// Array attribute
setAttributes({ images: [...images, newImage] });

Attribute Defaults (block.json)

"attributes": {
    "title": { "type": "string", "default": "" },
    "count": { "type": "number", "default": 3 },
    "isEnabled": { "type": "boolean", "default": true },
    "images": { "type": "array", "default": [] }
}

Common Patterns

Placeholder State

import { Placeholder } from '@wordpress/components';
import { gallery as blockIcon } from '@wordpress/icons';

{!hasContent && (
    <Placeholder
        icon={blockIcon}
        label={__('Block Name', 'theme-oh-my-brand')}
        instructions={__('Instructions for the user.', 'theme-oh-my-brand')}
    >
        <Button variant="primary" onClick={handleAdd}>
            {__('Add Content', 'theme-oh-my-brand')}
        </Button>
    </Placeholder>
)}

Toolbar Controls

import { BlockControls } from '@wordpress/block-editor';
import { ToolbarGroup, ToolbarButton } from '@wordpress/components';

<BlockControls>
    <ToolbarGroup>
        <ToolbarButton icon={edit} label={__('Edit', 'theme-oh-my-brand')} onClick={handleEdit} />
        <ToolbarButton icon={trash} label={__('Remove', 'theme-oh-my-brand')} onClick={handleRemove} />
    </ToolbarGroup>
</BlockControls>

Related Skills


References