react-native-web
react-native-web copied to clipboard
Image: modern API and implementation
Is your feature request related to a problem? Please describe.
Provider a high-performance Image
implementation for web. This issue is to discuss how we should rewrite Image
to support modern web features.
Describe a solution you'd like
The new Image
should implement the following feature requests…
- Support browser ML-generated
alt
text.- #1636
- Support browser lazy loading.
- #1542
- Support browser crossOrigin configuration.
- #2171
- #2207
- Display image in Windows High Contrast mode.
- #1298
- Support image DPI switching.
- #2153
- Support for CSS like
backgroundBlendMode
.- #2091
- #2139
- Available for SSR.
The new Image
will not support the following APIs…
- No
tintColor
,resizeMode
, andshadow*
styles. - No
repeat
value forresizeMode
.
The proposed Image
API looks like this…
<Image
alternativeText=''
crossOrigin='anonymous'
decoding='async'
draggable='false'
focusable='false'
loading='lazy'
nativeID=''
onError={() => {}}
onLoad={() => {}}
source={[ {uri, scale}, {uri, scale} ]}
style={{}}
testID=''
/>
Any dynamic loading via XHR/fetch would have to be layered upon that.
Additional context
Please comment with addition context and use cases.
We have two uses for the image queryCache I wanted to share here
- When dataSaver is on, we try and get the largest cached variant already loaded (and use that instead) to save data.
// If data saver is on, then attempt to use the highest quality image we've already loaded to save data,
// Exclude tiny variant (pre-data) saver images, so that we have a minimum quality
const dataSaverCachedSource =
dataSaver &&
loadingVariant &&
Math.max(loadingVariant.width, loadingVariant.height) >= MIN_RESOLUTION_CACHED_DATA_SAVER_THRESHOLD
? loadingVariant.uri
: null;
const src = dataSaverCachedSource || _selectSource(photo);
- We use
defaultSource
to show the largest loaded image while the full-size variant loads:
<Image
defaultSource={loadingVariant && loadingVariant.uri}
draggable
onContextMenu={onContextMenu}
// If there's a fallback image showing, that's good enough and we can swallow the error
onError={loadingVariant ? undefined : onError}
onLoad={onLoad}
// These just set internal state and shows a little loading spinner so people know we are upscaling the image
onLoadEnd={this._handleLoadEnd}
onLoadStart={this._handleLoadStart}
source={src}
/>
Looking forward to this change!
Some possible solutions for implementing some of the props with CSS:
- object-fit to implement resizeMode
It would be cool if React Native could add something similar to object-position otherwise it would be nice if we could use it in React Native Web somehow.
@necolas just to make sure I'm understanding correctly - the proposed new API will be in tension with the React Native API such that it won't have built-in support for HTTP verbs like headers, for example, in the source
property?
as this is already not present in the react-native-web
Image
implementation, this would be fine by our team 😄 i was hopeful that https://github.com/necolas/react-native-web/pull/1470 (or what looks to be a rebased version in https://github.com/necolas/react-native-web/pull/1843) would land, but I'm of course mindful of the fact that the React Native Image
component API isn't aligned with the direction you'd like to go!
our use case, FWIW, is secure images served behind an authentication layer in our API server; i'm not sure how common of a use case this is. if there is a way to somehow shoehorn this in, I would love to see that or help make that happen. If not, our web-specific workarounds will live to see another day 😸
I'd give a +1 to @viggyfresh's use-case of wanting to serve images behind auth.
For example, Expo (which wraps RNWeb) doesn't really have cookie support, so instead suggests using token-based auth: https://github.com/expo/expo/issues/6756#issuecomment-730683664 . (Even if cookies were supported, I believe having all client-side data stored together in LocalStorage, and communicated via http headers, is a more modern approach).
So without support for cookies or http headers when serving images, I think the only option remaining becomes to stick the auth token in the querystring. I suppose there's not really anything wrong with that, but for consistency it would be nice if the same auth mechanism could be used everywhere.
@jamesisaac
I think the only option remaining becomes to stick the auth token in the querystring. I suppose there's not really anything wrong with that
Sending private tokens in the query string is not recommended because unlike headers, they often show up in logs.
Adding one more idea here for the image rework. Right now I think it's impossible to load images with crossorigin="anonymous". In my testing, even if I go into RNW source and add it to the loader and hidden image, there's no equivalent attribute for background image so two requests get made, one properly anon from the ImageLoader, but then a second from background-image.
I've built this as an alternative to the RN web implementation if that can provide ideas for improvements. Def has lots of tradeoffs and doesn't implement all RN features.
The main idea is to extend the source prop with a sources
config that pretty much matches https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source. This sources data can be generated by a webpack plugin for local images or returned by a backend for remote images. This allow supporting different sizes for densities via srcSet or different image formats.
https://github.com/th3rdwave/web-image
#2171 is covered by this as well