Skip to content

Commit 3a459ca

Browse files
authored
chore(next/image)!: mark onLoadingComplete as deprecated in favor of onLoad (#56944)
## History We used to pass `onLoad` through directly to the underlying img so `onLoadingComplete` was introduced in order to handle the case when `placeholder="blur"` was used and `onLoad` would trigger before the placeholder was removed. We have since changed the behavior of `onLoad` so that it acts the same as `onLoadingComplete` and therefore `onLoadingComplete` is no longer needed. ## What is this PR doing? This PR marks `onLoadingComplete` as deprecated in favor of `onLoad`. In the future, we may remove `onLoadingComplete`.
1 parent 6ed4fdd commit 3a459ca

File tree

6 files changed

+54
-24
lines changed

6 files changed

+54
-24
lines changed

docs/02-app/02-api-reference/01-components/image.mdx

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,24 +40,24 @@ export default function Page() {
4040
Here's a summary of the props available for the Image Component:
4141

4242
<div style={{ overflowX: 'auto', width: '100%' }}>
43-
| Prop | Example | Type | Required |
44-
| ----------------------------------------- | ------------------------------------ | --------------- | -------- |
45-
| [`src`](#src) | `src="/profile.png"` | String | Yes |
46-
| [`width`](#width) | `width={500}` | Integer (px) | Yes |
47-
| [`height`](#height) | `height={500}` | Integer (px) | Yes |
48-
| [`alt`](#alt) | `alt="Picture of the author"` | String | Yes |
49-
| [`loader`](#loader) | `loader={imageLoader}` | Function | - |
50-
| [`fill`](#fill) | `fill={true}` | Boolean | - |
51-
| [`sizes`](#sizes) | `sizes="(max-width: 768px) 100vw"` | String | - |
52-
| [`quality`](#quality) | `quality={80}` | Integer (1-100) | - |
53-
| [`priority`](#priority) | `priority={true}` | Boolean | - |
54-
| [`placeholder`](#placeholder) | `placeholder="blur"` | String | - |
55-
| [`style`](#style) | `style={{objectFit: "contain"}}` | Object | - |
56-
| [`onLoadingComplete`](#onloadingcomplete) | `onLoadingComplete={img => done())}` | Function | - |
57-
| [`onLoad`](#onload) | `onLoad={event => done())}` | Function | - |
58-
| [`onError`](#onerror) | `onError(event => fail()}` | Function | - |
59-
| [`loading`](#loading) | `loading="lazy"` | String | - |
60-
| [`blurDataURL`](#blurdataurl) | `blurDataURL="data:image/jpeg..."` | String | - |
43+
| Prop | Example | Type | Status |
44+
| ----------------------------------------- | ------------------------------------ | --------------- | ---------- |
45+
| [`src`](#src) | `src="/profile.png"` | String | Required |
46+
| [`width`](#width) | `width={500}` | Integer (px) | Required |
47+
| [`height`](#height) | `height={500}` | Integer (px) | Required |
48+
| [`alt`](#alt) | `alt="Picture of the author"` | String | Required |
49+
| [`loader`](#loader) | `loader={imageLoader}` | Function | - |
50+
| [`fill`](#fill) | `fill={true}` | Boolean | - |
51+
| [`sizes`](#sizes) | `sizes="(max-width: 768px) 100vw"` | String | - |
52+
| [`quality`](#quality) | `quality={80}` | Integer (1-100) | - |
53+
| [`priority`](#priority) | `priority={true}` | Boolean | - |
54+
| [`placeholder`](#placeholder) | `placeholder="blur"` | String | - |
55+
| [`style`](#style) | `style={{objectFit: "contain"}}` | Object | - |
56+
| [`onLoadingComplete`](#onloadingcomplete) | `onLoadingComplete={img => done())}` | Function | Deprecated |
57+
| [`onLoad`](#onload) | `onLoad={event => done())}` | Function | - |
58+
| [`onError`](#onerror) | `onError(event => fail()}` | Function | - |
59+
| [`loading`](#loading) | `loading="lazy"` | String | - |
60+
| [`blurDataURL`](#blurdataurl) | `blurDataURL="data:image/jpeg..."` | String | - |
6161
</div>
6262

6363
## Required Props
@@ -286,6 +286,8 @@ Remember that the required width and height props can interact with your styling
286286
<Image onLoadingComplete={(img) => console.log(img.naturalWidth)} />
287287
```
288288

289+
> **Warning**: Deprecated since Next.js 14 in favor of [`onLoad`](#onload).
290+
289291
A callback function that is invoked once the image is completely loaded and the [placeholder](#placeholder) has been removed.
290292

291293
The callback function will be called with one argument, a reference to the underlying `<img>` element.
@@ -302,9 +304,9 @@ The callback function will be called with one argument, a reference to the under
302304
<Image onLoad={(e) => console.log(e.target.naturalWidth)} />
303305
```
304306

305-
A callback function that is invoked when the image is loaded.
307+
A callback function that is invoked once the image is completely loaded and the [placeholder](#placeholder) has been removed.
306308

307-
The load event might occur before the image placeholder is removed and the image is fully decoded. If you want to wait until the image has fully loaded, use [`onLoadingComplete`](#onloadingcomplete) instead.
309+
The callback function will be called with one argument, the Event which has a `target` that references the underlying `<img>` element.
308310

309311
<AppOnly>
310312

@@ -445,7 +447,7 @@ The `**` syntax does not work in the middle of the pattern.
445447

446448
### `domains`
447449

448-
> **Warning**: We recommend configuring strict [`remotePatterns`](#remotepatterns) instead of `domains` in order to protect your application from malicious users. Only use `domains` if you own all the content served from the domain.
450+
> **Warning**: Deprecated since Next.js 14. We recommend configuring strict [`remotePatterns`](#remotepatterns) instead of `domains` in order to protect your application from malicious users. Only use `domains` if you own all the content served from the domain.
449451
450452
Similar to [`remotePatterns`](#remotepatterns), the `domains` configuration can be used to provide a list of allowed hostnames for external images.
451453

examples/with-cloudinary/components/SharedModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export default function SharedModal({
8181
height={navigation ? 853 : 1280}
8282
priority
8383
alt="Next.js Conf image"
84-
onLoadingComplete={() => setLoaded(true)}
84+
onLoad={() => setLoaded(true)}
8585
/>
8686
</motion.div>
8787
</AnimatePresence>

examples/with-sfcc/components/ProductCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default function ProductCard({ product }) {
2222
? 'scale-110 blur-2xl grayscale'
2323
: 'scale-100 blur-0 grayscale-0'
2424
)}
25-
onLoadingComplete={() => setLoading(false)}
25+
onLoad={() => setLoading(false)}
2626
/>
2727
</div>
2828
<div className="mt-4 flex items-center justify-between text-base font-medium text-gray-900">

packages/next/src/shared/lib/get-img-props.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ export type ImageProps = Omit<
3838
placeholder?: PlaceholderValue
3939
blurDataURL?: string
4040
unoptimized?: boolean
41+
/**
42+
* @deprecated Use `onLoad` instead.
43+
* @see https://nextjs.org/docs/app/api-reference/components/image#onload
44+
*/
4145
onLoadingComplete?: OnLoadingComplete
4246
/**
4347
* @deprecated Use `fill` prop instead of `layout="fill"` or change import to `next/legacy/image`.
@@ -500,7 +504,7 @@ export function getImgProps(
500504
}
501505
if ('ref' in rest) {
502506
warnOnce(
503-
`Image with src "${src}" is using unsupported "ref" property. Consider using the "onLoadingComplete" property instead.`
507+
`Image with src "${src}" is using unsupported "ref" property. Consider using the "onLoad" property instead.`
504508
)
505509
}
506510

@@ -523,6 +527,12 @@ export function getImgProps(
523527
}
524528
}
525529

530+
if (onLoadingComplete) {
531+
warnOnce(
532+
`Image with src "${src}" is using deprecated "onLoadingComplete" property. Please use the "onLoad" property instead.`
533+
)
534+
}
535+
526536
for (const [legacyKey, legacyValue] of Object.entries({
527537
layout,
528538
objectFit,

test/integration/next-image-new/app-dir/test/index.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,15 @@ function runTests(mode) {
357357
() => browser.eval(`document.getElementById("msg9").textContent`),
358358
'loaded 1 img9 with dimensions 400x400'
359359
)
360+
361+
if (mode === 'dev') {
362+
const warnings = (await browser.log('browser'))
363+
.map((log) => log.message)
364+
.join('\n')
365+
expect(warnings).toMatch(
366+
/Image with src "(.*)" is using deprecated "onLoadingComplete" property/gm
367+
)
368+
}
360369
})
361370

362371
it('should callback native onLoad with sythetic event', async () => {

test/integration/next-image-new/default/test/index.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,15 @@ function runTests(mode) {
358358
() => browser.eval(`document.getElementById("msg9").textContent`),
359359
'loaded 1 img9 with dimensions 400x400'
360360
)
361+
362+
if (mode === 'dev') {
363+
const warnings = (await browser.log('browser'))
364+
.map((log) => log.message)
365+
.join('\n')
366+
expect(warnings).toMatch(
367+
/Image with src "(.*)" is using deprecated "onLoadingComplete" property/gm
368+
)
369+
}
361370
})
362371

363372
it('should callback native onLoad with sythetic event', async () => {

0 commit comments

Comments
 (0)