next.js
next.js copied to clipboard
Support picture tag in next/image
Feature
- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using
fixes #number
- [x] Integration tests added
- [x] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
Feature request: #25393
Currently next/image only supports next-gen images such as WebP via Accept header, because there's no way to handle fallback for old browsers. This means that image hosts or CDNs that only support changing format based on the URL cannot safely deliver next-gen formats. It also makes it harder to cache images.
The standards-compliant way to do this is to generate a <picture>
tag with a <source>
tag for each supported format.
This PR keeps the default behavior the same: it generates an <img>
tag and uses content negotiation in the server for next-gen formats. However it adds a "formats" key to the image config, which can include next gen image formats. The supproted values for this depends on the handler, and would be just webp
by default. If the formats
array is present, then the <img>
tag would be exactly the same as with auto format, but it would be wrapped with a <picture>
tag and would include a <source>
element for each requested type, with type
attribute containing the mimetype. These pass an extra "format" option to the handler function when generating the URLs. The default handler would generate these URLs with an additional f
parameter, which the image server would use to choose a format to generate.
The server implementation adds an optional f
param, which if set to a valid next-gen image format (i.e. WebP), then returns that format, ignoring accept headers.
This is fully backwards-compatible, as the <img>
tag is unchanged and still uses the auto format, and the <picture>
tag would only be included if the formats
array was included in the config.
I have included an implementation of the component and the server, integration tests for both, and docs. I have not included telemetry (because I'm unsure of the best way to add it).
Would this work for using a totally different image ant certain size for art direction?
I was hoping to use the <picture>
tag along with my next/image
<Image>
tags to fetch a different image based on the user's preferred color scheme.
Would this kind of use-case be supported if/when this PR goes through?
Here's what I have in mind specifically. The following code works with <img>
but fails with <Image>
(probably because of the <span>
being added in there by next/image
):
<picture>
<source
srcSet="https://via.placeholder.com/100/333333/ffffff.png"
media="(prefers-color-scheme: dark)"
/>
<img
src='https://via.placeholder.com/100/dddddd/000000.png'
width='100px'
height='100px'
alt='Placeholder image'
/>
</picture>
+1 :up: what's the progress since the PR opened for almost a full year? @ascorbic
We're exploring options for where to take next/image
based on community feedback and will have something to share in the next few months.
Any updates on this @leerob ? Looks like it's not in next 13 unless i've missed it?
@ascorbic This will screw up pseudo-classes like :first-child
, :nth-of-type
used on the image.
Update: We have extracted the core logic from next/image
into a new unstable_getImgProps()
function.
This allows usage outside of <Image>
, such as:
- Working with
background-image
orimage-set
- Working with canvas
context.drawImage()
or simplynew Image()
- Working with
<picture>
media queries to implement Art Direction or Light/Dark Mode images
Example
import { unstable_getImgProps as getImgProps } from 'next/image'
export default function Page() {
const common = { alt: 'Hero', width: 800, height: 400 }
const { props: { srcSet: dark } } = getImgProps({ ...common, src: '/dark.png' })
const { props: { srcSet: light, ...rest } } = getImgProps({ ...common, src: '/light.png' })
return (<picture>
<source media="(prefers-color-scheme: dark)" srcSet={dark} />
<source media="(prefers-color-scheme: light)" srcSet={light} />
<img {...rest} />
</picture>)
}
PR: https://github.com/vercel/next.js/pull/51205