Your cart is currently empty!

Develop a Post Carousel Block using Swiper.js
Here’s how to develop a post carousel block for the Twenty Twenty-Four theme.
We’ll create a custom block that uses the Swiper.js library for the carousel functionality.
1. Create a Child Theme (Recommended)
It’s always best to create a child theme when making modifications so your changes aren’t overwritten when the parent theme is updated.
- Create a new folder in
wp-content/themes
calledtwentytwentyfour-child
. - Inside this folder, create two files:
style.css
andfunctions.php
.style.css
: CSS/* Theme Name: Twenty Twenty-Four Child Theme URI: https://example.com/twentytwentyfour-child/ Description: My custom child theme for Twenty Twenty-Four Author: Your Name Author URI: https://example.com Template: twentytwentyfour Version: 1.0.0 License: GNU General Public License v2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Text Domain: twentytwentyfour-child */
functions.php
: PHP<?php add_action( 'wp_enqueue_scripts', 'twentytwentyfour_child_enqueue_styles' ); function twentytwentyfour_child_enqueue_styles() { wp_enqueue_style( 'twentytwentyfour-parent-style', get_template_directory_uri() . '/style.css' ); wp_enqueue_style( 'twentytwentyfour-child-style', get_stylesheet_directory_uri() . '/style.css', array('twentytwentyfour-parent-style'), wp_get_theme()->get('Version') ); } ?>
- Activate the child theme from your WordPress admin dashboard (
Appearance > Themes
).
2. Register the Custom Block
Now, we’ll create the necessary files for our custom block.
- Inside your child theme folder (
twentytwentyfour-child
), create a new folder calledblocks
. - Inside the
blocks
folder, create another folder calledpost-carousel
. - Inside
post-carousel
, create the following files:block.json
(Block metadata)index.js
(Block JavaScript for editor)editor.scss
(Editor-specific styles)style.scss
(Frontend and editor styles)render.php
(Frontend rendering)
twentytwentyfour-child/blocks/post-carousel/block.json
JSON
{
"name": "twentytwentyfour-child/post-carousel",
"title": "Post Carousel",
"description": "A carousel of your latest posts.",
"category": "widgets",
"icon": "images-alt2",
"apiVersion": 2,
"keywords": ["posts", "carousel", "slider"],
"supports": {
"html": false,
"align": ["wide", "full"]
},
"attributes": {
"postsToShow": {
"type": "number",
"default": 3
},
"category": {
"type": "string"
}
},
"editorScript": "file:./index.js",
"editorStyle": "file:./editor.css",
"style": "file:./style.css",
"render": "file:./render.php"
}
twentytwentyfour-child/blocks/post-carousel/index.js
This file will contain the JavaScript for the block in the editor. We’ll use @wordpress/block-editor
, @wordpress/components
, and @wordpress/data
to build our controls.
JavaScript
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, QueryControls, SelectControl, RangeControl } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { html } from '@wordpress/element';
// Import Swiper React components
import { Swiper, SwiperSlide } from 'swiper/react';
import { Navigation, Pagination } from 'swiper/modules';
// Import Swiper styles
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
registerBlockType('twentytwentyfour-child/post-carousel', {
edit: ({ attributes, setAttributes }) => {
const { postsToShow, category } = attributes;
const blockProps = useBlockProps();
const categories = useSelect((select) => {
const terms = select('core').getEntityRecords('taxonomy', 'category', { per_page: -1 });
return terms ? terms.map((term) => ({ label: term.name, value: term.id })) : [];
}, []);
const posts = useSelect(
(select) => {
const query = {
per_page: postsToShow,
_embed: true,
};
if (category) {
query.categories = category;
}
return select('core').getEntityRecords('postType', 'post', query);
},
[postsToShow, category]
);
return (
<div {...blockProps}>
<InspectorControls>
<PanelBody title="Carousel Settings">
<RangeControl
label="Number of posts"
value={postsToShow}
onChange={(value) => setAttributes({ postsToShow: value })}
min={1}
max={10}
/>
{categories && categories.length > 0 && (
<SelectControl
label="Filter by Category"
value={category}
options={[{ label: 'All Categories', value: '' }, ...categories]}
onChange={(value) => setAttributes({ category: value })}
/>
)}
</PanelBody>
</InspectorControls>
<div className="wp-block-twentytwentyfour-child-post-carousel__editor-preview">
{posts && posts.length > 0 ? (
<Swiper
modules={[Navigation, Pagination]}
spaceBetween={30}
slidesPerView={1}
navigation
pagination={{ clickable: true }}
breakpoints={{
768: {
slidesPerView: 2,
},
1024: {
slidesPerView: 3,
},
}}
>
{posts.map((post) => (
<SwiperSlide key={post.id}>
<div className="post-carousel-item">
{post._embedded['wp:featuredmedia'] &&
post._embedded['wp:featuredmedia'][0] && (
<img
src={post._embedded['wp:featuredmedia'][0].source_url}
alt={post._embedded['wp:featuredmedia'][0].alt_text || post.title.rendered}
/>
)}
<h3>{post.title.rendered}</h3>
{post.excerpt.rendered && (
<div dangerouslySetInnerHTML={{ __html: post.excerpt.rendered }} />
)}
<a href={post.link} target="_blank" rel="noopener noreferrer">
Read More
</a>
</div>
</SwiperSlide>
))}
</Swiper>
) : (
<p>No posts found.</p>
)}
</div>
</div>
);
},
save: () => {
return null; // Rendered via PHP
},
});
Note on index.js
:
- This uses JSX, so you’ll need a build process to compile it (e.g.,
@wordpress/scripts
). We’ll set that up next. - We’re importing Swiper directly here for the editor preview. The frontend will load Swiper separately.
twentytwentyfour-child/blocks/post-carousel/editor.scss
SCSS
.wp-block-twentytwentyfour-child-post-carousel {
border: 1px dashed #ccc;
padding: 15px;
.wp-block-twentytwentyfour-child-post-carousel__editor-preview {
.post-carousel-item {
text-align: center;
img {
max-width: 100%;
height: auto;
display: block;
margin-bottom: 10px;
}
h3 {
font-size: 1.2em;
margin-bottom: 5px;
}
p {
font-size: 0.9em;
color: #555;
}
a {
display: inline-block;
margin-top: 10px;
padding: 5px 10px;
background-color: #007cba;
color: #fff;
text-decoration: none;
border-radius: 3px;
}
}
}
}
twentytwentyfour-child/blocks/post-carousel/style.scss
SCSS
.wp-block-twentytwentyfour-child-post-carousel {
.swiper {
width: 100%;
height: auto;
}
.swiper-slide {
display: flex;
justify-content: center;
align-items: center;
.post-carousel-item {
text-align: center;
padding: 20px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
background-color: #fff;
img {
max-width: 100%;
height: auto;
display: block;
margin: 0 auto 15px auto;
object-fit: cover; // Ensure images cover the area
aspect-ratio: 16/9; // Maintain aspect ratio for images
}
h3 {
font-size: 1.5em;
margin-bottom: 10px;
color: #333;
}
p {
font-size: 1em;
line-height: 1.6;
color: #666;
}
a {
display: inline-block;
margin-top: 15px;
padding: 8px 15px;
background-color: #007cba;
color: #fff;
text-decoration: none;
border-radius: 4px;
transition: background-color 0.3s ease;
&:hover {
background-color: #005f8f;
}
}
}
}
.swiper-button-next,
.swiper-button-prev {
color: #007cba;
}
.swiper-pagination-bullet-active {
background: #007cba;
}
}
twentytwentyfour-child/blocks/post-carousel/render.php
This file handles the frontend rendering of the block.
PHP
<?php
/**
* Server-side rendering of the post carousel block.
*
* @package TwentyTwentyFourChild
*/
$args = array(
'posts_per_page' => $attributes['postsToShow'],
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
);
if ( ! empty( $attributes['category'] ) ) {
$args['cat'] = (int) $attributes['category'];
}
$posts = new WP_Query( $args );
if ( $posts->have_posts() ) :
?>
<div <?php echo get_block_wrapper_attributes(); ?>>
<div class="swiper post-carousel-swiper">
<div class="swiper-wrapper">
<?php
while ( $posts->have_posts() ) :
$posts->the_post();
?>
<div class="swiper-slide">
<div class="post-carousel-item">
<?php if ( has_post_thumbnail() ) : ?>
<img src="<?php echo esc_url( get_the_post_thumbnail_url( get_the_ID(), 'medium' ) ); ?>" alt="<?php echo esc_attr( get_the_title() ); ?>">
<?php endif; ?>
<h3><?php the_title(); ?></h3>
<div class="post-carousel-excerpt">
<?php the_excerpt(); ?>
</div>
<a href="<?php the_permalink(); ?>" class="post-carousel-read-more">Read More</a>
</div>
</div>
<?php
endwhile;
?>
</div>
<div class="swiper-pagination"></div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
</div>
<?php
wp_reset_postdata();
else :
?>
<p <?php echo get_block_wrapper_attributes(); ?>>No posts found.</p>
<?php
endif;
3. Set up a Build Process for the Block
Gutenberg blocks typically require a build process (like webpack via @wordpress/scripts
) to compile JSX, SCSS, and other modern JavaScript features.
- Navigate to your child theme’s root directory (
twentytwentyfour-child
) in your terminal. - Initialize npm: Bash
npm init -y
- Install
@wordpress/scripts
and Swiper: Bashnpm install @wordpress/scripts swiper
- Add build scripts to your
package.json
: Openpackage.json
and add these lines under thescripts
section: JSON"scripts": { "build": "wp-scripts build --entry-point=./blocks/post-carousel/index.js --output-path=./blocks/post-carousel/ --output-css=./blocks/post-carousel/style.css --output-editor-css=./blocks/post-carousel/editor.css", "start": "wp-scripts start --entry-point=./blocks/post-carousel/index.js --output-path=./blocks/post-carousel/ --output-css=./blocks/post-carousel/style.css --output-editor-css=./blocks/post-carousel/editor.css" },
build
: Creates production-ready minified files.start
: Watches for changes and rebuilds automatically (useful during development).
- Run the build command: Bash
npm run build
This will createindex.js
,editor.css
, andstyle.css
(and their minified versions) inside yourpost-carousel
block folder.
4. Enqueue Swiper on the Frontend
We need to enqueue Swiper’s CSS and JS on the frontend only when our block is present.
- Open
functions.php
in your child theme. - Add the following code to enqueue Swiper and initialize the carousel: PHP
<?php add_action( 'wp_enqueue_scripts', 'twentytwentyfour_child_enqueue_styles' ); function twentytwentyfour_child_enqueue_styles() { wp_enqueue_style( 'twentytwentyfour-parent-style', get_template_directory_uri() . '/style.css' ); wp_enqueue_style( 'twentytwentyfour-child-style', get_stylesheet_directory_uri() . '/style.css', array('twentytwentyfour-parent-style'), wp_get_theme()->get('Version') ); } add_action( 'init', 'twentytwentyfour_child_register_blocks' ); function twentytwentyfour_child_register_blocks() { register_block_type( get_stylesheet_directory() . '/blocks/post-carousel' ); // Enqueue Swiper on the frontend conditionally add_action( 'wp_enqueue_scripts', 'twentytwentyfour_child_enqueue_swiper_frontend' ); } function twentytwentyfour_child_enqueue_swiper_frontend() { if ( has_block( 'twentytwentyfour-child/post-carousel' ) ) { // Swiper CSS wp_enqueue_style( 'swiper-css', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css', array(), '11' ); // Swiper JS wp_enqueue_script( 'swiper-js', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js', array(), '11', true // In footer ); // Your custom script to initialize Swiper wp_enqueue_script( 'twentytwentyfour-child-post-carousel-init', get_stylesheet_directory_uri() . '/js/post-carousel-init.js', array( 'swiper-js' ), wp_get_theme()->get('Version'), true // In footer ); } }
5. Create the Swiper Initialization Script
Create a js
folder in your child theme’s root, and inside it, create post-carousel-init.js
.
twentytwentyfour-child/js/post-carousel-init.js
JavaScript
document.addEventListener('DOMContentLoaded', function() {
// Initialize Swiper on all post carousel blocks
const carousels = document.querySelectorAll('.wp-block-twentytwentyfour-child-post-carousel .post-carousel-swiper');
carousels.forEach(carousel => {
new Swiper(carousel, {
modules: [Swiper.Navigation, Swiper.Pagination],
loop: true,
spaceBetween: 30,
slidesPerView: 1,
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
pagination: {
el: '.swiper-pagination',
clickable: true,
},
breakpoints: {
768: {
slidesPerView: 2,
spaceBetween: 30,
},
1024: {
slidesPerView: 3,
spaceBetween: 30,
},
},
});
});
});
Summary of File Structure:
twentytwentyfour-child/
├── style.css
├── functions.php
├── js/
│ └── post-carousel-init.js
├── blocks/
│ └── post-carousel/
│ ├── block.json
│ ├── index.js (Generated by npm build)
│ ├── index.asset.php (Generated by npm build)
│ ├── editor.css (Generated by npm build)
│ ├── style.css (Generated by npm build)
│ └── render.php
├── node_modules/ (Created by npm install)
└── package.json (Created by npm init)
How to Use:
- Activate your
Twenty Twenty-Four Child
theme. - Make sure you have some posts published (with featured images for best results).
- Run
npm run build
in yourtwentytwentyfour-child
directory to compile the block’s assets. - Edit a page or post in your WordPress admin.
- Add a new block and search for “Post Carousel”.
- Insert the block. You should see a preview of your posts in a carousel format.
- Use the block settings in the sidebar to adjust the number of posts and filter by category.
- Save/Update the page/post and view it on the frontend to see the working carousel.
This comprehensive guide should get you a fully functional post carousel block for the Twenty Twenty-Four theme!
Leave a Reply