{"id":19279440,"url":"https://github.com/thepuskar/react-lazy-load-image","last_synced_at":"2026-04-27T11:31:10.704Z","repository":{"id":124207845,"uuid":"576887970","full_name":"thepuskar/react-lazy-load-image","owner":"thepuskar","description":"React Component and custom to lazy load images and other components/elements using Intersection Observer API natively present in the browser.","archived":false,"fork":false,"pushed_at":"2023-01-02T15:56:49.000Z","size":193,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-03T00:35:39.412Z","etag":null,"topics":["custom-hooks","intersection-observer","lazyload","lazyload-image","react","react-hooks","typescript"],"latest_commit_sha":null,"homepage":"react-lazy-load-image.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thepuskar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-11T10:13:45.000Z","updated_at":"2023-08-10T06:57:35.000Z","dependencies_parsed_at":null,"dependency_job_id":"2b07e51f-a5ce-4177-8efb-535e24e82057","html_url":"https://github.com/thepuskar/react-lazy-load-image","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/thepuskar/react-lazy-load-image","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thepuskar%2Freact-lazy-load-image","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thepuskar%2Freact-lazy-load-image/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thepuskar%2Freact-lazy-load-image/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thepuskar%2Freact-lazy-load-image/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thepuskar","download_url":"https://codeload.github.com/thepuskar/react-lazy-load-image/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thepuskar%2Freact-lazy-load-image/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32335295,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"online","status_checked_at":"2026-04-27T02:00:06.769Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["custom-hooks","intersection-observer","lazyload","lazyload-image","react","react-hooks","typescript"],"created_at":"2024-11-09T21:15:02.083Z","updated_at":"2026-04-27T11:31:10.686Z","avatar_url":"https://github.com/thepuskar.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## React Lazy Load Image\n\nReact Component and custom to lazy load images and other components/elements. using [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) natively present in the browser.\n\n\n# useIntersectionObserver\n\nThe useIntersectionObserver is a custom hook in React that allows you to detect when an element is in the viewport of a user's browser. It takes in an object with a ref property and an optional options property. The ref property is a reference object to an element in the DOM, and the options property is an object with configuration options for the intersection observer instance.\n\nThe hook returns an IntersectionObserverEntry object if the element is in the viewport, otherwise it returns undefined. The entry object contains information about the intersection between the element and the viewport, such as the intersection ratio and whether the element is fully or partially in the viewport.\n\nThe hook uses the useIsomorphicEffect hook to set up an intersection observer instance on the element when the component mounts and clean it up when the component unmounts. The intersection observer instance is configured with the options passed in the options object.\n\nThe hook also includes a state variable entry and a state updater function updateEntry that stores the IntersectionObserverEntry object returned by the intersection observer instance and updates the component's state with it.\n\nIf the triggerOnce option is set to true, the hook will check for intersection only once and will disconnect the intersection observer instance after the intersection.\n\nThe default values for the rootMargin and threshold options are '0px' and [0], respectively. The root option defaults to null.\n\n## The Hook\n\n```ts filename=\"useIntersectionObserver.ts\" {3} copy\nimport { RefObject, useState } from 'react'\nimport { useIsomorphicEffect } from './useIsomorphicEffect'\n\nconst DEFAULT_ROOT_MARGIN = '0px'\nconst DEFAULT_THRESHOLD = [0]\n\ninterface IIntersectionObserverProperties {\n  ref?: RefObject\u003cElement\u003e | null\n  options?: IntersectionObserverOptions\n}\n\ninterface IntersectionObserverOptions {\n  triggerOnce?: boolean\n  threshold?: number | number[]\n  root?: Element | null | undefined\n  rootMargin?: string\n}\n\nexport function useIntersectionObserver({\n  ref,\n  options = {\n    threshold: DEFAULT_THRESHOLD,\n    root: null,\n    rootMargin: DEFAULT_ROOT_MARGIN,\n    triggerOnce: false\n  }\n}: IIntersectionObserverProperties): IntersectionObserverEntry | undefined {\n  const { threshold, root, rootMargin, triggerOnce } = options\n\n  const [entry, setEntry] = useState\u003cIntersectionObserverEntry\u003e()\n  const frozen = entry?.isIntersecting \u0026\u0026 triggerOnce\n\n  const updateEntry = ([entry]: IntersectionObserverEntry[]): void =\u003e {\n    setEntry(entry)\n  }\n\n  useIsomorphicEffect(() =\u003e {\n    const node = ref?.current\n    const hasIOSupport = !!window.IntersectionObserver\n\n    if (!hasIOSupport || frozen || !node) return\n\n    const observerParams = { threshold, root, rootMargin }\n    const observer = new IntersectionObserver(updateEntry, observerParams)\n    observer.observe(node)\n\n    return () =\u003e observer.disconnect()\n  }, [ref?.current, JSON.stringify(threshold), root, rootMargin, frozen])\n  return entry\n}\n```\n\n## Usage\n\n```tsx filename=\"LazyImage.tsx\"\nimport { useRef } from 'react'\nimport { useIntersectionObserver } from './hooks'\n\ninterface IImage {\n  url: string\n}\n\nexport const ImageComponent = (props: IImage) =\u003e {\n  const ref = useRef\u003cHTMLDivElement | null\u003e(null)\n\n  const entry = useIntersectionObserver({\n    ref,\n    options: {\n      threshold: 0.25,\n      triggerOnce: true\n    }\n  })\n\n  const isVisible = !!entry?.isIntersecting\n\n  return (\n    \u003cdiv\n      className='image-container'\n      ref={ref}\n      style={{\n        paddingBottom: `${(3024 / 4032) * 100}%`,\n        width: '100%'\n      }}\n    \u003e\n      {isVisible ? (\n        \u003c\u003e\n          \u003cimg className='image isLoaded' src={props?.url} /\u003e\n        \u003c/\u003e\n      ) : null}\n    \u003c/div\u003e\n  )\n}\n```\n\n# useProgressiveImage\n`useProgressiveImage` is a custom React hook that allows for lazy loading of images by progressively rendering an image in low quality first, and then replacing it with a high quality version once it is in view.\n\nThe hook takes in two arguments:\n- `lowQualitySrc`: a string representing the URL of the low quality version of the image.\n- `highQualitySrc`: a string representing the URL of the high quality version of the image.\n\nIt returns an array with three elements:\n\n- `src:string`: the current src of the image, either the lowQualitySrc or the highQualitySrc.\n- `style:{ blur: boolean }`: an object with a boolean value indicating whether or not the image is currently in low quality (true if it is, false if it is not).\n- `ref:RefObject\u003cHTMLImageElement\u003e`: a ref object that can be attached to an img element to observe its intersection with the viewport.\n\n## The Hook \n\n```ts filename=\"useProgressiveImage.ts\"\nimport { useState, useEffect, useRef, RefObject } from 'react'\nimport 'intersection-observer'\nimport { useIsomorphicEffect } from './useIsomorphicEffect'\n\nexport const useProgressiveImage = (\n  lowQualitySrc: string,\n  highQualitySrc: string\n): [string, { blur: boolean }, RefObject\u003cHTMLImageElement\u003e] =\u003e {\n  const [src, setSrc] = useState\u003cstring\u003e(lowQualitySrc)\n  const observerRef = useRef\u003cHTMLImageElement\u003e(null)\n\n  useEffect(() =\u003e {\n    setSrc(lowQualitySrc)\n    const observer = new IntersectionObserver(\n      (entries) =\u003e {\n        entries.forEach((entry) =\u003e {\n          if (entry.isIntersecting) {\n            setSrc(highQualitySrc)\n            observer.disconnect()\n          }\n        })\n      },\n      { threshold: 0.01 }\n    )\n    if (observerRef.current) {\n      observer.observe(observerRef.current)\n    }\n  }, [lowQualitySrc, highQualitySrc, observerRef])\n\n  return [src, { blur: src === lowQualitySrc }, observerRef]\n}\n\n```\n\n## Usage\n\n```tsx filename=\"ProgressiveImage.tsx\"\nimport { useProgressiveImage } from '../../hooks/useProgressiveImage'\n\ninterface IImageAttributes {\n  lowQualitySrc: string\n  highQualitySrc: string\n  [key: string]: string\n}\n\nexport const ProgressiveImage = (props: IImageAttributes) =\u003e {\n  const { lowQualitySrc, highQualitySrc, alt, height, width, ...rest } = props\n\n  const [src, { blur }, observerRef] = useProgressiveImage(\n    lowQualitySrc,\n    highQualitySrc\n  )\n\n  return (\n    \u003cimg\n      ref={observerRef as React.RefObject\u003cHTMLImageElement\u003e}\n      src={src}\n      style={{\n        width: width ?? '100%',\n        height: height ?? '100%',\n        filter: blur ? 'blur(20px)' : 'none',\n        transition: blur ? 'none' : 'filter 0.3s ease-out'\n      }}\n      alt={alt ?? 'Progressive image'}\n      {...rest}\n    /\u003e\n  )\n}\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthepuskar%2Freact-lazy-load-image","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthepuskar%2Freact-lazy-load-image","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthepuskar%2Freact-lazy-load-image/lists"}