diff --git a/packages/react-notion-x/package.json b/packages/react-notion-x/package.json
index 9cc6c3f37..6cd87f534 100644
--- a/packages/react-notion-x/package.json
+++ b/packages/react-notion-x/package.json
@@ -64,7 +64,6 @@
"react-fast-compare": "catalog:",
"react-hotkeys-hook": "catalog:",
"react-image": "catalog:",
- "react-lazy-images": "catalog:",
"react-modal": "catalog:"
},
"devDependencies": {
diff --git a/packages/react-notion-x/src/components/lazy-image.tsx b/packages/react-notion-x/src/components/lazy-image.tsx
index 7af234d09..768b27170 100644
--- a/packages/react-notion-x/src/components/lazy-image.tsx
+++ b/packages/react-notion-x/src/components/lazy-image.tsx
@@ -1,6 +1,5 @@
import { normalizeUrl } from 'notion-utils'
import React from 'react'
-import { ImageState, LazyImageFull } from 'react-lazy-images'
import { useNotionContext } from '../context'
import { cs } from '../utils'
@@ -59,6 +58,23 @@ export function LazyImage({
[zoomable, attachZoom]
)
+ const [isLazyLoaded, setIsLazyLoaded] = React.useState(false)
+ const onLoadLazy = React.useCallback(() => {
+ setIsLazyLoaded(true)
+ }, [])
+
+ const lazyImageRef = React.useCallback(
+ (image: HTMLImageElement) => {
+ attachZoomRef?.(image)
+
+ // if the image is cached, we can trigger the onLoadLazy immediately
+ if (image.complete) {
+ onLoadLazy()
+ }
+ },
+ [attachZoomRef, onLoadLazy]
+ )
+
if (previewImage) {
const aspectRatio = previewImage.originalHeight / previewImage.originalWidth
@@ -81,60 +97,54 @@ export function LazyImage({
)
}
- return (
- // @ts-expect-error LazyImage types are out-of-date.
-
- {({ imageState, ref }) => {
- const isLoaded = imageState === ImageState.LoadSuccess
- const wrapperStyle: React.CSSProperties = {
- width: '100%'
- }
- const imgStyle: React.CSSProperties = {}
-
- if (height) {
- wrapperStyle.height = height
- } else {
- imgStyle.position = 'absolute'
- wrapperStyle.paddingBottom = `${aspectRatio * 100}%`
- }
-
- return (
-
-

-
-

-
- )
- }}
-
- )
+ return (() => {
+ const wrapperStyle: React.CSSProperties = {
+ width: '100%'
+ }
+ const imgStyle: React.CSSProperties = {}
+
+ if (height) {
+ wrapperStyle.height = height
+ } else {
+ imgStyle.position = 'absolute'
+ wrapperStyle.paddingBottom = `${aspectRatio * 100}%`
+ }
+
+ return (
+
+

+
+

+
+ )
+ })()
} else {
// TODO: GracefulImage doesn't seem to support refs, but we'd like to prevent
// invalid images from loading as error states
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1b9dbfc19..3b4d0683d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -120,9 +120,6 @@ catalogs:
react-image:
specifier: ^4.0.3
version: 4.1.0
- react-lazy-images:
- specifier: ^1.1.0
- version: 1.1.0
react-modal:
specifier: ^3.16.3
version: 3.16.3
@@ -396,9 +393,6 @@ importers:
react-image:
specifier: 'catalog:'
version: 4.1.0(@babel/runtime@7.27.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
- react-lazy-images:
- specifier: 'catalog:'
- version: 1.1.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-modal:
specifier: 'catalog:'
version: 3.16.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -4470,9 +4464,6 @@ packages:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
- invariant@2.2.4:
- resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
-
ipaddr.js@1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
@@ -6238,11 +6229,6 @@ packages:
react: '>=16.8'
react-dom: '>=16.8'
- react-intersection-observer@6.4.2:
- resolution: {integrity: sha512-gL6YrkhniA0tIbyDbUterzBwKh61vHR520rsKULel5T37gG4YP07wnWI3WoqOcKK5bKAu0PZB2FHD7/OjawN+w==}
- peerDependencies:
- react: ^15.0.0 || ^16.0.0 || ^17.0.0
-
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
@@ -6252,12 +6238,6 @@ packages:
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
- react-lazy-images@1.1.0:
- resolution: {integrity: sha512-h5DHFhkMJyh2qsDl3hXWu6d+On10FsgHtRJ+BH7xjgsFOvsqaii9CEwEESqPJrrAiHo1qrN1LgzrV8X3zctHKA==}
- peerDependencies:
- react: ^15 || ^16
- react-dom: ^15 || ^16
-
react-lifecycles-compat@3.0.4:
resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==}
@@ -7230,9 +7210,6 @@ packages:
resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
engines: {node: '>=18'}
- unionize@2.2.0:
- resolution: {integrity: sha512-lHXiL6LPVuRYBGCLOdUd4GMHoAGqM0HtYHAZcA6pUEiwN1nk+LEYlh8bud7saeL0bkFntJzCPEPVVJeFm3Cqsg==}
-
unique-string@2.0.0:
resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
engines: {node: '>=8'}
@@ -12349,10 +12326,6 @@ snapshots:
hasown: 2.0.2
side-channel: 1.1.0
- invariant@2.2.4:
- dependencies:
- loose-envify: 1.4.0
-
ipaddr.js@1.9.1: {}
ipaddr.js@2.2.0: {}
@@ -14334,25 +14307,12 @@ snapshots:
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
- react-intersection-observer@6.4.2(react@19.1.0):
- dependencies:
- '@babel/runtime': 7.26.10
- invariant: 2.2.4
- react: 19.1.0
-
react-is@16.13.1: {}
react-is@17.0.2: {}
react-is@18.3.1: {}
- react-lazy-images@1.1.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
- dependencies:
- react: 19.1.0
- react-dom: 19.1.0(react@19.1.0)
- react-intersection-observer: 6.4.2(react@19.1.0)
- unionize: 2.2.0
-
react-lifecycles-compat@3.0.4: {}
react-modal@3.16.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
@@ -15572,8 +15532,6 @@ snapshots:
unicorn-magic@0.3.0: {}
- unionize@2.2.0: {}
-
unique-string@2.0.0:
dependencies:
crypto-random-string: 2.0.0
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 05544bd25..7b1d32cd9 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -43,7 +43,6 @@ catalog:
react-fast-compare: ^3.2.0
react-hotkeys-hook: ^4.5.1
react-image: ^4.0.3
- react-lazy-images: ^1.1.0
react-modal: ^3.16.3
react-scripts: 5.0.1
react-tweet-embed: ^2.0.0