react-fastload 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 react-fastload contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,297 @@
1
+ # react-fastload
2
+
3
+ **An adaptive resource-loading scheduler for React — with real,
4
+ browser-measured performance metrics instead of marketing claims.**
5
+
6
+ <p>
7
+ <img alt="npm version" src="https://img.shields.io/npm/v/react-fastload?color=cb3837&label=npm" />
8
+ <img alt="license" src="https://img.shields.io/badge/license-MIT-lightgrey" />
9
+ <img alt="types" src="https://img.shields.io/badge/types-TypeScript-3178c6" />
10
+ <img alt="bundle" src="https://img.shields.io/badge/tree--shakeable-yes-brightgreen" />
11
+ </p>
12
+
13
+ One shared scheduler coordinates *when* images, video, audio, and
14
+ dynamically imported components actually fetch — based on viewport
15
+ proximity, priority, network conditions, and a concurrency budget —
16
+ instead of each resource type solving "don't load this yet" independently
17
+ with no coordination between them.
18
+
19
+ ```tsx
20
+ import { FastLoadProvider, SmartImage, SmartVideo, SmartAudio, lazyComponent } from "react-fastload";
21
+
22
+ const Analytics = lazyComponent(() => import("./Analytics"), { priority: "LOW" });
23
+
24
+ function App() {
25
+ return (
26
+ <FastLoadProvider strategy="adaptive" preloadDistance={1000}>
27
+ <SmartImage src="/hero.webp" alt="Hero" priority="CRITICAL" />
28
+ <SmartImage src="/card.webp" alt="Card" priority="auto" />
29
+ <SmartVideo src="/demo.mp4" poster="/poster.webp" priority="LOW" />
30
+ <SmartAudio src="/theme.mp3" label="Theme song" priority="IDLE" />
31
+ <Analytics />
32
+ </FastLoadProvider>
33
+ );
34
+ }
35
+ ```
36
+
37
+ ## Table of contents
38
+
39
+ - [Installation](#installation)
40
+ - [Why this exists](#why-this-exists)
41
+ - [How it works](#how-it-works)
42
+ - [How it differs from native lazy loading / React.lazy / code splitting](#how-it-differs)
43
+ - [Quick start](#quick-start)
44
+ - [Reading live metrics](#reading-live-metrics)
45
+ - [Debug mode](#debug-mode)
46
+ - [API reference](#api-reference)
47
+ - [Browser compatibility](#browser-compatibility)
48
+ - [When to use it — and when not to](#when-to-use-it--and-when-not-to)
49
+ - [Benchmarks — and their current limitations](#benchmarks--and-their-current-limitations)
50
+ - [Limitations](#limitations)
51
+ - [Roadmap](#roadmap)
52
+ - [Development](#development)
53
+ - [Contributing](#contributing)
54
+ - [License](#license)
55
+
56
+ ## Installation
57
+
58
+ ```bash
59
+ npm install react-fastload
60
+ # or
61
+ pnpm add react-fastload
62
+ # or
63
+ yarn add react-fastload
64
+ ```
65
+
66
+ Peer dependencies: `react >= 17`, `react-dom >= 17`. Ships ESM + CommonJS
67
+ builds and full TypeScript declarations; React is not bundled.
68
+
69
+ ## Why this exists
70
+
71
+ Native `loading="lazy"` and `React.lazy` each solve one narrow slice of
72
+ "don't fetch this yet," independently, with **no shared concept of
73
+ priority and no shared concurrency budget across resource types.** A page
74
+ with many below-the-fold images, a video, and a few lazy components can
75
+ still burst-request everything with no coordination — the network has no
76
+ way to know your `HIGH`-priority hero image matters more than a `LOW`-
77
+ priority footer icon that happened to scroll into view a moment earlier.
78
+
79
+ ReactFastLoad puts images, video, and components through **one registry
80
+ and one priority-aware scheduler**, so priority is meaningful across the
81
+ whole page, not just within one resource type.
82
+
83
+ ## How it works
84
+
85
+ ```
86
+ Resource → Registry → Priority Engine → Viewport/Network/User signals
87
+ → Scheduler → Loading decision → Browser resource → Metrics
88
+ ```
89
+
90
+ - **Registry** — bookkeeping for every resource ReactFastLoad knows about:
91
+ id, type, priority, state, viewport distance, timestamps.
92
+ - **Priority Engine** — turns declared priority (`CRITICAL` → `IDLE`) plus
93
+ live signals (viewport distance, connection quality) into an effective
94
+ score. `CRITICAL` always loads first, unconditionally.
95
+ - **Scheduler** — dispatches eligible resources within a concurrency cap,
96
+ in priority order, and logs every decision it makes.
97
+ - **Metrics** — real `PerformanceObserver` / Navigation / Resource Timing
98
+ reads, kept strictly separate from ReactFastLoad's own internal
99
+ bookkeeping (see [Benchmarks](#benchmarks--and-their-current-limitations)).
100
+
101
+ Two things that happen automatically, not as opt-in features: **request
102
+ deduplication** (two components rendering the same `src` share one load,
103
+ not two — see `LoadManager`'s reference counting in
104
+ [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md)) and **abort-on-unmount**
105
+ (an in-flight, not-yet-loaded resource is cancelled via `AbortController`
106
+ once its last consumer unmounts, rather than finishing a fetch nobody
107
+ needs anymore).
108
+
109
+ Full detail in [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md).
110
+
111
+ ## How it differs
112
+
113
+ | | Native `loading="lazy"` | `React.lazy` | ReactFastLoad |
114
+ |---|---|---|---|
115
+ | Cross-resource priority | ❌ | ❌ | ✅ shared priority levels |
116
+ | Shared concurrency budget | ❌ (browser-managed, opaque) | ❌ | ✅ configurable |
117
+ | Works across images + video + audio + components | per-`<img>` only | components only | ✅ all four, one scheduler |
118
+ | Configurable preload distance | limited/inconsistent | n/a | ✅ `preloadDistance` |
119
+ | Debug visibility into *why* something loaded when | ❌ | ❌ | ✅ debug panel + decision log |
120
+
121
+ It doesn't replace these mechanisms — `SmartImage` still sets
122
+ `decoding="async"` and `fetchpriority`; `lazyComponent` still relies on
123
+ your bundler's code splitting. It adds a coordination layer on top.
124
+
125
+ ## Quick start
126
+
127
+ ```tsx
128
+ import { FastLoadProvider, SmartImage, SmartVideo, lazyComponent, useFastLoadMetrics } from "react-fastload";
129
+
130
+ const Analytics = lazyComponent(() => import("./Analytics"), { priority: "LOW" });
131
+
132
+ function Gallery({ items }: { items: { id: string; src: string; alt: string }[] }) {
133
+ return (
134
+ <FastLoadProvider strategy="adaptive" preloadDistance={1000}>
135
+ <SmartImage src="/hero.webp" alt="Hero" priority="CRITICAL" />
136
+
137
+ {items.map((item) => (
138
+ <SmartImage key={item.id} src={item.src} alt={item.alt} priority="auto" />
139
+ ))}
140
+
141
+ <SmartVideo src="/demo.mp4" poster="/demo-poster.webp" priority="LOW" />
142
+ <Analytics />
143
+ </FastLoadProvider>
144
+ );
145
+ }
146
+ ```
147
+
148
+ ## Reading live metrics
149
+
150
+ ```tsx
151
+ function PerfBadge() {
152
+ const { lcp, deferredRequests, bytesDeferred } = useFastLoadMetrics();
153
+ return (
154
+ <div>
155
+ LCP: {lcp ? `${Math.round(lcp)}ms` : "measuring…"} · deferred: {deferredRequests} (
156
+ {(bytesDeferred / 1024).toFixed(0)} KB est.)
157
+ </div>
158
+ );
159
+ }
160
+ ```
161
+
162
+ Every field is documented in [docs/API.md](./docs/API.md) as one of three
163
+ kinds — **browser-observed** (from `PerformanceObserver`/Navigation/
164
+ Resource Timing), **internal** (ReactFastLoad's own bookkeeping), or
165
+ **estimated** (only as accurate as the `estimatedSize` you provide) — so
166
+ you always know what you're looking at.
167
+
168
+ ## Debug mode
169
+
170
+ ```tsx
171
+ <FastLoadProvider debug>
172
+ <App />
173
+ </FastLoadProvider>
174
+ ```
175
+
176
+ Renders a floating panel (dev builds only) listing every registered
177
+ resource, its state, and the scheduler's recent load/defer/prefetch
178
+ decisions with reasons — useful for verifying *why* something loaded when
179
+ it did, rather than guessing.
180
+
181
+ ## API reference
182
+
183
+ See [docs/API.md](./docs/API.md) for the full `<FastLoadProvider>`,
184
+ `<SmartImage>`, `<SmartVideo>`, `<SmartAudio>`, `lazyComponent()`, hooks,
185
+ and type reference. `LoadManager`, `ResourceRegistry`, `PriorityEngine`, and
186
+ `Scheduler` are also exported directly for building custom resource
187
+ wrappers on the same scheduler.
188
+
189
+ ## Browser compatibility
190
+
191
+ Every feature degrades gracefully instead of throwing:
192
+
193
+ | Feature | Fallback when unsupported |
194
+ |---|---|
195
+ | `IntersectionObserver` | Resource reports as immediately eligible |
196
+ | Network Information API | `ConnectionInfo` fields are `null`/`false`; no connection-based adjustment |
197
+ | `PerformanceObserver` (LCP/CLS/paint) | Corresponding metric fields stay `null`, never estimated |
198
+ | `fetchPriority` attribute | Omitted; `loading`/`decoding` still applied |
199
+ | `requestIdleCallback` | Falls back to a short `setTimeout` |
200
+
201
+ ## When to use it — and when not to
202
+
203
+ **Use it when** your page has enough below-the-fold images/video/
204
+ components that load order and concurrency actually matter, and you want
205
+ one consistent priority model plus real metrics to verify the effect.
206
+
207
+ **Skip it when** your page only has a handful of resources (native
208
+ `loading="lazy"` is simpler and sufficient), or you need guaranteed load
209
+ order regardless of viewport (use `priority="CRITICAL"` + `strategy="eager"`
210
+ on those specific resources instead of reaching for a different tool).
211
+
212
+ ## Benchmarks — and their current limitations
213
+
214
+ The `/benchmark` app compares a Baseline page (plain `<img>`/`<video>`/
215
+ `React.lazy`) against a ReactFastLoad page, reading real
216
+ `PerformanceObserver`/Navigation/Resource Timing values — nothing is
217
+ hardcoded. **That said, treat any single run's numbers as illustrative,
218
+ not conclusive**, until run under the protocol below. An early internal
219
+ run surfaced exactly the kind of confound this warning exists for: a TTFB
220
+ drop that a client-side scheduler cannot legitimately produce, which
221
+ almost always means the two pages weren't served under identical
222
+ navigation/cache/dev-server conditions. If you see something similar,
223
+ it's a benchmark-setup bug, not a real effect — see
224
+ [docs/METHODOLOGY.md](./docs/METHODOLOGY.md) for the full breakdown of
225
+ which metrics can and can't be affected by a client-side library, and for
226
+ known caveats (e.g. `transferSize` reporting `0` for opaque cross-origin
227
+ responses).
228
+
229
+ **For a defensible comparison:**
230
+ 1. Serve both pages from a **production build**, not the dev server.
231
+ 2. Alternate Baseline/ReactFastLoad runs in **fresh browser contexts**
232
+ (no shared cache/service worker) rather than switching pages in the
233
+ same tab.
234
+ 3. Run each mode **multiple times** (e.g. 10 runs) and report median/p75,
235
+ not a single sample.
236
+ 4. Report **requests avoided** and **bytes transferred** as separate
237
+ numbers — a library can cut transfer size substantially while barely
238
+ changing request count, and conflating the two overstates what
239
+ changed.
240
+ 5. Only compare numbers gathered under identical connection/device
241
+ conditions.
242
+
243
+ The benchmark app now supports exactly this: three content-weight
244
+ scenarios (Light/Heavy/Extreme) and a repeated-run harness that reports
245
+ median/p75/p95 rather than a single sample — see
246
+ [benchmark/README.md](./benchmark/README.md).
247
+
248
+ ## Roadmap
249
+
250
+ Several larger ideas (request deduplication, a real dependency graph,
251
+ service-worker integration, predictive prefetch, chunked/range loading,
252
+ a multi-level cache) have been proposed for a future version. See
253
+ [docs/ROADMAP.md](./docs/ROADMAP.md) for an honest triage of which of
254
+ those are worth building next, which need more design first, and which
255
+ I'd push back on or scope down — rather than a promise to build all of
256
+ it.
257
+
258
+ ## Limitations
259
+
260
+ - The scheduler only coordinates resources it's explicitly told about —
261
+ it never intercepts `fetch`, monkey-patches globals, or touches
262
+ third-party scripts.
263
+ - `CRITICAL`-priority resources are never delayed, by design — don't mark
264
+ something `CRITICAL` unless it genuinely must load first regardless of
265
+ network conditions.
266
+ - `bytesDeferred` is only as accurate as the `estimatedSize` you pass per
267
+ resource — omit it and that resource contributes `0`, which can make
268
+ the deferred-bytes total look artificially low even when real bytes
269
+ were deferred. Pass `estimatedSize` on `SmartImage`/`SmartVideo`/
270
+ `SmartAudio` for a meaningful number.
271
+ - LCP/CLS can still change after being read; see
272
+ [docs/METHODOLOGY.md](./docs/METHODOLOGY.md).
273
+
274
+ ## Development
275
+
276
+ ```bash
277
+ npm install
278
+ npm run typecheck
279
+ npm test
280
+ npm run build
281
+ ```
282
+
283
+ ```bash
284
+ cd benchmark
285
+ npm install
286
+ npm run dev
287
+ ```
288
+
289
+ ## Contributing
290
+
291
+ Issues and PRs welcome. Please include or update tests for any behavioral
292
+ change to `core/`, `observers/`, or `metrics/` — these are the modules the
293
+ rest of the library's correctness depends on.
294
+
295
+ ## License
296
+
297
+ MIT — see [LICENSE](./LICENSE).