I have a react component that is the detail view from a list.
I am trying to replace the image with a default image if the image does not exist and there is a 404 er
Since there is no perfect answer, I am posting the snippet I use. I am using reusable Image
component that fallbacks to fallbackSrc
.
Since the fallback image could fail again and trigger infinite loop of re-rendering, I added errored
state.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class Image extends Component {
constructor(props) {
super(props);
this.state = {
src: props.src,
errored: false,
};
}
onError = () => {
if (!this.state.errored) {
this.setState({
src: this.props.fallbackSrc,
errored: true,
});
}
}
render() {
const { src } = this.state;
const {
src: _1,
fallbackSrc: _2,
...props
} = this.props;
return (
<img
src={src}
onError={this.onError}
{...props}
/>
);
}
}
Image.propTypes = {
src: PropTypes.string,
fallbackSrc: PropTypes.string,
};
import OriginalImage from '../../originalImg.png'
import ReplacementImage from '../../replaceImg.png'
<img
src= OriginalImage
alt="example"
onError={(e) => {
e.target.src = ReplacementImage //replacement image imported above
e.target.style = 'padding: 8px; margin: 16px' // inline styles in html format
}}
/>
this is what I'm currently using.
e.target.onerror = null
If Error Image Also Fails to Load
jsx
<img
src={imageSrc}
onError={(e) => (e.target.onerror = null, e.target.src = imageErrorSrc)}/>
Arthur's answer will result in infinite callbacks if fallback image also fails.
To avoid that, first set a state in the constructor for imageLoadError as true :
constructor(props) {
super(props);
this.state = {
imageLoadError: true,
};
}
and then check for this state value in onError
function to avoid infinite callbacks,
the code will look like this :-
<img
src={"https://if_this_url_fails_go_to_onError"}
onError={e => {
if(this.state.imageLoadError) {
this.setState({
imageLoadError: false
});
e.target.src = 'fallbackImage.png';
}
}}
/>
Ran into a similar problem and the best solution i could find was Georgii Oleinikov's answer. (Doesn't require making new imageLoadError
state as suggested by Nitesh Ranjan in his answer)
onError={(e)=>{ if (e.target.src !== "image_path_here"){
e.target.onerror = null;
e.target.src="image_path_here";}
}
}
e.target.onerror = null
is not needed (and doesn't really help) because the if condition is enough to prevent the infinite loop(if backup image fails to load as well).
So:
onError={(e)=>{ if (e.target.src !== "image_path_here"){
e.target.src="image_path_here";}
}
}
EDIT: The other way around is to set a flag outside the return brackets and check for the flag in the if statement. Code should look something like this:
render(){
let errorflag=true;
return(
<img alt='' src={imageUrl}
onError={(e)=>{ if (errorflag){ errorflag=false; e.target.src=url; } }} />
);
}
As it was mentioned in one of the comments, the best solution is to use react-image library. Using onError will fail when you try to serve static version of your react website after build.
Here is super simple and straightforward example how to use react-image, just import Img component
import {Img} from 'react-image'
And later specify a list of src that you try to load
<Img
src={['images/image1.svg', 'images/default.svg']}
alt="Some title"
/>
If 1st url not found, the 2nd will be loaded, there are also some other pretty cool features like showing a spinner while image is loading or displaying some other component in case none of the listed images are available