diff --git a/packages/next/client/image.tsx b/packages/next/client/image.tsx
index 5eea24447eab361..6a793cdae1cd7b8 100644
--- a/packages/next/client/image.tsx
+++ b/packages/next/client/image.tsx
@@ -884,6 +884,7 @@ const ImageElement = ({
onLoadingCompleteRef,
setBlurComplete,
setIntersection,
+ onLoad,
onError,
isVisible,
...rest
@@ -933,6 +934,9 @@ const ImageElement = ({
onLoadingCompleteRef,
setBlurComplete
)
+ if (onLoad) {
+ onLoad(event)
+ }
}}
onError={(event) => {
if (placeholder === 'blur') {
diff --git a/test/integration/image-component/default/pages/on-load.js b/test/integration/image-component/default/pages/on-load.js
new file mode 100644
index 000000000000000..81b9b4cb0172559
--- /dev/null
+++ b/test/integration/image-component/default/pages/on-load.js
@@ -0,0 +1,101 @@
+import { useState } from 'react'
+import Image from 'next/image'
+
+const Page = () => {
+ // Hoisted state to count each image load callback
+ const [idToCount, setIdToCount] = useState({})
+ const [clicked, setClicked] = useState(false)
+
+ return (
+
+
Test onLoad
+
+ This is the native onLoad which doesn't work as many places as
+ onLoadingComplete
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ImageWithMessage({ id, idToCount, setIdToCount, ...props }) {
+ const [msg, setMsg] = useState('[LOADING]')
+ const style =
+ props.layout === 'fill'
+ ? { position: 'relative', width: '64px', height: '64px' }
+ : {}
+ return (
+ <>
+
+ {
+ let count = idToCount[id] || 0
+ count++
+ idToCount[id] = count
+ setIdToCount(idToCount)
+ const msg = `loaded ${count} ${e.target.id} with native onLoad`
+ setMsg(msg)
+ console.log(msg)
+ }}
+ {...props}
+ />
+
+ {msg}
+
+ >
+ )
+}
+
+export default Page
diff --git a/test/integration/image-component/default/test/index.test.js b/test/integration/image-component/default/test/index.test.js
index fc09281dec0c132..a9d2ab902bad483 100644
--- a/test/integration/image-component/default/test/index.test.js
+++ b/test/integration/image-component/default/test/index.test.js
@@ -289,6 +289,80 @@ function runTests(mode) {
}
})
+ it('should callback native onLoad in most cases', async () => {
+ let browser = await webdriver(appPort, '/on-load')
+
+ await browser.eval(
+ `document.getElementById("footer").scrollIntoView({behavior: "smooth"})`
+ )
+
+ await check(
+ () => browser.eval(`document.getElementById("img1").currentSrc`),
+ /test(.*)jpg/
+ )
+ await check(
+ () => browser.eval(`document.getElementById("img2").currentSrc`),
+ /test(.*).png/
+ )
+ await check(
+ () => browser.eval(`document.getElementById("img3").currentSrc`),
+ /test\.svg/
+ )
+ await check(
+ () => browser.eval(`document.getElementById("img4").currentSrc`),
+ /test(.*)ico/
+ )
+ await check(
+ () => browser.eval(`document.getElementById("msg1").textContent`),
+ 'loaded 1 img1 with native onLoad'
+ )
+ await check(
+ () => browser.eval(`document.getElementById("msg2").textContent`),
+ 'loaded 1 img2 with native onLoad'
+ )
+ await check(
+ () => browser.eval(`document.getElementById("msg3").textContent`),
+ 'loaded 1 img3 with native onLoad'
+ )
+ await check(
+ () => browser.eval(`document.getElementById("msg4").textContent`),
+ 'loaded 1 img4 with native onLoad'
+ )
+ await check(
+ () => browser.eval(`document.getElementById("msg8").textContent`),
+ 'loaded 1 img8 with native onLoad'
+ )
+ await check(
+ () =>
+ browser.eval(
+ `document.getElementById("img8").getAttribute("data-nimg")`
+ ),
+ 'intrinsic'
+ )
+ await check(
+ () => browser.eval(`document.getElementById("img8").currentSrc`),
+ /wide.png/
+ )
+ await browser.eval('document.getElementById("toggle").click()')
+ // The normal `onLoad()` is triggered by lazy placeholder image
+ // so ideally this would be "2" instead of "3" count
+ await check(
+ () => browser.eval(`document.getElementById("msg8").textContent`),
+ 'loaded 3 img8 with native onLoad'
+ )
+ await check(
+ () =>
+ browser.eval(
+ `document.getElementById("img8").getAttribute("data-nimg")`
+ ),
+ 'fixed'
+ )
+ await check(
+ () => browser.eval(`document.getElementById("img8").currentSrc`),
+ /test-rect.jpg/
+ )
+ })
+
it('should work with image with blob src', async () => {
let browser
try {