Skip to content

Commit b735a3c

Browse files
authored
Pass back img attributes in render callback (#4)
Closes #1 * Update README with render callbacks Includes src, srcSet and alt for LazyImage, and a note on how they keep things consistent and DRY. * Run prettier on README * Add alt, src, srcSet to render callbacks * Fixes with TS checks * Update stories with new callbacks * Fix alt in story
1 parent 8d43ca4 commit b735a3c

File tree

5 files changed

+173
-158
lines changed

5 files changed

+173
-158
lines changed

README.md

Lines changed: 78 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -106,21 +106,17 @@ import { LazyImage } from "react-lazy-images";
106106

107107
<LazyImage
108108
src="/img/porto_buildings_large.jpg"
109-
placeholder={() => (
110-
<img
111-
src="/img/porto_buildings_lowres.jpg"
112-
alt="Buildings with tiled exteriors, lit by the sunset."
113-
/>
114-
)}
115-
actual={() => (
116-
<img
117-
src="/img/porto_buildings_large.jpg"
118-
alt="Buildings with tiled exteriors, lit by the sunset."
119-
/>
109+
alt="Buildings with tiled exteriors, lit by the sunset."
110+
placeholder={({ alt }) => (
111+
<img src="/img/porto_buildings_lowres.jpg" alt={alt} />
120112
)}
113+
actual={({ src, alt, srcSet }) => <img src={src} alt={alt} />}
121114
/>;
122115
```
123116

117+
Note that while you can set the rendered components to be anything you want, you most likely want to use the same `src`, `srcSet` and `alt` attributes in an `<img>` eventually.
118+
To keep this consistent, and reduce repetition, the render callbacks pass those attributes back to you.
119+
124120
[You can play around with this library on Codesandbox](https://codesandbox.io/s/jnn9wjkj1w).
125121

126122
Additionally, make sure you understand [how to polyfill IntersectionObserver](#polyfill-intersectionobserver) and [strategies for when JS is not available](#fallback-without-javascript).
@@ -140,30 +136,34 @@ Thus, whether you want to display a simple `<img>`, your own `<Image>`, or even
140136
```jsx
141137
<LazyImage
142138
src="/img/porto_buildings_large.jpg"
143-
// This is rendered first
139+
alt="Buildings with tiled exteriors, lit by the sunset."
140+
141+
// This is rendered first, notice how the src is different
144142
placeholder={
145-
() =>
146-
<img src="/img/porto_buildings_lowres.jpg" alt="Buildings with tiled exteriors, lit by the sunset." />
143+
({alt}) =>
144+
<img src="/img/porto_buildings_lowres.jpg" alt={alt} />
147145
}
148-
// This is rendered once in view
146+
// This is rendered once in view; we use the src and alt above for consistency
149147
actual={
150-
() =>
151-
<img src="/img/porto_buildings_large.jpg" alt="Buildings with tiled exteriors, lit by the sunset." />
148+
({src, alt}) =>
149+
<img src={src} alt={alt} />
152150
}
153151
/>
154152

155153
// Perhaps you want a container?
156154
<LazyImage
155+
src="/img/porto_buildings_large.jpg"
156+
alt="Buildings with tiled exteriors, lit by the sunset."
157157
placeholder={
158-
() =>
159-
<div className={`LazyImage-Placeholder`}">
160-
<img src="/img/porto_buildings_lowres.jpg" alt="Buildings with tiled exteriors, lit by the sunset." />
158+
({alt}) =>
159+
<div className={'LazyImage-Placeholder'}">
160+
<img src="/img/porto_buildings_lowres.jpg" alt={alt} />
161161
</div>
162162
}
163163
actual={
164-
() =>
165-
<div className={`LazyImage-Actual`}>
166-
<img src="/img/porto_buildings_large.jpg" alt="Buildings with tiled exteriors, lit by the sunset." />
164+
({src, alt}) =>
165+
<div className={'LazyImage-Actual'}>
166+
<img src={src} alt={alt} />
167167
</div>
168168
}
169169
/>
@@ -181,25 +181,25 @@ In those cases, consider `LazyImageFull`:
181181
import {LazyImageFull, ImageState} from 'react-lazy-images';
182182
183183
// Function as child
184-
// `src` and `srcSet` are passed back to the render callback for convenience/consistency
184+
// `src`, `alt` and `srcSet` are passed back to the render callback for convenience/consistency
185185
<LazyImageFull src='/img/porto_buildings_large.jpg'>
186-
{({src, srcSet, imageState}) =>
186+
{({src, alt, srcSet, imageState}) =>
187187
<img
188188
src={imageState === ImageState.LoadSuccess ? src : '/img/porto_buildings_lowres.jpg'}
189+
alt={alt}
189190
style={{opacity: ImageState.LoadSuccess ? '1' : '0.5'}}
190-
alt="Buildings with tiled exteriors, lit by the sunset." />
191191
/>
192192
}
193193
</LazyImageFull>
194194
195195
// render prop
196196
<LazyImageFull
197197
src='/img/porto_buildings_large.jpg'
198-
render={({src, srcSet, imageState}) =>
198+
render={({src, alt, srcSet, imageState}) =>
199199
<img
200200
src={imageState === ImageState.LoadSuccess ? src : '/img/porto_buildings_lowres.jpg'}
201+
alt={alt}
201202
style={{opacity: ImageState.LoadSuccess ? '1' : '0.5'}}
202-
alt="Buildings with tiled exteriors, lit by the sunset." />
203203
/>
204204
}
205205
/>
@@ -228,16 +228,17 @@ This behaviour is provided with the `src` prop:
228228
// so that the image can be requested before rendering
229229
<LazyImage
230230
src="/img/porto_buildings_large.jpg"
231+
alt="Buildings with tiled exteriors, lit by the sunset."
231232
placeholder={
232-
() =>
233+
({alt}) =>
233234
<div className={`LazyImage-Placeholder`}">
234-
<img src="/img/porto_buildings_lowres.jpg" alt="Buildings with tiled exteriors, lit by the sunset." />
235+
<img src="/img/porto_buildings_lowres.jpg" alt={alt} />
235236
</div>
236237
}
237238
actual={
238-
() =>
239+
({src, alt}) =>
239240
<div className={`LazyImage-Actual`}>
240-
<img src="/img/porto_buildings_large.jpg" alt="Buildings with tiled exteriors, lit by the sunset." />
241+
<img src={src} alt={alt} />
241242
</div>
242243
}
243244
/>
@@ -252,13 +253,9 @@ You can choose what to display on Loading and Error using the render props `load
252253
```jsx
253254
<div className="bg-light-silver h5 w-100">
254255
<LazyImage
255-
src="https://www.fillmurray.com/notanimage"
256-
actual={() => (
257-
<img
258-
src="/img/porto_buildings_large.jpg"
259-
alt="Buildings with tiled exteriors, lit by the sunset."
260-
/>
261-
)}
256+
src="/image/brokenimagenotherewhoops.jpg"
257+
alt="Buildings with tiled exteriors, lit by the sunset."
258+
actual={({ src, alt }) => <img src={src} alt={alt} />}
262259
loading={() => (
263260
<div>
264261
<p className="pa3 f5 lh-copy near-white">Loading...</p>
@@ -288,18 +285,11 @@ This behaviour is available by using a `loadEagerly` prop:
288285
<LazyImage
289286
loadEagerly
290287
src="/img/porto_buildings_large.jpg"
291-
placeholder={() => (
292-
<img
293-
src="/img/porto_buildings_lowres.jpg"
294-
alt="Buildings with tiled exteriors, lit by the sunset."
295-
/>
296-
)}
297-
actual={() => (
298-
<img
299-
src="/img/porto_buildings_large.jpg"
300-
alt="Buildings with tiled exteriors, lit by the sunset."
301-
/>
288+
alt="Buildings with tiled exteriors, lit by the sunset."
289+
placeholder={({ alt }) => (
290+
<img src="/img/porto_buildings_lowres.jpg" alt={alt} />
302291
)}
292+
actual={({ src, alt }) => <img src={src} alt={alt} />}
303293
/>
304294
```
305295
@@ -350,14 +340,15 @@ I also think that using the server method, albeit safe, would be messy with some
350340
351341
**Silver lining:**
352342
353-
There is generally no case where `<noscript>` will be rendered by client-side react.
343+
There is generally no case where `<noscript>` will be rendered by client-side react.
354344
This means that, if you are in charge of server-rendering and you trust your bundling setup, then you can have this fallback!
355345
Look at [`src/fallbackUtils.tsx`](./src/fallbackUtils.tsx) for a function that can work.
356346
You would probably do something like this:
357347
358348
```jsx
359349
<LazyImage
360350
src="actualImgSrc"
351+
alt="alt description here"
361352
placeholder={//the usual}
362353
actual={//the usual}
363354
/>
@@ -414,33 +405,42 @@ Read the notes section either on Storybook or the story source if you are wonder
414405
[The starter on Codesandbox](https://codesandbox.io/s/jnn9wjkj1w) has a good basis for two popular presentational patterns.
415406
In particular, it shows intrinsic placeholders and fading in the actual image.
416407
408+
You might be thinking that this library has a lot of wiring exposed.
409+
This is very much intended.
410+
If this library were to provide presentational patterns out of the box, then it would lead to many issues and PRs about what ultimately is opinion.
411+
Being inclined to fork a library just to add a prop is not a nice situation to be in, compared to writing one abstracted component for your specific use case.
412+
The behaviour and loading patterns are configurable, because those are what this library is about.
413+
The presentation can be derived from those plus, crucially, any specific needs your application has.
414+
417415
## API Reference
418416
419417
**`<LazyImage />`** accepts the following props:
420418
421-
| Name | Type | Default | Required | Description |
422-
| ----------------- | --------------------------------------- | ----------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------- |
423-
| **src** | String | | true | The source of the image to load |
424-
| **srcSet** | String | | false | If your images use srcset, you can pass the `srcSet` prop to provide that information for preloading. |
425-
| **actual** | Function (render prop) | | true | Component to display once image has loaded |
426-
| **placeholder** | Function (render prop) | undefined | false | Component to display while no request for the actual image has been made |
427-
| **loading** | Function (render prop) | placeholder | false | Component to display while the image is loading |
428-
| **error** | Function (render prop) | actual (broken image) | false | Component to display if the image loading has failed (render prop) |
429-
| **loadEagerly** | Boolean | false | false | Whether to skip checking for viewport and always show the 'actual' component |
430-
| **observerProps** | {threshold: number, rootMargin: string} | {threshold: 0.01, rootMargin: "50px 0px"} | false | Subset of props for the IntersectionObserver |
431-
432-
[You can consult Typescript types in the code](./src/LazyImage.tsx) as a more exact definition.
419+
| Name | Type | Default | Required | Description |
420+
| ----------------- | -------------------------------------------------------------------------- | ----------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------- |
421+
| **src** | String | | true | The source of the image to load |
422+
| **alt** | String | | false | The alt text description of the image you are loading |
423+
| **srcSet** | String | | false | If your images use srcset, you can pass the `srcSet` prop to provide that information for preloading. |
424+
| **actual** | Function (render callback) of type ({src, alt, srcSet}) => React.ReactNode | | true | Component to display once image has loaded |
425+
| **placeholder** | Function (render callback) of type ({alt}) => React.ReactNode | undefined | false | Component to display while no request for the actual image has been made |
426+
| **loading** | Function (render callback) of type () => React.ReactNode | placeholder | false | Component to display while the image is loading |
427+
| **error** | Function (render callback) of type () => React.ReactNode | actual (broken image) | false | Component to display if the image loading has failed (render prop) |
428+
| **loadEagerly** | Boolean | false | false | Whether to skip checking for viewport and always show the 'actual' component |
429+
| **observerProps** | {threshold: number, rootMargin: string} | {threshold: 0.01, rootMargin: "50px 0px"} | false | Subset of props for the IntersectionObserver |
433430
434431
**`<LazyImageFull />`** accepts the following props:
435432
436-
| Name | Type | Default | Required | Description |
437-
| ----------------- | --------------------------------------------------------------- | ----------------------------------------- | -------------------- | ----------------------------------------------------------------------------------------------------- |
438-
| **src** | String | | true | The source of the image to load |
439-
| **srcSet** | String | | false | If your images use srcset, you can pass the `srcSet` prop to provide that information for preloading. |
440-
| **loadEagerly** | Boolean | false | false | Whether to skip checking for viewport and always show the 'actual' component |
441-
| **observerProps** | {threshold: number, rootMargin: string} | {threshold: 0.01, rootMargin: "50px 0px"} | false | Subset of props for the IntersectionObserver |
442-
| **render** | Function of type ({src, srcSet, imageState}) => React.ReactNode | | true (or `children`) | Function to call that renders based on the props and state provided to it by LazyImageFull |
443-
| **children** | Function of type ({src, srcSet, imageState}) => React.ReactNode | | true (or `render`) | Function to call that renders based on the props and state provided to it by LazyImageFull |
433+
| Name | Type | Default | Required | Description |
434+
| ----------------- | -------------------------------------------------------------------- | ----------------------------------------- | -------------------- | ----------------------------------------------------------------------------------------------------- |
435+
| **src** | String | | true | The source of the image to load |
436+
| **alt** | String | | false | The alt text description of the image you are loading |
437+
| **srcSet** | String | | false | If your images use srcset, you can pass the `srcSet` prop to provide that information for preloading. |
438+
| **loadEagerly** | Boolean | false | false | Whether to skip checking for viewport and always show the 'actual' component |
439+
| **observerProps** | {threshold: number, rootMargin: string} | {threshold: 0.01, rootMargin: "50px 0px"} | false | Subset of props for the IntersectionObserver |
440+
| **render** | Function of type ({src, alt, srcSet, imageState}) => React.ReactNode | | true (or `children`) | Function to call that renders based on the props and state provided to it by LazyImageFull |
441+
| **children** | Function of type ({src, alt, srcSet, imageState}) => React.ReactNode | | true (or `render`) | Function to call that renders based on the props and state provided to it by LazyImageFull |
442+
443+
[You can consult Typescript types in the code](./src/LazyImage.tsx) for more context.
444444
445445
## Feedback
446446
@@ -455,18 +455,19 @@ See [`ROADMAP.md`](./ROADMAP.md) for information and ideas about where the proje
455455
I would love to have contributions on this! Are there more patterns that we can expose and simplify? Is something not clear? See `CONTRIBUTING.md` for details.
456456
457457
## Thanks and Inspiration
458+
458459
Here are some resources whose ideas resonate with me and have informed this library.
459460
460-
- Jeremy Wagner's writing on [Lazy Loading Images and Video](https://developers.google.com/web/fundamentals/performance/lazy-loading-guidance/images-and-video/) is a good reference for the problem and solutions space.
461+
* Jeremy Wagner's writing on [Lazy Loading Images and Video](https://developers.google.com/web/fundamentals/performance/lazy-loading-guidance/images-and-video/) is a good reference for the problem and solutions space.
461462
462-
- The library backing this one, [react-intersection-observer](https://github.com/thebuilder/react-intersection-observer).
463-
Further thanks for demonstrating Storybook as documentation for lazy-loading.
463+
* The library backing this one, [react-intersection-observer](https://github.com/thebuilder/react-intersection-observer).
464+
Further thanks for demonstrating Storybook as documentation for lazy-loading.
464465
465-
- [José M. Pérez has a good resource on lazy loading](https://jmperezperez.com/high-performance-lazy-loading/)
466+
* [José M. Pérez has a good resource on lazy loading](https://jmperezperez.com/high-performance-lazy-loading/)
466467
467-
- [Paul Lewis' implementation of lazy image loading](https://github.com/GoogleChromeLabs/sample-media-pwa/blob/master/src/client/scripts/helpers/lazy-load-images.js) has the concept of pre-loading images before swapping.
468+
* [Paul Lewis' implementation of lazy image loading](https://github.com/GoogleChromeLabs/sample-media-pwa/blob/master/src/client/scripts/helpers/lazy-load-images.js) has the concept of pre-loading images before swapping.
468469
469-
- [Dave Rupert has a good guide on intrinsic image placeholders](https://daverupert.com/2015/12/intrinsic-placeholders-with-picture/)
470+
* [Dave Rupert has a good guide on intrinsic image placeholders](https://daverupert.com/2015/12/intrinsic-placeholders-with-picture/)
470471
471472
## License
472473

0 commit comments

Comments
 (0)