Making the Juxtapose Block
This post walks through my process of creating the Juxtapose Block for the WordPress block editor. The block implements the JuxtaposeJS library by Knight Lab which allows you to place two images side-by-side and use a slider to reveal the difference between them.
Here's an example:
Alfa Romeo GTV
📌 Note: This block was integrated into the Jetpack plugin as the Image Compare block. If you are looking to add this to your site, use the Jetpack plugin to enable.
Breaking down the problem
When creating a block, I tend to work backwards. I figure out what I need for the final published view, and then create the pieces to get me there.
For this block, all the real functionality and heavy lifting is done within the JuxtaposeJS library. The library also makes it straight-forward to implement. You include the JS and CSS files, and then the necessary markup. Once in place the library will find and automatically convert the images properly.
The markup is basically:
<div class="juxtapose">
<img src="image1.jpg" />
<img src="image2.jpg" />
</div>
Stub out Plugin and Block
I initially started the Juxtapose block using ES5 syntax, which is easy to start since there is no build step. As the block got more complex, and the hope for more contributors, I decided to switch to the simpler ES6+ syntax using JSX; it is easier to read and code but requires a bit more setup.
To switch my plugin to ES6, I used the code from gutenberg-examples repo which includes all the pieces of package.json, webpack, and babel. I edited lightly to fit the directory structure I already had in place. If you need help setting up, see my screencast for setting up a Gutenberg development environment for complete info.
So now I can install the dependencies and build using:
npm install
npm start
I modified block.js
so the edit function simply returned, Hey, I'm a Juxtapose block!
and the save function returned the necessary markup with hard-coded images. I then tweaked the PHP plugin code to enqueue the appropriate files until the block returned the markup working as expected. Everything hard-coded at first.
At this point my block code was basically:
edit: () => {
return (
<div> Hey I'm a Juxtapose block </div>
);
},
save: () => {
return (
<div className='juxtapose'>
<img src="./img/800-600.jpg"/>
<img src="http://placekitten.com/800/600"/>
</div>
);
}
Map out Data
The next step in planning my block was to lay out the data I needed, in this case two images, a Before and After. In fact, I simply needed the src
value. By adding a classname to each image tag I could distinguish the two images and use that for setting the block attributes.
The save markup becomes:
save: ({ attributes }) => {
return (
<div className="juxtapose">
<img src={attributes.imageBefore} className="imgBefore" />
<img src={attributes.imageAfter} className="imgAfter" />
</div>
);
};
The block attributes can be set from the markup using the selector and class, see the attributes documentation for other methods of extracting data to attributes.
attributes: {
imageBefore: {
type: 'string',
source: 'attribute',
attribute: 'src',
selector: '.imgBefore',
},
imageAfter: {
type: 'string',
source: 'attribute',
attribute: 'src',
selector: '.imgAfter',
},
},
Editor View
The final piece is adding the editor view to set the image attributes. This one is a little tricky, I knew the component I wanted to use, MediaPlaceholder, but the reason I knew was because I was involved in initially creating it.
There are a few ways to discover available components, the method I typically use is to browse core components and see what they use; Gallery or Image block in this case.
WP Storybook is another method, it is a pretty cool tool which allows you to browse and see available components. WP Storybook isn't complete with all components. There is a set of components in the wp-editor package available, often I just have to browse the source to find what I need.
For Juxtapose block, I use the MediaPlaceholder component from wp.editor
which handles all drag-and-drop uploading and selecting an image from the media library.
Here is one of the chunks that manage uploads, setting the attribute after an image is selected:
<div className="img-edit-before">
<MediaPlaceholder
onSelect={(el) => {
setAttributes({ imageBefore: el.url });
}}
allowedTypes={["image"]}
labels={{ title: "Image Before" }}
/>
</div>
My block is more or less working after adding both before and after upload chunks.
Finishing Touches
The remaining parts of creating the block is cleanup and adding some nice touches. For one, after selecting an image, I want to display the image selected and not the placeholder. This requires setting a ternary to handle the if-else statement.
If the image attribute is defined, show an image in editor view, else show the placeholder component.
{ imageBefore ? (
<img src={imageBefore} />
) : (
<div className='img-edit-before'>
<MediaPlaceholder
...
/>
</div>
)}
Also, once both images are uploaded I want the editor block view to look similar to the published view, so I load and trigger the Juxtapose library. This makes the block look the same as it would once published.
I'm continuing to add features from the original library, for example a caption and InspectorControls for orientation mode have been added. See the block source for more, and issues if you want to help contribute more features.
Epilogue
I collaborated with the wonderful designer sarahmonster who had the initial idea of turning Knight Lab modules into blocks.
The gist of this post is to give a general direction when trying to create a new block, how to think and start putting it together. See the commits and issues log if you want to see the real messiness of creation, which all creation is messy.