Skip to content

Commit 4cc7f11

Browse files
authored
Update to stable: next/future/image, remotePatterns, unoptimized (vercel#40142)
This PR updates a few features from experimental to stable status: - `next/future/image` component - `remotePatterns` configuration - `unoptimized` configuration
1 parent 643447e commit 4cc7f11

File tree

33 files changed

+133
-270
lines changed

33 files changed

+133
-270
lines changed

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

+27-47
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,23 @@
11
---
2-
description: Try the latest Image Optimization with the experimental `next/future/image` component.
2+
description: Try the latest Image Optimization with the new `next/future/image` component.
33
---
44

55
# next/future/image
66

77
<details>
88
<summary><b>Version History</b></summary>
99

10-
| Version | Changes |
11-
| --------- | -------------------------------------------- |
12-
| `v12.3.0` | Changed `alt` property to required. |
13-
| `v12.2.4` | Support for `fill` property added. |
14-
| `v12.2.0` | Experimental `next/future/image` introduced. |
10+
| Version | Changes |
11+
| --------- | --------------------------------------------------------------------------------------------------------------------------- |
12+
| `v12.3.0` | `next/future/image` component stable. `remotePatterns` config stable. `unoptimized` config stable. `alt` property required. |
13+
| `v12.2.4` | `fill` property added. |
14+
| `v12.2.0` | Experimental `next/future/image` component introduced. |
1515

1616
</details>
1717

18-
The `next/future/image` component is an experiment to improve both the performance and developer experience of `next/image` by using the native `<img>` element with better default behavior.
18+
The `next/future/image` component improves both the performance and developer experience of `next/image` by using the native `<img>` element with better default behavior.
1919

20-
This new component is considered experimental and therefore not covered by semver, and may cause unexpected or broken application behavior. This component uses browser native [lazy loading](https://caniuse.com/loading-lazy-attr), which may fallback to eager loading for older browsers before Safari 15.4. When using the blur-up placeholder, older browsers before Safari 12 will fallback to empty placeholder. When using styles with `width`/`height` of `auto`, it is possible to cause [Layout Shift](https://web.dev/cls/) on older browsers before Safari 15 that don't [preserve the aspect ratio](https://caniuse.com/mdn-html_elements_img_aspect_ratio_computed_from_attributes). For more details, see [this MDN video](https://www.youtube.com/watch?v=4-d_SoCHeWE).
21-
22-
To use `next/future/image`, add the following to your `next.config.js` file:
23-
24-
```js
25-
module.exports = {
26-
experimental: {
27-
images: {
28-
allowFutureImage: true,
29-
},
30-
},
31-
}
32-
```
20+
This component uses browser native [lazy loading](https://caniuse.com/loading-lazy-attr), which may fallback to eager loading for older browsers before Safari 15.4. When using the blur-up placeholder, older browsers before Safari 12 will fallback to empty placeholder. When using styles with `width`/`height` of `auto`, it is possible to cause [Layout Shift](https://web.dev/cls/) on older browsers before Safari 15 that don't [preserve the aspect ratio](https://caniuse.com/mdn-html_elements_img_aspect_ratio_computed_from_attributes). For more details, see [this MDN video](https://www.youtube.com/watch?v=4-d_SoCHeWE).
3321

3422
## Comparison
3523

@@ -374,14 +362,12 @@ You can also [generate a solid color Data URL](https://png-pixel.com) to match t
374362
When true, the source image will be served as-is instead of changing quality,
375363
size, or format. Defaults to `false`.
376364

377-
This prop can be assigned to all images by updating `next.config.js` with the following experimental configuration:
365+
This prop can be assigned to all images by updating `next.config.js` with the following configuration:
378366

379367
```js
380368
module.exports = {
381-
experimental: {
382-
images: {
383-
unoptimized: true,
384-
},
369+
images: {
370+
unoptimized: true,
385371
},
386372
}
387373
```
@@ -399,23 +385,19 @@ Other properties on the `<Image />` component will be passed to the underlying
399385

400386
### Remote Patterns
401387

402-
> Note: The `remotePatterns` configuration is currently **experimental** and subject to change. Please use [`domains`](#domains) for production use cases.
403-
404388
To protect your application from malicious users, configuration is required in order to use external images. This ensures that only external images from your account can be served from the Next.js Image Optimization API. These external images can be configured with the `remotePatterns` property in your `next.config.js` file, as shown below:
405389

406390
```js
407391
module.exports = {
408-
experimental: {
409-
images: {
410-
remotePatterns: [
411-
{
412-
protocol: 'https',
413-
hostname: 'example.com',
414-
port: '',
415-
pathname: '/account123/**',
416-
},
417-
],
418-
},
392+
images: {
393+
remotePatterns: [
394+
{
395+
protocol: 'https',
396+
hostname: 'example.com',
397+
port: '',
398+
pathname: '/account123/**',
399+
},
400+
],
419401
},
420402
}
421403
```
@@ -426,15 +408,13 @@ Below is another example of the `remotePatterns` property in the `next.config.js
426408

427409
```js
428410
module.exports = {
429-
experimental: {
430-
images: {
431-
remotePatterns: [
432-
{
433-
protocol: 'https',
434-
hostname: '**.example.com',
435-
},
436-
],
437-
},
411+
images: {
412+
remotePatterns: [
413+
{
414+
protocol: 'https',
415+
hostname: '**.example.com',
416+
},
417+
],
438418
},
439419
}
440420
```

docs/api-reference/next/image.md

+20-29
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ description: Enable Image Optimization with the built-in Image component.
1616

1717
| Version | Changes |
1818
| --------- | --------------------------------------------------------------------------------------------------------- |
19+
| `v12.3.0` | `remotePatterns` and `unoptimized` configuration is stable. |
1920
| `v12.2.0` | Experimental `remotePatterns` and experimental `unoptimized` configuration added. `layout="raw"` removed. |
2021
| `v12.1.1` | `style` prop added. Experimental[\*](#experimental-raw-layout-mode) support for `layout="raw"` added. |
2122
| `v12.1.0` | `dangerouslyAllowSVG` and `contentSecurityPolicy` configuration added. |
@@ -93,8 +94,6 @@ The layout behavior of the image as the viewport changes size.
9394
- When `fill`, the image will stretch both width and height to the dimensions of the parent element, provided the parent element is relative.
9495
- This is usually paired with the [`objectFit`](#objectFit) property.
9596
- Ensure the parent element has `position: relative` in their stylesheet.
96-
- When `raw`[\*](#experimental-raw-layout-mode), the image will be rendered as a single image element with no wrappers, sizers or other responsive behavior.
97-
- If your image styling will change the size of a `raw` image, you should include the `sizes` property for proper image serving. Otherwise your image will be requested as though it has fixed width and height.
9897
- [Demo background image](https://image-component.nextjs.gallery/background)
9998

10099
### loader
@@ -324,14 +323,12 @@ const Example = () => {
324323
When true, the source image will be served as-is instead of changing quality,
325324
size, or format. Defaults to `false`.
326325

327-
This prop can be assigned to all images by updating `next.config.js` with the following experimental configuration:
326+
This prop can be assigned to all images by updating `next.config.js` with the following configuration:
328327

329328
```js
330329
module.exports = {
331-
experimental: {
332-
images: {
333-
unoptimized: true,
334-
},
330+
images: {
331+
unoptimized: true,
335332
},
336333
}
337334
```
@@ -351,23 +348,19 @@ Other properties on the `<Image />` component will be passed to the underlying
351348

352349
### Remote Patterns
353350

354-
> Note: The `remotePatterns` configuration is currently **experimental** and subject to change. Please use [`domains`](#domains) for production use cases.
355-
356351
To protect your application from malicious users, configuration is required in order to use external images. This ensures that only external images from your account can be served from the Next.js Image Optimization API. These external images can be configured with the `remotePatterns` property in your `next.config.js` file, as shown below:
357352

358353
```js
359354
module.exports = {
360-
experimental: {
361-
images: {
362-
remotePatterns: [
363-
{
364-
protocol: 'https',
365-
hostname: 'example.com',
366-
port: '',
367-
pathname: '/account123/**',
368-
},
369-
],
370-
},
355+
images: {
356+
remotePatterns: [
357+
{
358+
protocol: 'https',
359+
hostname: 'example.com',
360+
port: '',
361+
pathname: '/account123/**',
362+
},
363+
],
371364
},
372365
}
373366
```
@@ -378,15 +371,13 @@ Below is another example of the `remotePatterns` property in the `next.config.js
378371

379372
```js
380373
module.exports = {
381-
experimental: {
382-
images: {
383-
remotePatterns: [
384-
{
385-
protocol: 'https',
386-
hostname: '**.example.com',
387-
},
388-
],
389-
},
374+
images: {
375+
remotePatterns: [
376+
{
377+
protocol: 'https',
378+
hostname: '**.example.com',
379+
},
380+
],
390381
},
391382
}
392383
```

docs/manifest.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@
427427
}
428428
},
429429
{
430-
"title": "next/future/image (experimental)",
430+
"title": "next/future/image",
431431
"path": "/docs/api-reference/next/future/image.md"
432432
},
433433
{

errors/invalid-images-config.md

+4-11
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,10 @@ module.exports = {
3131
dangerouslyAllowSVG: false,
3232
// set the Content-Security-Policy header
3333
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
34-
// the following are experimental features, and may cause breaking changes
35-
},
36-
experimental: {
37-
images: {
38-
// limit of 50 objects
39-
remotePatterns: [],
40-
// when true, every image will be unoptimized
41-
unoptimized: false,
42-
// when true, allow `next/future/image` to be imported
43-
allowFutureImage: false,
44-
},
34+
// limit of 50 objects
35+
remotePatterns: [],
36+
// when true, every image will be unoptimized
37+
unoptimized: false,
4538
},
4639
}
4740
```

examples/cms-sanity/next.config.js

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
/** @type {import('next').NextConfig} */
22
module.exports = {
3-
experimental: {
4-
images: {
5-
allowFutureImage: true,
6-
},
7-
},
83
images: {
9-
domains: ['cdn.sanity.io', 'source.unsplash.com'],
4+
remotePatterns: [
5+
{ hostname: 'cdn.sanity.io' },
6+
{ hostname: 'source.unsplash.com' },
7+
],
108
},
119
}

packages/next/build/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2405,7 +2405,7 @@ export default async function build(
24052405
const { deviceSizes, imageSizes } = images
24062406
;(images as any).sizes = [...deviceSizes, ...imageSizes]
24072407
;(images as any).remotePatterns = (
2408-
config?.experimental?.images?.remotePatterns || []
2408+
config?.images?.remotePatterns || []
24092409
).map((p: RemotePattern) => ({
24102410
// Should be the same as matchRemotePattern()
24112411
protocol: p.protocol,

packages/next/build/webpack-config.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,12 @@ export function getDefineEnv({
196196
path: config.images.path,
197197
loader: config.images.loader,
198198
dangerouslyAllowSVG: config.images.dangerouslyAllowSVG,
199-
experimentalUnoptimized: config?.experimental?.images?.unoptimized,
200-
experimentalFuture: config.experimental?.images?.allowFutureImage,
199+
unoptimized: config?.images?.unoptimized,
201200
...(dev
202201
? {
203202
// pass domains in development to allow validating on the client
204203
domains: config.images.domains,
205-
experimentalRemotePatterns:
206-
config.experimental?.images?.remotePatterns,
204+
remotePatterns: config.images?.remotePatterns,
207205
}
208206
: {}),
209207
}),

packages/next/client/future/image.tsx

+3-16
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@ import {
1515
import { ImageConfigContext } from '../../shared/lib/image-config-context'
1616
import { warnOnce } from '../../shared/lib/utils'
1717

18-
const {
19-
experimentalFuture = false,
20-
experimentalRemotePatterns = [],
21-
experimentalUnoptimized,
22-
} = (process.env.__NEXT_IMAGE_OPTS as any) || {}
2318
const configEnv = process.env.__NEXT_IMAGE_OPTS as any as ImageConfigComplete
2419
const allImgs = new Map<
2520
string,
@@ -475,10 +470,7 @@ function defaultLoader({
475470
)
476471
}
477472

478-
if (
479-
!src.startsWith('/') &&
480-
(config.domains || experimentalRemotePatterns)
481-
) {
473+
if (!src.startsWith('/') && (config.domains || config.remotePatterns)) {
482474
let parsedSrc: URL
483475
try {
484476
parsedSrc = new URL(src)
@@ -492,7 +484,7 @@ function defaultLoader({
492484
if (process.env.NODE_ENV !== 'test') {
493485
// We use dynamic require because this should only error in development
494486
const { hasMatch } = require('../../shared/lib/match-remote-pattern')
495-
if (!hasMatch(config.domains, experimentalRemotePatterns, parsedSrc)) {
487+
if (!hasMatch(config.domains, config.remotePatterns, parsedSrc)) {
496488
throw new Error(
497489
`Invalid src prop (${src}) on \`next/image\`, hostname "${parsedSrc.hostname}" is not configured under images in your \`next.config.js\`\n` +
498490
`See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host`
@@ -530,11 +522,6 @@ export default function Image({
530522
blurDataURL,
531523
...all
532524
}: ImageProps) {
533-
if (!experimentalFuture && process.env.NODE_ENV !== 'test') {
534-
throw new Error(
535-
`The "next/future/image" component is experimental and may be subject to breaking changes. To enable this experiment, please include \`experimental: { images: { allowFutureImage: true } }\` in your next.config.js file.`
536-
)
537-
}
538525
const configContext = useContext(ImageConfigContext)
539526
const config: ImageConfig = useMemo(() => {
540527
const c = configEnv || configContext || imageConfigDefault
@@ -600,7 +587,7 @@ export default function Image({
600587
unoptimized = true
601588
isLazy = false
602589
}
603-
if (experimentalUnoptimized) {
590+
if (config.unoptimized) {
604591
unoptimized = true
605592
}
606593

packages/next/client/image.tsx

+3-8
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ function normalizeSrc(src: string): string {
2222
return src[0] === '/' ? src.slice(1) : src
2323
}
2424

25-
const { experimentalRemotePatterns = [], experimentalUnoptimized } =
26-
(process.env.__NEXT_IMAGE_OPTS as any) || {}
2725
const configEnv = process.env.__NEXT_IMAGE_OPTS as any as ImageConfigComplete
2826
const loadedImageURLs = new Set<string>()
2927
const allImgs = new Map<
@@ -137,10 +135,7 @@ function defaultLoader({
137135
)
138136
}
139137

140-
if (
141-
!src.startsWith('/') &&
142-
(config.domains || experimentalRemotePatterns)
143-
) {
138+
if (!src.startsWith('/') && (config.domains || config.remotePatterns)) {
144139
let parsedSrc: URL
145140
try {
146141
parsedSrc = new URL(src)
@@ -154,7 +149,7 @@ function defaultLoader({
154149
if (process.env.NODE_ENV !== 'test') {
155150
// We use dynamic require because this should only error in development
156151
const { hasMatch } = require('../shared/lib/match-remote-pattern')
157-
if (!hasMatch(config.domains, experimentalRemotePatterns, parsedSrc)) {
152+
if (!hasMatch(config.domains, config.remotePatterns, parsedSrc)) {
158153
throw new Error(
159154
`Invalid src prop (${src}) on \`next/image\`, hostname "${parsedSrc.hostname}" is not configured under images in your \`next.config.js\`\n` +
160155
`See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host`
@@ -673,7 +668,7 @@ export default function Image({
673668
if (typeof window !== 'undefined' && loadedImageURLs.has(src)) {
674669
isLazy = false
675670
}
676-
if (experimentalUnoptimized) {
671+
if (config.unoptimized) {
677672
unoptimized = true
678673
}
679674

packages/next/export/index.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -321,8 +321,7 @@ export default async function exportApp(
321321

322322
const {
323323
i18n,
324-
images: { loader = 'default' },
325-
experimental,
324+
images: { loader = 'default', unoptimized },
326325
} = nextConfig
327326

328327
if (i18n && !options.buildExport) {
@@ -344,7 +343,7 @@ export default async function exportApp(
344343
if (
345344
isNextImageImported &&
346345
loader === 'default' &&
347-
!experimental?.images?.unoptimized &&
346+
!unoptimized &&
348347
!hasNextSupport
349348
) {
350349
throw new Error(

0 commit comments

Comments
 (0)