You can take it one step further by adding fade-in transition when changing images. The code below is my CrossFadeImage component. Just copy and use it instead of the normal img component.
The CrossFadeImage has 2 images, top and bottom. bottom is stacked on top and is used to display the image that need animating, in this case the old image that will be faded-out when switching,
At idle state, top displays the current image while bottom is the previous image but in transparent
CrossFadeImage will do the following things when detecting props.src changes
- Reset both the srcs to cancel any currently running animations
- Set top's src to the new image andbottom's src to the current image that will be faded-out next frame
- Set bottomto transparent to kick-off the transition
import React from "react";
const usePrevious = <T extends any>(value: T) => {
  const ref = React.useRef<T>();
  React.useEffect(() => {
    ref.current = value;
  }, [value]);
  return ref.current;
};
const useRequestAnimationFrame = (): [(cb: () => void) => void, Function] => {
  const handles = React.useRef<number[]>([]);
  const _raf = (cb: () => void) => {
    handles.current.push(requestAnimationFrame(cb));
  };
  const _resetRaf = () => {
    handles.current.forEach((id) => cancelAnimationFrame(id));
    handles.current = [];
  };
  return [_raf, _resetRaf];
};
type ImageProps = {
  src: string;
  alt?: string;
  transitionDuration?: number;
  curve?: string;
};
const CrossFadeImage = (props: ImageProps) => {
  const { src, alt, transitionDuration = 0.35, curve = "ease" } = props;
  const oldSrc = usePrevious(src);
  const [topSrc, setTopSrc] = React.useState<string>(src);
  const [bottomSrc, setBottomSrc] = React.useState<string>("");
  const [bottomOpacity, setBottomOpacity] = React.useState(0);
  const [display, setDisplay] = React.useState(false);
  const [raf, resetRaf] = useRequestAnimationFrame();
  React.useEffect(() => {
    if (src !== oldSrc) {
      resetRaf();
      setTopSrc("");
      setBottomSrc("");
      raf(() => {
        setTopSrc(src);
        setBottomSrc(oldSrc!);
        setBottomOpacity(99);
        raf(() => {
          setBottomOpacity(0);
        });
      });
    }
  });
  return (
    <div
      className="imgContainer"
      style={{
        position: "relative",
        height: "100%"
      }}
    >
      {topSrc && (
        <img
          style={{
            position: "absolute",
            opacity: display ? "100%" : 0,
            transition: `opacity ${transitionDuration}s ${curve}`
          }}
          onLoad={() => setDisplay(true)}
          src={topSrc}
          alt={alt}
        />
      )}
      {bottomSrc && (
        <img
          style={{
            position: "absolute",
            opacity: bottomOpacity + "%",
            transition: `opacity ${transitionDuration}s ${curve}`
          }}
          src={bottomSrc}
          alt={alt}
        />
      )}
    </div>
  );
};
export default CrossFadeImage;
Usage
<CrossFadeImage
  src={image}
  alt="phonee"
  transitionDuration={0.35}
  curve="ease-in-out"
/>
Live Demo
