Let’s talk about building better WordPress blocks! If you’ve been diving into block development, you’ve probably bumped into block.json, useBlockProps, and InspectorControls . They feel like the core ingredients for making your blocks do cool things. But how do they all play nice together to create a smooth editing experience and ultimately, a better website for your users?
Think of block.json as your block’s blueprint. It’s where you declare what your block is, its settings, and crucially, its attributes. useBlockProps is the workhorse that helps you apply those attributes to your block’s wrapper element and manage its core features like alignment and responsiveness. And InspectorControls? That’s your block’s control panel, the place where users can tweak those attributes you so carefully defined. Tying these three together isn’t just good practice; it’s essential for building functional and user-friendly Gutenberg blocks.
So, let’s break down how you can effectively combine these powerful tools to create truly dynamic and customizable WordPress blocks.
Before we can even think about using useBlockProps or InspectorControls, we need to lay the groundwork with block.json. This file is the heart of your block’s definition, and it’s where you’ll declare all the data your block will manage – its attributes.
What are Block Attributes?
At their simplest, block attributes are the pieces of data that define the state and content of your block. They’re like the properties of a JavaScript object, but specifically for your WordPress block. When a user edits your block, they’re essentially changing the values of these attributes.
- Types of Attributes: Attributes can be of various types, such as:
string: For text content, headings, etc.number: For sizes, counts, etc.boolean: For toggling features on or off.object: For more complex data structures.array: For lists of items.
Declaring Attributes in block.json
The attributes key in your block.json file is where you list all the attributes your block will support. Each attribute needs a type and optionally a default value, which is what the attribute will be set to when the block is first inserted.
“`json
{
“name”: “my-plugin/my-awesome-block”,
“title”: “My Awesome Block”,
“icon”: “smiley”,
“category”: “widgets”,
“attributes”: {
“message”: {
“type”: “string”,
“default”: “Hello, world!”
},
“fontSize”: {
“type”: “number”,
“default”: 16
},
“showIcon”: {
“type”: “boolean”,
“default”: true
}
}
}
“`
In this example:
messageis a string attribute, defaulting to “Hello, world!”.fontSizeis a number attribute, defaulting to 16.showIconis a boolean attribute, defaulting to true.
These declarations tell WordPress: “This block has a message, a font size, and a flag to show an icon.” Now, how do we actually use these in our block’s JavaScript?
Accessing Attributes in Your JavaScript
When you import your block into your JavaScript file (usually the edit.js and save.js files), the attributes defined in block.json become available through the attributes object passed to your component.
In your edit.js component, you’ll typically use a hook like useBlockProps. This hook conveniently gives you access to your block’s attributes, along with other useful props.
“`javascript
import { useBlockProps } from ‘@wordpress/block-editor’;
import { InspectorControls } from ‘@wordpress/block-editor’; // We’ll get to this soon!
import { PanelBody, TextControl, RangeControl } from ‘@wordpress/components’;
function Edit({ attributes, setAttributes }) {
const blockProps = useBlockProps(); // Get the props from the hook
return (
<>
{attributes.showIcon && 😊}
Message: {attributes.message}
Font Size: {attributes.fontSize}px
{/ InspectorControls will go here /}
>
);
}
export default Edit;
“`
Notice how attributes.message, attributes.fontSize, and attributes.showIcon are directly accessible. The setAttributes function (also provided by the useBlockProps hook, though often implicitly through the component props) is what you’ll use later to update these values from your controls.
To effectively utilize block attributes, the `useBlockProps` hook, and `InspectorControls` in your WordPress development, it’s essential to understand how these components interact within the block editor. For further insights into optimizing your website’s performance, you might find it helpful to read a related article on improving your site’s speed using Google PageSpeed Insights. You can access it here: Google PageSpeed Insights. This resource provides valuable tips that can enhance your website’s overall functionality and user experience.
useBlockProps: The Wrapper’s Best Friend
useBlockProps is a hook that simplifies applying many common block properties and attributes to your block’s wrapper element. It’s designed to handle things like alignment, responsiveness, and even custom classes without you having to manually manage them.
What useBlockProps Does
When you call useBlockProps(), it returns an object containing props that you should spread onto your block’s outermost element. This object might include:
className: For applying alignment classes, responsive classes, and any custom classes you might define.style: For inline styles, especially useful for things like padding or margin if you expose those as attributes.id: If your block uses an ID.- Other core block-related props.
By using useBlockProps, you ensure your block plays nicely with WordPress’s core features.
Applying useBlockProps in edit.js and save.js
It’s crucial to use useBlockProps in both your edit.js (for the editor view) and save.js (for how the block is rendered on the front end). This ensures consistency.
In edit.js:
“`javascript
import { useBlockProps } from ‘@wordpress/block-editor’;
function Edit({ attributes }) {
// Grab the props from the hook
const blockProps = useBlockProps({
// You can add className or style here that will be merged
className: ‘my-custom-block-class’,
});
return (
{attributes.showIcon && 😊}
Message: {attributes.message}
Font Size: {attributes.fontSize}px
);
}
export default Edit;
“`
In save.js:
The save.js file is responsible for outputting the static HTML markup for your block. useBlockProps.save() is used here.
“`javascript
import { useBlockProps } from ‘@wordpress/block-editor’;
function save({ attributes }) {
const blockProps = useBlockProps.save(); // Use the save version
return (
{attributes.showIcon && 😊}
Message: {attributes.message}
Font Size: {attributes.fontSize}px
);
}
export default save;
“`
The magic here is that useBlockProps (and useBlockProps.save()) will automatically add classes for alignment (e.g., alignwide, alignfull) if the user selects them in the block sidebar or inspector. It also handles things like padding and margin based on theme or user settings.
You can also pass an object to useBlockProps to merge additional classes or styles. For example, useBlockProps({ className: 'my-extra-class' }) will add my-extra-class to the automatically generated classes.
InspectorControls: The User’s Control Panel
InspectorControls is a component provided by @wordpress/block-editor that allows you to define the UI elements that appear in the block’s sidebar (the “Inspector”). This is where users will interact with your block’s attributes to customize its appearance and behavior.
Structuring Inspector Controls
InspectorControls is typically wrapped inside a PanelBody or PanelRow component from @wordpress/components. This organizes your controls into collapsible sections within the sidebar.
PanelBody: Creates a collapsible section with a title.PanelRow: A simpler container for a single control or a row of controls.
Common Components for Controls
WordPress provides a rich set of UI components to build your inspector. Some commonly used ones include:
TextControl: For inputting text.RangeControl: For selecting a numerical value within a range (like font size, spacing).ToggleControl: For boolean (on/off) settings.SelectControl: For dropdown choices.ColorPalette: For color selection.MediaUpload: For choosing images, videos, etc.
Connecting Controls to Attributes
This is where the integration happens. Each control component has a value prop (to display the current attribute value) and an onChange prop (a function that’s called when the user changes the value). This onChange function is where you’ll use the setAttributes function to update the corresponding block attribute.
Let’s revisit our edit.js component and add some InspectorControls:
“`javascript
import { useBlockProps } from ‘@wordpress/block-editor’;
import { InspectorControls } from ‘@wordpress/block-editor’;
import { PanelBody, TextControl, RangeControl, ToggleControl } from ‘@wordpress/components’;
function Edit({ attributes, setAttributes }) { // Now we need setAttributes
const blockProps = useBlockProps();
return (
<>
{/ This is where the InspectorControls will appear in the sidebar /}
{/ Text Control for the message attribute /}
label=”Your Message” value={attributes.message} onChange={(newMessage) => setAttributes({ message: newMessage })} /> {/ Range Control for the fontSize attribute /} label=”Font Size (px)” value={attributes.fontSize} onChange={(newFontSize) => setAttributes({ fontSize: newFontSize })} min={12} max={36} step={1} /> {/ Toggle Control for the showIcon attribute /} label=”Show Icon” checked={attributes.showIcon} // ‘checked’ for toggles onChange={(newShowIcon) => setAttributes({ showIcon: newShowIcon })} /> {/ This is the block’s content in the editor /} {attributes.showIcon && 😊} Message: {attributes.message} ${attributes.fontSize}px }}> This text has a dynamic font size. > ); } export default Edit; “` In this example: Now, let’s outline the typical workflow for creating a block that leverages First, sit down and figure out what your block needs to do. What pieces of information should the user be able to control? These will become your block attributes. Then, define these in “`json { “name”: “my-plugin/cta-block”, “title”: “Call To Action”, “icon”: “megaphone”, “category”: “widgets”, “attributes”: { “headline”: { “type”: “string”, “default”: “Sign Up Today!” }, “description”: { “type”: “string”, “default”: “Don’t miss out on our exclusive offers.” }, “buttonUrl”: { “type”: “string”, “default”: “#” }, “buttonLabel”: { “type”: “string”, “default”: “Learn More” }, “showBackground”: { “type”: “boolean”, “default”: false }, “buttonColor”: { “type”: “string”, “default”: “#0073aa” } } } “` This is where you’ll create the visual representation of your block in the editor and set up the controls in the sidebar. “`javascript import { useBlockProps } from ‘@wordpress/block-editor’; import { InspectorControls } from ‘@wordpress/block-editor’; import { PanelBody, TextControl, ToggleControl, Button, ColorPalette, MediaUpload, MediaUploadCheck } from ‘@wordpress/components’; import { __ } from ‘@wordpress/i18n’; // For internationalization function Edit({ attributes, setAttributes }) { const blockProps = useBlockProps({ className: ‘cta-block-editor’, }); // Helper function to update attributes const updateAttributes = (newAttrs) => { setAttributes(newAttrs); }; return ( <> label={__(“Headline”, “my-plugin”)} value={attributes.headline} onChange={(newValue) => updateAttributes({ headline: newValue })} /> label={__(“Description”, “my-plugin”)} value={attributes.description} onChange={(newValue) => updateAttributes({ description: newValue })} textarea /> label={__(“Button URL”, “my-plugin”)} value={attributes.buttonUrl} onChange={(newValue) => updateAttributes({ buttonUrl: newValue })} /> label={__(“Button Label”, “my-plugin”)} value={attributes.buttonLabel} onChange={(newValue) => updateAttributes({ buttonLabel: newValue })} /> label={__(“Show Background Image”, “my-plugin”)} checked={attributes.showBackground} onChange={(newValue) => updateAttributes({ showBackground: newValue })} /> {__(“Button Color”, “my-plugin”)} value={attributes.buttonColor} onChange={(newColor) => updateAttributes({ buttonColor: newColor })} colors={[ { name: ‘blue’, color: ‘#0073aa’ }, { name: ‘red’, color: ‘#d63638’ }, { name: ‘green’, color: ‘#822433’ }, ]} /> > ); } export default Edit; “` In the editor, we display the headline, description, and button. The This file determines how your block will be rendered on the public-facing website. “`javascript import { useBlockProps } from ‘@wordpress/block-editor’; function save({ attributes }) { const blockProps = useBlockProps.save(); return ( {attributes.description} {attributes.buttonLabel} {/ The showBackground attribute would be used to conditionally render a background element /} ); } export default save; “` Here, Let’s say we wanted to add background image functionality to our CTA block. This involves more than just simple text or color. “`json { “name”: “my-plugin/cta-block”, “title”: “Call To Action”, “icon”: “megaphone”, “category”: “widgets”, “attributes”: { “headline”: { … }, “description”: { … }, “buttonUrl”: { … }, “buttonLabel”: { … }, “showBackground”: { “type”: “boolean”, “default”: false }, “backgroundImage”: { “type”: “object”, // Store image ID, URL, alt text “default”: { “url”: “”, “id”: null, “alt”: “” } }, “buttonColor”: { … } } } “` We’ll use “`javascript import { Fragment } from ‘@wordpress/element’; // Import Fragment import { useBlockProps } from ‘@wordpress/block-editor’; import { InspectorControls } from ‘@wordpress/block-editor’; import { PanelBody, TextControl, ToggleControl, Button, ColorPalette, MediaUpload, MediaUploadCheck } from ‘@wordpress/components’; import { __ } from ‘@wordpress/i18n’; function Edit({ attributes, setAttributes }) { const blockProps = useBlockProps({ className: ‘cta-block-editor’, style: { // Apply background image style dynamically backgroundImage: attributes.showBackground && attributes.backgroundImage.url ? backgroundSize: ‘cover’, backgroundPosition: ‘center’, padding: ’20px’, // Example padding color: ‘#fff’, // Example text color for background overlay }, }); const updateAttributes = (newAttrs) => { setAttributes(newAttrs); }; const onSelectImage = (media) => { if (!media.url) { updateAttributes({ backgroundImage: { url: “”, id: null, alt: “” } }); return; } updateAttributes({ backgroundImage: { url: media.url, id: media.id, alt: media.alt } }); }; return ( <> {/ … other controls … /} label={__(“Use Background Image”, “my-plugin”)} checked={attributes.showBackground} onChange={(newValue) => updateAttributes({ showBackground: newValue })} /> {attributes.showBackground && ( onSelect={onSelectImage} allowedTypes={[‘image’]} value={attributes.backgroundImage.id} render={({ open }) => ( )} /> )} {attributes.backgroundImage.url && ( Image URL: {attributes.backgroundImage.url.substring(0, 30)}… )} {/ … color controls … /} > ); } export default Edit; “` “`javascript import { useBlockProps } from ‘@wordpress/block-editor’; function save({ attributes }) { const blockProps = useBlockProps.save({ style: { // Apply background image style dynamically on save backgroundImage: attributes.showBackground && attributes.backgroundImage.url ? backgroundSize: ‘cover’, backgroundPosition: ‘center’, padding: ’20px’, // Ensure consistency with editor color: ‘#fff’, // Same as editor }, }); return ( ); } export default save; “` Here, This kind of dynamic styling using In the world of WordPress block development, understanding how to effectively use block attributes, along with the useBlockProps and InspectorControls, can significantly enhance the user experience and functionality of your custom blocks. For a deeper dive into these concepts, you might find it helpful to explore a related article that discusses practical applications and best practices. This resource can provide valuable insights and examples to further your understanding of these essential tools. You can check it out here. As you get more comfortable, here are some tips to keep your block development smooth and efficient. By following these best practices and being mindful of potential pitfalls, you’ll be well on your way to building robust, user-friendly, and well-integrated WordPress blocks. So, there you have it. By understanding how These aren’t just separate pieces of a puzzle; they’re designed to work in concert. When you combine them correctly, you create blocks that are: The WordPress block editor, Gutenberg, is a powerful system, and mastering these core components is fundamental to building effective and engaging content experiences for your website visitors. Keep experimenting, keep building, and you’ll find that these tools become second nature as you craft increasingly impressive WordPress blocks.
TextControl’s value is bound to attributes.message. When the user types, the onChange function is called, updating the message attribute using setAttributes({ message: newMessage }).RangeControl for fontSize and the ToggleControl for showIcon are connected. Notice the use of checked for ToggleControl.
attributes.message and attributes.fontSize to dynamically render content. The font size is even applied using an inline style for demonstration.
Bringing It All Together: A Practical Workflow
block.json attributes, useBlockProps, and InspectorControls.Step 1: Define Your Block’s Needs (
block.json)
block.json:Step 2: Build the Editor Experience (
edit.js)useBlockProps hook will automatically handle things like padding and alignment. The InspectorControls provides the UI for users to change these values. Notice how the buttonColor is directly changing the text color of the headline and the background of the button in the editor for immediate feedback.Step 3: Define the Front-End Output (
save.js)useBlockProps.save() is vital here.{attributes.headline}
useBlockProps.save() ensures that any alignment or responsive classes applied in the editor are carried over to the front end. The HTML structure is minimal, relying on the attributes to define its content and some basic styling.Handling More Complex Attributes (e.g., Media)
Adding
backgroundImage Attribute to block.jsonUpdating
edit.js for Background ImageMediaUpload and MediaUploadCheck for this.url(${attributes.backgroundImage.url}) : ‘none’,Updating
save.js for Background Imageurl(${attributes.backgroundImage.url}) : ‘none’,useBlockProps is used not just to apply core classes but also to apply dynamic inline styles for the background image. This ensures that the visual representation in the editor closely matches the front-end output.useBlockProps and attributes is why the integration is so powerful. You’re not just changing text; you’re affecting the entire visual presentation of your block.Best Practices and Common Pitfalls
Keep
block.json Clean and Organized
primaryColor instead of just color).type in block.json matches the actual data you’re storing. Mismatches can lead to unexpected behavior.Leverage
useBlockProps Fully
useBlockProps in both edit.js and save.js. This is paramount for consistency and ensures core features work correctly.className or style with useBlockProps. Understand what classes WordPress is adding and avoid conflicting with them unless absolutely necessary.useBlockProps handles alignment (full, wide, default). If you need more granular responsiveness (e.g., different padding on small screens), you might need to expose custom attributes for those.Design User-Friendly Inspector Controls (
InspectorControls)
PanelBody to group related controls. For instance, all text-related settings go in one panel, styling in another.label prop. The __ function is essential for making your block translatable.min, max, step) are appropriate for the attribute they control.edit.js, try to reflect the chosen attribute values visually as much as possible. This gives users immediate confirmation that their changes are working. For example, if a user picks a button color, make the button in the editor reflect that color.Handling State and Updates (
setAttributes)
TextControl, directly calling setAttributes in onChange is fine.updateAttributes as shown in the CTA example. This makes your onChange handlers cleaner.backgroundImage: create a new object with the modified url, id, and alt.Debugging Tips
console.log(): Sprinkle console.log() statements in your edit.js and save.js files to inspect attribute values, blockProps, and the data passed to components.@wordpress/components and @wordpress/block-editor. The official documentation is your friend.Conclusion: The Synergy of Core Components
block.json defines your block’s data, how useBlockProps elegantly handles its presentation and core features, and how InspectorControls provide the interface for user customization, you’ve unlocked the key to building sophisticated WordPress blocks.
block.json is your contract – it declares what data your block can have and its default state.useBlockProps is your reliable assistant – it takes those attributes and applies them to your block’s wrapper in a standard, WordPress-compliant way, handling things like alignment and responsiveness automatically.InspectorControls is your user’s direct line to modifying those declared attributes, providing a clear and organized interface for them to make your block their own.
block.json definition removes guesswork.