149

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;
3
  • I ran into this problem, and I don't have the code to assist at the moment, but what I did was place the javascript checking in componentdidmount, which looks for an image error, if they occur, a callback is fired which replaces that image with the default image. Commented Dec 4, 2015 at 21:35
  • Please, accept the answer by Georgii Oleinikov, because currently best scored answer may produce eternal loop and so is not good at all. Commented Nov 29, 2018 at 14:50
  • Found this video tutorial on this - youtu.be/90P1_xCaim4 which actually helped me building a full fledged image component for my application. I also found this along with an awesome preloaders for my image component - youtu.be/GBHBjv6xfY4. By combining both you can provide a wonderfull UX for the users. Commented Jan 13, 2019 at 16:19

28 Answers 28

285

This works best for me

<img 
  src={record.picture}
  onError={({ currentTarget }) => {
    currentTarget.onerror = null; // prevents looping
    currentTarget.src="image_path_here";
  }}
/>
Sign up to request clarification or add additional context in comments.

13 Comments

Seems like this method could lead to an infinite callback if "image_path_here" produces and error...
@tomhughes it will prevent infinite callbacks when "image_path_here" fails
@DeepakMallah I followed your code <img src={imageUrl} className={cs.image} onError={(e) => {e.target.src = 'https://upload.wikimedia.org/wikipedia/en/c/c3/The_Martian_2014.jpg'; e.target.onError = null;}} /> However, the console in Safari still shows the error Failed to load resource: the server responded with a status of 404 (Not Found). Is it normal? How to remove this console error? Thanks.
@vizsatiz Try replacing e.target with e.currentTarget
I copied and pasted this in and added a console.log in the onError function, and it was continually running. I don't think the currentTarget.onerror = null; line prevents looping.
|
49

Since there is no perfect answer, I am posting the snippet I use. I am using reusable Image component that falls back 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,
};

2 Comments

A little note: not working if you're using React with server side rendering because images are being loaded asynchronously and by the time the hydration is made all errors will already have been triggered.
I expanded this answer using TypeScript and adding Placeholder support stackoverflow.com/a/68378797/3457769
41

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


3 Comments

Love this. But which part of it constitutes a hook? This looks like an image component written in TypeScript that handles the error when the image is not found. Still love it, though.
useState is a hook
Thanks @Marcos. I wrongly assumed you were referring to a custom hook with your answer.
33

You can use uncontrolled component:

<img src={this.state.img} ref={img => this.img = img} onError={
    () => this.img.src = 'img/default.img'
}>

Comments

17

It's So Simple

e.target.onerror = null If Error Image Also Fails to Load jsx

<img
    src={props.image}
    alt={props.title}
    onError={(e) =>
      (e.target.onerror = null)(
        (e.target.src =
          "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Stadtbild_M%C3%BCnchen.jpg/2560px-Stadtbild_M%C3%BCnchen.jpg")
      )
    }
  />

4 Comments

If imageErrorSrc is an invalid url, then onError runs infinitely. why?
okay so 2 years later i feel this might not be the best way, cause this directly applies to the dom, and on rerenders will run again, better to use a react only way
You should put that caveat in your answer, not down here in a comment.
e.target.onerror = null doesn't stop the infinite loop.
11

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;

2 Comments

But bootstrap require Jquery bro
@GuilhermeNunes React Bootstrap doesn't!
11

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

Comments

8

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; } }} />
            );
} 

1 Comment

best answer ever solved my problem thanks man
7

@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"; } }}

2 Comments

e.target.onerror = null is unneeded. Still, this has to be accepted answer.
e.target.onerror = null doesn't stop the infinite loop.
5
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.

Comments

5

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} ... /></>

Comments

4

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

Comments

4

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

event.target properties

Comments

3

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;

1 Comment

You shouldn't store state in the <img /> component, but rather in the containing <Image /> component, which would mean refactoring it as a stateful component and using something like this.setState({ src: defaultSrc }).
3

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

react-image-fallback

3 Comments

The last published change to this repo was two years ago. An alternative is react-image.
using react-image should be accepted answer as it really solves all the problem that you may face when serving static build of react website
I achieved the goal using npmjs.com/package/react-image. Your answer inspired me to look for an npm instead of creating my own solution. Thanks a lot. :)
2

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!

Comments

2

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

Comments

1

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!

1 Comment

using useEffect just for image is expensive. It will cause performance issues
0

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

Comments

0

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

Comments

0

Previous versions have the bug; they don't count that src could be changed. So I made my ultimate solution and it:

  1. Supports typing
  2. Support case when src is changed
  3. Forwards ref
  4. Doesn't ignore onError (means you can pass onError to ImageWithFallback 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}
      />
    );
  },
);

Comments

0

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

Comments

0

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;

Comments

0

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} />
  },
)

Comments

0

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

Comments

0

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}
            />
        );
    }
}

Comments

0

2022 Aug Updated Answer

el.target.onerror = null; //Not worked for me also not prevents looping.

The best solution is setting errcount attribute to the element.

Then check if attribute is set, no need to set src again.

also you can add logic to retry for 1,2,3...n times.

<img onError={this.addDefaultSrc} src={BaseURL + item.ImageUrl} />

addDefaultSrc(el) {
  var errcount = parseInt(el.target.getAttribute("errcount"));
  if (isNaN(errcount)) {
    el.target.setAttribute("errcount", "1");
    el.target.src = BaseURL + 'Images/no-image.jpg';
  }
  //else
  //el.target.src = "";
}

Comments

-2

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.