react-media-optimizer 1.0.1 → 1.0.2

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.
Files changed (2) hide show
  1. package/README.md +493 -186
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,393 +1,700 @@
1
1
  # 🚀 React Media Optimizer
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/react-media-optimizer.svg)](https://www.npmjs.com/package/react-media-optimizer)
4
- [![npm downloads](https://img.shields.io/npm/dm/react-media-optimizer.svg)](https://npmjs.com/package/react-media-optimizer)
5
- [![bundle size](https://img.shields.io/bundlephobia/minzip/react-media-optimizer)](https://bundlephobia.com/package/react-media-optimizer)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
- [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
3
+
4
+
5
+ [![npm version](https://img.shields.io/npm/v/react-media-optimizer.svg)](https://www.npmjs.com/package/react-media-optimizer)[![npm downloads](https://img.shields.io/npm/dm/react-media-optimizer.svg)](https://npmjs.com/package/react-media-optimizer)[![bundle size](https://img.shields.io/bundlephobia/minzip/react-media-optimizer)](https://bundlephobia.com/package/react-media-optimizer)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
7
+
8
+
8
9
 
9
10
  **Drop-in image & video optimization for React applications.** Automatically compress, lazy-load, and convert media to improve performance, UX, and SEO with minimal effort.
10
11
 
12
+
13
+
11
14
  > 📊 **Average improvements:** 60% faster LCP, 75% smaller images, 40% better SEO scores
12
15
 
16
+
17
+
13
18
  ---
14
19
 
20
+
21
+
15
22
  ## ✨ Why Choose React Media Optimizer?
16
23
 
24
+
25
+
17
26
  | Feature | Benefit | Impact |
27
+
18
28
  |---------|---------|--------|
29
+
19
30
  | **Auto Lazy Loading** | Images/videos load only when visible | ⬇️ 50-80% initial page weight |
31
+
20
32
  | **WebP/WebM Conversion** | Modern formats with better compression | ⬇️ 25-35% smaller file sizes |
33
+
21
34
  | **Client-side Compression** | Reduce upload sizes before server | ⬇️ 60-80% upload bandwidth |
35
+
22
36
  | **SSR/SSG Safe** | Works with Next.js, Gatsby, Remix | ✅ Zero hydration errors |
37
+
23
38
  | **Zero Configuration** | Sensible defaults out of the box | ⏱️ 5-minute integration |
24
39
 
40
+
41
+
25
42
  ---
26
43
 
44
+
45
+
27
46
  ## 📦 Installation
28
47
 
48
+
49
+
29
50
  Install using your preferred package manager.
30
51
 
52
+
53
+
31
54
  ### npm
55
+
32
56
  ```bash
33
- npm install react-media-optimizer
57
+ npm install react-media-optimizer
34
58
  ```
59
+
35
60
  ### yarn
36
61
  ```bash
37
- yarn add react-media-optimizer
62
+ yarn add react-media-optimizer
38
63
  ```
64
+
39
65
  ### pnpm
66
+
40
67
  ```bash
41
- pnpm add react-media-optimizer
68
+ pnpm add react-media-optimizer
42
69
  ```
70
+
43
71
  ## 📌 Peer Dependencies
72
+
44
73
  ### This package requires React 16.8+ (Hooks support).
45
74
 
75
+
76
+
46
77
 
47
78
  ```jsx
48
79
  {
49
- "react": ">=16.8.0",
50
- "react-dom": ">=16.8.0"
80
+ "react": ">=16.8.0",
81
+ "react-dom": ">=16.8.0"
51
82
  }
52
83
  ```
53
84
 
54
-
85
+
86
+
87
+
55
88
 
56
89
  ## 🚀 Quick Start
57
90
 
91
+
92
+
58
93
  ### 1. Optimized Image (Component)
59
94
 
95
+
96
+
60
97
  ```jsx
61
- import { OptimizedImage } from 'react-media-optimizer';
62
-
63
- function HeroSection() {
64
- return (
65
- <OptimizedImage
66
- src="https://example.com/hero-banner.jpg"
67
- alt="Product showcase"
68
- width={1920}
69
- height={1080}
70
- lazy={true}
71
- webp={true}
72
- quality={85}
73
- placeholderSrc="/blur-placeholder.jpg"
74
- className="rounded-lg shadow-xl"
75
- />
76
- );
77
- }
98
+ import { OptimizedImage } from 'react-media-optimizer';
99
+
100
+ function HeroSection() {
101
+
102
+ return (
103
+
104
+ <OptimizedImage
105
+
106
+ src="https://example.com/hero-banner.jpg"
107
+
108
+ alt="Product showcase"
109
+
110
+ width={1920}
111
+
112
+ height={1080}
113
+
114
+ lazy={true}
115
+
116
+ webp={true}
117
+
118
+ quality={85}
119
+
120
+ placeholderSrc="/blur-placeholder.jpg"
121
+
122
+ className="rounded-lg shadow-xl"
123
+
124
+ />
125
+
126
+ );
127
+ }
78
128
  ```
129
+
79
130
  ### 2. Optimized Image (Hook for Custom Use)
80
131
 
132
+
133
+
81
134
  ```jsx
82
- import { useOptimizedImage } from 'react-media-optimizer';
83
-
84
- function CustomImageGallery({ images }) {
85
- return images.map((image) => {
86
- const { src, isLoading, error, elementRef } = useOptimizedImage({
87
- src: image.url,
88
- lazy: true,
89
- webp: true,
90
- quality: 90,
91
- });
92
-
93
- return (
94
- <div key={image.id} className="gallery-item">
95
- <img
96
- ref={elementRef}
97
- src={src}
98
- alt={image.title}
99
- loading="lazy"
100
- className={isLoading ? 'opacity-50' : 'opacity-100'}
101
- />
102
- {isLoading && <span className="loader">Loading...</span>}
103
- </div>
104
- );
105
- });
135
+
136
+ import { useOptimizedImage } from 'react-media-optimizer';
137
+
138
+ function CustomImageGallery({ images }) {
139
+
140
+ return images.map((image) => {
141
+
142
+ const { src, isLoading, error, elementRef } = useOptimizedImage({
143
+
144
+ src: image.url,
145
+
146
+ lazy: true,
147
+
148
+ webp: true,
149
+
150
+ quality: 90,
151
+
152
+ });
153
+
154
+ return (
155
+
156
+ <div key={image.id} className="gallery-item">
157
+
158
+ <img
159
+
160
+ ref={elementRef}
161
+
162
+ src={src}
163
+
164
+ alt={image.title}
165
+
166
+ loading="lazy"
167
+
168
+ className={isLoading ? 'opacity-50' : 'opacity-100'}
169
+
170
+ />
171
+
172
+ {isLoading && <span className="loader">Loading...</span>}
173
+
174
+ </div>
175
+
176
+ );
177
+
178
+ });
179
+
106
180
  }
181
+
107
182
  ```
108
183
 
184
+
185
+
109
186
  ### **3. API Reference Section:**
110
187
 
111
188
  ```jsx
112
- import { OptimizedVideo } from 'react-media-optimizer';
113
-
114
- function ProductDemo() {
115
- return (
116
- <OptimizedVideo
117
- src="https://example.com/demo.mp4"
118
- poster="/video-poster.jpg"
119
- width="100%"
120
- height="auto"
121
- lazy={true}
122
- webm={true}
123
- mp4={true}
124
- controls
125
- autoPlay={false}
126
- muted
127
- playsInline
128
- />
129
- );
189
+ import { OptimizedVideo } from 'react-media-optimizer';
190
+
191
+ function ProductDemo() {
192
+
193
+ return (
194
+
195
+ <OptimizedVideo
196
+
197
+ src="https://example.com/demo.mp4"
198
+
199
+ poster="/video-poster.jpg"
200
+
201
+ width="100%"
202
+
203
+ height="auto"
204
+
205
+ lazy={true}
206
+
207
+ webm={true}
208
+
209
+ mp4={true}
210
+
211
+ controls
212
+
213
+ autoPlay={false}
214
+
215
+ muted
216
+
217
+ playsInline
218
+
219
+ />
220
+
221
+ );
222
+
130
223
  }
224
+
131
225
  ```
226
+
132
227
  ## 📖 API Reference
133
228
 
229
+
230
+
134
231
  ### `<OptimizedImage />` Component
135
232
 
233
+
234
+
136
235
  | Prop | Type | Default | Description |
236
+
137
237
  |------|------|---------|-------------|
238
+
138
239
  | **src** | `string` | **Required** | Image source URL |
240
+
139
241
  | **alt** | `string` | `""` | Accessibility description |
242
+
140
243
  | **lazy** | `boolean` | `true` | Enable lazy loading |
244
+
141
245
  | **webp** | `boolean` | `true` | Convert to WebP when supported |
246
+
142
247
  | **quality** | `number` | `85` | Image quality (1-100) |
248
+
143
249
  | **placeholderSrc** | `string` | `undefined` | Loading placeholder image |
250
+
144
251
  | **fallbackSrc** | `string` | `undefined` | Fallback on error |
252
+
145
253
  | **showLoadingIndicator** | `boolean` | `true` | Visual loading state |
146
254
 
255
+
256
+
147
257
  ### `useOptimizedImage()` Hook
148
258
 
259
+
260
+
149
261
  ```typescript
150
- interface UseOptimizedImageOptions {
151
- src: string;
152
- lazy?: boolean; // default: true
153
- webp?: boolean; // default: true
154
- quality?: number; // default: 85
155
- fallbackSrc?: string;
156
- onLoad?: () => void;
157
- onError?: () => void;
158
- }
262
+ interface UseOptimizedImageOptions {
263
+
264
+ src: string;
265
+
266
+ lazy?: boolean; // default: true
267
+
268
+ webp?: boolean; // default: true
159
269
 
270
+ quality?: number; // default: 85
271
+
272
+ fallbackSrc?: string;
273
+
274
+ onLoad?: () => void;
275
+
276
+ onError?: () => void;
277
+
278
+ }
160
279
  // Returns:
280
+
161
281
  const {
162
- src, // Optimized source URL
163
- isLoading, // Loading state
164
- error, // Error object if failed
165
- elementRef, // React ref for lazy loading
282
+
283
+ src, // Optimized source URL
284
+
285
+ isLoading, // Loading state
286
+
287
+ error, // Error object if failed
288
+
289
+ elementRef, // React ref for lazy loading
290
+
166
291
  } = useOptimizedImage(options);
167
- ```
292
+
293
+ ```
294
+
168
295
  ### <OptimizedVideo /> Component
169
296
 
297
+
298
+
170
299
  | Prop | Type | Default | Description |
300
+
171
301
  |------|------|---------|-------------|
302
+
172
303
  | **src** | `string` | **Required** | Video source URL (`.mp4` or `.webm`) |
304
+
173
305
  | **poster** | `string` | `undefined` | Video poster image |
306
+
174
307
  | **lazy** | `boolean` | `true` | Lazy load video |
308
+
175
309
  | **webm** | `boolean` | `true` | Prefer WebM format |
310
+
176
311
  | **mp4** | `boolean` | `true` | Include MP4 fallback |
177
312
 
313
+
314
+
178
315
  ## 🛠️ Advanced Features
316
+
179
317
  ### 📦 Image Compression Before Upload
180
318
 
319
+
320
+
181
321
  ```jsx
182
- import { compressImage, calculateSizeReduction } from 'react-media-optimizer';
183
-
184
- async function handleImageUpload(file) {
185
- try {
186
- const compressedFile = await compressImage(file, {
187
- quality: 0.8, // 80% quality
188
- maxWidth: 1920, // Resize if wider
189
- maxHeight: 1080, // Resize if taller
190
- });
191
-
192
- const reduction = calculateSizeReduction(
193
- file.size,
194
- compressedFile.size
195
- );
196
- // Example output: "75.3% smaller"
197
-
198
- return compressedFile;
199
- } catch (error) {
200
- console.error('Compression failed:', error);
201
- return file; // Fallback to original
202
- }
322
+ import { compressImage, calculateSizeReduction } from 'react-media-optimizer';
323
+
324
+ async function handleImageUpload(file) {
325
+
326
+ try {
327
+
328
+ const compressedFile = await compressImage(file, {
329
+
330
+ quality: 0.8, // 80% quality
331
+
332
+ maxWidth: 1920, // Resize if wider
333
+
334
+ maxHeight: 1080, // Resize if taller
335
+
336
+ });
337
+
338
+
339
+
340
+ const reduction = calculateSizeReduction(
341
+
342
+ file.size,
343
+
344
+ compressedFile.size
345
+
346
+ );
347
+
348
+ // Example output: "75.3% smaller"
349
+
350
+
351
+
352
+ return compressedFile;
353
+
354
+ } catch (error) {
355
+
356
+ console.error('Compression failed:', error);
357
+
358
+ return file; // Fallback to original
359
+
203
360
  }
361
+
362
+ }
363
+
204
364
  ```
205
365
 
366
+
367
+
206
368
 
207
369
  ---
208
370
 
371
+
372
+
209
373
  ### 🖼 WebP Detection & Conversion
210
374
 
211
- ```jsx
375
+
212
376
 
213
- import { supportsWebP, convertToWebP } from 'react-media-optimizer';
377
+ ```jsx
378
+ import { supportsWebP, convertToWebP } from 'react-media-optimizer';
214
379
 
215
380
  // Detect browser support
216
- const webpSupported = await supportsWebP();
381
+
382
+ const webpSupported = await supportsWebP();
383
+
384
+
217
385
 
218
386
  // Convert URLs (requires CDN support)
219
- const imageUrl = 'https://example.com/image.jpg';
220
- const optimizedUrl = webpSupported
221
- ? convertToWebP(imageUrl)
222
- : imageUrl;
387
+
388
+ const imageUrl = 'https://example.com/image.jpg';
389
+
390
+ const optimizedUrl = webpSupported
391
+
392
+ ? convertToWebP(imageUrl)
393
+
394
+ : imageUrl;
395
+
223
396
  ```
397
+
224
398
  ## 📊 Performance Impact
399
+
225
400
  Before & After Comparison:
226
401
 
402
+
403
+
227
404
  | Metric | Standard Images | With React Media Optimizer | Improvement |
405
+
228
406
  |--------|----------------|---------------------------|------------|
407
+
229
408
  | Largest Contentful Paint | 4.2s | 1.1s | ⬇️ 74% faster |
409
+
230
410
  | Total Page Weight | 8.7 MB | 1.9 MB | ⬇️ 78% smaller |
411
+
231
412
  | Time to Interactive | 5.8s | 2.3s | ⬇️ 60% faster |
413
+
232
414
  | SEO Score | 72/100 | 94/100 | ⬆️ 22 points |
233
415
 
416
+
417
+
234
418
  *Based on average e-commerce site with 50 images*
235
419
 
420
+
421
+
236
422
  ## 🏗️ Framework Integration
237
423
 
424
+
425
+
238
426
  ### Next.js
239
427
 
240
- ```jsx
241
- // app/page.js
242
- import { OptimizedImage } from 'react-media-optimizer';
243
-
244
- export default function HomePage() {
245
- return (
246
- <OptimizedImage
247
- src="/nextjs-optimized.jpg"
248
- alt="Next.js optimized"
249
- width={1200}
250
- height={630}
251
- priority={true} // Load immediately for LCP
252
- />
253
- );
428
+
429
+
430
+ ```js
431
+ import { OptimizedImage } from 'react-media-optimizer';
432
+
433
+ export default function HomePage() {
434
+
435
+ return (
436
+
437
+ <OptimizedImage
438
+
439
+ src="/nextjs-optimized.jpg"
440
+
441
+ alt="Next.js optimized"
442
+
443
+ width={1200}
444
+
445
+ height={630}
446
+
447
+ priority={true} // Load immediately for LCP
448
+
449
+ />
450
+
451
+ );
452
+
254
453
  }
255
454
 
455
+
456
+
256
457
  ```
458
+
257
459
  ### Gatsby
258
460
 
259
- ```jsx
260
- // src/pages/index.js
261
- import { OptimizedImage } from 'react-media-optimizer';
262
-
263
- const IndexPage = () => (
264
- <OptimizedImage
265
- src={data.file.publicURL}
266
- alt="Gatsby site"
267
- width={800}
268
- height={600}
269
- lazy={false} // Critical image
270
- />
461
+
462
+
463
+ ```js
464
+ import { OptimizedImage } from 'react-media-optimizer';
465
+
466
+ const IndexPage = () => (
467
+
468
+ <OptimizedImage
469
+
470
+ src={data.file.publicURL}
471
+
472
+ alt="Gatsby site"
473
+
474
+ width={800}
475
+
476
+ height={600}
477
+
478
+ lazy={false} // Critical image
479
+
480
+ />
481
+
271
482
  );
272
483
 
273
- export default IndexPage;
484
+
485
+
486
+ export default IndexPage;
487
+
274
488
  ```
489
+
275
490
  ### Remix
276
491
 
277
- ```jsx
278
- // app/routes/_index.tsx
279
- import { OptimizedImage } from 'react-media-optimizer';
280
-
281
- export default function Index() {
282
- return (
283
- <OptimizedImage
284
- src="/remix-image.jpg"
285
- alt="Remix app"
286
- width={800}
287
- height={400}
288
- webp={true}
289
- />
290
- );
492
+
493
+
494
+ ```tsx
495
+ import { OptimizedImage } from 'react-media-optimizer';
496
+
497
+ export default function Index() {
498
+
499
+ return (
500
+
501
+ <OptimizedImage
502
+
503
+ src="/remix-image.jpg"
504
+
505
+ alt="Remix app"
506
+
507
+ width={800}
508
+
509
+ height={400}
510
+
511
+ webp={true}
512
+
513
+ />
514
+
515
+ );
516
+
291
517
  }
518
+
292
519
  ```
520
+
293
521
  ---
522
+
294
523
  ## ⚡ Best Practices
524
+
295
525
  ### 1. Prioritize Critical Images
296
526
 
527
+
528
+
297
529
  ```jsx
530
+
298
531
  // Above-the-fold hero image
532
+
299
533
  <OptimizedImage
300
- src="/hero.jpg"
301
- alt="Hero"
302
- lazy={false} // Load immediately
303
- priority={true}
534
+
535
+ src="/hero.jpg"
536
+
537
+ alt="Hero"
538
+
539
+ lazy={false} // Load immediately
540
+
541
+ priority={true}
542
+
304
543
  />
305
544
 
545
+
546
+
306
547
  // Below-the-fold gallery images
548
+
307
549
  <OptimizedImage
308
- src="/gallery-1.jpg"
309
- alt="Gallery item"
310
- lazy={true} // Lazy load
550
+
551
+ src="/gallery-1.jpg"
552
+
553
+ alt="Gallery item"
554
+
555
+ lazy={true} // Lazy load
556
+
311
557
  />
558
+
312
559
  ```
313
560
 
561
+
562
+
314
563
  ### 2. Use Placeholders for Better UX
315
564
 
565
+
316
566
  ```jsx
567
+
317
568
  <OptimizedImage
318
- src="/product.jpg"
319
- alt="Product"
320
- placeholderSrc="/blur-placeholder.jpg"
321
- showLoadingIndicator={true}
569
+
570
+ src="/product.jpg"
571
+
572
+ alt="Product"
573
+
574
+ placeholderSrc="/blur-placeholder.jpg"
575
+
576
+ showLoadingIndicator={true}
577
+
322
578
  />
579
+
323
580
  ```
581
+
324
582
  ## 3. Set Appropriate Quality
325
583
 
584
+
585
+
326
586
  ```jsx
587
+
327
588
  // High quality for product photos
328
- <OptimizedImage quality={90} />
329
589
 
330
- // Medium quality for thumbnails
331
- <OptimizedImage quality={70} />
590
+ <OptimizedImage quality={90} />
591
+
592
+
593
+
594
+ // Medium quality for thumbnails
595
+
596
+ <OptimizedImage quality={70} />
597
+
598
+
332
599
 
333
600
  // Low quality for background images
334
- <OptimizedImage quality={50} />
601
+
602
+ <OptimizedImage quality={50} />
603
+
335
604
  ```
336
605
 
606
+
607
+
337
608
  ## 🐛 Troubleshooting
609
+
338
610
  | Issue | Solution |
611
+
339
612
  |-------|---------|
613
+
340
614
  | Images not lazy loading | Ensure parent container has `overflow: auto` or `overflow: scroll` |
615
+
341
616
  | WebP not working | Check if your CDN supports auto-format conversion |
617
+
342
618
  | Compression fails | Verify file is an image and Canvas API is supported |
619
+
343
620
  | TypeScript errors | Update to latest version or check peer dependencies |
344
621
 
622
+
623
+
345
624
  ### Debug Mode:
346
625
 
626
+
627
+
347
628
  ```jsx
629
+
348
630
  <OptimizedImage
349
- src="/image.jpg"
350
- alt="Debug"
351
- debug={true} // Logs optimization steps
631
+
632
+ src="/image.jpg"
633
+
634
+ alt="Debug"
635
+
636
+ debug={true} // Logs optimization steps
637
+
352
638
  />
639
+
353
640
  ```
641
+
354
642
  ## 🔄 Migration Guide
643
+
355
644
  ### From standard <img> tags
645
+
356
646
  ```diff
647
+
357
648
  - <img src="/image.jpg" alt="Example" />
358
- + <OptimizedImage
359
- + src="/image.jpg"
360
- + alt="Example"
361
- + width={800}
362
- + height={600}
649
+
650
+ + <OptimizedImage
651
+
652
+ + src="/image.jpg"
653
+
654
+ + alt="Example"
655
+
656
+ + width={800}
657
+
658
+ + height={600}
659
+
363
660
  + />
661
+
364
662
  ```
663
+
365
664
  ### From Next.js Image
665
+
366
666
  ```diff
667
+
367
668
  - import Image from 'next/image';
669
+
368
670
  - <Image src="/img.jpg" alt="Example" width={800} height={600} />
671
+
369
672
  + import { OptimizedImage } from 'react-media-optimizer';
370
- + <OptimizedImage src="/img.jpg" alt="Example" width={800} height={600} />
371
- ```
372
673
 
373
- ## Features
674
+ + <OptimizedImage src="/img.jpg" alt="Example" width={800} height={600} />
374
675
 
375
- - ✅ Image & video lazy loading
376
- - ✅ Client-side compression
377
- - ✅ WebP/WebM detection
378
- - ✅ SSR/SSG compatibility
379
- - ✅ TypeScript support
676
+ ```
380
677
 
381
- ## 📄 License
678
+
382
679
 
383
- MIT © 2026 Yared Abebe
680
+ ## Features
384
681
 
682
+
385
683
 
684
+ - ✅ Image & video lazy loading
386
685
 
686
+ - ✅ Client-side compression
387
687
 
688
+ - ✅ WebP/WebM detection
388
689
 
690
+ - ✅ SSR/SSG compatibility
389
691
 
692
+ - ✅ TypeScript support
390
693
 
694
+
391
695
 
696
+ ## 📄 License
392
697
 
698
+
393
699
 
700
+ MIT © 2026 Yared Abebe
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-media-optimizer",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Drop-in React component for auto-optimized images & media with lazy loading, WebP conversion, and performance optimization",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",