1

I'm trying to show spinners while images load on a Next.js site. I'm using NextUI as the UI library.

I have a bunch of cards on the site, and want to show a spinner while the image loads. I have tried with a React hook and ternary operator:

import Loader from '../components/Loader'
import spinner from '../public/images/spinner.svg'

const Item = ({ item }) => {

  const [isLoading, setIsLoading] = useState(false);
 
  useEffect(() => {
    const fetchData = async () => {
      setIsLoading(true);
      const result = await {item};   
      setIsLoading(false);
    };
    fetchData();
  }, []);
  
  return (
    <Link href={`/item/${item.id}`}>
      <Card flat isHoverable isPressable css={{ mw: "400px", alignContent: 'center'}}>
        {isLoading ? (
          <Loader />
        ) : (
         <Card.Image src={item.pic} alt={item.id} layout='responsive' css={{ alignContent: 'center'}} placeholder='blur' blurDataURL='/../public/images/spinner.svg'/>
        )}

("Loader" is correct, I have renamed the "Loading" from the UI library)

I have also tried using Next's blurDataUrl property, which also does not seem to work.

casr
  • 77
  • 6
  • Try to move your setters outside the async call. `useEffect(() => { setIsLoading(true); const fetchData = async () => { const result = await {item}; }; fetchData(); setIsLoading(false); }, []);` – D. Mohamed Sep 29 '22 at 08:50
  • @D.Mohamed Didn't work, thanks for the suggestion though. – casr Sep 29 '22 at 14:54

1 Answers1

1

This worked for me, when image is being loaded a circular loaded is shown

import {useState, useEffect} from 'react';
import Image from "next/image";
import { CircularProgress } from "@mui/material";
import { styled } from "@mui/system";

export default function ServiceImage(props: any) {  
  const { src, alt } = props.props;
  const [isImageLoaded, setIsImageLoaded] = useState(false);
  useEffect(()=> {
    setIsImageLoaded(false);
  }, [src, alt]);

  const StyledImage = styled(Image)(({ theme }) => ({
    objectFit: "cover",    
    objectPosition: "50% 50%",
    [theme.breakpoints.up("md")]: {
      objectFit: "contain",
    }
  }));

  return (
    <>
    <StyledImage src={src} alt={alt} fill onLoadingComplete={() => {
      setIsImageLoaded(true);
    }}
    sx={{
      opacity: isImageLoaded ? 1 : 0,
      transitionDuration: "500ms",
      transitionProperty: "opacity",
      transitionTimingFunction: "ease-out",
    }}
    />
    <CircularProgress size={80} thickness={3} sx={{ 
      position: "absolute", 
      top:"50%", 
      left: "50%", 
      transform: "translate(-50%, -50%) !important",
      opacity: !isImageLoaded ? 1 : 0,
      transitionDuration: "500ms",
      transitionProperty: "opacity",
      transitionTimingFunction: "ease-out",
  }} />
    </>
  );
}
atazmin
  • 4,757
  • 1
  • 32
  • 23