Animate Any WordPress Block when Scrolling


Learn how to animate any WordPress block when it scrolls into view. There’s very little code and you can add it to any WordPress site without installing a new plugin. We’ll use the off-the-shelf AOS library to do the main work for us, and we’ll add a bit of code to functions.php to make it work. It’s a nice way of adding a bit of bling to a site’s front page 😎 πŸ₯‡

How It Will Work

We’re going to create a simple shortcode that lets us wrap any WordPress Gutenberg Block in a <div> element. We’ll give this element some specific properties, and the AOS library will do the rest for us.

In the Block Editor…

Shortcode to animate WordPress blocks

…In the Front End


So… all we need to do is…

  • Create a shortcode called “aos_anim” (in PHP) and pass some AOS parameters into it, like anim=”…” and duration=”…”
    • This shortcode will take a closing shortcode so it can be wrapped around one or more blocks.
  • Enqueue the AOS assets.
  • Create a few of lines JavaScript to link it together in the browser.

Before We Start

First off, make sure you’re using a custom child theme so you can edit functions.php. In your custom child theme’s main folder, create a new sub-folder called “aos-anim“. Then grab the latest stable version of AOS from GitHub. At the time of writing, the stable version was 2.3.4…

Download the zip file, extract it, and go into the “dist” (distribution) folder. You should see two files in there, called aos.css and aos.js. Copy these into the aos-anim folder in your custom child theme.

In this “aos-anim” directory, create an empty text file called aos-anim-frontend.js. We’ll put a bit of JavaScript code in there later. For now, the file just needs to exist.

In the main directory of your custom child theme, create a new PHP file called aos-anim.php. Again, just leave it empty for now, but make sure you save it.

Next, open functions.php in your custom child theme’s main folder and add the following couple of lines to it: This will pull our new code into the theme so we can register our shortcode.

 * WP Tutorials AOS Anim.
require_once dirname(__FILE__) . '/aos-anim.php';

That’s the scaffold in-place. Reload your site and make sure you’ve not broken anything.

Let’s Write the Actual Code

The main lump of code is pretty straightforward. Open aos-anim.php and paste the following into it, then look through the comments to see what it’s doing.


 * WP Tutorials :: Animate on Show.
 * Thanks to: AOS:

// Block direct access.
defined('WPINC') || die();

const WPT_AOS_VERSION = '2.3.4';

 * Test to see if some content is either just a shortcode by itself, or just a
 * shortcode wrapped in paragraph tags.
function wpt_is_a_shortcode(string $content) {
	$is_a_shortcode = false;

	$strip_tags = array('/^</p>/i', '/<p>$/i', '/^<p>/i', '/</p>$/i');
	$test_content = preg_replace($strip_tags, '', trim($content));
	if (strlen($test_content < 3)) {
		// Not a shortcode
	} elseif (substr_count($test_content, '[') != 1) {
		// Not a shortcode
	} elseif (substr_count($test_content, '[') != 1) {
		// Not a shortcode
	} elseif (substr($test_content, 0, 1) != '[') {
		// Not a shortcode
	} elseif (substr($test_content, -1) != ']') {
		// Not a shortcode
	} else {
		$is_a_shortcode = true;

	return $is_a_shortcode;

 * Add the animate-on-show assets to the frontend, with our small  piece of
 * JavaScript to activate it after the page has loaded.
function wpt_require_aos() {
	global $wpt_has_aos_been_required;

	if (!is_admin() && is_null($wpt_has_aos_been_required)) {
		$base_url = get_stylesheet_directory_uri() . '/' . pathinfo(__FILE__, PATHINFO_FILENAME) . '/';
		$version = wp_get_theme()->get('Version');

		wp_enqueue_script('aos', $base_url . 'aos.js', null, WPT_AOS_VERSION, true);
		wp_enqueue_style('aos', $base_url . 'aos.css', null, WPT_AOS_VERSION);

		wp_enqueue_script('wpt-aos', $base_url . 'aos-anim-frontend.js', array('aos'), $version, true);

		$wpt_has_aos_been_required = true;

function do_shortcode_wpt_aos_anim($atts = array(), $content = null) {
	$html = '';

	if (is_admin()) {
		// Don't do anything.
	} elseif (wp_doing_ajax()) {
		// Don't do anything.
	} else {
		// Enqueue the assets.

		// Options listed here:
		// The "class" option is own own - not one of AOS' options.
		// The "anim" option is special, and maps to data-aos=".."
		$args = shortcode_atts(
				'anim' => 'fade-up',
				'offset' => 120,
				'delay' => 0,
				'duration' => 400,
				'easing' => 'ease',
				'anchor' => '',
				'anchor-placement' => 'top-bottom',
				'once' => false,
				'tag' => 'div',
				'class' => '',

		if (empty($args['anim'])) {
			$html .= $content;
		} elseif (empty($tag = $args['tag'])) {
			// ...
		} else {
			// Open our container (usually a div).
			$html .= sprintf('<%s data-aos="%s"', $tag, esc_attr($args['anim']));

			// If we've been passed something in class="..." then add it now.
			if (!empty($args['class'])) {
				$html .= sprintf(' class="%s"', esc_attr($args['class']));

			// Remove everything from the $args array that we won't want to
			// pass through to data-aos-... properties.

			// Loop through the $args and add them as data-aos-... properties - apart from
			// the "anim" $key, because we've already added that one as data-aos="..."
			foreach ($args as $key => $value) {
				if (($key != 'anim') && !empty($value)) {
					$html .= sprintf(' data-aos-%s="%s"', $key, esc_attr($value));

			$html .= '>';

			// Sometimes do_shortcode returns a string that starts with a
			// closing paragraphc tag and ends with an opening paragraph tag.
			// This is invalid HTML, so we should detect this and fix it.
			$patterns = array('/^</p>/i', '/<p>$/i');
			$content = preg_replace($patterns, '', $content);

			// If the content is just a shortcode by itself then WordPress will
			// wrap it in paragraph tags. Strip these out to give us the raw
			// content from the shortcode.
			if (wpt_is_a_shortcode($content)) {
				$inner_html = trim(do_shortcode($content));
				$patterns = array('/^<p>/i', '/</p>$/i');
				$inner_html = preg_replace($patterns, '', $inner_html);
			} else {
				$inner_html = do_shortcode($content);

			// Add the inner HTML to the output.
			$html .= $inner_html;

			// Close our container... Job done.
			$html .= sprintf('</%s>', $tag);


	return $html;
add_shortcode('aos_anim', 'do_shortcode_wpt_aos_anim');

That’s all there is to the PHP. Now, we just need to tie things together in the frontend with a bit of JavaScript. The AOS documentation says all we need to do is call AOS.init(). We’ll use a standard jQuery snippet to do this once the window/document has fully loaded. We could easily do it without using jQuery, but I’ve used it here to keep the code consistent with the other tutorials on this site.

Open aos-anim/aos-anim-frontend.js, paste the following into it, and save the file.

(function($) {
    'use strict';

    $(window).on('load', function() {
        if (typeof AOS != 'undefined') {
            // Uncomment this to verify that the JS is loading correctly in the browser's JS Console.
            // console.log('Initialise AOS');


Finish & Test

Example AOS animation opeining shortcode
Example opening shortcode.

Make sure everything is saved, then create/edit some content on your site. The shortcode will work without any parameters at all, or you can pass things like anim="zoom-in-left". Just choose from any of the list of available AOS animations.

importantDon’t forget the closing shortcode when you’re wrapping your blocks.

Going Further

You’ll see that we only enqueue the AOS assets the first time our shortcode gets used on a page. This is to keep your site clean… reducing unused CSS/JS on pages that don’t have animations. However, you can call wpt_require_aos() from a wp_enqueue_scripts action handler so you can use animations all over the place.

After you’ve called wpt_require_aos(), you can also start loading things like data-aos="flip-left" into your post archive pages too. It looks pretty cool when mixed with a masonry gallery.

Happy animating! 😎 πŸ‘

Like This Tutorial?

Let us know

WordPress plugins for developers

11 thoughts on “Animate Any WordPress Block when Scrolling”

  1. I can’t add “require_once dirname(__FILE__) . ‘/aos-anim.php’;” to functions.php.
    The system display as following:

    Your PHP code changes were rolled back due to an error on line 0 of file Unknown. Please fix and try saving again.

    • The problem might not be with the require_once code. There might be a problem in “aos-anim.php” itself. I’ve just retested it here and it works fine.

      Maybe there is something different about your server/configuration? What version of WordPress and PHP are you using? I just tested it on WordPress 5.8.2 and PHP 8.0.13. Also, can you check your site’s error log? There is probably some useful information in there.

      • WordPress 5.8.2, PHP 7.4

        Fatal error: Uncaught Error: syntax error, unexpected β€˜all’ (T_STRING)
        in /home/akidstar/public_html/wp-content/themes/porto-child/aos-anim.php on line 118

        • Your error says Fatal error: Uncaught Error: syntax error, unexpected β€˜all’ (T_STRING)
          in …/themes/porto-child/aos-anim.php on line 118

          …but the code for aos-anim.php says that line 118 is:

          $content = preg_replace($patterns, '', $content);

          The only thing I can recommend here is that you try to replace the contents of aos-anim.php with the code from the tutorial again. Maybe something went wrong when you copied-and-pasted it the first time?

    • Hi
      I can see the problem. When you’ve added your shortcode block, you forgot to open the short with a square bracket “[” and close with with another one “]”. I think that’s all you need to do.

      • πŸ˜‚πŸ˜‚πŸ˜‚ Thank you!

        Now nothing apears – only blank block between: [aos_anim anim="flip-right"] [/aos_anim]
        Any idea what is wrong?

        • That should work correctly.

          What sort of block have you got between the two shortcodes? I will try to replicate the problem. Maybe the recent update to WordPress 6 has broken something?

          It definitely works with image blocks, because I use this code in my own projects, using Astra as a parent theme. Are you using a page builder? I haven’t tested the code with things like Elementor.


Leave a comment