Quick Guide: Implement Lazy-Loading without Plugin

Lazy-load is a way to prevent images from loading before we scroll past it. There are many plugins that help you with this, some themes even have a built-in lazy-load function.

Here we will show you that it’s not hard to implement it yourself.

Step 1: Replace SRC Attribute

<!-- Convert this -->

<img src=".." srcset="...">

<!-- into -->

<img data-src="..." data-srcset="...">

When an image has src or srcset attribute, it will always load no matter what. So our first step is to temporarily prefix them with data-.

In WordPress, we can filter the content and replace them with regex:

add_filter( 'the_content', 'my_lazyload_content_images' );
add_filter( 'widget_text', 'my_lazyload_content_images' );
add_filter( 'wp_get_attachment_image_attributes', 'my_lazyload_attachments', 10, 2 );

// Replace the image attributes in Post/Page Content
function my_lazyload_content_images( $content ) {
  $content = preg_replace( '/(<img.+)(src)/Ui', '$1data-$2', $content );
  $content = preg_replace( '/(<img.+)(srcset)/Ui', '$1data-$2', $content );
  return $content;

// Replace the image attributes in Post Listing, Related Posts, etc.
function my_lazyload_attachments( $atts, $attachment ) {
  $atts['data-src'] = $atts['src'];
  unset( $atts['src'] );
  if( isset( $atts['srcset'] ) ) {
    $atts['data-srcset'] = $atts['srcset'];
    unset( $atts['srcset'] );

  return $atts;

Step 2: Add Scroll Listener

When the image reaches your viewport, it should replace data-src into src, that way the image will start loading.

( function() { 'use strict';
  let images = document.querySelectorAll('img[data-src]');
  document.addEventListener('DOMContentLoaded', onReady);
  function onReady() {
    // Show above-the-fold images first

    // scroll listener
    window.addEventListener( 'scroll', showImagesOnView, false );
  // Show the image if reached on viewport
  function showImagesOnView( e ) {
    for( let i of images ) {
      if( i.getAttribute('src') ) { continue; } // SKIP if already displayed
      // Compare the position of image and scroll
      let bounding = i.getBoundingClientRect();
      let isOnView = bounding.top >= 0 &&
      bounding.left >= 0 &&
      bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
      bounding.right <= (window.innerWidth || document.documentElement.clientWidth);
      if( isOnView ) {
        i.setAttribute( 'src', i.dataset.src );
        if( i.getAttribute('srcset') ) {
          i.setAttribute( 'srcset', i.dataset.srcset );

Don’t forget to enqueue the script:

add_action( 'wp_enqueue_scripts', 'my_lazyload_assets', 10 );
function my_lazyload_assets() {
  $js_dir = get_stylesheet_directory_uri() . '/js';
  wp_enqueue_script( 'my-lazyload', $js_dir . '/lazyload.js', [], '', true );

Step 3: Add Animation

You can customize the animation purely with CSS. For example, here’s a simple fade-in effect:

img[data-src] {
  opacity: 0;
  transition: opacity .25s ease-in-out;
  will-change: opacity;

/* appear animation */
img[data-src][src] {
  opacity: 1;

Feel free to change the transition timing to your taste and play around with translate.


Lazyload has become a necessity in all blogs and corporate websites. You don’t have to worry about SEO because Google themselves has confirmed that they can detect lazy-load.

There’s nothing wrong with using a plugin for this feature, but it’s good to know how it’s done. You can also customize it to your needs like customizing the animation and adding a delay.

If this code doesn’t work for you, please let us know in the comment below. It might clash with your themes or some plugins.

Default image
Henner Setyono
A web developer who mainly build custom theme for WordPress since 2013. Young enough to never had to deal with using Table as layout.
Leave a Reply