Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(image-optimizer): Add support for AVIF #27432

Closed
wants to merge 11 commits into from
2 changes: 1 addition & 1 deletion docs/basic-features/image-optimization.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Since version **10.0.0**, Next.js has a built-in Image Component and Automatic I

The Next.js Image Component, [`next/image`](/docs/api-reference/next/image.md), is an extension of the HTML `<img>` element, evolved for the modern web.

The Automatic Image Optimization allows for resizing, optimizing, and serving images in modern formats like [WebP](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) when the browser supports it. This avoids shipping large images to devices with a smaller viewport. It also allows Next.js to automatically adopt future image formats and serve them to browsers that support those formats.
The Automatic Image Optimization allows for resizing, optimizing, and serving images in modern formats like [AVIF and WebP](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) when the browser supports it. This avoids shipping large images to devices with a smaller viewport. It also allows Next.js to automatically adopt future image formats and serve them to browsers that support those formats.

Automatic Image Optimization works with any image source. Even if the image is hosted by an external data source, like a CMS, it can still be optimized.

Expand Down
2 changes: 1 addition & 1 deletion docs/migrating/from-create-react-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Since version **10.0.0**, Next.js has a built-in [Image Component and Automatic

The Next.js Image Component, [`next/image`](/docs/api-reference/next/image.md), is an extension of the HTML `<img>` element, evolved for the modern web.

The Automatic Image Optimization allows for resizing, optimizing, and serving images in modern formats like [WebP](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) when the browser supports it. This avoids shipping large images to devices with a smaller viewport. It also allows Next.js to automatically adopt future image formats and serve them to browsers that support those formats.
The Automatic Image Optimization allows for resizing, optimizing, and serving images in modern formats like [AVIF and WebP](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) when the browser supports it. This avoids shipping large images to devices with a smaller viewport. It also allows Next.js to automatically adopt future image formats and serve them to browsers that support those formats.

Instead of optimizing images at build time, Next.js optimizes images on-demand, as users request them. Your build times aren't increased, whether shipping 10 images or 10 million images.

Expand Down
2 changes: 1 addition & 1 deletion docs/migrating/from-gatsby.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ Since version **10.0.0**, Next.js has a built-in [Image Component and Automatic

The Next.js Image Component, [`next/image`](/docs/api-reference/next/image.md), is an extension of the HTML `<img>` element, evolved for the modern web.

The Automatic Image Optimization allows for resizing, optimizing, and serving images in modern formats like [WebP](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) when the browser supports it. This avoids shipping large images to devices with a smaller viewport. It also allows Next.js to automatically adopt future image formats and serve them to browsers that support those formats.
The Automatic Image Optimization allows for resizing, optimizing, and serving images in modern formats like [AVIF and WebP](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) when the browser supports it. This avoids shipping large images to devices with a smaller viewport. It also allows Next.js to automatically adopt future image formats and serve them to browsers that support those formats.

### Migrating from Gatsby Image

Expand Down
8 changes: 5 additions & 3 deletions packages/next/server/image-optimizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ import { sendEtagResponse } from './send-payload'
import { getContentType, getExtension } from './serve-static'
import chalk from 'chalk'

//const AVIF = 'image/avif'
const AVIF = 'image/avif'
const WEBP = 'image/webp'
const PNG = 'image/png'
const JPEG = 'image/jpeg'
const GIF = 'image/gif'
const SVG = 'image/svg+xml'
const CACHE_VERSION = 3
const MODERN_TYPES = [/* AVIF, */ WEBP]
const MODERN_TYPES = [AVIF, WEBP]
const ANIMATABLE_TYPES = [WEBP, PNG, GIF]
const VECTOR_TYPES = [SVG]
const BLUR_IMG_SIZE = 8 // should match `next-image-loader`
Expand Down Expand Up @@ -352,7 +352,9 @@ export async function imageOptimizer(
transformer.resize(width)
}

if (contentType === WEBP) {
if (contentType === AVIF) {
transformer.avif({ quality })
} else if (contentType === WEBP) {
Comment on lines +355 to +357
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you've just added the support for the case when sharp exists in deps, but what about the squoosh fallback?
in the official squoosh repository, I do see that AVIF is supported ;) should we bring it to Next repo? @styfle @timneutkens wdyt?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it will need to be in both. I'm going to update squoosh soon after we get a few bugs fixed

transformer.webp({ quality })
} else if (contentType === PNG) {
transformer.png({ quality })
Expand Down
Binary file not shown.
53 changes: 38 additions & 15 deletions test/integration/image-optimizer/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -319,21 +319,44 @@ function runTests({ w, isDev, domains = [], ttl, isSharp }) {
// FIXME: await expectWidth(res, w)
})

it('should resize relative url and Chrome accept header as webp', async () => {
const query = { url: '/test.png', w, q: 80 }
const opts = {
headers: { accept: 'image/avif,image/webp,image/apng,image/*,*/*;q=0.8' },
}
const res = await fetchViaHTTP(appPort, '/_next/image', query, opts)
expect(res.status).toBe(200)
expect(res.headers.get('Content-Type')).toBe('image/webp')
expect(res.headers.get('Cache-Control')).toBe(
`public, max-age=0, must-revalidate`
)
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
await expectWidth(res, w)
})
if (!isSharp) {
it('should resize relative url and Chrome accept header as webp', async () => {
const query = { url: '/test.png', w, q: 80 }
const opts = {
headers: {
accept: 'image/avif,image/webp,image/apng,image/*,*/*;q=0.8',
},
}
const res = await fetchViaHTTP(appPort, '/_next/image', query, opts)
expect(res.status).toBe(200)
expect(res.headers.get('Content-Type')).toBe('image/webp')
expect(res.headers.get('Cache-Control')).toBe(
`public, max-age=0, must-revalidate`
)
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
await expectWidth(res, w)
})
}

if (isSharp) {
it('should resize relative url and Chrome accept header as avif', async () => {
const query = { url: '/test.png', w, q: 80 }
const opts = {
headers: {
accept: 'image/avif,image/webp,image/apng,image/*,*/*;q=0.8',
},
}
const res = await fetchViaHTTP(appPort, '/_next/image', query, opts)
expect(res.status).toBe(200)
expect(res.headers.get('Content-Type')).toBe('image/avif')
expect(res.headers.get('cache-control')).toBe(
'public, max-age=0, must-revalidate'
)
expect(res.headers.get('etag')).toBeTruthy()
// FIXME: await expectWidth(res, w)
})
}

if (domains.includes('localhost')) {
it('should resize absolute url from localhost', async () => {
Expand Down