Create a Slider Block for WordPress with SwiperJS

Damon Cook Avatar

·

Carousels, slideshows, sliders, whatever you call them, are controversial and complex pieces of our web interactions. More often than not, they’re a means to shove a bunch of ideas into a single part of the user interface. Usually, clients follow the herd mentality and request they have one, or it becomes a means for multiple stakeholders to compromise on what takes precedence in information architecture.

We all know they could be better for overall site performance and offer many user experience obstacles. So, why would you want to build one? 🙃

I’m making some compelling counter-arguments, but there is still a time and place when you will need one, and we’ll cover all the steps to create your own.

As of writing this, no slider block is currently available in WordPress core’s library. There are several existing slider block plugins, but we want to have control over and learn the intricacies of our own. This way, we can focus on keeping our code lean and agile and extend it to our needs.

I’m not going to lie, there are a lot of steps to get things like this working, but you’ll learn a lot along the way, like:

  • Working with and extending the @wordpress/scripts package.
  • Pulling in 3rd-party dependencies and integrating them to behave smoothly within the WordPress editor.
  • Use @wordpress/components to allow your clients to modify the properties of the slider.
  • And much more.

Here is a demonstration of what we’ll be creating.

Prerequisites

Beyond some working knowledge of HTML, CSS, PHP, and JavaScript. You’ll also need familiarity with the following:

  • Node and npm: You should have Node.js and npm installed on your machine. I recommend using nvm (node version manager), which allows you to install and manage different versions of Node easily.
  • Block development: Preferably, you’ve at least built your first block and are familiar with the key concepts and files that make up block registration and rendering (editor and front end).
  • Build tools: Preferably, you’re already familiar with @wordpress/create-block and @wordpress/scripts , and you’re comfortable running commands from a Command Line Interface (CLI).

Why SwiperJS?

There are many slider libraries out there that are drop-in solutions. We want one with a history of stability, extendability, and modularity. This way, we can pull in bits and pieces of things we need and leave out what we do not want. SwiperJS has been around for a while and is very modular.

Check out the SwiperJS Demos page to see all possible variations you can create.

Setting Up

First, we need to create a block and a WordPress plugin to organize our custom functionality and the @wordpress/create-block tool is super handy for this.

Generate Our Plugin and Block Files

Create our WordPress plugin and first block with @wordpress/create-block

npx @wordpress/create-block@latest slider --namespace wpe --variant dynamic && cd slider
Code language: Bash (bash)

Let’s break down what happened for those new to the @wordpress/create-block package. Behind the scenes, @wordpress/create-block does a lot of things for us, including establishing our NPM developer dependencies, which include the helpful @wordpress/scripts package, and creating a Slider block plugin with the following organization:

  • slider.php – The main plugin file, which registers a slider block.
  • src/ – This is where the @wordpress/create-block package places our block’s files.
  • build/ – This is where the @wordpress/scripts package will compile and create our block build for us. We should not have to modify anything in the directory, as it is overwritten each time with compiled assets.

The valuable @wordpress/scripts package is worth exploring if you’re new to its capabilities. For now, know that it is a series of reusable webpack-driven scripts that can watch and compile our final block assets for us.

Final directory structure after running @wordpress/create-block.

├── build
│   ├── block.json
│   ├── index.asset.php
│   ├── index.css
│   ├── index.js
│   ├── render.php
│   ├── style-index.css
│   ├── view.asset.php
│   └── view.js
├── package.json
├── readme.txt
├── slider.php
└── src
    ├── block.json
    ├── edit.js
    ├── editor.scss
    ├── index.js
    ├── render.php
    ├── style.scss
    └── view.js
Code language: CSS (css)

Activate Plugin and Test Our Initial Progress

We’ve finished our first round of setting up and scaffolding our plugin.  Let’s activate the Slider plugin in a WordPress development environment to ensure we can insert the Slider block in the editor.

It is always good to check your progress! Of course, there is not much to see, and we have a simple block with a blue background right now. This is what @wordpress/create-block typically gives us to start.

Install Our Dependencies

We want to install our project dependencies, including swiper (for SwiperJS, of course), postcss-import, and postcss-preset-env, which allows us to import SwiperJS’s CSS when it is time.

Our one-liner for installing our dependencies and developer dependencies.

npm install --save swiper && npm install --save-dev postcss-import postcss-preset-env
Code language: Bash (bash)

Configure PostCSS

By default, when we created our WordPress plugin and the initial block with the @wordpress/create-block package it installed @wordpress/scripts as a devDependency. The @wordpress/scripts package comes with a pre-configured webpack setup, which includes PostCSS. We need to extend the existing PostCSS configuration to recognize and handle the CSS @import statements that we’ll use to roll up all of SwiperJS’s CSS with any of our own.

Be sure to create a new postcss.config.js file in the root of your plugin and place the following code within.

Our postcss.config.js file configures PostCSS to allow importing of Swiper’s CSS modules.

module.exports = () => {
	const config = {
		plugins: {
			'postcss-import': {},
			'postcss-preset-env': {},
		},
	};
	return config;
};
Code language: JavaScript (javascript)

This will come in handy later when we update the src/styles.scss.

Verifying Our Initial Plugin Setup

We should now have our WordPress plugin, a Slider block, and our dependencies installed. Great!

We’ll focus on extending our Slider block for the remainder of this tutorial. Be sure to start watching your block for changes and compiling them automatically in the background. You can do this by running npm start, which tells @wordpress/scripts to watch our block for changes and compile them if anything is changed (to the build/ directory).

Check out the final codebase Slider Block GitHub repo. Feel free to download the final plugin or fork the repo and make it all your own.

Planning Our Block Customizations

When you’re getting ready to create a custom block, planning what you’ll need and what already exists that you can leverage is ideal.

We want to allow users to add a Slider block to the editor and automatically have it populated with example slides. We will utilize WordPress’s existing Cover block to represent our slides. This gives us existing affordances like image attachment, overlay colors, and nesting of additional blocks (Columns, Buttons, Headings, and many more).

With this in mind, we’ll update our slider block’s block.json.

Extending the Slider Block

We’ll open the Slider block’s block.json file and update the overall block’s definitions.

The Slider block’ssrc/block.json file is where we extend definitions for the overall block.

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "wpe/slider",
	"title": "Slider",
	"category": "widgets",
	"description": "Display your images in a horizontal carousel.",
	"icon": "slides",
	"keywords": ["carousel", "slideshow"],
	"attributes": {
		"autoplay": {
			"type": "boolean",
			"default": true
		},
		"navigation": {
			"type": "boolean",
			"default": true
		},
		"pagination": {
			"type": "boolean",
			"default": true
		}
	},
	"example": {},
	"supports": {
		"html": false,
		"align": ["wide", "full"],
		"className": true,
		"color": {
			"background": true,
			"gradients": true,
			"link": true,
			"text": true
		},
		"spacing": {
			"padding": true,
			"margin": ["top", "bottom"]
		}
	},
	"textdomain": "wpe",
	"version": "0.1.0",
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"render": "file:./render.php",
	"style": "file:./style-index.css",
	"viewScript": "file:./view.js"
}
Code language: JSON / JSON with Comments (json)

We’re defining some new attributes for the Slider block, which we’ll use to allow users to choose whether they want certain features assigned to each Slider block. In our case, we’ll let them toggle whether the slider should autoplay and have navigation or pagination. Check out this Block API Reference if you want to dig deeper into how attributes work.

We also want our Slider block to opt into certain features that WordPress offers blocks, like alignment, colors, and spacing. This is why we’re defining supports in our block.json. Be sure to check out the complete list of Block Supports and consider extending what is here to offer additional features.

Import SwiperJS and Create a Default Object

We’re going to try to keep things organized (why not). Let’s import all of our SwiperJS dependencies and create a simple default SwiperJS object that we can utilize in both our editor (back-end) and front-end views.

Create a new src/swiper-init.js file.

We’re importing all of our Swiper JavaScript core and module dependencies and setting up a default object, which we can later use in multiple contexts.

/**
 * Swiper dependencies
 *
 * @see https://swiperjs.com/get-started
 */
import { Swiper } from 'swiper';
import { Autoplay, Keyboard, Navigation, Pagination } from 'swiper/modules';

/**
 * Initialize the slider.
 *
 * @param {Element} container HTMLElement.
 * @param {Object}  options   Slider parameters.
 *
 * @return {Object} Returns initialized slider instance.
 *
 * @see https://swiperjs.com/swiper-api#parameters
 */
export function SwiperInit( container, options = {} ) {
	const parameters = {
		autoplay: options?.autoplay ?? true,
		centeredSlides: options?.centerSlides ?? false,
		createElements: true,
		grabCursor: options?.grabCursor ?? true,
		initialSlide: 0,
		keyboard: true,
		modules: [ Autoplay, Keyboard, Navigation, Pagination ],
		navigation: options?.navigation ?? false,
		pagination: options?.pagination ?? false,
		simulateTouch: options?.simulateTouch ?? true,
	};

	return new Swiper( container, parameters );
}
Code language: JavaScript (javascript)

We’re importing Swiper and some key Swiper modules at the top of the swiper-init.js file, and then we’re exporting our SwiperInit() function, which contains some defaults for us to return a new Swiper() instance.

Import SwiperJS CSS Styling

We’ll want to pull in SwiperJS’s CSS as well, which is critical to allowing the slider to slide.

Open up the existing src/style.scss and replace it with the following.

We’re importing our Swiper CSS dependencies and applying some of our own styles in src/style.scss

@import "swiper/css";
@import "swiper/css/a11y";
@import "swiper/css/autoplay";
@import "swiper/css/navigation";
@import "swiper/css/pagination";

.wp-block-wpe-slider {
	--swiper-theme-color: var(--wp--preset--color--accent, var(--wp--preset--color--primary));
	--swiper-navigation-size: 48px;
	--swiper-navigation-color: var(--swiper-theme-color);
	--swiper-pagination-bullet-horizontal-gap: 0.25rem;
	--swiper-pagination-bullet-vertical-gap: 0.25rem;
	--swiper-pagination-bullet-size: 0.75rem;
	--swiper-pagination-bullet-opacity: 1;
	--swiper-pagination-bullet-inactive-color: black;
	--swiper-pagination-bullet-inactive-opacity: .2;
}
Code language: CSS (css)

Remember, we set up a custom postcss.config.js file earlier? This was critical to allow the @wordpress/scripts dependency, which we’re using to watch and build our final blocks, to be able to recognize and inspect dependencies from our install node_modules directory. This allows us to pull our CSS dependencies from our install swiper dependency.

Of course, this file is watched and pulled in as a dependency through the src/index.js file. Open it up, and you’ll see the existing import './style.scss'; near the top. Ultimately, this file is compiled and written to the final build/style-index.css and defined as a dependency in the Slider’s block.json file (via the "style"). This tells WordPress to load the CSS in both the editor and the front end.

Create a New constants.js File

I prefer to keep some key variables in a separate constants.js file. This allows fellow developers to quickly open it up and change key parameters for our final slider (if they choose to 😉).

Let’s create a new constants.js file and place the following code.

We’re defining key variables in a new src/constants.js file. We’ll reference these later in the src/slider.js file.

/**
 * These are the block we'll allow to be inserted
 * as a slide.
 */
export const ALLOWED_BLOCKS = [ 'core/cover' ];

/**
 * This is the default block we'll use for our slide.
 */
export const DEFAULT_BLOCK = 'core/cover';

/**
 * These are the attributes we assign for our DEFAULT_BLOCK.
 */
export const DEFAULT_BLOCK_ATTRIBUTES = {
	align: 'center',
	className: 'swiper-slide',
	contentPosition: 'bottom left',
	customOverlayColor: '#000000',
	dimRatio: 20,
	layout: {
		type: 'constrained',
	},
	style: {
		color: {
			text: '#ffffff',
		},
		elements: {
			heading: {
				color: {
					text: '#ffffff',
				},
			},
			link: {
				color: {
					text: '#ffffff',
				},
			},
		},
		spacing: {
			padding: {
				top: 'var:preset|spacing|large',
				bottom: 'var:preset|spacing|large',
				left: 'var:preset|spacing|large',
				right: 'var:preset|spacing|large',
			},
		},
	},
};

/**
 * These are the default inner blocks we'll use
 * when our DEFAULT_BLOCK is inserted.
 */
export const DEFAULT_INNERBLOCK = 'core/paragraph';

/**
 * These are the attributes we assign for our default
 * inner blocks.
 */
export const DEFAULT_INNERBLOCK_ATTRIBUTES = {
	fontSize: 'large',
	style: {
		color: {
			text: '#ffffff',
		},
	},
};

/**
 * Some default Unsplash images...
 * (feel free to replace)
 */
export const PLACEHOLDER_IMG_1 = 'https://source.unsplash.com/kdl8xDDD6iA';
export const PLACEHOLDER_IMG_2 = 'https://source.unsplash.com/cRUZICCU_Xg';
export const PLACEHOLDER_IMG_3 = 'https://source.unsplash.com/lUF3cqG6n7s';
Code language: JavaScript (javascript)

We’ll reference these in our Slider function-based component.

Are you new to WordPress block development? Perhaps, you’re a seasoned developer and need to brush up on key concepts. Check out Anatomy of a Block.

Define the Slider Edit Function

The edit.js file is where we establish all the state and logic for everything the user will see and interact with in the editor.

Add Imports

First, let’s start by adding all of our @import statements at the top. These include all of our WordPress dependencies, followed by our Slider component (we’ll create this next) and our editor.scss.

At the beginning of our src/edit.js file where we start importing all of our dependencies.

/**
 * WordPress dependencies
 */
import {
	useBlockProps,
	useBlockEditContext,
	InspectorControls,
} from '@wordpress/block-editor';
import { PanelBody, PanelRow, ToggleControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
 * Internal Dependencies
 */
import { Slider } from './slider';
// Editor styling.
import './editor.scss';
Code language: JavaScript (javascript)

Define the Main Edit Function

We’re going to define our primary Edit function next, which you can place right after all your imports in the edit.js file.

We’re defining our main Edit function, which references some components we still need to create but will soon.

/**
 * The edit function describes the structure of your block in the context of the
 * editor. This represents what the editor will render when the block is used.
 *
 * @see https://developer.wordpress.org/block-editor/developers/block-api/block-edit-save/#edit
 *
 * @param {Object}   props               Properties passed to the function.
 * @param {Object}   props.attributes    Available block attributes.
 * @param {Function} props.setAttributes Function that updates individual attributes.
 *
 * @return {Element} Element to render.
 */
export default function Edit( { attributes, setAttributes } ) {
	const { autoplay, navigation, pagination } = attributes;
	const { clientId } = useBlockEditContext();
	const blockProps = useBlockProps();

	return (
		<>
			<div { ...blockProps }>
				<Slider attributes={ attributes } clientId={ clientId } />
			</div>

			<InspectorControls>
				<PanelBody title={ __( 'Settings', 'wpe' ) }>
					<PanelRow>
						<ToggleControl
							label={ __( 'Autoplay', 'wpe' ) }
							checked={ autoplay }
							onChange={ ( value ) =>
								setAttributes( { autoplay: value } )
							}
							help={ __(
								'“Autoplay” will automatically advance the slides. Note: this is intentionally disabled in the editor, but will affect the front end.'
							) }
						/>
					</PanelRow>
					<PanelRow>
						<ToggleControl
							label={ __( 'Navigation', 'wpe' ) }
							checked={ navigation }
							onChange={ ( value ) =>
								setAttributes( { navigation: value } )
							}
							help={ __(
								'“Navigation” will display arrows so user can navigate forward/backward.'
							) }
						/>
					</PanelRow>
					<PanelRow>
						<ToggleControl
							label={ __( 'Pagination', 'wpe' ) }
							checked={ pagination }
							onChange={ ( value ) =>
								setAttributes( { pagination: value } )
							}
							help={ __(
								'“Pagination” will display dots along the bottom for user to click through slides.'
							) }
						/>
					</PanelRow>
				</PanelBody>
			</InspectorControls>
		</>
	);
}
Code language: JavaScript (javascript)

A lot is happening here; we’ll step through it piece by piece.

In our Edit function, we’re passing down our block’s attributes for navigation, pagination, and autoplay (line 14). The Edit function has a built-in function setAttributes, which allows us to update attribute values conveniently.

We’re using WordPress’s handy PanelBody, PanelRow, and ToggleControl components (near the bottom) to allow users to toggle these features on and off in the editor. Again, we imported these in the previous code snippet placed at the top of the file.

The Slider component will represent the actual markup for our SwiperJS-driven slider. Let’s get that set up next.

Custom Slider Component with useRefEffect

We’re calling the Slider component within our Edit function, which we still need to create. Create a new file called slider.js and place the following code.

Our Slider component uses memoization to cache our sliderRef, which we use to instantiate Swiper.

/**
 * WordPress dependencies
 */
import {
	store as blockEditorStore,
	ButtonBlockAppender,
	useInnerBlocksProps,
} from '@wordpress/block-editor';
import { useRefEffect } from '@wordpress/compose';
import { select, subscribe } from '@wordpress/data';
import { memo } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal Dependencies
 */
import { SwiperInit } from './swiper-init';
import {
	ALLOWED_BLOCKS,
	DEFAULT_BLOCK,
	DEFAULT_BLOCK_ATTRIBUTES,
	DEFAULT_INNERBLOCK,
	DEFAULT_INNERBLOCK_ATTRIBUTES,
	PLACEHOLDER_IMG_1,
	PLACEHOLDER_IMG_2,
	PLACEHOLDER_IMG_3,
} from './constants';

/**
 * Slider component.
 */
export const Slider = memo( ( { clientId, attributes } ) => {
	const sliderRef = useRefEffect( ( element ) => {
		const options = {
			...attributes,
			...{
				autoplay: false,
				grabCursor: false,
				simulateTouch: false,
			},
		};

		// Initialize slider.
		let slider = SwiperInit( element, options );

		// Store the current slide order to detect changes, such as adding, removing, or reordering slides.
		let slideOrder = select( blockEditorStore ).getBlockOrder( clientId );

		// Subscribe slider update events like adding, removing, or reordering slides.
		const unsubscribeSliderUpdateListener = subscribe( () => {
			const currentSlidesOrder =
				select( blockEditorStore ).getBlockOrder( clientId );

			// Check if the slider has been changed.
			if ( currentSlidesOrder.toString() !== slideOrder.toString() ) {
				const selectedBlock =
					select( blockEditorStore ).getSelectedBlock();
				const slideAdded =
					currentSlidesOrder.length > slideOrder.length;
				const slideRemoved =
					currentSlidesOrder.length < slideOrder.length;
				const slideMoved =
					currentSlidesOrder.length === slideOrder.length;
				const activeIndex = slider.activeIndex;

				// Store the current slide order before destroying the slider instance.
				slideOrder = currentSlidesOrder;
				slider.destroy();

				window.requestAnimationFrame( () => {
					// Initialize slider.
					slider = SwiperInit( element, options );

					// Determine where the slider should go.
					let slideToIndex = activeIndex;
					if ( slideAdded ) {
						slideToIndex = slideOrder.length;
					} else if ( slideRemoved ) {
						slideToIndex = activeIndex - 1;
					} else if ( slideMoved ) {
						slideToIndex = slideOrder.findIndex(
							( clientId ) => clientId === selectedBlock.clientId // eslint-disable-line no-shadow
						);
					}

					if ( slideToIndex < 0 ) {
						slideToIndex = 0;
					}

					slider.slideTo( slideToIndex, 0 );
				} );
			}
		} );

		return () => {
			unsubscribeSliderUpdateListener();
			slider.destroy();
		};
	} );

	// Our nested innerblocks that will be inserted by default.
	const innerBlocksProps = useInnerBlocksProps(
		{ className: 'swiper-wrapper' },
		{
			allowedBlocks: ALLOWED_BLOCKS,
			defaultBlock: {
				name: DEFAULT_BLOCK,
				attributes: {
					url: `${ PLACEHOLDER_IMG_3 }`,
					...DEFAULT_BLOCK_ATTRIBUTES,
				},
			},
			directInsert: true,
			orientation: 'horizontal',
			template: [
				[
					DEFAULT_BLOCK,
					{
						url: `${ PLACEHOLDER_IMG_1 }`,
						...DEFAULT_BLOCK_ATTRIBUTES,
					},
					[
						[
							DEFAULT_INNERBLOCK,
							{
								placeholder: __( 'Slide title…', 'wpe' ),
								...DEFAULT_INNERBLOCK_ATTRIBUTES,
							},
						],
					],
				],
				[
					DEFAULT_BLOCK,
					{
						url: `${ PLACEHOLDER_IMG_2 }`,
						...DEFAULT_BLOCK_ATTRIBUTES,
					},
					[
						[
							DEFAULT_INNERBLOCK,
							{
								placeholder: __( 'Slide title…', 'wpe' ),
								...DEFAULT_INNERBLOCK_ATTRIBUTES,
							},
						],
					],
				],
			],
			renderAppender: false,
			templateInsertUpdatesSelection: true,
		}
	);

	return (
		<>
			<div className="swiper" ref={ sliderRef }>
				<div { ...innerBlocksProps } />
			</div>

			<ButtonBlockAppender
				className="slider-appender has-icon"
				rootClientId={ clientId }
			/>
		</>
	);
} );
Code language: JavaScript (javascript)

This is a rather large and complex component that encompasses a lot of functionality. So, let’s break it down.

  • Line 5 – We’re utilizing the useRefEffect dependency from the @wordpress/compose package. This is similar to React’s useEffect, but it allows for a callback when a dependency changes.
  • Line 44 – We grab our SwiperJS default object and initialize our slider.
  • Line 21-66 – We’re utilizing @wordpress/data package’s subscribe() listener function to watch for state changes and merge the state with our newly instantiated SwiperJS element. This allows us to tell SwiperJS when new slides are shifted around.
  • Line 95-98 – We’re performing some cleanup for our SwiperJS instance.
  • Line 102-152 – This is where we pass in all of our inner block definitions, which will automatically populate the slider when it is inserted with some core blocks.
  • Finally, we return our components. We have two main components we’re adding here:
    • Our Swiper element, which we assign our ref to and pass down the innerBlockProps with our block template.
    • ButtonBlockAppender – This is another useful component of the @wordpress/block-editor package, which allows us to append a button for users to click and add a slide along the bottom of the Slider.

Creating block template arrays can be tricky. It is usually best to block it all out in the editor, switch to code view, copy and paste the necessary code markup, and utilize the handy WPHTML Converter to convert your markup into JavaScript or PHP.

Define the Slider save Function

We need to establish our saving logic for our overall slider block. Remember, we have set the Edit logic, but we still need to ensure our block’s final markup and properties are saved in the post_content for retrieval and further editing.

We can accomplish this by adding save.js to our slider block and adding the following code.

We need to add our save function to save our final slider block markup to post_content. Create a new file: src/save.js

/**
 * WordPress dependencies
 */
import { InnerBlocks } from '@wordpress/block-editor';

/**
 * The save function defines the way in which the different attributes should
 * be combined into the final markup, which is then serialized by the block
 * editor into `post_content`.
 *
 * @see https://developer.wordpress.org/block-editor/developers/block-api/block-edit-save/#save
 *
 * @return {Element} Element to render.
 */
export default function Save() {
	return <InnerBlocks.Content />;
}
Code language: JavaScript (javascript)

We’re leveraging WordPress’s InnerBlocks component to return our saved slider content. Then we have to make sure we import our saving file within our block’s entry point: src/index.js

Modify the existing src/index.js to import our save function and return it within the registerBlockType() handler.

/**
 * Registers a new block provided a unique name and an object defining its behavior.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
 */
import { registerBlockType } from '@wordpress/blocks';

/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * All files containing `style` keyword are bundled together. The code used
 * gets applied both to the front of your site and to the editor.
 *
 * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
 */
import './style.scss';

/**
 * Internal dependencies
 */
import Edit from './edit';
import save from './save';
import metadata from './block.json';

/**
 * Every block starts by registering a new block type definition.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
 */
registerBlockType( metadata.name, {
	/**
	 * @see ./edit.js
	 */
	edit: Edit,

	/**
	 * @see ./save.js
	 */
	save,
} );

Code language: JavaScript (javascript)

Define the render.php Display Logic

We must establish our final display logic for the block, which visitors will see for the end product. This is all handled within the block’s render.php.

The src/render.php file is where we handle our final display logic.

<?php
/**
 * Slider block
 *
 * @var array     $attributes Block attributes.
 * @var string    $content    Block default content.
 * @var \WP_Block $block      Block instance.
 */

$autoplay   = empty( $attributes['autoplay'] ) ? false : $attributes['autoplay'];
$navigation = empty( $attributes['navigation'] ) ? false : $attributes['navigation'];
$pagination = empty( $attributes['pagination'] ) ? false : $attributes['pagination'];

$swiper_attr = array(
	'autoplay'   => $autoplay,
	'navigation' => $navigation,
	'pagination' => $pagination,
);
$swiper_attr = htmlspecialchars( wp_json_encode( $swiper_attr ) );

$wrapper_attributes = get_block_wrapper_attributes(
	array(
		'class' => 'swiper',
	)
);
?>

<div <?php echo wp_kses_data( $wrapper_attributes ) . 'data-swiper="' . esc_attr( $swiper_attr ) . '"'; ?>>

	<div class="swiper-wrapper">
		<?php echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
	</div>

</div><!-- .swiper -->
Code language: PHP (php)

In the code above, we’re checking for our $autoplay, $navigation, $pagination settings and assigning them as data attributes to our final <div> . It is crucial to merge these properties with get_block_wrapper_attributes(), which allows us to pass through the items we added to our slider block.json "supports" definition. If a user wants to alter the alignment or colors of the block, then this is how WordPress will be aware of these final attributes.

Update view.js Front End JavaScript

We will import our handy ./swiper-init again and reuse this within our front-end instantiation code. Open up the src/view.js file and replace it with the following code.

Within the src/view.js is where the front-end JavaScript for our block is established and enqueued within WordPress’s dependency queue.

/**
 * Shared Swiper config.
 */
import { SwiperInit } from './swiper-init';

document.addEventListener( 'DOMContentLoaded', () => {
	const containers = document.querySelectorAll( '.swiper' );

	// Return early, and often.
	if ( ! containers.length ) {
		return;
	}

	// Loop through all sliders and assign Swiper object.
	containers.forEach( ( element ) => {
		// We could pass in some unique options here.
		let options = {};

		try {
			options = JSON.parse( element.dataset.swiper );
		} catch ( e ) {
			// eslint-disable-next-line no-console
			console.error( e );
			return;
		}

		// Slider 🚀
		SwiperInit( element, options );
	} );
} );
Code language: JavaScript (javascript)

Final Build & Next Steps

If you had npm start running in the background the whole time then you should be in an excellent place to test things out. Otherwise, feel free to run npm run build to create a final build of your blocks, open up a new post, add a slider block, and test everything out.

Remember that SwiperJS has a lot of bells and whistles, and we’ve created an opinionated version of a slider. You could keep going further and consider extending things, like:

Check out the final codebase Slider Block GitHub repo. Feel free to download the final plugin or fork the repo and make it all your own.

Conclusion

I hope you found this tutorial helpful and easy to follow. It was quite the journey, and we covered a lot of ground. Please let me know if you hit any hurdles along the way or if you went even further with your Slider block.

Don’t be shy; reach out on Twitter if you want to share or have any questions.