Lazy loading images
- Dominant language
- TypeScript
- Stars
- 285
- Forks
- 307
- PR merge metrics
- No merged PRs in 30d
Description
# Feature Request: Add `lazyLoading` config option to defer image fetching
## Description
The `@editorjs/image` plugin currently loads **all images eagerly** at editor initialization time, regardless of their position in the document. There is no built-in way to defer image loading for off-screen blocks.
This becomes a significant problem when using a **custom uploader backed by a private/authenticated endpoint** — every image URL in the document triggers an immediate network request the moment the editor renders, even for images far below the fold that the user may never reach.
---
## Problem
When an Editor.js document contains many image blocks (e.g. a long article or report), all images are fetched simultaneously on load. The current implementation unconditionally assigns `src` at render time:
```ts
// src/ui.ts (current behavior)
const attributes: { [key: string]: string | boolean } = {
src: url, // always set immediately
};
```
### Impact
- **Unnecessary backend requests** — private image endpoints are called for every image in the document, even those never seen by the user.
- **Performance degradation** — large documents with many images cause a burst of parallel requests on load, slowing down the initial render.
- **Increased server costs** — authenticated or CDN-backed image servers are hit unnecessarily.
- **Poor UX on slow connections** — bandwidth is consumed by off-screen images instead of prioritizing visible content.
---
## Steps to Reproduce
1. Configure `@editorjs/image` with a custom uploader pointing to a private backend.
2. Load a document with 10+ image blocks.
3. Open the Network tab in DevTools.
4. Observe that **all image requests fire simultaneously** on editor load, including images far below the viewport.
---
## Proposed Solution
Add an optional `lazyLoading` boolean to the plugin config. When enabled, the `src` attribute should be withheld until the image container approaches the viewport, using an `IntersectionObserver`.
### Config API
```ts
// ImageConfig (types.ts)
interface ImageConfig {
// ...existing options
/**
* Enables lazy loading for images.
* When true, images are only fetched when they approach the viewport.
* @default false
*/
lazyLoading?: boolean;
}
```
### Usage
```ts
const editor = new EditorJS({
tools: {
image: {
class: ImageTool,
config: {
uploader: { /* your custom uploader */ },
lazyLoading: true, // 👈
},
},
},
});
```
## Alternatives Considered
- **Native `loading="lazy"` attribute** — simpler, but the image's **_load event_** is never triggered because we have the preloader container.
---
## Additional Context
- Affects most severely: documents with many images, private/authenticated backends, slow network connections.
- `IntersectionObserver` is [[supported in all modern browsers](https://caniuse.com/intersectionobserver)](https://caniuse.com/intersectionobserver).
- This change is fully **backwards compatible** — `lazyLoading` defaults to `false`.
---
## Are you willing to contribute a PR?
Yes, a working implementation has been prototyped and validated locally. Happy to submit a pull request if the maintainers are open to this feature.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.