skip to Main Content

I have created a Section component which will take in an image as a property and its children as content to be displayed within the section, so the component would look as follows…

<Section image={'path/to/image'}>
 //content
</Section>

The component will take the image property and set it as a url for background-image style…

let sectionStyle = {
  backgroundImage: `url(${this.props.image})`
}

which will then be processed in the return element…

return (
  <section
    style={this.props.image ? sectionStyle : null}>
    <div>
      {this.props.children}
    </div>
  </section>
)

My question is, is it possible to Lazyload the background image whilst also not compromising the contents availability for SEO? in other words i want to avoid LazyLoading the entire Section, but somehow LazyLoad just the image associated with the Section.

4

Answers


  1. I created a library for lazy-loading images. It’s main feature is to provide dynamic resizing of images, but it can also solve your problem. I recently PR’d in some changes for background image lazy loading.

    https://github.com/peterlazzarino/react-adaptive-image

    Here’s an example you could use for lazy background image loading that will resolve images from imgur. In my production app my image resolver points at an image server, but this is totally customize-able. You will just need to style the .header-image class to have a height and width.

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { initImages } from 'react-adaptive-image';
    import AdaptiveImage from 'react-adaptive-image';
    
    initImages({
        imageResolver: function(image){
            return `https://i.imgur.com/${image.fileName}`
        }
    })
    
    class App extends React.Component {
        render() {
          return (
            <AdaptiveImage backgroundImage className="header-image" fileName={'U6zWkcZ.png'} />
          );
        }
      }
    
    ReactDOM.render(<App />, document.getElementById('react-root'));
    
    Login or Signup to reply.
  2. Here is a simple component to lazy load images:

    class LazyImage extends React.Component {
      state = { src: null };
    
      componentDidMount() {
        const { src } = this.props;
    
        const imageLoader = new Image();
        imageLoader.src = src;
    
        imageLoader.onload = () => {
          this.setState({ src });
        };
      }
    
      render() {
        return <img src={this.state.src || this.props.placeholder} />;
      }
    }
    

    You would call it with <LazyImage src='path/to/hd.jpg' placeholder='path/to/placeholder.jpg' />

    Login or Signup to reply.
  3. An updated version of @naoise-golden ‘s answer

    import PropTypes from 'prop-types';
    import React from 'react';
    
    export default class LazyImage extends React.Component {
      constructor (props) {
        super(props);
        this.state = {
          src: null,
        };
      }
    
      componentDidMount () {
        const { src } = this.props;
        console.log('LazyImage componentDidMount props:', this.props);
    
        const imageLoader = new Image();
        imageLoader.src = src;
    
        imageLoader.onload = () => {
          console.log('LazyImage loaded src:', src);
          this.setState({ src });
        };
      }
    
      render () {
        const { placeholder, className, height, width, alt } = this.props;
        const { src } = this.state;
        return (
          <img src={src || placeholder} className={className} height={height} width={width} alt={alt} />
        );
      }
    }
    
    LazyImage.propTypes = {
      src: PropTypes.string,
      placeholder: PropTypes.string,
      className: PropTypes.string,
      height: PropTypes.number,
      width: PropTypes.number,
      alt: PropTypes.string,
    };
    
    Login or Signup to reply.
  4. In order to defer loading background-image, you will need to create a stylesheet for the CSS properties that load any file with url, since you don’t want these images to delay the first contentful paint.
    For instance:

    FirstModal.module.less

    This file has the vital CSS properties that will be loaded first…

    .styles {
        &-container {
            position: absolute;
            width: 100%;
            height: 100%;
            display: flex;
            flex-direction: column;
            align-content: center;
            justify-content: center;
            align-items: center;
        }
    }
    

    firstModalBackground.module.less

    This file will load after the critical CSS…

    .styles {
        &-container {
            background: url('../../images/code/code.jpg') no-repeat center center fixed;
            background-size: cover;
        }
    }
    

    For demonstration purposes, I will use React.Component here, but, if you want to try to optimize things, you can also use React.PureComponent (I tested it and everything worked fine).

    firstModal.jsx

    const classNames = require('classnames');
    const React = require('react)';
    const {stylesContainer} = require('./firstModal.module.less');
    
    class FirstModal extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                classNames: classNames([stylesContainer])
            };
        }
    
        async componentDidMount() {
            const backgroundImage = await import(
                './firstModalBackground.module.less'
            );
            this.setState({
                classNames: [
                    classNames(this.state.classNames, [backgroundImage.stylesContainer]),
                ]
            });
        }
    
        render() {
            // console.log(this.state.classNames);
            return <div className={this.state.classNames}>It werks!</div>;
        }
    }
    
    module.exports = FirstModal;
    

    You could even take a step further, if you have a low resolution image that loads faster, you can have a "three-step background-image loading", for instance, on componentDidMount:

        async componentDidMount() {
            const lowResolutionBackgroundImage = await import(
                '../greeting-page/firstModalLowResBackground.module.less'
            );
            const baseClass = this.state.classNames;
            this.setState({
                classNames: [
                    classNames(baseclass,
                        lowResolutionBackgroundImage.stylesContainer),
                    ]
                });
            const backgroundImage = await import(
                './firstModalBackground.module.less'
            );
            this.setState({
                classNames: [
                    classNames(baseClass,
                        backgroundImage.stylesContainer),
                ]
            });
        }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search