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

Remove layout raw image experiment #38006

Merged
merged 5 commits into from
Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 23 additions & 34 deletions docs/api-reference/next/image.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ description: Enable Image Optimization with the built-in Image component.
<details>
<summary><b>Version History</b></summary>

| Version | Changes |
| --------- | ----------------------------------------------------------------------------------------------------- |
| `v12.2.0` | Experimental `remotePatterns` and experimental `unoptimized` configuration added. |
| `v12.1.1` | `style` prop added. Experimental[\*](#experimental-raw-layout-mode) support for `layout="raw"` added. |
| `v12.1.0` | `dangerouslyAllowSVG` and `contentSecurityPolicy` configuration added. |
| `v12.0.9` | `lazyRoot` prop added. |
| `v12.0.0` | `formats` configuration added.<br/>AVIF support added.<br/>Wrapper `<div>` changed to `<span>`. |
| `v11.1.0` | `onLoadingComplete` and `lazyBoundary` props added. |
| `v11.0.0` | `src` prop support for static import.<br/>`placeholder` prop added.<br/>`blurDataURL` prop added. |
| `v10.0.5` | `loader` prop added. |
| `v10.0.1` | `layout` prop added. |
| `v10.0.0` | `next/image` introduced. |
| Version | Changes |
| --------- | --------------------------------------------------------------------------------------------------------- |
| `v12.2.0` | Experimental `remotePatterns` and experimental `unoptimized` configuration added. `layout="raw"` removed. |
| `v12.1.1` | `style` prop added. Experimental[\*](#experimental-raw-layout-mode) support for `layout="raw"` added. |
| `v12.1.0` | `dangerouslyAllowSVG` and `contentSecurityPolicy` configuration added. |
| `v12.0.9` | `lazyRoot` prop added. |
| `v12.0.0` | `formats` configuration added.<br/>AVIF support added.<br/>Wrapper `<div>` changed to `<span>`. |
| `v11.1.0` | `onLoadingComplete` and `lazyBoundary` props added. |
| `v11.0.0` | `src` prop support for static import.<br/>`placeholder` prop added.<br/>`blurDataURL` prop added. |
| `v10.0.5` | `loader` prop added. |
| `v10.0.1` | `layout` prop added. |
| `v10.0.0` | `next/image` introduced. |

</details>

Expand All @@ -51,7 +51,7 @@ When using an external URL, you must add it to

The `width` property can represent either the _rendered_ width or _original_ width in pixels, depending on the [`layout`](#layout) and [`sizes`](#sizes) properties.

When using `layout="intrinsic"`, `layout="fixed"`, or `layout="raw"`, the `width` property represents the _rendered_ width in pixels, so it will affect how large the image appears.
When using `layout="intrinsic"` or `layout="fixed"` the `width` property represents the _rendered_ width in pixels, so it will affect how large the image appears.

When using `layout="responsive"`, `layout="fill"`, the `width` property represents the _original_ width in pixels, so it will only affect the aspect ratio.

Expand All @@ -61,7 +61,7 @@ The `width` property is required, except for [statically imported images](/docs/

The `height` property can represent either the _rendered_ height or _original_ height in pixels, depending on the [`layout`](#layout) and [`sizes`](#sizes) properties.

When using `layout="intrinsic"`, `layout="fixed"`, or `layout="raw"`, the `height` property represents the _rendered_ height in pixels, so it will affect how large the image appears.
When using `layout="intrinsic"` or `layout="fixed"` the `height` property represents the _rendered_ height in pixels, so it will affect how large the image appears.

When using `layout="responsive"`, `layout="fill"`, the `height` property represents the _original_ height in pixels, so it will only affect the aspect ratio.

Expand All @@ -75,13 +75,12 @@ The `<Image />` component accepts a number of additional properties beyond those

The layout behavior of the image as the viewport changes size.

| `layout` | Behavior | `srcSet` | `sizes` | Has wrapper and sizer |
| ---------------------------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -------- | --------------------- |
| `intrinsic` (default) | Scale *down* to fit width of container, up to image size | `1x``2x` (based on [imageSizes](#image-sizes)) | N/A | yes |
| `fixed` | Sized to `width` and `height` exactly | `1x``2x` (based on [imageSizes](#image-sizes)) | N/A | yes |
| `responsive` | Scale to fit width of container | `640w``750w`, ... `2048w``3840w` (based on [imageSizes](#image-sizes) and [deviceSizes](#device-sizes)) | `100vw` | yes |
| `fill` | Grow in both X and Y axes to fill container | `640w``750w`, ... `2048w``3840w` (based on [imageSizes](#image-sizes) and [deviceSizes](#device-sizes)) | `100vw` | yes |
| `raw`[\*](#experimental-raw-layout-mode) | Raw `<img>` without styles and native lazy loading | Behaves like `responsive` if the image has the `sizes` prop, and like `fixed` if it does not | optional | no |
| `layout` | Behavior | `srcSet` | `sizes` | Has wrapper and sizer |
| --------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ------- | --------------------- |
| `intrinsic` (default) | Scale *down* to fit width of container, up to image size | `1x``2x` (based on [imageSizes](#image-sizes)) | N/A | yes |
| `fixed` | Sized to `width` and `height` exactly | `1x``2x` (based on [imageSizes](#image-sizes)) | N/A | yes |
| `responsive` | Scale to fit width of container | `640w``750w`, ... `2048w``3840w` (based on [imageSizes](#image-sizes) and [deviceSizes](#device-sizes)) | `100vw` | yes |
| `fill` | Grow in both X and Y axes to fill container | `640w``750w`, ... `2048w``3840w` (based on [imageSizes](#image-sizes) and [deviceSizes](#device-sizes)) | `100vw` | yes |

- [Demo the `intrinsic` layout (default)](https://image-component.nextjs.gallery/layout-intrinsic)
- When `intrinsic`, the image will scale the dimensions down for smaller viewports, but maintain the original dimensions for larger viewports.
Expand Down Expand Up @@ -134,7 +133,7 @@ const MyImage = (props) => {

A string that provides information about how wide the image will be at different breakpoints. Defaults to `100vw` (the full width of the screen) when using `layout="responsive"` or `layout="fill"`.

If you are using `layout="fill"`, `layout="responsive"`, or `layout="raw"`[\*](#experimental-raw-layout-mode) it's important to assign `sizes` for any image that takes up less than the full viewport width.
If you are using `layout="fill"` or `layout="responsive"` it's important to assign `sizes` for any image that takes up less than the full viewport width.

For example, when the parent element will constrain the image to always be less than half the viewport width, use `sizes="50vw"`. Without `sizes`, the image will be sent at twice the necessary resolution, decreasing performance.

Expand Down Expand Up @@ -179,7 +178,7 @@ In some cases, you may need more advanced usage. The `<Image />` component optio

Allows [passing CSS styles](https://reactjs.org/docs/dom-elements.html#style) to the underlying image element.

Note that all `layout` modes other than `"raw"`[\*](#experimental-raw-layout-mode) apply their own styles to the image element, and these automatic styles take precedence over the `style` prop.
Note that all `layout` modes apply their own styles to the image element, and these automatic styles take precedence over the `style` prop.

Also keep in mind that the required `width` and `height` props can interact with your styling. If you use styling to modify an image's `width`, you must set the `height="auto"` style as well, or your image will be distorted.

Expand Down Expand Up @@ -554,17 +553,7 @@ module.exports = {

### Experimental "raw" layout mode

The image component currently supports an additional `layout="raw"` mode, which renders the image without wrappers or styling. This layout mode is currently an experimental feature, while user feedback is gathered. As there is the possibility of breaking changes to the `layout="raw"` interface, the feature is locked behind an experimental feature flag. If you would like to use the `raw` layout mode, you must add the following to your `next.config.js`:

```js
module.exports = {
experimental: {
images: {
layoutRaw: true,
},
},
}
```
The `layout="raw"` experiment has been moved to a new module. Please use [`next/future/image`](/docs/api-reference/next/future/image.md) instead.

### Animated Images

Expand Down
106 changes: 19 additions & 87 deletions packages/next/client/image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@ import { ImageConfigContext } from '../shared/lib/image-config-context'
import { warnOnce } from '../shared/lib/utils'
import { normalizePathTrailingSlash } from './normalize-trailing-slash'

const {
experimentalLayoutRaw = false,
experimentalRemotePatterns = [],
experimentalUnoptimized,
} = (process.env.__NEXT_IMAGE_OPTS as any) || {}
const { experimentalRemotePatterns = [], experimentalUnoptimized } =
(process.env.__NEXT_IMAGE_OPTS as any) || {}
const configEnv = process.env.__NEXT_IMAGE_OPTS as any as ImageConfigComplete
const loadedImageURLs = new Set<string>()
const allImgs = new Map<
Expand Down Expand Up @@ -74,7 +71,6 @@ const VALID_LAYOUT_VALUES = [
'fixed',
'intrinsic',
'responsive',
'raw',
undefined,
] as const
type LayoutValue = typeof VALID_LAYOUT_VALUES[number]
Expand Down Expand Up @@ -175,10 +171,7 @@ function getWidths(
layout: LayoutValue,
sizes: string | undefined
): { widths: number[]; kind: 'w' | 'x' } {
if (
sizes &&
(layout === 'fill' || layout === 'responsive' || layout === 'raw')
) {
if (sizes && (layout === 'fill' || layout === 'responsive')) {
// Find all the "vw" percent sizes used in the sizes prop
const viewportWidthRe = /(^|\s)(1?\d?\d)vw/g
const percentSizes = []
Expand Down Expand Up @@ -333,19 +326,6 @@ function handleLoading(
onLoadingCompleteRef.current({ naturalWidth, naturalHeight })
}
if (process.env.NODE_ENV !== 'production') {
if (layout === 'raw') {
const heightModified =
img.height.toString() !== img.getAttribute('height')
const widthModified = img.width.toString() !== img.getAttribute('width')
if (
(heightModified && !widthModified) ||
(!heightModified && widthModified)
) {
warnOnce(
`Image with src "${src}" has either width or height modified, but not the other. If you use CSS to change the size of your image, also include the styles 'width: "auto"' or 'height: "auto"' to maintain the aspect ratio.`
)
}
}
if (img.parentElement?.parentElement) {
const parent = getComputedStyle(img.parentElement.parentElement)
if (!parent.position) {
Expand Down Expand Up @@ -460,11 +440,7 @@ export default function Image({
unoptimized = true
isLazy = false
}
if (
typeof window !== 'undefined' &&
loadedImageURLs.has(src) &&
layout !== 'raw'
) {
if (typeof window !== 'undefined' && loadedImageURLs.has(src)) {
isLazy = false
}
if (experimentalUnoptimized) {
Expand All @@ -478,7 +454,7 @@ export default function Image({
rootMargin: lazyBoundary || '200px',
disabled: !isLazy,
})
const isVisible = !isLazy || isIntersected || layout === 'raw'
const isVisible = !isLazy || isIntersected

const wrapperStyle: JSX.IntrinsicElements['span']['style'] = {
boxSizing: 'border-box',
Expand Down Expand Up @@ -529,9 +505,6 @@ export default function Image({
objectPosition,
}

if (process.env.NODE_ENV !== 'production' && layout !== 'raw' && style) {
}

if (process.env.NODE_ENV !== 'production') {
if (!src) {
throw new Error(
Expand All @@ -541,17 +514,18 @@ export default function Image({
)
}
if (!VALID_LAYOUT_VALUES.includes(layout)) {
if ((layout as any) === 'raw') {
throw new Error(
`The layout="raw" experiment has been moved to a new module. Please import \`next/future/image\` instead.`
)
}
throw new Error(
`Image with src "${src}" has invalid "layout" property. Provided "${layout}" should be one of ${VALID_LAYOUT_VALUES.map(
String
).join(',')}.`
)
}
if (layout === 'raw' && !experimentalLayoutRaw) {
throw new Error(
`The "raw" layout is currently experimental and may be subject to breaking changes. To use layout="raw", include \`experimental: { images: { layoutRaw: true } }\` in your next.config.js file.`
)
}

if (
(typeof widthInt !== 'undefined' && isNaN(widthInt)) ||
(typeof heightInt !== 'undefined' && isNaN(heightInt))
Expand All @@ -577,36 +551,9 @@ export default function Image({
`Image with src "${src}" has both "priority" and "loading='lazy'" properties. Only one should be used.`
)
}
if (layout === 'raw') {
if (objectFit) {
throw new Error(
`Image with src "${src}" has "layout='raw'" and "objectFit='${objectFit}'". For raw images, these and other styles should be specified using the "style" attribute.`
)
}
if (objectPosition) {
throw new Error(
`Image with src "${src}" has "layout='raw'" and "objectPosition='${objectPosition}'". For raw images, these and other styles should be specified using the "style" attribute.`
)
}
if (lazyRoot) {
throw new Error(
`Image with src "${src}" has "layout='raw'" and "lazyRoot='${lazyRoot}'". For raw images, native lazy loading is used so "lazyRoot" cannot be used.`
)
}
if (lazyBoundary) {
throw new Error(
`Image with src "${src}" has "layout='raw'" and "lazyBoundary='${lazyBoundary}'". For raw images, native lazy loading is used so "lazyBoundary" cannot be used.`
)
}
}
if (
sizes &&
layout !== 'fill' &&
layout !== 'responsive' &&
layout !== 'raw'
) {
if (sizes && layout !== 'fill' && layout !== 'responsive') {
warnOnce(
`Image with src "${src}" has "sizes" property but it will be ignored. Only use "sizes" with "layout='fill'", "layout='responsive'", or "layout='raw'`
`Image with src "${src}" has "sizes" property but it will be ignored. Only use "sizes" with "layout='fill'" or "layout='responsive'"`
)
}
if (placeholder === 'blur') {
Expand Down Expand Up @@ -655,7 +602,7 @@ export default function Image({
}
}

if (style && layout !== 'raw') {
if (style) {
let overwrittenStyles = Object.keys(style).filter(
(key) => key in layoutStyle
)
Expand Down Expand Up @@ -704,21 +651,14 @@ export default function Image({
}
}
}
const imgStyle = Object.assign({}, style, layout === 'raw' ? {} : layoutStyle)
const svgBlurPlaceholder = `url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http%3A//www.w3.org/2000/svg' xmlns%3Axlink='http%3A//www.w3.org/1999/xlink' viewBox='0 0 ${widthInt} ${heightInt}'%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='50'%3E%3C/feGaussianBlur%3E%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='1 1'%3E%3C/feFuncA%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Cimage filter='url(%23b)' x='0' y='0' height='100%25' width='100%25' href='${blurDataURL}'%3E%3C/image%3E%3C/svg%3E");`
const imgStyle = Object.assign({}, style, layoutStyle)
const blurStyle =
placeholder === 'blur' && !blurComplete
? {
backgroundSize: objectFit || 'cover',
backgroundPosition: objectPosition || '0% 0%',
...(layout === 'raw' && blurDataURL?.startsWith('data:image')
? {
backgroundImage: svgBlurPlaceholder,
}
: {
filter: 'blur(20px)',
backgroundImage: `url("${blurDataURL}")`,
}),
filter: 'blur(20px)',
backgroundImage: `url("${blurDataURL}")`,
}
: {}
if (layout === 'fill') {
Expand Down Expand Up @@ -852,9 +792,7 @@ export default function Image({
}
return (
<>
{layout === 'raw' ? (
<ImageElement {...imgElementArgs} />
) : (
{
<span style={wrapperStyle}>
{hasSizer ? (
<span style={sizerStyle}>
Expand All @@ -880,7 +818,7 @@ export default function Image({
) : null}
<ImageElement {...imgElementArgs} />
</span>
)}
}
{priority ? (
// Note how we omit the `href` attribute, as it would only be relevant
// for browsers that do not support `imagesrcset`, and in those cases
Expand Down Expand Up @@ -937,12 +875,9 @@ const ImageElement = ({
<img
{...rest}
{...imgAttributes}
{...(layout === 'raw' ? { height: heightInt, width: widthInt } : {})}
decoding="async"
data-nimg={layout}
className={className}
// @ts-ignore - TODO: upgrade to `@types/react@17`
loading={layout === 'raw' ? loading : undefined}
style={{ ...imgStyle, ...blurStyle }}
ref={useCallback(
(img: ImgElementWithDataProp) => {
Expand Down Expand Up @@ -1005,9 +940,6 @@ const ImageElement = ({
sizes: noscriptSizes,
loader,
})}
{...(layout === 'raw'
? { height: heightInt, width: widthInt }
: {})}
decoding="async"
data-nimg={layout}
style={imgStyle}
Expand Down
Loading