Skip to content

Commit abddee7

Browse files
authored
Change alt to required in next/future/image (vercel#40136)
This `alt` attribute is required by `<img>` according to the HTML spec, so we should also make it required for `next/future/image`. In the cases where it is not needed, it can be set to the empty string. https://html.spec.whatwg.org/multipage/images.html#alt
1 parent b522b94 commit abddee7

File tree

7 files changed

+77
-10
lines changed

7 files changed

+77
-10
lines changed

docs/api-reference/next/future/image.md

+12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ description: Try the latest Image Optimization with the experimental `next/futur
99

1010
| Version | Changes |
1111
| --------- | -------------------------------------------- |
12+
| `v12.3.0` | Changed `alt` property to required. |
1213
| `v12.2.4` | Support for `fill` property added. |
1314
| `v12.2.0` | Experimental `next/future/image` introduced. |
1415

@@ -43,6 +44,7 @@ Compared to `next/image`, the new `next/future/image` component has the followin
4344
- Removes `lazyBoundary` prop since there is no native equivalent
4445
- Removes `lazyRoot` prop since there is no native equivalent
4546
- Removes `loader` config in favor of [`loader`](#loader) prop
47+
- Changed `alt` prop from optional to required
4648

4749
## Known Browser Bugs
4850

@@ -175,6 +177,16 @@ The `height` property represents the _rendered_ height in pixels, so it will aff
175177

176178
Required, except for [statically imported images](/docs/basic-features/image-optimization.md#local-images) or images with the [`fill` property](#fill).
177179

180+
### alt
181+
182+
The `alt` property is used to describe the image for screen readers and search engines. It is also the fallback text if images have been disabled or an error occurs while loading the image.
183+
184+
It should contain text that could replace the image [without changing the meaning of the page](https://html.spec.whatwg.org/multipage/images.html#general-guidelines). It is not meant to supplement the image and should not repeat information that is already provided in the captions above or below the image.
185+
186+
If the image is [purely decorative](https://html.spec.whatwg.org/multipage/images.html#a-purely-decorative-image-that-doesn't-add-any-information) or [not intended for the user](https://html.spec.whatwg.org/multipage/images.html#an-image-not-intended-for-the-user), the `alt` property should be an empty string (`alt=""`).
187+
188+
[Learn more](https://html.spec.whatwg.org/multipage/images.html#alt)
189+
178190
## Optional Props
179191

180192
The `<Image />` component accepts a number of additional properties beyond those which are required. This section describes the most commonly-used properties of the Image component. Find details about more rarely-used properties in the [Advanced Props](#advanced-props) section.

packages/next/client/future/image.tsx

+8-2
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,10 @@ function isStaticImport(src: string | StaticImport): src is StaticImport {
100100

101101
export type ImageProps = Omit<
102102
JSX.IntrinsicElements['img'],
103-
'src' | 'srcSet' | 'ref' | 'width' | 'height' | 'loading'
103+
'src' | 'srcSet' | 'ref' | 'alt' | 'width' | 'height' | 'loading'
104104
> & {
105105
src: string | StaticImport
106+
alt: string
106107
width?: number | string
107108
height?: number | string
108109
fill?: boolean
@@ -116,7 +117,7 @@ export type ImageProps = Omit<
116117
onLoadingComplete?: OnLoadingComplete
117118
}
118119

119-
type ImageElementProps = Omit<ImageProps, 'src' | 'loader'> & {
120+
type ImageElementProps = Omit<ImageProps, 'src' | 'alt' | 'loader'> & {
120121
srcString: string
121122
imgAttributes: GenImgAttrsResult
122123
heightInt: number | undefined
@@ -392,6 +393,11 @@ const ImageElement = ({
392393
img
393394
)
394395
}
396+
if (img.getAttribute('alt') === null) {
397+
console.error(
398+
`Image is missing required "alt" property. Please add Alternative Text to describe the image for screen readers and search engines.`
399+
)
400+
}
395401
}
396402
if (img.complete) {
397403
handleLoading(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react'
2+
import Image from 'next/future/image'
3+
import testJPG from '../public/test.jpg'
4+
5+
export default function Page() {
6+
return (
7+
<div>
8+
<p>Missing alt</p>
9+
10+
<Image src={testJPG} />
11+
</div>
12+
)
13+
}

test/integration/image-future/default/test/index.test.ts

+10
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,16 @@ function runTests(mode) {
685685
)
686686
})
687687

688+
it('should show missing alt error', async () => {
689+
const browser = await webdriver(appPort, '/missing-alt')
690+
691+
expect(await hasRedbox(browser)).toBe(false)
692+
693+
await check(async () => {
694+
return (await browser.log()).map((log) => log.message).join('\n')
695+
}, /Image is missing required "alt" property/gm)
696+
})
697+
688698
it('should show error when missing width prop', async () => {
689699
const browser = await webdriver(appPort, '/missing-width')
690700

test/integration/image-future/typescript/pages/invalid.tsx

+17-3
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,34 @@ const Invalid = () => {
55
return (
66
<div>
77
<h1>Invalid TS</h1>
8-
<Image id="invalid-src" src={new Date()} width={500} height={500}></Image>
8+
<Image
9+
id="invalid-src"
10+
alt="invalid-src"
11+
src={new Date()}
12+
width={500}
13+
height={500}
14+
/>
915
<Image
1016
id="invalid-width"
17+
alt="invalid-width"
1118
src="https://image-optimization-test.vercel.app/test.jpg"
1219
width={new Date()}
1320
height={500}
14-
></Image>
21+
/>
1522
<Image
1623
id="invalid-placeholder"
24+
alt="invalid-placeholder"
1725
src="https://image-optimization-test.vercel.app/test.jpg"
1826
width="500"
1927
height="500"
2028
placeholder="invalid"
21-
></Image>
29+
/>
30+
<Image
31+
id="missing-alt"
32+
src="https://image-optimization-test.vercel.app/test.jpg"
33+
width={500}
34+
height={500}
35+
/>
2236
<p id="stubtext">This is the invalid usage</p>
2337
</div>
2438
)

test/integration/image-future/typescript/pages/valid.tsx

+15-3
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ const Page = () => {
1212
<p>Valid TS</p>
1313
<Image
1414
id="width-and-height-num"
15+
alt="width-and-height-num"
1516
src="https://image-optimization-test.vercel.app/test.jpg"
1617
width={500}
1718
height={500}
1819
/>
1920
<Image
2021
id="width-and-height-str"
22+
alt="width-and-height-str"
2123
src="https://image-optimization-test.vercel.app/test.jpg"
2224
width="500"
2325
height="500"
@@ -36,39 +38,49 @@ const Page = () => {
3638
/>
3739
<Image
3840
id="quality-str"
41+
alt="quality-str"
3942
src="https://image-optimization-test.vercel.app/test.jpg"
4043
quality="80"
4144
width={500}
4245
height={500}
4346
/>
4447
<Image
4548
id="data-protocol"
49+
alt="data-protocol"
4650
src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="
4751
width={100}
4852
height={100}
4953
/>
5054
<Image
5155
id="placeholder-and-blur-data-url"
56+
alt="placeholder-and-blur-data-url"
5257
src="https://image-optimization-test.vercel.app/test.jpg"
5358
width={500}
5459
height={500}
5560
placeholder="blur"
5661
blurDataURL="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="
5762
/>
58-
<Image id="no-width-and-height" src={testTall} />
63+
<Image
64+
id="no-width-and-height"
65+
alt="no-width-and-height"
66+
src={testTall}
67+
/>
5968
<Image
6069
id="object-src-with-placeholder"
70+
alt="object-src-with-placeholder"
6171
src={testTall}
6272
placeholder="blur"
6373
/>
64-
<Image id="object-src-with-svg" src={svg} />
65-
<Image id="object-src-with-avif" src={avif} />
74+
<Image id="object-src-with-svg" alt="object-src-with-svg" src={svg} />
75+
<Image id="object-src-with-avif" alt="object-src-with-avif" src={avif} />
6676
<ImageCard
6777
id="image-card"
78+
alt="image-card"
6879
src="https://image-optimization-test.vercel.app/test.jpg"
6980
/>
7081
<DynamicSrcImage
7182
id="dynamic-src"
83+
alt="dynamic-src"
7284
src="https://image-optimization-test.vercel.app/test.jpg"
7385
width={400}
7486
height={400}

test/production/typescript-basic/app/pages/image-import.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ export default function Page() {
66
return (
77
<>
88
<h1>Example Image Usage</h1>
9-
<Image src="/test.jpg" width={200} height={200} />
9+
<Image src="/test.jpg" width={200} height={200} alt="" />
1010
<hr />
11-
<FutureImage src={png} />
11+
<FutureImage src={png} alt="" />
1212
</>
1313
)
1414
}

0 commit comments

Comments
 (0)