Skip to content

Commit

Permalink
Change alt to required in next/future/image (#40136)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
styfle committed Aug 31, 2022
1 parent b522b94 commit abddee7
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 10 deletions.
12 changes: 12 additions & 0 deletions docs/api-reference/next/future/image.md
Expand Up @@ -9,6 +9,7 @@ description: Try the latest Image Optimization with the experimental `next/futur

| Version | Changes |
| --------- | -------------------------------------------- |
| `v12.3.0` | Changed `alt` property to required. |
| `v12.2.4` | Support for `fill` property added. |
| `v12.2.0` | Experimental `next/future/image` introduced. |

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

## Known Browser Bugs

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

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

### alt

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.

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.

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=""`).

[Learn more](https://html.spec.whatwg.org/multipage/images.html#alt)

## Optional Props

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.
Expand Down
10 changes: 8 additions & 2 deletions packages/next/client/future/image.tsx
Expand Up @@ -100,9 +100,10 @@ function isStaticImport(src: string | StaticImport): src is StaticImport {

export type ImageProps = Omit<
JSX.IntrinsicElements['img'],
'src' | 'srcSet' | 'ref' | 'width' | 'height' | 'loading'
'src' | 'srcSet' | 'ref' | 'alt' | 'width' | 'height' | 'loading'
> & {
src: string | StaticImport
alt: string
width?: number | string
height?: number | string
fill?: boolean
Expand All @@ -116,7 +117,7 @@ export type ImageProps = Omit<
onLoadingComplete?: OnLoadingComplete
}

type ImageElementProps = Omit<ImageProps, 'src' | 'loader'> & {
type ImageElementProps = Omit<ImageProps, 'src' | 'alt' | 'loader'> & {
srcString: string
imgAttributes: GenImgAttrsResult
heightInt: number | undefined
Expand Down Expand Up @@ -392,6 +393,11 @@ const ImageElement = ({
img
)
}
if (img.getAttribute('alt') === null) {
console.error(
`Image is missing required "alt" property. Please add Alternative Text to describe the image for screen readers and search engines.`
)
}
}
if (img.complete) {
handleLoading(
Expand Down
13 changes: 13 additions & 0 deletions test/integration/image-future/default/pages/missing-alt.js
@@ -0,0 +1,13 @@
import React from 'react'
import Image from 'next/future/image'
import testJPG from '../public/test.jpg'

export default function Page() {
return (
<div>
<p>Missing alt</p>

<Image src={testJPG} />
</div>
)
}
10 changes: 10 additions & 0 deletions test/integration/image-future/default/test/index.test.ts
Expand Up @@ -685,6 +685,16 @@ function runTests(mode) {
)
})

it('should show missing alt error', async () => {
const browser = await webdriver(appPort, '/missing-alt')

expect(await hasRedbox(browser)).toBe(false)

await check(async () => {
return (await browser.log()).map((log) => log.message).join('\n')
}, /Image is missing required "alt" property/gm)
})

it('should show error when missing width prop', async () => {
const browser = await webdriver(appPort, '/missing-width')

Expand Down
20 changes: 17 additions & 3 deletions test/integration/image-future/typescript/pages/invalid.tsx
Expand Up @@ -5,20 +5,34 @@ const Invalid = () => {
return (
<div>
<h1>Invalid TS</h1>
<Image id="invalid-src" src={new Date()} width={500} height={500}></Image>
<Image
id="invalid-src"
alt="invalid-src"
src={new Date()}
width={500}
height={500}
/>
<Image
id="invalid-width"
alt="invalid-width"
src="https://image-optimization-test.vercel.app/test.jpg"
width={new Date()}
height={500}
></Image>
/>
<Image
id="invalid-placeholder"
alt="invalid-placeholder"
src="https://image-optimization-test.vercel.app/test.jpg"
width="500"
height="500"
placeholder="invalid"
></Image>
/>
<Image
id="missing-alt"
src="https://image-optimization-test.vercel.app/test.jpg"
width={500}
height={500}
/>
<p id="stubtext">This is the invalid usage</p>
</div>
)
Expand Down
18 changes: 15 additions & 3 deletions test/integration/image-future/typescript/pages/valid.tsx
Expand Up @@ -12,12 +12,14 @@ const Page = () => {
<p>Valid TS</p>
<Image
id="width-and-height-num"
alt="width-and-height-num"
src="https://image-optimization-test.vercel.app/test.jpg"
width={500}
height={500}
/>
<Image
id="width-and-height-str"
alt="width-and-height-str"
src="https://image-optimization-test.vercel.app/test.jpg"
width="500"
height="500"
Expand All @@ -36,39 +38,49 @@ const Page = () => {
/>
<Image
id="quality-str"
alt="quality-str"
src="https://image-optimization-test.vercel.app/test.jpg"
quality="80"
width={500}
height={500}
/>
<Image
id="data-protocol"
alt="data-protocol"
src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="
width={100}
height={100}
/>
<Image
id="placeholder-and-blur-data-url"
alt="placeholder-and-blur-data-url"
src="https://image-optimization-test.vercel.app/test.jpg"
width={500}
height={500}
placeholder="blur"
blurDataURL="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="
/>
<Image id="no-width-and-height" src={testTall} />
<Image
id="no-width-and-height"
alt="no-width-and-height"
src={testTall}
/>
<Image
id="object-src-with-placeholder"
alt="object-src-with-placeholder"
src={testTall}
placeholder="blur"
/>
<Image id="object-src-with-svg" src={svg} />
<Image id="object-src-with-avif" src={avif} />
<Image id="object-src-with-svg" alt="object-src-with-svg" src={svg} />
<Image id="object-src-with-avif" alt="object-src-with-avif" src={avif} />
<ImageCard
id="image-card"
alt="image-card"
src="https://image-optimization-test.vercel.app/test.jpg"
/>
<DynamicSrcImage
id="dynamic-src"
alt="dynamic-src"
src="https://image-optimization-test.vercel.app/test.jpg"
width={400}
height={400}
Expand Down
4 changes: 2 additions & 2 deletions test/production/typescript-basic/app/pages/image-import.tsx
Expand Up @@ -6,9 +6,9 @@ export default function Page() {
return (
<>
<h1>Example Image Usage</h1>
<Image src="/test.jpg" width={200} height={200} />
<Image src="/test.jpg" width={200} height={200} alt="" />
<hr />
<FutureImage src={png} />
<FutureImage src={png} alt="" />
</>
)
}

0 comments on commit abddee7

Please sign in to comment.