Reputation: 361
I'm currently working on my first React app, and I'm facing something tough (for me).
<div
className="container-fluid"
id="cityDetail"
style={{
backgroundImage: `url(${camera.lastPhoto})`,
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
backgroundSize: "cover",
height: "100vh"
}}
>
I would like to lazy load the backgroundImage.
I haven't yet found good components/utilities for doing this. Does anyone have any ideas?
Upvotes: 18
Views: 22415
Reputation: 2683
Here is an example using GSAP to set the background image inline using a scroll trigger.
<LazyLoadBgImg src="http://placekeanu.com/540" className="some-css-class">
<h1>Tristan is the best</h1>
</LazyLoadBgImg>
The component:
import React, { useEffect, useRef, useState } from "react";
import { ScrollTrigger } from "gsap/ScrollTrigger";
const LazyLoadBgImg = ({src, className, children}) => {
const lazyLoadRef = useRef();
const [loadImage, setLoadImage] = useState(false);
useEffect(() => {
ScrollTrigger.create({
trigger: lazyLoadRef.current,
start: "top bottom",
onEnter: () => setLoadImage(true),
});
}, []);
return (
<div ref={lazyLoadRef} className={className} style={{backgroundImage: (loadImage ? "url(" + src + ")" : '')}}>
{children}
</div>
)
}
export default LazyLoadBgImg;
Upvotes: 0
Reputation: 1
import { LazyLoadComponent } from 'react-lazy-load-image-component';
...
<LazyLoadComponent id={key_id}>
<div style={{ backgroundImage: `url(${src})` }} />
</LazyLoadComponent>
Upvotes: 0
Reputation: 143
For lazy loading images there is Compoent for it but i am not sure will it work for background images or not. anyway react-lazy-load-image-componen which you can install with yarn or npm like below:
npm i --save react-lazy-load-image-component
or
yarn add react-lazy-load-image-component
then you need to import it in your react app:
import React from 'react';
import { LazyLoadImage } from 'react-lazy-load-image-component';
const MyImage = ({ image }) => (
<div>
<LazyLoadImage
alt={image.alt}
height={image.height}
src={image.src} // use normal <img> attributes as props
width={image.width} />
<span>{image.caption}</span>
</div>
);
export default MyImage;
and for props into it you can see this link: https://www.npmjs.com/package/react-lazy-load-image-component
Upvotes: 0
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 those images to delay the first contentful paint.
For instance:
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;
}
}
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).
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: 0
Reputation: 3156
With Functional Component Like This :
function LazyBackground({src}) {
const [source, setSource] = useState("preload.jpg")
useEffect(() => {
const img = new Image()
img.src = src
img.onload = () => setSource(src)
}, [src])
return(
<div style={{backgroundImage: `url(${source})`}}></div>
)
}
Usage :
<LazyBackground src={"https://example.com/your_image.jpg"}/>
Upvotes: 0
Reputation: 2224
Here's a custom React hook based on Naoise's answer:
const useProgressiveImage = src => {
const [sourceLoaded, setSourceLoaded] = useState(null)
useEffect(() => {
const img = new Image()
img.src = src
img.onload = () => setSourceLoaded(src)
}, [src])
return sourceLoaded
}
Usage:
const Component = (source, placeholder) => {
const loaded = useProgressiveImage(source)
return (
<div style={{ backgroundImage: `url(${loaded || placeholder})` }} />
)
}
Upvotes: 39
Reputation: 8913
Here is a simple component to lazy load images:
class LazyBackground 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 <div {...this.props} style={{ backgroundImage: `url(${this.state.src || this.props.placeholder})` }} />;
}
}
You would call it with <LazyBackground src='path/to/hd.jpg' placeholder='path/to/placeholder.jpg' />
Upvotes: 28