11/* eslint-disable @next/next/no-img-element */
22"use client" ;
3- import { useState } from "react" ;
3+ import { useRef , useState } from "react" ;
4+ import { useIsomorphicLayoutEffect } from "../../lib/useIsomorphicLayoutEffect" ;
45import { cn } from "../../lib/utils" ;
56
67type imgElementProps = React . DetailedHTMLProps <
@@ -25,19 +26,39 @@ export function Img(props: imgElementProps) {
2526 const { className, fallback, skeleton, ...restProps } = props ;
2627 const defaultSkeleton = < div className = "animate-pulse bg-accent" /> ;
2728 const defaultFallback = < div className = "bg-muted" /> ;
29+ const imgRef = useRef < HTMLImageElement > ( null ) ;
30+
31+ useIsomorphicLayoutEffect ( ( ) => {
32+ const imgEl = imgRef . current ;
33+ if ( ! imgEl ) {
34+ return ;
35+ }
36+ if ( imgEl . complete ) {
37+ setStatus ( "loaded" ) ;
38+ } else {
39+ function handleLoad ( ) {
40+ setStatus ( "loaded" ) ;
41+ }
42+ imgEl . addEventListener ( "load" , handleLoad ) ;
43+ return ( ) => {
44+ imgEl . removeEventListener ( "load" , handleLoad ) ;
45+ } ;
46+ }
47+ } , [ ] ) ;
2848
2949 return (
3050 < div className = "relative" >
3151 < img
3252 { ...restProps }
33- onLoad = { ( ) => {
34- setStatus ( "loaded" ) ;
35- } }
53+ // avoid setting empty src string to prevent request to the entire page
54+ src = { restProps . src || undefined }
55+ ref = { imgRef }
3656 onError = { ( ) => {
3757 setStatus ( "fallback" ) ;
3858 } }
3959 style = { {
4060 opacity : status === "loaded" ? 1 : 0 ,
61+ ...restProps . style ,
4162 } }
4263 alt = { restProps . alt || "" }
4364 className = { cn (
@@ -48,6 +69,7 @@ export function Img(props: imgElementProps) {
4869
4970 { status !== "loaded" && (
5071 < div
72+ { ...restProps }
5173 className = { cn (
5274 "fade-in-0 absolute inset-0 overflow-hidden transition-opacity duration-300 [&>*]:h-full [&>*]:w-full" ,
5375 className ,
0 commit comments