react.js Replace img src onerror
JavascriptReactjsHttp Status-Code-404Javascript Problem Overview
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 error.
I would normally use the onerror method in the img tag but that doesn't seem to be working.
I am not sure how to do this with react.
Here is my component:
import React from 'react';
import {Link} from 'react-router';
import ContactStore from '../stores/ContactStore'
import ContactActions from '../actions/ContactActions';
class Contact extends React.Component {
constructor(props) {
super(props);
this.state = ContactStore.getState();
this.onChange = this.onChange.bind(this);
}
componentDidMount() {
ContactStore.listen(this.onChange);
ContactActions.getContact(this.props.params.id);
}
componentWillUnmount() {
ContactStore.unlisten(this.onChange);
}
componentDidUpdate(prevProps) {
if (prevProps.params.id !== this.props.params.id) {
ContactActions.getContact(this.props.params.id);
}
}
onChange(state) {
this.setState(state);
}
render() {
return (
<div className='container'>
<div className='list-group'>
<div className='list-group-item animated fadeIn'>
<h4>{this.state.contact.displayname}</h4>
<img src={this.state.imageUrl} />
</div>
</div>
</div>
);
}
}
export default Contact;
Javascript Solutions
Solution 1 - Javascript
This works best for me
<img
src={record.picture}
onError={({ currentTarget }) => {
currentTarget.onerror = null; // prevents looping
currentTarget.src="image_path_here";
}}
/>
Solution 2 - Javascript
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,
};
Solution 3 - Javascript
You can use uncontrolled component:
<img src={this.state.img} ref={img => this.img = img} onError={
() => this.img.src = 'img/default.img'
}>
Solution 4 - Javascript
2021 Updated Answer using React Functional Components, Hooks and TypeScript
// ImageWithFallback.tsx
import React, { ImgHTMLAttributes, useState } from 'react'
interface Props extends ImgHTMLAttributes<any> {
fallback: string
}
export default function ImageWithFallback({ fallback, src, ...props }: Props) {
const [imgSrc, setImgSrc] = useState<string | undefined>(src)
const onError = () => setImgSrc(fallback)
return <img src={imgSrc ? imgSrc : fallback} onError={onError} {...props} />
}
Solution 5 - Javascript
Its So Simple
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)}/>
Solution 6 - Javascript
You need just define onError handler than change the state which will trigger component render method and eventually component will re-render with placeholder.
Please, don't use jQuery and React together!
import React from 'react';
import {Link} from 'react-router';
import ContactStore from '../stores/ContactStore'
import ContactActions from '../actions/ContactActions';
class Contact extends React.Component {
constructor(props) {
super(props);
this.state = ContactStore.getState();
this.onChange = this.onChange.bind(this);
}
componentDidMount() {
ContactStore.listen(this.onChange);
ContactActions.getContact(this.props.params.id);
}
componentWillUnmount() {
ContactStore.unlisten(this.onChange);
}
componentDidUpdate(prevProps) {
if (prevProps.params.id !== this.props.params.id) {
ContactActions.getContact(this.props.params.id);
}
}
onChange(state) {
this.setState(state);
}
onError() {
this.setState({
imageUrl: "img/default.png"
})
}
render() {
return (
<div className='container'>
<div className='list-group'>
<div className='list-group-item animated fadeIn'>
<h4>{this.state.contact.displayname}</h4>
<img onError={this.onError.bind(this)} src={this.state.imageUrl} />
</div>
</div>
</div>
);
}
export default Contact;
Solution 7 - Javascript
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';
}
}}
/>
Solution 8 - Javascript
@DepH's answer is nice, but it does produce and infinite loop if your error source also doesn't load. This helped me avoid the callback loop:
onError={(e)=>{ if (e.target.src !== "image_path_here")
{ e.target.onerror = null; e.target.src="image_path_here"; } }}
Solution 9 - Javascript
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.
Solution 10 - Javascript
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; } }} />
);
}
Solution 11 - Javascript
Here's an answer using hooks:
import React, { useState } from 'react'
/**
* Returns an object that can
* be spread onto an img tag
* @param {String} img
* @param {String} fallback
* @returns {Object} { src: String, onError: Func }
*/
function useFallbackImg(img, fallback) {
const [src, setImg] = useState(img)
function onError(e) {
console.log('Missing img', img, e)
// React bails out of hook renders if the state
// is the same as the previous state, otherwise
// fallback erroring out would cause an infinite loop
setImg(fallback)
}
return { src, onError }
}
/**
* Usage <Image src='someUrl' fallback='fallbackUrl' alt='something' />
*/
function Image({src, fallback, ...rest}) {
const imgProps = useFallbackImg(src, fallback)
return <img {...imgProps} {...rest} />
}
And if you are want to handle the src
prop changing, you can pass a key
prop of the src
.
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key
<Image key='someUrl' src='someUrl' fallback='fallbackUrl' alt='...' />
The only extreme contrived edge case where using a key like this might fail is with sibling components. I think only one sibling node will render if they have the same key. To get around this you could probably wrap the Image in a <>
Fragment
.
<><Image key={srcProp} ... /></>
<><Image key={srcProp} ... /></>
Solution 12 - Javascript
I wrote like this.
import React, { useState } from 'react';
import NoImageSVG from './noImage.svg';
const ImgWithFallback: React.FunctionComponent<{ src: string; alt: string; className: string }> = ({
src,
alt,
className,
}) => {
const [isUndefined, updateIsUndefined] = useState(false);
const onError = () => {
updateIsUndefined(true);
};
if (isUndefined) {
return (
<div className={className}>
<NoImageSVG width='5rem' height='5rem' />
</div>
);
}
return <img src={src} alt={alt} className={className} onError={onError} />;
};
export default React.memo(ImgWithFallback, () => true);
Solution 13 - Javascript
I took @Skay's answer and created a reusable Image component. Posting in case it helps anyone:
import React, { PropTypes } from 'react';
const Image = ({src, fallbackSrc, ...other}) => {
let element;
const changeSrc = newSrc => {
element.src = newSrc;
};
return (
<img src={src}
onError={() => changeSrc(fallbackSrc)}
ref={el => element=el}
{...other} />
);
};
Image.propTypes = {
src: PropTypes.string,
fallbackSrc: PropTypes.string
};
export default Image;
Solution 14 - Javascript
Even though this is an old question if you are looking of a clean solution you can use react-image-fallback
library.
<ReactImageFallback
src="my-image.png"
fallbackImage="my-backup.png"
initialImage="loader.gif"
alt="cool image should be here"
className="my-image" />
Solution 15 - Javascript
For those like me who also wanted to change the styles of the element and/or change the img source, just do something like this:
<img
src={'original src url goes here'}
alt="example"
onError={(e) => {
e.target.src = '/example/noimage.png' // some replacement image
e.target.style = 'padding: 8px; margin: 16px' // inline styles in html format
}}
/>
Hope it helps!
Solution 16 - Javascript
I used the above method of Arthurs making e.target.onerror = null to stop the infinite loop , but still the infinite loop happened. So , to stop the infinite loop I had to use the below method.I had to find the actual property onError and make it null.
<img src={imageSource}
onError={(e) => {
e.target[Object.keys(e.target).filter(prop=>prop.includes('EventHandler'))[0]].onError = null;
e.target.src = 'images/avatar.png'; }}
/>
Solution 17 - Javascript
For SSR (Server Side Rendering)...
So, here's a workaround that works (for me)!
const Img: FC<
DetailedHTMLProps<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>
> = ({ src, ...props }): JSX.Element => {
const [hasRendered, setHasRendered] = useState(false);
const imgRef = useRef<HTMLImageElement | null>(null);
useEffect(() => {
if (imgRef.current && hasRendered) {
imgRef.current!.src = src || '';
}
}, [src, hasRendered]);
useEffect(() => {
setHasRendered(true);
}, []);
return (
<img
{...props}
ref={imgRef as any}
alt={props.alt || 'image'}
aria-hidden={true}
onError={...}
onLoad={...}
/>
);
};
So, the magic happens in the two useEffect
hooks. (Using just one didn't work).
Basically, the second useEffect
ensures the first hook is triggered (or component re-renders) a second time (after initial render), due to the hasRendered
dep, which then forces the image src to be set in that hook which then triggers the events on the client!
Solution 18 - Javascript
That's how I did it.
class Pix extends React.Component{
constructor(props){
super(props);
this.state={link: this.props.link};
this.onError=this.onError.bind(this);
}
onError(){
console.log("error: could not find picture");
this.setState(function(){ return {link: "missing.png"}; });
};
render(){
return <img onError={this.onError} src={this.state.link}/>;
}
}
Solution 19 - Javascript
You can use object
if that's ok with your requirement. Something like below will work perfectly fine
<object data={expected_image} type="image/jpg">
<img src={DEFAULT} alt="404" />
</object>
Check this answer for more details https://stackoverflow.com/a/29111371/1334182
Solution 20 - Javascript
If anyone is using image src with require then onError doesn't work as -
<img src={require(`./../../assets/images/${props.imgName}.png`)} className="card-img" alt={props.name} />
then require throws an error, where I tried multiple ways and came to try and catch block solution as -
let imgSrc;
try {
imgSrc = require(`./../../assets/images/${props.imgName}.png`);
} catch {
imgSrc = require(`./../../assets/images/default.png`);
}
and use as
<img src={imgSrc} className="card-img" alt={props.name} />
Solution 21 - Javascript
Previous versions have the bug; they don't count that src
could be changed. So I made my ultimate solution and it:
- Supports typing
- Support case when
src
is changed - Forwards ref
- Doesn't ignore
onError
(means you can passonError
toImageWithFallback
like you usually do with<img />
)
Here it is:
import React, { useState, useCallback, useEffect } from 'react';
import noImage from 'src/svg/no-image.svg';
export const ImageWithFallback = React.forwardRef(
(
{
onError,
...props
}: React.DetailedHTMLProps<
React.ImgHTMLAttributes<HTMLImageElement>,
HTMLImageElement
>,
ref: React.Ref<HTMLImageElement>,
) => {
const [imageLoadFailed, setImageLoadFailed] = useState<boolean>(false);
const handleError = useCallback(
(e: React.SyntheticEvent<HTMLImageElement, Event>) => {
if (imageLoadFailed) return;
setImageLoadFailed(true); // to avoid infinite loop
if (onError) {
onError(e);
}
},
[imageLoadFailed, setImageLoadFailed, onError],
);
useEffect(() => {
setImageLoadFailed(false); // in case `src` is changed
}, [props.src]);
return (
<img
{...props}
src={imageLoadFailed ? noImage : props.src}
onError={handleError}
ref={ref}
/>
);
},
);
Solution 22 - Javascript
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
Solution 23 - Javascript
Try this custom Image component:
import React, { useRef } from 'react';
import PropTypes from 'prop-types';
import defaultErrorImage from 'assets/images/default-placeholder-image.png';
const Image = ({ src, alt, className, onErrorImage }) => {
const imageEl = useRef(null);
return (
<img
src={src}
alt={alt}
className={className}
onError={() => {
imageEl.current.src = onErrorImage;
}}
ref={imageEl}
/>
);
};
Image.defaultProps = {
onErrorImage: defaultErrorImage,
};
Image.propTypes = {
src: PropTypes.string.isRequired,
alt: PropTypes.string.isRequired,
className: PropTypes.string.isRequired,
onErrorImage: PropTypes.string,
};
export default Image;
Solution 24 - Javascript
Typescript version:
const Avatar = (): JSX.Element => {
function imageErrorHandler(e: React.SyntheticEvent<HTMLImageElement, Event>) {
const el = e.target as HTMLImageElement
el.onerror = null
el.src = '/fallback.png'
}
return <img src={'/smth.png'} onError={imageErrorHandler}/>
},
)
With forwardRef and possible null src:
import { forwardRef } from 'react'
type Props = Omit<React.ComponentPropsWithoutRef<'img'>, 'src'> & { src?: null | string }
const Avatar = forwardRef<HTMLImageElement, Props>(
({ src, ...rest }, ref): JSX.Element => {
function imageErrorHandler(e: React.SyntheticEvent<HTMLImageElement, Event>) {
const el = e.target as HTMLImageElement
el.onerror = null
el.src = '/fallback.png'
}
return <img src={src || '/alternative.png'} onError={imageErrorHandler} ref={ref} {...rest} />
},
)
Solution 25 - Javascript
With the help of @emil's solution above I created this little functional component. It's using a fallback src on First error and removing the img on second error, from fallback src.
import React, { useState } from 'react'
function ImageFallback({ src, fallbackSrc, ...props }) {
const [state, setState] = useState({ src: src, errored: false })
//update(next img) state onMount
useEffect(() => {
setState({
src: src,
errored: false,
})
}, [src])
//update (remove) state onUnMount
useEffect(() => {
return () => {
setState({
src: null,
errored: false,
})
}
}, [])
const onError = () => {
//1st error
if (!state.errored) {
setState({
src: fallbackSrc,
errored: true,
});
} else if (state.errored && state.src) {
//2nd error
//when error on fallbacksrc - remove src
setState({
src: null,
errored: true,
});
}
}
return (
state.src && <img
src={state.src}
onError={onError}
{...props}
/>
)
}
export default ImageFallback
Usage ...
<ImageFallback src={anySrc} fallbackSrc={anyFallbackSrc} className={classes.logo} alt='' />
Solution 26 - Javascript
I expanded @Emils solution using TypeScript and added
- placeholder support while loading
import * as React from "react";
type Props = {
src: string,
fallbackSrc: string,
placeholderColor?: string,
className?: string,
}
type State = {
src: string,
errored: boolean,
loaded: boolean
}
export default class Image extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
src: props.src,
errored: false,
loaded: false
};
}
onError = () => {
if (!this.state.errored) {
this.setState({
src: this.props.fallbackSrc,
errored: true,
});
}
}
onLoad = () => {
if(!this.state.loaded){
this.setState({loaded: true});
}
}
render() {
let style = {
backgroundColor: this.props?.placeholderColor || "white"
};
if(this.state.loaded){
style.backgroundColor = "transparent";
}
return (
<img
style={style}
onLoad={this.onLoad}
onError={this.onError}
{...this.props}
src={this.state.src}
/>
);
}
}
Solution 27 - Javascript
this works for me .
{<img className="images"
src={`/images/${student.src ? student.src : "noimage.png" }`} alt=
{student.firstname} />}
student is the name of my array and noimage the image, when there is no image is display.