En este post, voy a documentar la creación de un miniplugin o «plugin monobloque» para WordPress. El objetivo es practicar habilidades de desarrollo creando un bloque con dos imágenes y un slider para mostrar un antes y después. Aunque existen soluciones similares, aquí busco hacerlo de forma rápida y simple utilizando InnerBlocks.
Instalación y Configuración Inicial
Para empezar, creamos nuestro plugin utilizando @wordpress/create-block
en un entorno de pruebas dentro del directorio wp-content/plugins
:
npx @wordpress/create-block@latest beforeAfterImages cd beforeafterimages npm start
Este comando genera la estructura base del plugin con un bloque estático. Luego, habilitamos el plugin en WordPress, creamos un post o página y agregamos el nuevo bloque que se llama «Before After Images» y se va a ver mas o menos así:

Configuración del Bloque en el Editor
En src/beforeafterimages/edit.js
, importamos useInnerBlocksProps
y definimos nuestro bloque:
import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor'; export default function Edit() { const blockProps = useBlockProps(); const innerBlocksProps = useInnerBlocksProps(blockProps, { allowedBlocks: ['core/image'], template: [['core/image'], ['core/image']], templateLock: 'all', }); return <div {...innerBlocksProps}></div>; }
Aquí definimos que bloques permite: allowedBlocks: ['core/image']
y que contendrá dos imágenes fijas: template: [['core/image'], ['core/image']]
, y bloqueamos el template con: templateLock: 'all'
para evitar la posibilidad de agregar más imágenes o eliminar alguna.
Nuestro bloque en el editor se va a ver de la siguiente manera:

Guardando el Bloque
En src/beforeafterimages/save.js
, usamos InnerBlocks.Content
para guardar el contenido del bloque:`
import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; export default function save() { return ( <div {...useBlockProps.save()}> <InnerBlocks.Content /> </div> ); }
Esto garantiza que las imágenes seleccionadas en el editor se guarden en el contenido y sean mostradas en el frontend.
Implementando el Slider
Para estructurar el slider, modificamos src/beforeafterimages/save.js
:
export default function save() { return ( <div {...useBlockProps.save()}> <div className="container"> <div className="image-container"> <InnerBlocks.Content /> </div> <input type="range" min="0" max="100" value="50" className="slider" /> <div className="slider-line"></div> <div className="slider-button"></div> </div> </div> ); }
Estilos en src/
/beforeafterimages
style.scss
.wp-block-create-block-beforeafterimages { display: grid; place-items: center; *, *::after, *::before { margin: 0; padding: 0; box-sizing: border-box; } .container { display: grid; place-content: center; position: relative; overflow: hidden; border-radius: 1rem; --position: 50%; } .image-container { max-width: 800px; max-height: 90vh; aspect-ratio: 1/1; figure { margin: 0; padding: 0; width: 100%; height: 100%; display: flex; img{ width: 100%; height: 100%; object-fit: cover; object-position: left; } figcaption{ position: absolute; bottom: 0; background-color: rgb(210, 210, 210); width: 100%; display: block; overflow: hidden; text-align: left; text-indent: 10px; } &:first-child { position: absolute; inset: 0; width: var(--position); figcaption{ z-index: 10; } } } } .slider { position: absolute; inset: 0; cursor: pointer; opacity: 0; /* for Firefox */ width: 100%; height: 100%; } .slider:focus-visible ~ .slider-button { outline: 5px solid black; outline-offset: 3px; } .slider-line { position: absolute; inset: 0; width: .2rem; height: 100%; background-color: #fff; left: var(--position); transform: translateX(-50%); pointer-events: none; } .slider-button { position: absolute; background-color: #fff; color: black; padding: .5rem; border-radius: 100vw; display: grid; place-items: center; top: 50%; left: var(--position); transform: translate(-50%, -50%); pointer-events: none; box-shadow: 1px 1px 1px hsl(0, 50%, 2%, .5); } }
JavaScript para el Frontend en src
/
/view.jsbeforeafterimages
const container = document.querySelector('.container'); document.querySelector('.slider').addEventListener('input', (e) => { container.style.setProperty('--position', `${e.target.value}%`); })
Mejorando la Vista en el Editor
Creamos
para reutilizar la estructura del slider:src/
/slider.jsbeforeafterimages
import React from 'react'; const Slider = () => ( <> <div class="slider-line" aria-hidden="true"></div> <div class="slider-button" aria-hidden="true"> <svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="currentColor" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none"></rect> <line x1="128" y1="40" x2="128" y2="216" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" ></line> <line x1="96" y1="128" x2="16" y2="128" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" ></line> <polyline points="48 160 16 128 48 96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" ></polyline> <line x1="160" y1="128" x2="240" y2="128" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" ></line> <polyline points="208 96 240 128 208 160" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" ></polyline> </svg> </div> </> ); export default Slider;
Luego, importamos y usamos <Slider />
en
y en src/
/edit.jsbeforeafterimages
src/beforeafterimages/save.js
:
import Slider from './slider'; return ( <div {...blockProps}> <div className="container"> <div {...innerBlocksProps} className="image-container"></div> <Slider /> </div> </div> );
Finalmente, ajustamos
para mejorar la vista en el editor.src/
editor.scssbeforeafterimages
.wp-block-create-block-beforeafterimages { display: block; min-height: auto; .container { display: block; position: static; overflow: hidden; border-radius: 0; --position: 0; width: 100%; .image-container { display: grid; grid-template-columns: 1fr 1fr; max-width: 100%; max-height: 100%; aspect-ratio: auto; figure { display:block; &:first-child { position: relative; inset: 0; width:100%; } } } } .slider-button, .slider-line { left:50%; } }
Con esto, hemos creado un bloque funcional con un slider de antes y después. ¡Ahora podemos seguir refinando y optimizando el código!
Por ejemplo podemos modificar el javascript para poder insertar mas de un slide en la misma pagina:
document.addEventListener('DOMContentLoaded', () => { const containers = document.querySelectorAll('.container'); const sliders = document.querySelectorAll('.slider'); containers.forEach((container, index) => { const slider = sliders[index]; if (slider) { slider.addEventListener('input', (e) => { container.style.setProperty('--position', `${e.target.value}%`); }); } }); });
Acá lo pueden ver funcionando:


Fuentes
- Slider script de este tutorial.
- InnerBlock del tutorial de Brian Coords.
- Repositorio del Código completo
- Guia de Create-Block