Turn a WordPress gallery block into a slider using slick.js
The default WordPress gallery block provides a simple and easy way to add a collection of images into a post or page on your site using the Gutenberg block editor.
However there are limited options in terms of its layout and like me you may not like the predefined break points in which the gallery images stack on mobile, if for example your gallery has a 3 column layout.
You may also wish to display your images as a slider (as this post is about) and there are no doubt many plugins already out there to achieve this that will add a new block to the editor. Instead we can simply harness the ease of having a WordPress core block and simply modifying its output to get the desired effect.
What you will learn
In order to turn the gallery into a slider we will use a JavaScript/jQuery plugin called slick.js. Essentially any slider plugin/library that can take some HTML mark-up and call some JavaScript initialisation/settings onto it could be used and this tutorial will show you how to parse blocks to find a gallery block and if you wanted to use a different slider you can do so. For the purposes of this tutorial we will use slick.js as it offers many features and WordPress already ships with jQuery.
As mentioned above you will learn how to use the parse_blocks
function to search through the posts content and from which find a gallery block (if present) and find the IDs to be used for loading the images into the HTML mark-up.
This functionality will be made into a custom shortcode, which can therefore be inserted anywhere into a post, so you can position the gallery slider anywhere on the page (within the post content). We will also see how to remove the default mark-up the core gallery block renders to the page (as of course it wouldn’t make sense to still load/show the default gallery!).
Lastly we will look at extending the shortcode to provide some custom attributes that will enable you to have multiple galleries in a post and a shortcode that matches the ID of each gallery so that it will load each one correctly.
Enqueue slick slider assets
Before we look at how retrieve the block content, let’s look at how to load the necessary slick.js slider JavaScript and CSS assets into your site. We will use the wp_enqueue_script
and wp_enqueue_style
functions and hook into the wp_enqueue_scripts
WordPress action.
The following functionality can be added to either the functions.php
file or via a custom plugin. For simplicity we will use the functions.php file for this tutorial (to use the code as a plugin see the summary and plugin code section).
When adding code to the functions.php of your theme it is strongly recommended to use a child theme, to ensure that your custom functionality is not lost when your theme is updated (if the theme you are using is not a custom one). See the child theme link to learn how to create one.
You can download the files from the slick.js website and upload them to your site (preferably to your child theme in a folder named assets or something similar) via FTP, control panel or whichever means your hosting allows. The files needed will be slick.css
, slick-theme.css
, and slick.min.js
. NOTE: Within the folder names assets (or whatever you named it) then within the CSS folder you will also need to create a folder called fonts and to place inside this, the contents found in the fonts folder of your downloaded version of slick slider as well as ajax-loader.gif.
With that in mind the following snippet shows how to load these files via the wp_enqueue_scripts
hook.
function load_slick_slider_assets() {
wp_enqueue_script( "slick-min", get_stylesheet_directory_uri() . '/assets/js/slick.min.js', array( 'jquery' ), '1.8.0', true );
wp_enqueue_style( "slick", get_stylesheet_directory_uri() . '/assets/css/slick.css' );
wp_enqueue_style( "slick-theme", get_template_directory_uri() . '/assets/css/slick-theme.css' );
}
add_action( 'wp_enqueue_scripts', 'load_slick_slider_assets' );
This is fairly straightforward if you’ve ever loaded assets into WordPress before. wp_enqueue_script
takes the unique handle “slick-min”, we then use get_stylesheet_directory_uri
in order to locate the files within the current theme (even when a child theme is used) and pass in the where the files are stored, in this case: /assets/js/slick.min.js
. The third parameter is for dependencies, therefore any scripts that need to load before our scripts, will be loaded first (in this case we reference jquery, which comes pre-installed with WordPress and is needed for using slick slider). The fourth parameter is the version number of the script (this depends on what version of slick slider you have downloaded). Lastly we specify “true” in order to load the script into the footer rather than the header. wp_enqueue_style
is very similar, except in this case we don’t need to specify any dependencies or version and it will be loaded into the header by default.
We can take the enqueuing function one step further and add some conditional checks, so that the assets only load on pages where they are needed (if you know they will only be needed in certain areas, or you can add multiple conditionals). This might look something like the following:
function load_slick_slider_assets() {
if ( is_single() && 'project' == get_post_type() ) {
wp_enqueue_script( "slick-min", get_template_directory_uri() . '/assets/js/slick.min.js', array( 'jquery' ), '1.8.0', true );
wp_enqueue_style( "slick", get_template_directory_uri() . '/assets/css/slick.css' );
wp_enqueue_style( "slick-theme", get_template_directory_uri() . '/assets/css/slick-theme.css' );
}
}
add_action( 'wp_enqueue_scripts', 'load_slick_slider_assets' );
The above code is using the conditional tag is_single()
and also checking if the current post type is equal to project. My website has a custom post type called projects and I only need to load the assets on the single page of a project, as this is the only place the sliders are used on my site. To see other conditional tags available in WordPress, see the conditional tags page in the developer handbook.
Parse blocks and creating the custom shortcode
Next we will look at how we can parse through the blocks from the post content in order to firstly see if a gallery block is present and then do something with its data. For this to work you will of course need to have a gallery block inserted into the page/post. We will create a function that ultimately returns some HTML mark-up with data from the images in the gallery such as the src and alt attributes for the slider to display the images. Click here to skip down to the completed function
This function could just return this data and you then call it in your single.php, page.php or whichever template you want. The issue with this is the gallery slider will always be in the same location depending where function is called.
Therefore we will turn the function into a custom shortcode, which can be added anywhere onto the page and in-between different pieces of content which is much more useful, as you might want the slider to appear in different places on different pages. You will ultimately have to add both a gallery and an accompanying shortcode block in the same post but it will give greater flexibility.
The following sections of code can be added to your functions.php
or as a custom plugin (see summary and plugin code).
Firstly we will obtain the global $post
variable to know which post/page we are working with and perform a check to see whether the post_content
actually contains a gallery block. We then call ob_start()
which is a PHP function which will allow us to store whatever code is below into a buffer and not output it until we return it at the end of the function (as shortcodes have to return a value and not echo it). This also allows us to easily go in and out of PHP, mixing in HTML for a more readable syntax.
function gutenberg_gallery_slick_slider_shortcode() {
global $post;
if ( has_block( 'gallery', $post->post_content ) ) :
ob_start(); ?>
The after PHP has been closed ?>
we add some basic HTML structure. This part you can use whatever syntax you want, whether you can a H2 in there or whatever class names you want, it is just a wrapper around which the slick slider will be called (in this case the slider will be initialised on the class “gallery-img”).
<section id="project-gallery">
<h2>Project Gallery</h2>
<div class="gallery-img">
Next what we need to use the parse_blocks
function and then loop through the blocks in the post and if the block matches the core/gallery
block we will extract some data from it to place inside an HTML <img> tag and caption.
<?php
$post_blocks = parse_blocks( $post->post_content );
foreach( $post_blocks as $block ) {
if( $block['blockName'] == 'core/gallery' ) {
$ids = $block['attrs']['ids'];
foreach( $ids as $id ) {
$image = wp_get_attachment_image_src( $id, 'full' );
$image_alt = get_post_meta( $id, '_wp_attachment_image_alt', true );
$image_caption = wp_get_attachment_caption( $id );
echo '<div class="item"><img data-lazy="'. $image[0] .'" data-src="'. $image[0] .'" alt="'. $image_alt .'"><div class="img-caption"><h3>' . $image_caption . '</h3></div></div>';
}
}
}
?>
As you can see in the above code the core gallery block contains a multidimensional array by which the ids array is stored with the attrs array of the block. We can do a foreach
loop of these ids and use them to get some information about each image, such as the src, alt tag and caption. These details are then placed inside a div of class item and then inside an <img> tag with the appropriate values for src alt.
data-lazy
is a slick slider setting for lazy-loading the images (see slider configuration). wp_get_attachment_image_src
takes the ID of the image and the second parameter is the image size to display (your theme may have different sizes to choose from). The image caption is not necessary (it is used on this website) but shows that there is multiple meta/attachment data you can retrieve from the gallery images.
Lastly we close off the HTML structure and return ob_get_clean()
which is a PHP function which will get the current buffer contents (stored from when we called ob_start()
earlier) in order to return the actual HTML content that is rendered by the shortcode. The final call is to add_shortcode
, the first parameter being the name of the shortcode, when it is inserted into the editor and the second parameter being the name of the custom function we created to parse the blocks and return the retrieved data.
</div>
</section>
<?php
endif;
return ob_get_clean();
} // end gutenberg_gallery_slick_slider_shortcode function
add_shortcode ( 'slick-slider-gallery', 'gutenberg_gallery_slick_slider_shortcode' );
Below is the full shortcode function code, which can be copied and added to your functions.php or via a custom plugin.
function gutenberg_gallery_slick_slider_shortcode() {
global $post;
if ( has_block( 'gallery', $post->post_content ) ) :
ob_start(); ?>
<section id="project-gallery">
<h2>Project Gallery</h2>
<div class="gallery-img">
<?php
$post_blocks = parse_blocks( $post->post_content );
foreach( $post_blocks as $block ) {
if( $block['blockName'] == 'core/gallery' ) {
$ids = $block[ 'attrs' ][ 'ids' ];
foreach( $ids as $id ) {
$image = wp_get_attachment_image_src( $id, 'full' );
$image_alt = get_post_meta( $id, '_wp_attachment_image_alt', true );
//$image_title = get_the_title( $id );
$image_caption = wp_get_attachment_caption( $id );
echo '<div class="item"><img data-lazy="'. $image[0] .'" data-src="'. $image[0] .'" alt="'. $image_alt .'"><div class="img-caption"><h3>' . $image_caption . '</h3></div></div>';
}
}
}
?>
</div>
</section>
<?php
endif;
return ob_get_clean();
} // end gutenberg_gallery_slick_slider_shortcode function
add_shortcode ( 'slick-slider-gallery', 'gutenberg_gallery_slick_slider_shortcode' );
Slick slider configuration
In order for the images to actually transform into a slider we need to initialise slick and add some optional settings. It would be recommended to create a new JavaScript file in a child theme and in order to get slick to work, this could look something like:
wp_enqueue_script( "gallery-slider", get_template_directory_uri() . '/assets/js/gallery-slider.js', array('jquery'), '1.0.0', true );
Replacing gallery-slider.js with the name of your js file and with the correct path if your folder isn’t named assets and added to the wp_enqueue_scripts
hook as shown in the Enqueue slick slider assets section.
We will target the class “gallery-img” which is the HTML wrapper around which each image from the gallery is added to in the previous sections code.
jQuery( document ).ready( function( $ ) {
$( '.gallery-img' ).slick({});
});
This will give you a slick slider with the default settings. There are many settings available with slick.js (see the slick.js website for the full list). Below are some of the options used on this site, including the lazyLoad
option and the CSS breakpoints at which to reduce the number of slides to show as the website is scaled down to smaller screen sizes. There are also options for overriding the default buttons with your own HTML, which can be styled via CSS accordingly.
jQuery( document ).ready( function( $ ) {
//Slick Slider settings
$( '.gallery-img' ).slick({
arrows: true,
dots: true,
slidesToShow: 3,
slidesToScroll: 3,
prevArrow: '<button class="slider-btn slide-arrow prev-arrow"><span class="left-arr">←</span></button>',
nextArrow: '<button class=" slider-btn slide-arrow next-arrow"><span class="right-arr">→</span></button>',
lazyLoad: 'ondemand',
responsive: [
{
breakpoint: 992,
settings: {
slidesToShow: 2,
slidesToScroll: 2,
}
},
{
breakpoint: 768,
settings: {
slidesToShow: 1,
slidesToScroll: 1,
}
}
]
});
});
Removing default gallery markup
With a gallery block inserted into a post and if you also add the shortcode functionality from above – [ slick-slider-gallery ]
you will see both the gallery and the slider (with whatever settings you have added to slick). Note: these images are from my project – crash character selector and the slider settings and CSS styling have been applied to make the images, and arrows look the way they do, you would need to apply your own styles for your slider to fit the aesthetic of your site.
Of course this is not the desired outcome, so we will add another function to remove the default gallery block output. The hook we will use is the render_block
hook, which is an aptly named filter for being able to change what a block renders. The following code again goes in function.php or the custom plugin:
function remove_gallery_block_html( $block_content, $block ) {
global $post;
//if the block isn't a gallery block return normal block content
if ( $block[ 'blockName' ] !== 'core/gallery' ) {
return $block_content;
}
//if the post contains the slick gallery shortcode, return just an empty string, removing the gallery markup
if ( has_shortcode( $post->post_content, 'slick-slider-gallery' ) ) {
return '';
}
return $block_content;
}
add_filter( 'render_block', 'remove_gallery_block_html', 10, 2 );
The above code firstly checks whether the block is not a gallery block (in such case return the normal block content), otherwise we do a has_shortcode
check, by passing in the current $post content and the name of our shortcode to see if it is present. Note: has_shortcode
can use a lot of resources if scanning a lot of content, so if all the galleries on your site will be converted to a slider this check is not necessary and you can simply return the empty string. This check is necessary however if you have a gallery block in a post/page that you don’t want to load as a slider.
Extending the shortcode with custom attributes
Having followed all the above steps, you will now have a working shortcode which added alongside a gallery block on a post/page, will turn it into a slick slider (with whatever slider settings and CSS styles you wish to apply).
There are three ways you could take the above code. firstly just use the function gutenberg_gallery_slick_slider_shortcode
but remove the call to add_shortcode
and rename the function and then simply call it within your chosen template (e.g. single.php). The second way will be to use it as a shortcode and insert the shortcode and gallery block into the post/page.
The issue with option one is the slider will always be in the same place depending where in the template it is called (this isn’t an issue if you know it should always be in the same place). The issue with option two is that despite being able to place the shortcode and render it anywhere within the content, it won’t work if you want to display more than one gallery. Therefore we can add some custom attributes to the shortcode to enable this.
Below is the new function in full, adding the custom attributes and check for if the gallery and shortcodes match. Underneath we will break it down to see the differences and what has been added where and what the changes actually do.
function gutenberg_gallery_slick_slider_shortcode( $atts ) {
global $post;
$attributes = shortcode_atts(
array(
'title' => 'Project Gallery',
'gallery_id' => ''
), $atts
);
if ( has_block( 'gallery', $post->post_content ) ) :
ob_start();
if ( ! empty( $attributes[ 'gallery_id'] ) ) : ?>
<section id="project-gallery">
<h2><?php echo esc_html__( $attributes[ 'title' ] ) ?></h2>
<div class="gallery-img">
<?php
endif;
$post_blocks = parse_blocks( $post->post_content );
foreach( $post_blocks as $block ) {
if( $block['blockName'] == 'core/gallery' ) {
if( get_block_id( $block ) == $attributes[ 'gallery_id' ] ){
$ids = $block[ 'attrs' ][ 'ids' ];
foreach( $ids as $id ) {
$image = wp_get_attachment_image_src( $id, 'full' );
$image_alt = get_post_meta( $id, '_wp_attachment_image_alt', true );
$image_caption = wp_get_attachment_caption( $id );
echo '<div class="item"><img data-lazy="'. $image[0] .'" data-src="'. $image[0] .'" alt="'. $image_alt .'"><div class="img-caption"><h3>' . $image_caption . '</h3></div></div>';
}
}
}
}
?>
</div>
</section>
<?php
endif;
return ob_get_clean();
} // end gutenberg_gallery_slick_slider_shortcode function
add_shortcode ( 'slick-slider-gallery', 'gutenberg_gallery_slick_slider_shortcode' );
At the top of our shortcode function we have added a variable called $attributes
and passed in $atts
as a parameter to the function, in order to retrieve the array of attributes a user passes in (which is also the second parameter of shortcode_atts
).
The first parameter of the shortcode_atts
function is an array of supported attributes for this shortcode, with a default value, with which the user can override with their own values. The title is optional but it is used here as an extra example, whereby if you wanted a title above each gallery and for it to have different text you can change it per shortcode. If you leave it blank in the shortcode e.g. title=""
or set the default value to an empty string (the same as with gallery_id
) it will display nothing until it is set in the shortcode in the Gutenberg editor. It can be output in the HTML like so:
<h2><?php echo esc_html__( $attributes[ 'title' ] ) ?></h2>
The more important attribute is the gallery_id
, which the default is set to be an empty string and this will be used to match the ID of the gallery block to the value passed to the shortcode attribute. We set it empty by default, therefore no images/slider will be shown until the gallery ID and shortcode gallery_id
match, as shown in the below images.
The way in which it is checked whether the gallery block ID and shortcode attribute passed in matches, is via this if statement within the first foreach
loop:
if( get_block_id( $block ) == $attributes[ 'gallery_id' ] )
The get_block_id
function (credit this stackoverflow answer) which is added above the shortcode function is as follows:
function get_block_id( $block ) {
if ( $block['blockName'] !== 'core/gallery' ) return;
$block_html = $block[ 'innerHTML' ];
$id_start = strpos( $block_html, 'id="' ) + 4;
if ( $id_start === false ) return;
$id_end = strpos( $block_html, '"', $id_start );
$block_id = substr( $block_html, $id_start, $id_end - $id_start );
return $block_id;
}
The gallery block ID is stored within the blocks innerHTML
attribute and therefore the function uses PHP functions strpos
and substr
in order to extract the ID value that is within the HTML of the block. Note: while we have filtered out displaying the blocks mark-up with the render_block
filter, the gallery content is still saved into the database along with its ID.
After putting this altogether, you can now add a shortcode like the following: [ slick-slider-gallery title="My Title" gallery_id="my-gallery" ]
(without the spaces between the brackets of the shortcode of course) and you can therefore have multiple galleries, set the IDs and display them wherever the shortcode is placed within the content.
Summary and plugin code
After following the above steps you will now have created a shortcode that when added to the Gutenberg editor along with a gallery block, will display the images as a slider using slick.js.
There are three ways the code could be edited/used:
- Remove the call to
add_shortcode
and therefore just call the function name that returnsob_get_clean()
in your desired template (e.g. single.php or if you have a custom post type something like single-project.php). - Keep it as a shortcode without attributes and add both the gallery and shortcode (the slider will be displayed where the shortcode is placed within the content editor)
- Lastly you can add the custom attributes in order to have multiple galleries, with the gallery block and shortcode IDs matching. This version of the shortcode would still work for adding a single gallery as well.
All of the above code has been made into a plugin and you can find it on github or alternatively download the zip file from there directly here and then upload to your site via the add new plugin screen. It is better to use custom code as a plugin as if you ever switch themes the plugin code is independent and will still work regardless.
WordPress is moving towards everything being a block based approach with full site editing around the corner (current WordPress version is 5.8 at the time of finishing writing this article). Therefore shortcodes may well be less prevalent in the future, however they are unlikely to go away entirely and still provide uses in many cases.
Logically the next step would be to create a separate and new block for this slider and not use a shortcode at all. That process is a lot more involved and the purpose of this tutorial was to take advantage of the fact we already currently have a gallery block and with some custom coding we can turn that existing gallery into a slider without a new block being created and it still works really well.
If you made it all the way through, many thanks for reading and happy coding!