Git Product home page Git Product logo

react-lazy-images's Introduction

React Lazy Images

Components and utilities for lazy image loading in React.

npm gzip size npm downloads dependencies

Table of Contents

Features

What it does not do by itself:

  • Polyfill IntersectionObserver. Adding polyfills is something you should do consciously at the application level. See Polyfilling IntersectionObserver for how to do this.
  • Dictate the kind of placeholders displayed. There are many ways to do it; you can use a simple box with a background color, a low-resolution image, some gradient, etc. In other words, this library focuses on loading the images once in view and supporting loading patterns around that. The presentational patterns are yours to decide! Fear not though, we cover both patterns in the examples section.

Install

This package is distributed via npm.

$ npm install --save react-lazy-images
# or
$ yarn add react-lazy-images

Then import according to your modules model and bundler, such as Rollup and Webpack:

// ES Modules
// For all possible functions to import look at the documentation
import { LazyImage } from "react-lazy-images";

/// CommonJS modules
const { LazyImage } = require("react-lazy-images");

A UMD version is also available on unpkg:

<script src="https://unpkg.com/react-lazy-images/dist/react-lazy-images.umd.js"></script>

Motivation

Browsers preload images; as soon as they encounter an <img> tag with a valid src, they kick off the request for the image (they even do this before the HTML has been parsed). Even in cases where a certain image is not in the viewport, it will be requested. This can have adverse effects for users, especially on mobile or metered connections.

This brings us to the basic premise of any Lazy Image Loading library:

  • Have a way to observe the visibility of the DOM elements
  • Prevent the browser from loading images directly
  • Once an image is in view, instruct the browser to load it and place it in the element

In vanilla JS, this means "hiding" the actual src in a data-src attribute, and using classes to indicate state, e.g. .isLazyLoaded .lazyLoad. On initialisation, a script queries for these classes and attributes, keeps track of visibily, and swaps data-src with an actual src, kicking off the browser request process. It can elect to preload the Image, and only swap once loaded.

With React, all this implicit state management is brought into one place, since you do not have to stash loading information in the DOM and pick it back up again. This can potentially mean a nicer, more composable codebase, and it was one of the main design goals for this library.

The way to do this visibility tracking has for the most part been listening for events such as scroll. This is synchronous by nature and can have performance implications. It also involves calling getBoundingClientRect() to calculate the interesection of the image with the viewport; this function causes relayout. This was the motivation for browsers providing IntersectionObserver. Using this API is not specific to React; it just seems like a good fit for this task nowadays.

Usage

Quick Start

If you want to just dive in, do this:

import { LazyImage } from "react-lazy-images";

<LazyImage
  src="/img/porto_buildings_large.jpg"
  alt="Buildings with tiled exteriors, lit by the sunset."
  placeholder={({ imageProps, ref }) => (
    <img ref={ref} src="/img/porto_buildings_lowres.jpg" alt={imageProps.alt} />
  )}
  actual={({ imageProps }) => <img {...imageProps} />}
/>;

โš ๏ธ It is important that you pass on the ref in placeholder, otherwise the detection of the element intersecting is impossible. โš ๏ธ

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. To keep this consistent, and reduce repetition, the render callbacks pass those attributes back to you.

You can play around with this library on Codesandbox.

Additionally, make sure you understand how to polyfill IntersectionObserver and strategies for when JS is not available.

From then on:

  • If you want to learn more about the API and the problem space, read the rest of this section.
  • If you need more fine-grained rendering, read about LazyImageFull.
  • If you want to list the props, see the API reference.

Customising what is displayed

The render prop pattern is used throughout in LazyImage. The LazyImage component handles the behaviour of tracking when the image is in view, but leaves the actual rendering up to the consumer. Thus, whether you want to display a simple <img>, your own <Image>, or even wrapped elements, it is simple to do so:

<LazyImage
  src="/img/porto_buildings_large.jpg"
  alt="Buildings with tiled exteriors, lit by the sunset."
  // This is rendered first, notice how the src is different
  placeholder={
    ({imageProps, ref}) =>
      <img ref={ref} src="/img/porto_buildings_lowres.jpg" alt={imageProps.alt} />
  }
  // This is rendered once in view; we use the src and alt above for consistency
  actual={
    ({imageProps}) =>
      <img {...imageProps} />
  }
/>

// Perhaps you want a container?
<LazyImage
  src="/img/porto_buildings_large.jpg"
  alt="Buildings with tiled exteriors, lit by the sunset."
  placeholder={
    ({imageProps, ref}) =>
      <div ref={ref} className={'LazyImage-Placeholder'}>
        <img src="/img/porto_buildings_lowres.jpg" alt={imageProps.alt} />
      </div>
  }
  actual={
    ({imageProps}) =>
      <div className={'LazyImage-Actual'}>
        <img {...imageProps} />
      </div>
  }
/>

These props are there to instruct the component what to render in those places, and they take some useful information (in this case, a className) from the LazyImage.

More control with LazyImageFull

LazyImage should work for most cases, but you might need more fine-grained rendering. One use case would be doing animations with CSS transitions, where re-rendering the component (which LazyImage does) would not be sufficient. In those cases, consider LazyImageFull:

import { LazyImageFull, ImageState } from "react-lazy-images";

// Function as child
// `src`, `alt` and `srcSet` are passed back to the render callback for convenience/consistency
<LazyImageFull src="/img/porto_buildings_large.jpg">
  {({ imageProps, imageState, ref }) => (
    <img
      {...imageProps}
      ref={ref}
      src={
        imageState === ImageState.LoadSuccess
          ? imageProps.src
          : "/img/porto_buildings_lowres.jpg"
      }
      style={{ opacity: ImageState.LoadSuccess ? "1" : "0.5" }}
    />
  )}
</LazyImageFull>;

This component takes a function as a child, which accepts {src, srcSet, imageState}. The various image states are imported as {ImageState}, and you can conditionally render based on them.

This technique can give you more fine-grained rendering if needed, but can potentially be more verbose. Any of the presentational patterns presented that are possible with LazyImage are also possible with LazyImageFull. (The opposite is not necessarily true, or at least has more duplication).

In fact, if you check src/LazyImage.tsx, you will see that LazyImage is implemented in terms of LazyImageFull!

Load ahead and threshold

Further control over the Intersection Observer can be provided through the observerProps prop object:

import { LazyImage } from "react-lazy-images";

<LazyImage
  src="/img/porto_buildings_large.jpg"
  alt="Buildings with tiled exteriors, lit by the sunset."
  placeholder={/* the usual */}
  actual={/* the usual */}
  observerProps={{
    rootMargin: "100px 0",
    threshold: 0.3
  }}
/>;

rootMargin: Margin around the window. This can have values similar to the CSS margin property, e.g. "10px 20px 30px 40px" (top, right, bottom, left) (defaulted to "50px 0px") This can provide control if you want to request your image a certain number of pixels ahead of where the user is scrolling.

threshold: Number between 0 and 1 indicating the percentage that should be visible before a request is sent. (defaulted to 0.01)

(See https://github.com/thebuilder/react-intersection-observer#api)

Load before swap

A common optimisation to the loading strategy is to preload the image before swapping it for the placeholder. In other words, once the image is in view, you can kick off a request to load the image, and only show it once fully loaded. This avoids presenting a half-loaded image (i.e. one that is still scanning top-to-bottom), and makes the transition smoother.

This behaviour is provided with the src prop:

// Note that the actual src is also provided separately,
// so that the image can be requested before rendering
<LazyImage
  src="/img/porto_buildings_large.jpg"
  alt="Buildings with tiled exteriors, lit by the sunset."
  placeholder={
    ({imageProps, ref}) =>
      <div ref={ref} className={`LazyImage-Placeholder`}">
        <img src="/img/porto_buildings_lowres.jpg" alt={imageProps.alt} />
      </div>
  }
  actual={
    ({imageProps}) =>
      <div className={`LazyImage-Actual`}>
        <img {...imageProps} />
      </div>
  }
/>

There is another case if you are using srcset for your images; LazyImage needs that information to preload the correct image. You can provide it with the srcSet prop.

Loading and Error states

You can choose what to display on Loading and Error using the render props loading and error:

<div className="bg-light-silver h5 w-100">
  <LazyImage
    src="/image/brokenimagenotherewhoops.jpg"
    alt="Buildings with tiled exteriors, lit by the sunset."
    actual={({ imageProps }) => <img {...imageProps} />}
    placeholder={({ ref }) => <div ref={ref} />}
    loading={() => (
      <div>
        <p className="pa3 f5 lh-copy near-white">Loading...</p>
      </div>
    )}
    error={() => (
      <div className="bg-light-red h-100 w-100">
        <p>There was an error fetching this image :(</p>
      </div>
    )}
  />
</div>

Eager loading / Server-Side Rendering (SSR)

What does SSR even mean in a lazy images context?

If you recall the basic premise, then you will know that we "hide" the intended image and display a placeholder. For the actual image request to kick off, Javascript has to have loaded, and detected that the image is in the viewport. In cases where you are server-side rendering, there can be a non-neglible amount of time until Javascript is available (i.e. it has to download, parse, execute). For those cases, it would be beneficial if we can mark images to render with the intended/final src by default, so that the browser can start requesting them as soon as it gets the HTML.

This behaviour is available by using a loadEagerly prop:

<LazyImage
  loadEagerly
  src="/img/porto_buildings_large.jpg"
  alt="Buildings with tiled exteriors, lit by the sunset."
  placeholder={({ imageProps, ref }) => (
    <img ref={ref} src="/img/porto_buildings_lowres.jpg" alt={imageProps.alt} />
  )}
  actual={({ imageProps }) => <img {...imageProps} />}
/>

While the usage is simple, the patterns in your app will not necessarily be so. Think about the cases where it is beneficial to do this, and apply it with intent. Examples might be eager-loading hero images, preloading the first few elements in a list and so on. Some of these use cases are provided as examples.

Debounce / Delay

In cases where you have a long list of images that the user might scroll through, then loading intermediate images can waste bandwidth and processing time. This is undesired. The way to handle it is with a minimum duration that the image has to stay within the viewport, before making the request. This is specified using the debounceDurationMs prop:

<LazyImage
  src="/img/porto_buildings_large.jpg"
  alt="Buildings with tiled exteriors, lit by the sunset."
  debounceDurationMs={1000}
  placeholder={({ imageProps, ref }) => (
    <img ref={ref} src="/img/porto_buildings_lowres.jpg" alt={imageProps.alt} />
  )}
  actual={({ imageProps }) => <img {...imageProps} />}
/>

Fallback without Javascript

If Javascript is disabled altogether by the user, then they will be stuck with the placeholder (and any images loaded eagerly). This is probably undesirable.

There are a few strategies for fallbacks. Most of them are variations on a <noscript> tag with the actual img and hiding the placeholder if JS is disabled. Here is what it looks like rendered:

// In the <head>
// Style applied only when JS is disabled
// Hide the LazyImage (since the actual one will be displayed in its place)
<noscript>
  <style>
    .LazyImage {
      display: none;
    }
  </style>
</noscript>

// Your component (as rendered)
// Placeholder since JS has not run; will be hidden with the style above.
<img class="LazyImage" src="/img/porto_buildings_lowres.jpg" alt="Buildings with tiled exteriors, lit by the sunset." />

// Declare the actual image as you would, inside a noscript
<noscript>
  <img src="/img/porto_buildings_large.jpg" alt="Buildings with tiled exteriors, lit by the sunset." />
</noscript>

Until v0.3.0, this library had a fallback API, in the form of a fallback render prop. This has been disabled due to issues with <noscript> in React causing the fallback to always load.

(See facebook/react#11423 for more details)

Current solutions involve either using dangerouslySetInnerHTML, which is not safe for arbitrary library use, or ReactDOMServer.renderToStaticMarkup. I thought it would be irresponsible to hide the fact that dangerouslySetInnerHTML is used from the user, so that excludes the first option. I also think that using the server method, albeit safe, would be messy with some bundling configurations (which would keep the entirety of react-dom/server).

Silver lining:

There is generally no case where <noscript> will be rendered by client-side react. This means that, if you are in charge of server-rendering and you trust your bundling setup, then you can have this fallback! Look at src/fallbackUtils.tsx for a function that can work. You would probably do something like this:

<LazyImage
  src="actualImgSrc"
  alt="alt description here"
  placeholder={//the usual}
  actual={//the usual}
/>
<Fallback>
  <img src="/img/porto_buildings_large.jpg" alt="Buildings with tiled exteriors, lit by the sunset." />
</Fallback>

Don't forget to also hide the .LazyImage as shown above.

This may or may not be good enough. Please open an issue to discuss your needs if that is the case :)

Polyfill IntersectionObserver

IntersectionObserver is generally well-supported, but it is still important to polyfill it! You can consult the usage data for IntersectionObserver here.

The polyfill itself is pretty small, on the order of 6k min, 2k gzipped.

The polyfill is available through npm:

npm install --save intersection-observer

And import it at your app's entry point:

import "intersection-observer";

Polyfill.io is an alternative method of distributing the polyfill if you wish.

About the polyfill

It is generally a good idea to know what you are adding to your codebase

The polyfill behaviour is to fall back to the older strategy; "debounced scroll listener and calculate bounding rectangle", as mentioned above. It will not be as performant as the native IntersectionObserver, but likely no worse than most implementations of the older strategy.

Examples

About understanding the library and loading patterns

A variety of usage examples and recipes are provided in the form of Storybook.

You can browse the documentation online or look at stories/.

Read the notes section either on Storybook or the story source if you are wondering about the specifics of each pattern demonstrated.

About abstracting over it and presentational patterns

The starter on Codesandbox has a good basis for two popular presentational patterns. In particular, it shows intrinsic placeholders and fading in the actual image.

You might be thinking that this library has a lot of wiring exposed. This is very much intended. 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. 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. The behaviour and loading patterns are configurable, because those are what this library is about. The presentation can be derived from those plus, crucially, any specific needs your application has.

API Reference

<LazyImage /> accepts the following props:

Name Type Default Required Description
src String true The source of the image to load
alt String false The alt text description of the image you are loading
srcSet String false If your images use srcset, you can pass the srcSet prop to provide that information for preloading.
sizes String false If your images use srcset, the sizes attribute helps the browser decide which source to load.
actual Function (render callback) of type ({imageProps}) => React.ReactNode true Component to display once image has loaded
placeholder Function (render callback) of type ({imageProps, ref}) => React.ReactNode undefined true Component to display while no request for the actual image has been made
loading Function (render callback) of type () => React.ReactNode placeholder false Component to display while the image is loading
error Function (render callback) of type () => React.ReactNode actual (broken image) false Component to display if the image loading has failed (render prop)
debounceDurationMs Number N/A false The minimum duration that the image has to be in the viewport before starting to load, in ms. This can help avoid loading images while the user scrolls quickly past them.
loadEagerly Boolean false false Whether to skip checking for viewport and always show the 'actual' component
observerProps {threshold: number, rootMargin: string} {threshold: 0.01, rootMargin: "50px 0px"} false Subset of props for the IntersectionObserver
experimentalDecode Boolean false false Decode the image off-main-thread using the Image Decode API. Test before using!

<LazyImageFull /> accepts the following props:

Name Type Default Required Description
src String true The source of the image to load
alt String false The alt text description of the image you are loading
srcSet String false If your images use srcset, you can pass the srcSet prop to provide that information for preloading.
sizes String false If your images use srcset, the sizes attribute helps the browser decide which source to load.
debounceDurationMs Number N/A false The minimum duration that the image has to be in the viewport before starting to load, in ms.
loadEagerly Boolean false false Whether to skip checking for viewport and always show the 'actual' component
observerProps {threshold: number, rootMargin: string} {threshold: 0.01, rootMargin: "50px 0px"} false Subset of props for the IntersectionObserver
children Function of type ({imageProps, imageState, ref}) => React.ReactNode true Function to call that renders based on the props and state provided to it by LazyImageFull
experimentalDecode Boolean false false Decode the image off-main-thread using the Image Decode API. Test before using!

You can consult Typescript types in the code for more context.

Feedback

I have some specific questions that I would like input on. If you want to go exploring, or have used the library and had gripes with it, then see FEEDBACK.md and let's have a discussion!

Roadmap

See ROADMAP.md for information and ideas about where the project is headed.

Contributing

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.

Thanks and Inspiration

Here are some resources whose ideas resonate with me and have informed this library.

License

MIT License ยฉ Fotis Papadogeorgopoulos

react-lazy-images's People

Contributors

csabapalfi avatar fpapado avatar radfahrer avatar seandemps avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-lazy-images's Issues

thoughts on <picture> support?

great library. I've enjoyed using it.

your roadmap doesn't mention < picture > support.

what is your thought on that.

any suggested workarounds to support both lazy loading images with this library but using < picture > to load the right image supported by the browser?

Debounce/throttling support

Firstly, fantastic job with this library! Love that it's built directly in Typescript :-)

Wondering how hard it would be to add a debounce/throttle option so that only images that stay within the viewport for a certain amount of time actually trigger the loading of their content?

Support for components other than images

Hi @fpapado, thank you for open-sourcing this library! I am wondering what your thoughts are around making this library work for more than just images?

Image is a special-case, and since you've already solved the harder one, would you be open to offering a component that lazy loads any child (and not just image)?

exception thrown when image is 404

I get the following error when I have an image that is not found (404) and I'm also importing `intersection-observer'

import 'intersection-observer'; \\ the package is just imported, no other code references it

Has anyone else seen this problem??

screenshot 2018-10-10 17 04 38

Re-enable No JS Fallback

Hello! First off I am a new user of react-lazy-images and want to thank you for your great work here!

Based on this comment and my personal experience using a noscript fallback in production (check out the Featured Venue images on this page with JS off) it seems like React has fixed the issue regarding using <noscript>. The code mentions that when this issue is fixed this library would once again offer a fallback API.

I have no problems with how this was originally implemented and documented. I understand this really is not a priority since it is easy to implement along side this module but figured I would bring it up to put it on your radar. I may also be able to find time to implement this and submit a pull request if that works better for you.

Visibility status and unload

Hello! I'm trying to replace a combo with react-visibility-sensor and react-image with react-lazy-images because of your SSR and srcset support, but one feature I'm missing is an onChange prop, so I can get events for when the component is considered in or out of view.

react-intersection-observer has an InView component. It would be nice to leverage that in some way.

Edit: or maybe I just use the InView component myself? Would that double up the event listeners?

Related, what's your opinion on unloading images when out of view?

Downloading images twice

When the placeholder is visible on the viewport is downloading the image twice, at the same time.

image

I debugged my app to see why this is happening, and I see that If I replace that Promise to just a Promise.resolve(), it is working downloading the image only once:

https://github.com/fpapado/react-lazy-images/blob/master/src/LazyImageFull.tsx#L366

What's the reason for downloading twice the image? I suppose that the second download is after replacing the placeholder with the img tag. Is this under control?

Last Chrome version keeps NotAsked state

In Firefox and Safari is working correctly. However I can't say the same using my Chrome Version 75.0.3770.100.

Using a carousel, or just scrolling, the images are not detected as visibles. Displaying always the placeholder, in my case; <div class="img-loading"></div>... The only way that I have to force to print the image, is doing resize of the window.

carousel

My current image component:

 <LazyImage
  src={src}
  {...altAndTitle}
  actual={({ imageProps }) => (
    <img
       draggable={false}
        {...imageProps}
        {...props}
      />
   )}
   error={() => (
       <img
          className="error-img"
           {...props}
            src={fallbackSrc}
         />
     )}
   placeholder={({ ref }) => <div className="img-loading" ref={ref} />}
/>

Overly verbose setState

Less of an issue and more of an FYI - as encouraged by your feedback.md ๐Ÿ˜€

I am authoring a library similar to yours (but without intersection-observer) so was digging into your source code to see your approach. Looks nice and thanks for some inspiration! I did notice that a lot of your setState code goes something like this:

this.setState((state, props) => ({
  ...state,
  imageState: ImageState.Loading
}));

According to the React docs state updates are merged so you don't have to spread state when you set it. I think you could actually just do something like this:

this.setState({
  imageState: ImageState.Loading
});

I could raise a PR but the project doesn't have any tests so I don't want to accidentally break something.

Documentation for background-images

You mention that component supports background-images. Can you provide any documentation / sample code on how to do so? For example, divs or any other elements with inline CSS background-image property.

fade in

hi,
is fade in supported? how would i go about implementing it?

after the debouncing, the image shows up too fast.

thanks

LaziImageFull.d.ts errors

I've just installed v1.1 and noticed that there's now a bunch of TS errors in IntelliJ, complaining about the LazyImageFull.d.ts file.

Error:(80, 41) TS1005: ',' expected.
Error:(81, 17) TS1005: ',' expected.
Error:(82, 18) TS1005: ',' expected.
Error:(83, 16) TS1005: ',' expected.
Error:(84, 20) TS1005: ',' expected.
Error:(86, 20) TS1005: ',' expected.
Error:(87, 6) TS1005: ',' expected.
Error:(89, 17) TS1005: ',' expected.
Error:(90, 18) TS1005: ',' expected.
Error:(91, 16) TS1005: ',' expected.
Error:(92, 20) TS1005: ',' expected.
Error:(94, 20) TS1005: ',' expected.
Error:(95, 6) TS1005: ',' expected.
Error:(96, 10) TS1109: Expression expected.
Error:(96, 18) TS1109: Expression expected.
Error:(102, 29) TS1005: ',' expected.
Error:(104, 24) TS1005: ',' expected.
Error:(105, 6) TS1005: ',' expected.
Error:(106, 23) TS1005: ',' expected.
Error:(107, 20) TS1005: ',' expected.
Error:(109, 20) TS1005: ',' expected.
Error:(110, 6) TS1005: ',' expected.
Error:(113, 24) TS1005: ',' expected.
Error:(114, 6) TS1005: ',' expected.
Error:(115, 23) TS1005: ',' expected.
Error:(116, 20) TS1005: ',' expected.
Error:(118, 20) TS1005: ',' expected.
Error:(119, 6) TS1005: ',' expected.
Error:(120, 10) TS1109: Expression expected.
Error:(120, 18) TS1109: Expression expected.
Error:(80, 35) TS2304: Cannot find name 'import'.
Error:(80, 54) TS2339: Property 'Unionized' does not exist on type '"unionize"'.
Error:(86, 14) TS2693: 'string' only refers to a type, but is being used as a value here.
Error:(88, 23) TS2339: Property 'MultiValueVariants' does not exist on type 'Promise<typeof "C:/Projects/_experiments/Q-view-react/node_modules/unionize/lib/index">'.
Error:(94, 14) TS2693: 'string' only refers to a type, but is being used as a value here.
Error:(102, 23) TS2304: Cannot find name 'import'.
Error:(102, 42) TS2339: Property 'Unionized' does not exist on type '"unionize"'.
Error:(104, 17) TS2693: 'boolean' only refers to a type, but is being used as a value here.
Error:(109, 14) TS2693: 'string' only refers to a type, but is being used as a value here.
Error:(111, 23) TS2339: Property 'MultiValueVariants' does not exist on type 'Promise<typeof "C:/Projects/_experiments/Q-view-react/node_modules/unionize/lib/index">'.
Error:(113, 17) TS2693: 'boolean' only refers to a type, but is being used as a value here.
Error:(118, 14) TS2693: 'string' only refers to a type, but is being used as a value here.

Not sure if you're seeing the same issues that end, but IntelliJ wasn't complaining with the previous version.

I've also had to upgrade my @types/react typedefs as the linter was complaining about the usage of React.RefObject

2 easily fixable errors in the *.d.ts files in dist

Hi, in both LazyImage.d.ts and LazyImageFull.d.ts react is imported incorrectly.
Instead of

import React from "react";

it should read

import * as React from "react";

The 2 offending lines are spit out in e.g. a webpack build, and it breaks auto-complete in VS Code. Fixing it manually makes the auto-completion appear.

I am not sure how the d.ts. files are generated, otherwise I would have made a PR, but here you go.

Warning: Can't perform a React state update on an unmounted component

Please fix this issue with possibility to memory leak

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method. (in Image)

Not using specific version on dependecies can cause build failed

I see that this package depends on package react-intersection-observer, but this may cause the build failed if you didn't set the specific version on your dependencies i think. Recently react-intersection-observer has publish new version, and my create-react-app project failed to build caused by this peer dependencies (i already put the issue on there )

I am trying to install the react-intersection-observer v6.1.1 manually, and it fix the build in my local, but didn't fix it on my netlify build. I also trying to fork the package and try to test it in my package.json using this command, to see is it really caused by this ^ on dependency version.

npm install dehamzah/react-lazy-images#master

But it failed to found the package when i start my cra project.

Image width and height

It would be nice if we can have the actual image width and height information at the imageProps eg.

                        actual={
                            ({imageProps}) => {
                                // imageProps.width & imageProps.height
                                return (<img {...imageProps} />);
                            }
                        }

so we can customize the actual width and size when needed without reloading the image again.

Error fallback image with loadEagerly?

I need an image that should be rendered asap (I'm using SSR with next.js), and currently I'm using the loadEagerly prop in this image... However, I found interesting that if for some reason this image doesn't exist, display the fallback image... Looks that with loadEagerly is only rendering the actual.

There is some another option to do it?

Add support for loadEagerly or Intersection Observer rootTarget

Use case:

I want to lazy load images inside of an image carousel. Specifically given 3 images: image0, images1, images2 where image0 is initially displayed in the viewport, I want to load only image0 and image1 initially. Then after the user transitions to image1 in the viewport, I want to load image2 (which is still not in view).

Possible solution:

I noticed that with the intersection observer you can designate a root element either via a DOM node or element id string. However no prop exists currently to target that node.

Another solution:

Don't get fancy and just eagerly load all the images

I noticed in the source code that there's a prop for loadEagerly. However, when I try and look thought the source code, I noticed that there's no reference to that prop.

Let me know how I can help contribute to this awesome project!

Is there any plan for a new version release?

I am aware that there are updates related to React 18 support in this library.
However, the new version has not been released yet.

Do you have any plans to deploy the update?

[Proposal] Add noscript fallback again

For crawlers is useful if we add the noscript tag for each image:

<noscript>
  <img src="image-to-lazy-load.jpg" alt="I'm an image!">
</noscript>

This would be nice if directly the react-lazy-images library take care of it. I see that was implemented in the past (v0.3.0). However, was removed after a react issue... But right now this issue is already fixed, so this feature can be activated again

Requirement of allowSyntheticDefaultImports and esModuleInterop for Typescript usage should be documented

I got weird typing errors after adding react-lazy-images to my project and didn't understand them until I read #13. Using the workaround described there works for me. I think, however, it would make this project easier to use for anyone using TypeScript if this requirement were clearly documented in the project's README. Otherwise you have to have the insight to look into the closed GitHub issues here to find #13.

Missing `alt` attribute

I see a lot of stuff in the API, but no alt attribute. This is a mission-critical one for a more accessible component.

Some usage tips:

  • alt takes a string value with the content.
  • If the image is decorative, the attribute value should be empty to ensure the filename is not read (alt="")

Here's a decision tree for choosing good alt text: https://www.w3.org/WAI/tutorials/images/decision-tree/

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.