Phillip Boateng
Phillip Boateng

Reputation: 1121

Is it possible to lazy load background images with react-lazyload

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.

Upvotes: 9

Views: 9715

Answers (4)

Overclocked Skid
Overclocked Skid

Reputation: 624

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),
            ]
        });
    }

Upvotes: 1

spodell
spodell

Reputation: 166

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,
};

Upvotes: 1

Naoise Golden
Naoise Golden

Reputation: 8893

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' />

Upvotes: 0

Peter Lazzarino
Peter Lazzarino

Reputation: 239

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'));

Upvotes: -1

Related Questions