What you asked for is not possible for blocks that rely on frontend javascript, this includes embeds.
It's also not possible using the Swiper react library. The Swiper library made the questionable choice of manually inspecting child components for the SwiperSlide
component name, preventing it from ever being used with child blocks.
Edit: Actually maybe it is! Since writing this, a PR was merged that may fix this and allow the code below to work as desired
It isn't possible using jQuery either with inner blocks as it conflicts with the way React works, and will cause virtual DOM problems.
The Closest You Can Get
Instead, if we use the React Swiper library, useBlockProps
, and useInnerBlocksProps
, we can implement this, and have editable slides, while keeping the slider.
However, Swiper will not see the specific arrangement of components it needs and will place them after the slider container instead of inside the wrapper.
The important thing to note, is this snippet:
const blockProps = useBlockProps();
const innerBlocksProps = useInnerBlocksProps( blockProps, {
allowedBlocks: [ 'tomjn/swiper-slide' ],
template: TEMPLATE,
});
const params = {...};
return (
<div { ...innerBlocksProps }>
<Swiper { ...params }>
{ innerBlocksProps.children }
</Swiper>
</div>
);
By using innerBlocksProps.children
we eliminate the 2 containing <div>
tags.
Here's my demo:
src/index.js
:
import { registerBlockType } from '@wordpress/blocks';
import {
InnerBlocks,
useBlockProps,
__experimentalUseInnerBlocksProps as useInnerBlocksProps
} from '@wordpress/block-editor';
import SwiperCore, { Navigation, Pagination, Scrollbar, A11y } from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/react';
// Import Swiper styles
import 'swiper/swiper-bundle.css';
// install Swiper components
SwiperCore.use([Navigation, Pagination, Scrollbar, A11y]);
const TEMPLATE = [
[ 'tomjn/swiper-slide', {}, [
[ 'core/paragraph', { placeholder: 'Enter slide content...' } ],
] ],
[ 'tomjn/swiper-slide', {}, [
[ 'core/paragraph', { placeholder: 'Enter slide content...' } ],
] ]
];
registerBlockType( 'tomjn/swiper', {
apiVersion: 2,
title: 'Swiper',
category: 'layout',
edit: () => {
const blockProps = useBlockProps();
const innerBlocksProps = useInnerBlocksProps( blockProps, {
allowedBlocks: [ 'tomjn/swiper-slide' ],
template: TEMPLATE,
});
const params = {
init: true,
paceBetween: 50,
slidesPerView: 2,
loop: true,
navigation: {
clickable: true,
},
spaceBetween: 30,
pagination: {
clickable: true,
}
};
return (
<div { ...innerBlocksProps }>
<Swiper { ...params }>
{ innerBlocksProps.children }
</Swiper>
</div>
);
},
save: () => {
return (
<div className="swiper-container">
<div className="swiper-wrapper">
<InnerBlocks.Content />
</div>
<div className="swiper-pagination"></div>
<div className="swiper-button-prev"></div>
<div className="swiper-button-next"></div>
</div>
);
},
} );
registerBlockType( 'tomjn/swiper-slide', {
apiVersion: 2,
title: 'Swiper Slide',
category: 'layout',
edit: () => {
const blockProps = useBlockProps();
return (
<SwiperSlide>
<div {...blockProps}>
<InnerBlocks />
</div>
</SwiperSlide>
);
},
save: () => {
return (
<div class="swiper-slide">
<InnerBlocks.Content />
</div>
);
},
} );
package.json
:
{
"dependencies": {
"swiper": "^6.4.5"
},
"devDependencies": {
"@wordpress/scripts": "^12.6.1"
},
"scripts": {
"build": "wp-scripts build",
"check-engines": "wp-scripts check-engines",
"check-licenses": "wp-scripts check-licenses",
"format:js": "wp-scripts format-js",
"lint:css": "wp-scripts lint-style",
"lint:js": "wp-scripts lint-js",
"lint:md:docs": "wp-scripts lint-md-docs",
"lint:md:js": "wp-scripts lint-md-js",
"lint:pkg-json": "wp-scripts lint-pkg-json",
"packages-update": "wp-scripts packages-update",
"start": "wp-scripts start",
"test:e2e": "wp-scripts test-e2e",
"test:unit": "wp-scripts test-unit-js"
}
}
plugin.php
:
<?php
/**
* Plugin Name: Toms Swiper Plugin
* Plugin URI: https://tomjn.com
* Description: Swiper no swiping.
* Requires at least: 5.5
* Requires PHP: 7.3
* Version: 1.0.0
* Author: Tom J Nowell
*/
defined( 'ABSPATH' ) || exit;
add_action(
'enqueue_block_editor_assets',
function() {
$asset = include plugin_dir_path( __FILE__ ) . 'build/index.asset.php';
wp_register_script(
'tomjn-swiper-block-js',
plugins_url( 'build/index.js', __FILE__ ),
$asset['dependencies'],
$asset['version'],
);
wp_register_style(
'tomjn-swiper-block-css',
plugins_url( 'build/index.css', __FILE__ ),
[],
$asset['version'],
);
}
);
/**
* Registers all block assets so that they can be enqueued through Gutenberg in
* the corresponding context.
*/
function tomjn_swiper_register_block() {
if ( ! function_exists( 'register_block_type' ) ) {
// Gutenberg is not active.
return;
}
register_block_type(
'tomjn/swiper',
[
'editor_script' => 'tomjn-swiper-block-js',
'editor_style' => 'tomjn-swiper-block-css',
]
);
register_block_type(
'tomjn/swiperslide',
[
'editor_script' => 'tomjn-swiper-block-js',
'editor_style' => 'tomjn-swiper-block-css',
]
);
}
add_action( 'init', 'tomjn_swiper_register_block' );
<ServerSideRender>
in edit, or would that be the only way?<Swiper>
component, then wrap the children in<SwiperSlide>
components. You can do this easily with a Swiper block and a Swiper Slide block, much like how core has a columns and a column block. Do that and your problem and all the workarounds and hacks disappear into thin air, it even provides a nice way to add new slides as you can make sure an inserter is always showing, and your inner blocks are always editable live<Swiper><InnerBlocks></Swiper>
where all inner blocks are wrapped in<SwiperSlide>
, the innerblocks add two wrapper divs which make swiper unusable. Also I tried the newuseInnerBlocksProps
hook, to get rid of the wrapper divs however then the parent<Swiper>
does not recognize itSwiperSlides
. For me it ends up in trying it the way I am asking for above.