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.mdsource 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
| File | Purpose |
|---|---|
| edit-gallery-example.tsx | Full gallery edit component (~155 lines) |
| edit.tsx | Edit 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
- typescript-standards - TypeScript conventions
- native-block-development - Block structure
- block-scaffolds - Edit component template