Skip to content

Commit

Permalink
feat(next/image): add overrideSrc prop (vercel#64221)
Browse files Browse the repository at this point in the history
  • Loading branch information
styfle authored Apr 9, 2024
1 parent 40e58f1 commit 8c9c1d2
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 1 deletion.
38 changes: 38 additions & 0 deletions docs/02-app/02-api-reference/01-components/image.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Here's a summary of the props available for the Image Component:
| [`onError`](#onerror) | `onError(event => fail()}` | Function | - |
| [`loading`](#loading) | `loading="lazy"` | String | - |
| [`blurDataURL`](#blurdataurl) | `blurDataURL="data:image/jpeg..."` | String | - |
| [`overrideSrc`](#overridesrc) | `overrideSrc="/seo.png"` | String | - |
</div>

## Required Props
Expand Down Expand Up @@ -424,6 +425,42 @@ module.exports = {
}
```

### `overrideSrc`

When providing the `src` prop to the `<Image>` component, both the `srcset` and `src` attributes are generated automatically for the resulting `<img>`.

```jsx filename="input.js"
<Image src="/me.jpg" />
```

```html filename="output.html"
<img
srcset="
/_next/image?url=%2Fme.jpg&w=640&q=75 1x,
/_next/image?url=%2Fme.jpg&w=828&q=75 2x
"
src="/_next/image?url=%2Fme.jpg&w=828&q=75"
/>
```

In some cases, it is not desirable to have the `src` attribute generated and you may wish to override it using the `overrideSrc` prop.

For example, when upgrading an existing website from `<img>` to `<Image>`, you may wish to maintain the same `src` attribute for SEO purposes such as image ranking or avoiding recrawl.

```jsx filename="input.js"
<Image src="/me.jpg" overrideSrc="/override.jpg" />
```

```html filename="output.html"
<img
srcset="
/_next/image?url=%2Fme.jpg&w=640&q=75 1x,
/_next/image?url=%2Fme.jpg&w=828&q=75 2x
"
src="/override.jpg"
/>
```

### Other Props

Other properties on the `<Image />` component will be passed to the underlying
Expand Down Expand Up @@ -963,6 +1000,7 @@ This `next/image` component uses browser native [lazy loading](https://caniuse.c

| Version | Changes |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `v14.2.0` | `overrideSrc` prop added. |
| `v14.1.0` | `getImageProps()` is stable. |
| `v14.0.0` | `onLoadingComplete` prop and `domains` config deprecated. |
| `v13.4.14` | `placeholder` prop support for `data:/image...` |
Expand Down
4 changes: 3 additions & 1 deletion packages/next/src/shared/lib/get-img-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type ImageProps = Omit<
placeholder?: PlaceholderValue
blurDataURL?: string
unoptimized?: boolean
overrideSrc?: string
/**
* @deprecated Use `onLoad` instead.
* @see https://nextjs.org/docs/app/api-reference/components/image#onload
Expand Down Expand Up @@ -245,6 +246,7 @@ export function getImgProps(
height,
fill = false,
style,
overrideSrc,
onLoad,
onLoadingComplete,
placeholder = 'empty',
Expand Down Expand Up @@ -671,7 +673,7 @@ export function getImgProps(
style: { ...imgStyle, ...placeholderStyle },
sizes: imgAttributes.sizes,
srcSet: imgAttributes.srcSet,
src: imgAttributes.src,
src: overrideSrc || imgAttributes.src,
}
const meta = { unoptimized, priority, placeholder, fill }
return { props, meta }
Expand Down
20 changes: 20 additions & 0 deletions test/integration/next-image-new/app-dir/app/override-src/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react'
import Image from 'next/image'

const Page = () => {
return (
<div>
<p>Override Src</p>
<Image
id="override-src"
alt=""
src="/test.jpg"
overrideSrc="/myoverride"
width={400}
height={400}
/>
</div>
)
}

export default Page
23 changes: 23 additions & 0 deletions test/integration/next-image-new/app-dir/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,29 @@ function runTests(mode) {
}
})

it('should work when using overrideSrc prop', async () => {
const browser = await webdriver(appPort, '/override-src')
await check(async () => {
const result = await browser.eval(
`document.getElementById('override-src').width`
)
if (result === 0) {
throw new Error('Incorrectly loaded image')
}

return 'result-correct'
}, /result-correct/)

await check(
() => browser.eval(`document.getElementById('override-src').currentSrc`),
/test(.*)jpg/
)
await check(
() => browser.eval(`document.getElementById('override-src').src`),
/myoverride/
)
})

it('should work with sizes and automatically use responsive srcset', async () => {
const browser = await webdriver(appPort, '/sizes')
const id = 'sizes1'
Expand Down
20 changes: 20 additions & 0 deletions test/integration/next-image-new/default/pages/override-src.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react'
import Image from 'next/image'

const Page = () => {
return (
<div>
<p>Override Src</p>
<Image
id="override-src"
alt=""
src="/test.jpg"
overrideSrc="/myoverride"
width={400}
height={400}
/>
</div>
)
}

export default Page
23 changes: 23 additions & 0 deletions test/integration/next-image-new/default/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,29 @@ function runTests(mode) {
}
})

it('should work when using overrideSrc prop', async () => {
const browser = await webdriver(appPort, '/override-src')
await check(async () => {
const result = await browser.eval(
`document.getElementById('override-src').width`
)
if (result === 0) {
throw new Error('Incorrectly loaded image')
}

return 'result-correct'
}, /result-correct/)

await check(
() => browser.eval(`document.getElementById('override-src').currentSrc`),
/test(.*)jpg/
)
await check(
() => browser.eval(`document.getElementById('override-src').src`),
/myoverride/
)
})

it('should work with sizes and automatically use responsive srcset', async () => {
const browser = await webdriver(appPort, '/sizes')
const id = 'sizes1'
Expand Down
23 changes: 23 additions & 0 deletions test/unit/next-image-get-img-props.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,27 @@ describe('getImageProps()', () => {
['src', '/_next/image?url=%2Ftest.png&w=256&q=75'],
])
})
it('should override src', async () => {
const { props } = getImageProps({
alt: 'a nice desc',
src: '/test.png',
overrideSrc: '/override.png',
width: 100,
height: 200,
})
expect(warningMessages).toStrictEqual([])
expect(Object.entries(props)).toStrictEqual([
['alt', 'a nice desc'],
['loading', 'lazy'],
['width', 100],
['height', 200],
['decoding', 'async'],
['style', { color: 'transparent' }],
[
'srcSet',
'/_next/image?url=%2Ftest.png&w=128&q=75 1x, /_next/image?url=%2Ftest.png&w=256&q=75 2x',
],
['src', '/override.png'],
])
})
})

0 comments on commit 8c9c1d2

Please sign in to comment.