# WordPress Gutenberg Block Editor Gutenberg is WordPress's modern block-based editor that revolutionizes content creation by treating all content elements (paragraphs, images, headings, etc.) as individual blocks. Each block can be independently added, arranged, styled, and configured, enabling users to create rich media pages through an intuitive visual interface. The project eliminates the need for shortcodes and custom HTML, making content creation accessible while maintaining powerful customization capabilities for developers. The architecture is organized as a monorepo with 100+ packages, each serving specific purposes: core packages handle block logic and data management, the block library provides 100+ pre-built blocks, editor packages deliver the full editing experience, and utility packages offer helper functions, hooks, and development tools. This modular design enables developers to extend WordPress with custom blocks, integrate REST API data, build admin interfaces with reusable components, and create full site editing experiences with template customization. ## Block Registration Register a new custom block type with namespace, attributes, and edit/save functions. ```js import { registerBlockType } from '@wordpress/blocks'; import { __ } from '@wordpress/i18n'; import { useBlockProps, RichText } from '@wordpress/block-editor'; registerBlockType( 'my-plugin/testimonial', { title: __( 'Testimonial' ), icon: 'format-quote', category: 'widgets', attributes: { author: { type: 'string', source: 'html', selector: '.author', }, quote: { type: 'string', source: 'html', selector: '.quote', }, }, edit: ( { attributes, setAttributes } ) => { const blockProps = useBlockProps(); return (
setAttributes( { quote } ) } placeholder={ __( 'Enter testimonial...' ) } /> setAttributes( { author } ) } placeholder={ __( 'Author name' ) } />
); }, save: ( { attributes } ) => { const blockProps = useBlockProps.save(); return (
{ attributes.quote }
{ attributes.author }
); }, } ); ``` ## Block Editor Provider Create a standalone block editor instance with state management. ```js import { BlockEditorProvider, BlockList, WritingFlow } from '@wordpress/block-editor'; import { useState } from '@wordpress/element'; import { createBlock } from '@wordpress/blocks'; function CustomEditor() { const [ blocks, updateBlocks ] = useState( [ createBlock( 'core/paragraph', { content: 'Hello World' } ) ] ); return ( ); } ``` ## Inspector Controls Add custom controls to the block settings sidebar for configuration options. ```js import { useBlockProps, InspectorControls, BlockControls, AlignmentToolbar } from '@wordpress/block-editor'; import { PanelBody, RangeControl, SelectControl, ToggleControl } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; export default function Edit( { attributes, setAttributes } ) { const { columns, showImages, imageSize, alignment } = attributes; const blockProps = useBlockProps(); return ( <> setAttributes( { alignment } ) } /> setAttributes( { columns } ) } min={ 1 } max={ 4 } /> setAttributes( { showImages } ) } /> { showImages && ( setAttributes( { imageSize } ) } /> ) }
{/* Block content */}
); } ``` ## Inner Blocks Create container blocks that accept nested child blocks with template configuration. ```js import { useBlockProps, InnerBlocks } from '@wordpress/block-editor'; const ALLOWED_BLOCKS = [ 'core/heading', 'core/paragraph', 'core/image', 'core/list' ]; const TEMPLATE = [ [ 'core/heading', { level: 2, placeholder: 'Section title...' } ], [ 'core/paragraph', { placeholder: 'Add description...' } ], ]; export default function Edit() { const blockProps = useBlockProps( { className: 'section-container' } ); return (
); } export function Save() { const blockProps = useBlockProps.save( { className: 'section-container' } ); return (
); } ``` ## Data Store Creation Create a custom Redux store for application state management with actions, selectors, and resolvers. ```js import { createReduxStore, register } from '@wordpress/data'; import apiFetch from '@wordpress/api-fetch'; const DEFAULT_STATE = { products: {}, cart: [], isLoading: false, }; const store = createReduxStore( 'my-shop', { reducer( state = DEFAULT_STATE, action ) { switch ( action.type ) { case 'SET_PRODUCT': return { ...state, products: { ...state.products, [ action.id ]: action.product, }, }; case 'ADD_TO_CART': return { ...state, cart: [ ...state.cart, action.productId ], }; case 'SET_LOADING': return { ...state, isLoading: action.isLoading, }; } return state; }, actions: { setProduct( id, product ) { return { type: 'SET_PRODUCT', id, product }; }, addToCart( productId ) { return { type: 'ADD_TO_CART', productId }; }, setLoading( isLoading ) { return { type: 'SET_LOADING', isLoading }; }, }, selectors: { getProduct( state, id ) { return state.products[ id ]; }, getCart( state ) { return state.cart; }, isLoading( state ) { return state.isLoading; }, }, resolvers: { *getProduct( id ) { yield { type: 'SET_LOADING', isLoading: true }; try { const product = yield apiFetch( { path: `/wp/v2/products/${ id }` } ); return { type: 'SET_PRODUCT', id, product }; } finally { yield { type: 'SET_LOADING', isLoading: false }; } }, }, } ); register( store ); ``` ## useSelect Hook Retrieve data from stores with automatic re-rendering on changes. ```js import { useSelect } from '@wordpress/data'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { store as coreStore } from '@wordpress/core-data'; function PostInfo() { const { blockCount, selectedBlocks, postTitle, hasUnsavedChanges } = useSelect( ( select ) => { const editor = select( blockEditorStore ); const editorStore = select( 'core/editor' ); return { blockCount: editor.getBlockCount(), selectedBlocks: editor.getSelectedBlockClientIds(), postTitle: editorStore.getCurrentPost().title, hasUnsavedChanges: editorStore.hasChangedContent(), }; }, [] ); return (

{ postTitle }

Total blocks: { blockCount }

Selected: { selectedBlocks.length }

{ hasUnsavedChanges && ⚠️ Unsaved changes }
); } ``` ## useDispatch Hook Access store actions for state mutations. ```js import { useDispatch } from '@wordpress/data'; import { store as noticesStore } from '@wordpress/notices'; import { store as coreStore } from '@wordpress/core-data'; function SaveButton( { postId } ) { const { createNotice } = useDispatch( noticesStore ); const { saveEntityRecord, editEntityRecord } = useDispatch( coreStore ); const handleSave = async () => { try { await saveEntityRecord( 'postType', 'post', postId ); createNotice( 'success', 'Post saved successfully!', { type: 'snackbar', isDismissible: true, } ); } catch ( error ) { createNotice( 'error', 'Failed to save post.', { type: 'snackbar', } ); } }; const updateTitle = ( title ) => { editEntityRecord( 'postType', 'post', postId, { title } ); }; return ( ); } ``` ## Entity Record Hooks Fetch and manipulate WordPress entities (posts, pages, users) with built-in loading states and save functions. ```js import { useEntityRecord, useEntityRecords } from '@wordpress/core-data'; import { Spinner, TextControl, Button } from '@wordpress/components'; function PageEditor( { pageId } ) { const { record: page, isResolving, edit, save, hasEdits } = useEntityRecord( 'postType', 'page', pageId ); if ( isResolving ) return ; return (
edit( { title } ) } /> edit( { slug } ) } />
); } function PagesList() { const { records: pages, isResolving } = useEntityRecords( 'postType', 'page', { per_page: 20, status: 'publish', orderby: 'date', order: 'desc', } ); if ( isResolving ) return ; return ( ); } ``` ## API Fetch Make REST API requests with automatic nonce handling and error management. ```js import apiFetch from '@wordpress/api-fetch'; import { addQueryArgs } from '@wordpress/url'; // GET request async function fetchPosts() { try { const posts = await apiFetch( { path: '/wp/v2/posts' } ); return posts; } catch ( error ) { console.error( 'Failed to fetch posts:', error ); } } // GET with query parameters async function searchPosts( searchTerm ) { const posts = await apiFetch( { path: addQueryArgs( '/wp/v2/posts', { search: searchTerm, per_page: 10, status: 'publish', _fields: 'id,title,excerpt', } ) } ); return posts; } // POST request async function createPost( postData ) { const newPost = await apiFetch( { path: '/wp/v2/posts', method: 'POST', data: { title: postData.title, content: postData.content, status: 'draft', categories: [ 1, 5 ], tags: [ 12, 34 ], }, } ); return newPost; } // PUT/PATCH request async function updatePost( postId, updates ) { const updatedPost = await apiFetch( { path: `/wp/v2/posts/${ postId }`, method: 'POST', data: updates, } ); return updatedPost; } // DELETE request async function deletePost( postId ) { await apiFetch( { path: `/wp/v2/posts/${ postId }`, method: 'DELETE', } ); } // Custom middleware apiFetch.use( ( options, next ) => { console.log( 'API Request:', options.path ); const result = next( options ); result.then( ( data ) => { console.log( 'API Response:', data ); } ); return result; } ); ``` ## Block Parsing and Serialization Parse HTML content into block objects and serialize blocks back to HTML. ```js import { parse, serialize, createBlock } from '@wordpress/blocks'; // Parse HTML string to blocks const htmlContent = `

First paragraph

Section Title

`; const blocks = parse( htmlContent ); // Returns: Array of block objects with name, attributes, innerBlocks // Create new blocks programmatically const paragraphBlock = createBlock( 'core/paragraph', { content: 'Hello World', align: 'center', } ); const headingBlock = createBlock( 'core/heading', { content: 'My Title', level: 2, } ); const galleryBlock = createBlock( 'core/gallery', { images: [ { id: 1, url: 'https://example.com/image1.jpg' }, { id: 2, url: 'https://example.com/image2.jpg' }, ], columns: 3, } ); // Serialize blocks to HTML const newBlocks = [ paragraphBlock, headingBlock, galleryBlock ]; const htmlOutput = serialize( newBlocks ); // Returns: "

Hello World

..." ``` ## Block Transforms Enable conversion between different block types and from/to shortcodes. ```js import { createBlock } from '@wordpress/blocks'; const transforms = { from: [ { type: 'block', blocks: [ 'core/paragraph' ], transform: ( { content } ) => { return createBlock( 'my-plugin/custom-quote', { text: content, } ); }, }, { type: 'block', blocks: [ 'core/list' ], transform: ( { values } ) => { return values.split( '
  • ' ).map( ( item ) => createBlock( 'my-plugin/list-item', { content: item.replace( /<\/?li>/g, '' ), } ) ); }, }, { type: 'shortcode', tag: 'testimonial', attributes: { author: { type: 'string', shortcode: ( { named: { author } } ) => author, }, quote: { type: 'string', shortcode: ( { named: { quote } } ) => quote, }, }, }, { type: 'raw', selector: 'blockquote.testimonial', schema: { blockquote: { children: { p: { children: {} }, cite: { children: {} }, }, }, }, transform: ( node ) => { return createBlock( 'my-plugin/testimonial', { quote: node.querySelector( 'p' ).textContent, author: node.querySelector( 'cite' ).textContent, } ); }, }, ], to: [ { type: 'block', blocks: [ 'core/paragraph' ], transform: ( { text } ) => { return createBlock( 'core/paragraph', { content: text, } ); }, }, { type: 'block', blocks: [ 'core/quote' ], transform: ( { text, author } ) => { return createBlock( 'core/quote', { value: `

    ${ text }

    `, citation: author, } ); }, }, ], }; export default transforms; ``` ## Server-Side Rendered Blocks Create dynamic blocks that render on the server with PHP while providing editor preview. ```php 'render_latest_posts_block', ) ); } add_action( 'init', 'register_latest_posts_block' ); function render_latest_posts_block( $attributes, $content ) { $posts_per_page = isset( $attributes['postsPerPage'] ) ? $attributes['postsPerPage'] : 5; $category = isset( $attributes['category'] ) ? $attributes['category'] : ''; $args = array( 'posts_per_page' => $posts_per_page, 'post_status' => 'publish', ); if ( ! empty( $category ) ) { $args['category_name'] = $category; } $posts = get_posts( $args ); if ( empty( $posts ) ) { return '

    No posts found.

    '; } ob_start(); ?> setAttributes( { postsPerPage } ) } min={ 1 } max={ 20 } /> setAttributes( { category } ) } />
    ); } ``` ## Rich Text Formatting Create custom inline formats for rich text content with toolbar controls. ```js import { registerFormatType, toggleFormat } from '@wordpress/rich-text'; import { RichTextToolbarButton } from '@wordpress/block-editor'; import { Icon } from '@wordpress/components'; // Register custom format registerFormatType( 'my-plugin/highlight', { title: 'Highlight', tagName: 'mark', className: 'highlight', edit: ( { isActive, value, onChange } ) => { return ( } title="Highlight" onClick={ () => { onChange( toggleFormat( value, { type: 'my-plugin/highlight', } ) ); } } isActive={ isActive } /> ); }, } ); // Register format with attributes registerFormatType( 'my-plugin/tooltip', { title: 'Tooltip', tagName: 'span', className: 'has-tooltip', attributes: { 'data-tooltip': 'data-tooltip', }, edit: ( { isActive, value, onChange, contentRef } ) => { const tooltipText = isActive ? value.activeFormats?.find( format => format.type === 'my-plugin/tooltip' )?.attributes?.[ 'data-tooltip' ] : ''; return ( { const text = window.prompt( 'Enter tooltip text:', tooltipText ); if ( text !== null ) { onChange( toggleFormat( value, { type: 'my-plugin/tooltip', attributes: { 'data-tooltip': text }, } ) ); } } } isActive={ isActive } /> ); }, } ); ``` ## Hooks and Filters Extend blocks and editor behavior using WordPress hooks system. ```js import { addFilter } from '@wordpress/hooks'; import { createHigherOrderComponent } from '@wordpress/compose'; import { InspectorControls } from '@wordpress/block-editor'; import { PanelBody, TextControl } from '@wordpress/components'; // Add custom attribute to all blocks addFilter( 'blocks.registerBlockType', 'my-plugin/custom-attribute', ( settings, name ) => { return { ...settings, attributes: { ...settings.attributes, customId: { type: 'string', default: '', }, }, }; } ); // Add custom control to block inspector const withInspectorControl = createHigherOrderComponent( ( BlockEdit ) => { return ( props ) => { const { attributes, setAttributes } = props; const { customId } = attributes; return ( <> setAttributes( { customId } ) } /> ); }; }, 'withInspectorControl' ); addFilter( 'editor.BlockEdit', 'my-plugin/with-inspector-control', withInspectorControl ); // Modify block save output addFilter( 'blocks.getSaveElement', 'my-plugin/add-custom-id', ( element, blockType, attributes ) => { if ( attributes.customId ) { return { ...element, props: { ...element.props, id: attributes.customId, }, }; } return element; } ); // Restrict allowed blocks addFilter( 'blocks.registerBlockType', 'my-plugin/restrict-blocks', ( settings, name ) => { // Hide specific blocks from inserter if ( [ 'core/verse', 'core/freeform' ].includes( name ) ) { return { ...settings, supports: { ...settings.supports, inserter: false, }, }; } return settings; } ); ``` ## Compose Utilities Use composition helpers for managing component lifecycle and behavior. ```js import { useInstanceId, useDebounce, usePrevious, useRefEffect, useCopyToClipboard, useMediaQuery, } from '@wordpress/compose'; import { useState, useEffect } from '@wordpress/element'; function AdvancedInput( { onChange } ) { const [ value, setValue ] = useState( '' ); const [ showSuccess, setShowSuccess ] = useState( false ); // Generate unique ID for accessibility const inputId = useInstanceId( AdvancedInput, 'input' ); // Debounce value changes const debouncedValue = useDebounce( value, 500 ); // Track previous value const previousValue = usePrevious( value ); // Responsive behavior const isMobile = useMediaQuery( '(max-width: 768px)' ); // Copy to clipboard const copyRef = useCopyToClipboard( value, () => { setShowSuccess( true ); setTimeout( () => setShowSuccess( false ), 2000 ); } ); // Focus management with cleanup const inputRef = useRefEffect( ( node ) => { if ( ! isMobile ) { node.focus(); } return () => { console.log( 'Input unmounted' ); }; }, [ isMobile ] ); // Call onChange with debounced value useEffect( () => { if ( debouncedValue !== previousValue ) { onChange?.( debouncedValue ); } }, [ debouncedValue, previousValue, onChange ] ); return (
    setValue( e.target.value ) } /> { showSuccess && ✓ Copied! }
    ); } ``` ## Slot Fill System Create extensible plugin areas using slots and fills for adding UI in specific locations. ```js import { Fill, Slot, SlotFillProvider } from '@wordpress/components'; import { PluginDocumentSettingPanel } from '@wordpress/edit-post'; import { registerPlugin } from '@wordpress/plugins'; // Create custom slot in your component function EditorSidebar() { return (

    Settings

    ); } // Fill the slot from another component/plugin function CustomPanel() { return (

    This content is injected via Fill

    ); } // Wrap in SlotFillProvider function App() { return ( ); } // Use WordPress built-in slots via plugins registerPlugin( 'my-plugin-sidebar', { render: () => { return (

    Custom settings content

    ); }, } ); ``` WordPress Gutenberg provides a comprehensive platform for building modern content editors and custom WordPress experiences. The primary use cases include developing custom blocks for unique content types, extending the editor with plugins that add panels and controls, building block-based themes with template customization, programmatically managing post content through data stores, creating admin interfaces using the component library, and implementing full site editing capabilities. The modular architecture enables developers to use individual packages independently or combine them for complete solutions. The integration patterns center around block registration with `registerBlockType`, state management through `@wordpress/data` stores with `useSelect` and `useDispatch` hooks, entity manipulation via `@wordpress/core-data` for WordPress REST API integration, and component composition using `@wordpress/components` UI library. The hooks system (`@wordpress/hooks`) enables extending existing blocks and editor behavior, while the SlotFill pattern provides extensible UI areas. Server-side rendering supports dynamic content generation, and the transform API enables seamless conversion between different block types and content formats. This ecosystem empowers developers to create sophisticated content editing experiences while maintaining consistency with WordPress design patterns.