Replies: 3 comments 2 replies
-
Having discussed some of my thinking and trade-offs with @atomicleopard, there is an alternate suggestion for Source definitionsAn alternate solution could be to define a module.exports : {
images: {
sources: {
small: '(min-width: 450px)'
medium: '(min-width: 768px)',
large: '(min-width: 1200px)',
xl: '(min-width: 2000px)',
}
},
} The props you pass could then be a matching object <Image
src={'/tile.jpg'}
sources={{
small: '/tile-sm.jpg',
medium: '/tile-med.jpg',
large: '/tile-lrg.jpg',
xl: '/tile-xl.jpg'
}}
/> FlexibilityI originally opted for plain number values in the Having it more open as strings would make this trickier but not impossible. module.exports : {
images: {
sources: {
tablet: '(min-width: 450px)'
desktop: '(min-width: 1200px)',
darkMode: 'prefers-color-scheme: dark',
highDensity: '(-webkit-min-device-pixel-ratio: 1.5)',
}
},
} could then be used as <Image
src={'/tile.jpg'}
sources={{
tablet: '/tile-tablet.jpg',
desktop: '/tile-desktop.jpg',
darkMode: '/tile-dark.jpg',
highDensity: '/tile-hd.jpg'
}}
/> Omitted valuesWith this suggestion, there was an expectation that if values were omitted during usage, they would not be included in the rendered output. <Image
src={'/tile.jpg'}
sources={{
desktop: '/tile-desktop.jpg',
darkMode: '/tile-dark.jpg'
}}
/> This adds great flexibility but equal complexity. BenefitsI previously noted
This named object approach makes a prop-based override simple, and ensures in instances where you want less or more <picture>
<source media="(min-width: 1200px)" srcset="tile-desktop.jpg 1x, tile-desktop-2x.jpg 2x">
<source media="prefers-color-scheme: dark" srcset="tile-dark.jpg 1x, tile-dark-2x.jpg 2x">
<img src="tile.jpg" width="400" height="300" alt="" />
</picture> Trade-offsThe reason I used a fixed set of values was to ensure that there were appropriate renditions used for each screen size, as the media query driven Using my example above, if no tablet source was provided we would end up serving an image too small (or too large), to tablet screens. <picture>
<source media="(min-width: 2000px)" srcset="tile-xl.jpg 1x, tile-xl-2x.jpg 2x">
<source media="(min-width: 1200px)" srcset="tile-lrg.jpg 1x, tile-lrg-2x.jpg 2x">
- <source media="(min-width: 768px)" srcset="tile-med.jpg 1x, tile-med-2x.jpg 2x">
<source media="(min-width: 450px)" srcset="tile-sm.jpg 1x, tile-sm-2x.jpg 2x">
<img src="tile.jpg" width="400" height="300" alt="" />
</picture> This is because the media query is what drives the size options in the In this scenario, to fill the missing <picture>
<source media="(min-width: 2000px)" srcset="tile-xl.jpg 1x, tile-xl-2x.jpg 2x">
<source media="(min-width: 1200px)" srcset="tile-lrg.jpg 1x, tile-lrg-2x.jpg 2x">
+ <source media="(min-width: 768px)" srcset="tile-lrg.jpg?w=768 1x, tile-lrg.jpg 2x">
<source media="(min-width: 450px)" srcset="tile-sm.jpg 1x, tile-sm-2x.jpg 2x">
<img src="tile.jpg" width="400" height="300" alt="" />
</picture> We could define a priority pattern, but it would be very opinionated. Priority patternThis is something we currently do for one of our projects. The pattern we use is as follows:
This pattern ensures a source is provided when there are enough sources defined to warrant rendering a In the scenario where neither This priority pattern could also be a module.exports : {
images: {
sources: {
small: {
media: '(min-width: 450px)',
priority: ['small', 'medium', 'large']
},
medium: {
media: '(min-width: 768px)',
priority: ['medium', 'large']
},
large: {
media: '(min-width: 1200px)',
priority: ['large', 'medium']
},
fallback: { // for `img` src value
priority: ['default', 'small', 'medium', 'large'] // default = `src` prop value, but could be potentially omitted
}
}
} Other people's mileage may vary. Whilst these decisions are driven by experiences across projects, this may be unique in the grand scheme of things. I'd be interested in any feedback the team has. |
Beta Was this translation helpful? Give feedback.
-
Wow, thank you for the amount of detail and effort that went into this. Incredibly useful! 🥳 There are currently two other related discussions around
The team is interested in exploring this more and @atcastle will be leading that work. |
Beta Was this translation helpful? Give feedback.
-
Update: We have extracted the core logic from This allows usage outside of
Exampleimport { 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: #51205 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Rationale
next/image does not currently support multiple image sources, commonly used for specific art direction at different screen sizes, rendered as a picture element.
Each
source
element includesmedia
value to target a specific media query, usually a screen-size range, andsrcSet
value to describe pixel density renditions or width valuesThe
picture
element will also usually describe alternatesources
for complimentary alternatemime/type
renditions.Idea
I'm keen to collaborate on a PR for the solution, but here are some ideas for how it could look like for end-users—I appreciate each of these will require further discussion.
Image
componentThe
Image
component could have a new complimentarysources
prop for specifying each source fileThe
src
prop could alternatively support an array of imagesEither option naively expects there is a source value for each media query in the
sourceSizes
definition innext.config
In lieu of variants specific for each media query, duplicate values can be provided to ensure the array is at the expected length—ideally type-safe somehow.
Source definitions
source
media queries could be managed in thenext.config
, to follow conventionThese values would define the number of
source
elements and theirmin-width
values respectively.This definition would generate the related media queries, eg.
Whilst there could be some reuse with
deviceSizes
, the default values provided by Next would produce a verbose number ofsource
elements if we used those to define the media queries.Format defintions
Specific progressive
mime/type
renditions could also be managed in thenext.config
This definition would generate the related
type
variant for each media querysource
, eg.srcSet
unitsIn my experience, each
source
element will most often include asrcSet
to describe pixel density renditions, rather than width values, as the media queries generally drive the width variants.I would expect the
srcSet
to use pixel density renditions by default, but ideally this would be an option innext.config
Further discussion
width
andheight
params for eachsource
elementIn my experience, art-directed renditions tend to have varied aspect-ratio values.
See here for a usage example, but each
source
can now be providedwidth
andheight
attributes to aid reducing CLS.How to best provide these optional values is up for discussion.
Media expressions
I have made an assumption that the media query definitions are range-based, using
min-width
declarations.There may be instances where users may want to define
max-width
or defined ranges instead.Ideally this would be an option in
next.config
The nested array value could be a way to define a range query, eg.
Alternatively, string-based logical operators may be more suitable;
There may also be instances where users want to provide more complex media queries targeting orientation or other features. I'm not sure how this would be supported.
Source range overrides
There may be instances where some images in your application either have more or less source renditions than the range defined in
sourceSizes
innext.config
We will likely require some prop-based override mechanism to aid these use cases.
Related
source
elements to target high-density displays, which could complement or compete with this proposalprefers
: this comment requests support for alternate images based on color-scheme preference, which could complement or compete with this proposalI have had to implement complex CMS-driven picture elements a few times in the wild now and am keen to collaborate on a PR for the solution.
I look forward to any feedback or suggestions!
Beta Was this translation helpful? Give feedback.
All reactions