react-media-optimizer 1.0.0 → 1.0.1
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/README.md +287 -299
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
# React Media Optimizer 🚀
|
|
1
|
+
# 🚀 React Media Optimizer
|
|
4
2
|
|
|
5
3
|
[](https://www.npmjs.com/package/react-media-optimizer)
|
|
6
4
|
[](https://npmjs.com/package/react-media-optimizer)
|
|
@@ -8,161 +6,147 @@ markdown
|
|
|
8
6
|
[](https://opensource.org/licenses/MIT)
|
|
9
7
|
[](https://www.typescriptlang.org/)
|
|
10
8
|
|
|
11
|
-
**Drop-in image & video optimization for React applications.** Automatically compress, lazy-load, and convert media to improve performance and
|
|
9
|
+
**Drop-in image & video optimization for React applications.** Automatically compress, lazy-load, and convert media to improve performance, UX, and SEO with minimal effort.
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
> 📊 **Average improvements:** 60% faster LCP, 75% smaller images, 40% better SEO scores
|
|
12
|
+
|
|
13
|
+
---
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
## ✨ Why Choose React Media Optimizer?
|
|
16
|
+
|
|
17
|
+
| Feature | Benefit | Impact |
|
|
18
|
+
|---------|---------|--------|
|
|
19
|
+
| **Auto Lazy Loading** | Images/videos load only when visible | ⬇️ 50-80% initial page weight |
|
|
20
|
+
| **WebP/WebM Conversion** | Modern formats with better compression | ⬇️ 25-35% smaller file sizes |
|
|
21
|
+
| **Client-side Compression** | Reduce upload sizes before server | ⬇️ 60-80% upload bandwidth |
|
|
22
|
+
| **SSR/SSG Safe** | Works with Next.js, Gatsby, Remix | ✅ Zero hydration errors |
|
|
23
|
+
| **Zero Configuration** | Sensible defaults out of the box | ⏱️ 5-minute integration |
|
|
24
|
+
|
|
25
|
+
---
|
|
24
26
|
|
|
25
27
|
## 📦 Installation
|
|
26
28
|
|
|
29
|
+
Install using your preferred package manager.
|
|
30
|
+
|
|
31
|
+
### npm
|
|
27
32
|
```bash
|
|
28
33
|
npm install react-media-optimizer
|
|
29
|
-
|
|
34
|
+
```
|
|
35
|
+
### yarn
|
|
36
|
+
```bash
|
|
30
37
|
yarn add react-media-optimizer
|
|
31
|
-
|
|
38
|
+
```
|
|
39
|
+
### pnpm
|
|
40
|
+
```bash
|
|
32
41
|
pnpm add react-media-optimizer
|
|
42
|
+
```
|
|
43
|
+
## 📌 Peer Dependencies
|
|
44
|
+
### This package requires React 16.8+ (Hooks support).
|
|
33
45
|
|
|
34
|
-
🚀 Quick Start
|
|
35
|
-
jsx
|
|
36
|
-
|
|
37
|
-
import { OptimizedImage, OptimizedVideo } from 'react-media-optimizer';
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
src="/large-image.jpg"
|
|
44
|
-
alt="Beautiful landscape"
|
|
45
|
-
width={800}
|
|
46
|
-
height={600}
|
|
47
|
-
lazy={true}
|
|
48
|
-
quality={85}
|
|
49
|
-
/>
|
|
50
|
-
|
|
51
|
-
<OptimizedVideo
|
|
52
|
-
src="/video.mp4"
|
|
53
|
-
poster="/video-poster.jpg"
|
|
54
|
-
width={1280}
|
|
55
|
-
height={720}
|
|
56
|
-
controls
|
|
57
|
-
lazy={true}
|
|
58
|
-
/>
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
47
|
+
```jsx
|
|
48
|
+
{
|
|
49
|
+
"react": ">=16.8.0",
|
|
50
|
+
"react-dom": ">=16.8.0"
|
|
61
51
|
}
|
|
52
|
+
```
|
|
62
53
|
|
|
63
|
-
📖 API Reference
|
|
64
|
-
<OptimizedImage />
|
|
65
54
|
|
|
66
|
-
The main image optimization component with smart defaults.
|
|
67
|
-
jsx
|
|
68
55
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<OptimizedImage
|
|
72
|
-
src="/path/to/image.jpg"
|
|
73
|
-
alt="Description"
|
|
74
|
-
width={400}
|
|
75
|
-
height={300}
|
|
76
|
-
lazy={true} // Enable lazy loading
|
|
77
|
-
webp={true} // Convert to WebP if supported
|
|
78
|
-
quality={85} // Image quality (1-100)
|
|
79
|
-
placeholderSrc="/placeholder.jpg" // Loading placeholder
|
|
80
|
-
fallbackSrc="/fallback.jpg" // Fallback on error
|
|
81
|
-
showLoadingIndicator={true} // Show loading state
|
|
82
|
-
className="custom-class" // Additional CSS classes
|
|
83
|
-
// All standard img props supported
|
|
84
|
-
/>
|
|
85
|
-
|
|
86
|
-
Props
|
|
87
|
-
Prop Type Default Description
|
|
88
|
-
src string Required Image source URL
|
|
89
|
-
alt string "" Alternative text for accessibility
|
|
90
|
-
lazy boolean true Enable lazy loading
|
|
91
|
-
webp boolean true Convert to WebP format when supported
|
|
92
|
-
quality number 85 Image quality (1-100)
|
|
93
|
-
placeholderSrc string undefined Placeholder image during loading
|
|
94
|
-
fallbackSrc string undefined Fallback image on error
|
|
95
|
-
showLoadingIndicator boolean true Show loading state visually
|
|
96
|
-
<OptimizedVideo />
|
|
97
|
-
|
|
98
|
-
Intelligent video component with lazy loading and format optimization.
|
|
99
|
-
jsx
|
|
56
|
+
## 🚀 Quick Start
|
|
100
57
|
|
|
101
|
-
|
|
58
|
+
### 1. Optimized Image (Component)
|
|
102
59
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
poster="/poster.jpg"
|
|
106
|
-
width={1280}
|
|
107
|
-
height={720}
|
|
108
|
-
lazy={true}
|
|
109
|
-
webm={true} // Provide WebM version for better compression
|
|
110
|
-
mp4={true} // Provide MP4 version for compatibility
|
|
111
|
-
controls
|
|
112
|
-
autoPlay
|
|
113
|
-
muted
|
|
114
|
-
// All standard video props supported
|
|
115
|
-
/>
|
|
116
|
-
|
|
117
|
-
Props
|
|
118
|
-
Prop Type Default Description
|
|
119
|
-
src string Required Video source URL
|
|
120
|
-
poster string undefined Video poster image
|
|
121
|
-
lazy boolean true Enable lazy loading
|
|
122
|
-
webm boolean true Use WebM format for better compression
|
|
123
|
-
mp4 boolean true Use MP4 format for compatibility
|
|
124
|
-
useOptimizedImage() Hook
|
|
60
|
+
```jsx
|
|
61
|
+
import { OptimizedImage } from 'react-media-optimizer';
|
|
125
62
|
|
|
126
|
-
|
|
127
|
-
|
|
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
|
+
}
|
|
78
|
+
```
|
|
79
|
+
### 2. Optimized Image (Hook for Custom Use)
|
|
128
80
|
|
|
81
|
+
```jsx
|
|
129
82
|
import { useOptimizedImage } from 'react-media-optimizer';
|
|
130
83
|
|
|
131
|
-
function
|
|
132
|
-
|
|
133
|
-
src
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
+
);
|
|
143
105
|
});
|
|
106
|
+
}
|
|
107
|
+
```
|
|
144
108
|
|
|
145
|
-
|
|
146
|
-
return <div className="loading">Loading...</div>;
|
|
147
|
-
}
|
|
109
|
+
### **3. API Reference Section:**
|
|
148
110
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
111
|
+
```jsx
|
|
112
|
+
import { OptimizedVideo } from 'react-media-optimizer';
|
|
152
113
|
|
|
114
|
+
function ProductDemo() {
|
|
153
115
|
return (
|
|
154
|
-
<
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
|
159
128
|
/>
|
|
160
129
|
);
|
|
161
130
|
}
|
|
131
|
+
```
|
|
132
|
+
## 📖 API Reference
|
|
133
|
+
|
|
134
|
+
### `<OptimizedImage />` Component
|
|
135
|
+
|
|
136
|
+
| Prop | Type | Default | Description |
|
|
137
|
+
|------|------|---------|-------------|
|
|
138
|
+
| **src** | `string` | **Required** | Image source URL |
|
|
139
|
+
| **alt** | `string` | `""` | Accessibility description |
|
|
140
|
+
| **lazy** | `boolean` | `true` | Enable lazy loading |
|
|
141
|
+
| **webp** | `boolean` | `true` | Convert to WebP when supported |
|
|
142
|
+
| **quality** | `number` | `85` | Image quality (1-100) |
|
|
143
|
+
| **placeholderSrc** | `string` | `undefined` | Loading placeholder image |
|
|
144
|
+
| **fallbackSrc** | `string` | `undefined` | Fallback on error |
|
|
145
|
+
| **showLoadingIndicator** | `boolean` | `true` | Visual loading state |
|
|
162
146
|
|
|
163
|
-
Hook
|
|
164
|
-
typescript
|
|
147
|
+
### `useOptimizedImage()` Hook
|
|
165
148
|
|
|
149
|
+
```typescript
|
|
166
150
|
interface UseOptimizedImageOptions {
|
|
167
151
|
src: string;
|
|
168
152
|
lazy?: boolean; // default: true
|
|
@@ -173,233 +157,237 @@ interface UseOptimizedImageOptions {
|
|
|
173
157
|
onError?: () => void;
|
|
174
158
|
}
|
|
175
159
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
160
|
+
// Returns:
|
|
161
|
+
const {
|
|
162
|
+
src, // Optimized source URL
|
|
163
|
+
isLoading, // Loading state
|
|
164
|
+
error, // Error object if failed
|
|
165
|
+
elementRef, // React ref for lazy loading
|
|
166
|
+
} = useOptimizedImage(options);
|
|
167
|
+
```
|
|
168
|
+
### <OptimizedVideo /> Component
|
|
169
|
+
|
|
170
|
+
| Prop | Type | Default | Description |
|
|
171
|
+
|------|------|---------|-------------|
|
|
172
|
+
| **src** | `string` | **Required** | Video source URL (`.mp4` or `.webm`) |
|
|
173
|
+
| **poster** | `string` | `undefined` | Video poster image |
|
|
174
|
+
| **lazy** | `boolean` | `true` | Lazy load video |
|
|
175
|
+
| **webm** | `boolean` | `true` | Prefer WebM format |
|
|
176
|
+
| **mp4** | `boolean` | `true` | Include MP4 fallback |
|
|
177
|
+
|
|
178
|
+
## 🛠️ Advanced Features
|
|
179
|
+
### 📦 Image Compression Before Upload
|
|
180
|
+
|
|
181
|
+
```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
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
186
205
|
|
|
187
|
-
import { OptimizedImage } from 'react-media-optimizer';
|
|
188
206
|
|
|
189
|
-
|
|
190
|
-
return (
|
|
191
|
-
<div className="product-grid">
|
|
192
|
-
{products.map((product) => (
|
|
193
|
-
<div key={product.id} className="product-card">
|
|
194
|
-
<OptimizedImage
|
|
195
|
-
src={product.imageUrl}
|
|
196
|
-
alt={product.name}
|
|
197
|
-
width={400}
|
|
198
|
-
height={400}
|
|
199
|
-
lazy={true}
|
|
200
|
-
quality={90}
|
|
201
|
-
placeholderSrc="/product-placeholder.jpg"
|
|
202
|
-
className="product-image"
|
|
203
|
-
/>
|
|
204
|
-
<h3>{product.name}</h3>
|
|
205
|
-
<p>{product.price}</p>
|
|
206
|
-
</div>
|
|
207
|
-
))}
|
|
208
|
-
</div>
|
|
209
|
-
);
|
|
210
|
-
}
|
|
207
|
+
---
|
|
211
208
|
|
|
212
|
-
|
|
213
|
-
jsx
|
|
209
|
+
### 🖼 WebP Detection & Conversion
|
|
214
210
|
|
|
215
|
-
|
|
216
|
-
return (
|
|
217
|
-
<article>
|
|
218
|
-
<OptimizedImage
|
|
219
|
-
src={post.heroImage}
|
|
220
|
-
alt={post.title}
|
|
221
|
-
width={1200}
|
|
222
|
-
height={630}
|
|
223
|
-
lazy={false} // Hero image should load immediately
|
|
224
|
-
quality={95}
|
|
225
|
-
className="hero-image"
|
|
226
|
-
/>
|
|
227
|
-
<h1>{post.title}</h1>
|
|
228
|
-
<div dangerouslySetInnerHTML={{ __html: post.content }} />
|
|
229
|
-
</article>
|
|
230
|
-
);
|
|
231
|
-
}
|
|
211
|
+
```jsx
|
|
232
212
|
|
|
233
|
-
|
|
234
|
-
Custom Loading Component
|
|
235
|
-
jsx
|
|
213
|
+
import { supportsWebP, convertToWebP } from 'react-media-optimizer';
|
|
236
214
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
return (
|
|
241
|
-
<div className="image-wrapper">
|
|
242
|
-
{isLoading && (
|
|
243
|
-
<div className="custom-loader">
|
|
244
|
-
<Spinner />
|
|
245
|
-
<span>Loading image...</span>
|
|
246
|
-
</div>
|
|
247
|
-
)}
|
|
248
|
-
<img
|
|
249
|
-
src={optimizedSrc}
|
|
250
|
-
alt={alt}
|
|
251
|
-
style={{ opacity: isLoading ? 0 : 1 }}
|
|
252
|
-
{...props}
|
|
253
|
-
/>
|
|
254
|
-
</div>
|
|
255
|
-
);
|
|
256
|
-
}
|
|
215
|
+
// Detect browser support
|
|
216
|
+
const webpSupported = await supportsWebP();
|
|
257
217
|
|
|
258
|
-
CDN
|
|
259
|
-
|
|
218
|
+
// Convert URLs (requires CDN support)
|
|
219
|
+
const imageUrl = 'https://example.com/image.jpg';
|
|
220
|
+
const optimizedUrl = webpSupported
|
|
221
|
+
? convertToWebP(imageUrl)
|
|
222
|
+
: imageUrl;
|
|
223
|
+
```
|
|
224
|
+
## 📊 Performance Impact
|
|
225
|
+
Before & After Comparison:
|
|
260
226
|
|
|
261
|
-
|
|
227
|
+
| Metric | Standard Images | With React Media Optimizer | Improvement |
|
|
228
|
+
|--------|----------------|---------------------------|------------|
|
|
229
|
+
| Largest Contentful Paint | 4.2s | 1.1s | ⬇️ 74% faster |
|
|
230
|
+
| Total Page Weight | 8.7 MB | 1.9 MB | ⬇️ 78% smaller |
|
|
231
|
+
| Time to Interactive | 5.8s | 2.3s | ⬇️ 60% faster |
|
|
232
|
+
| SEO Score | 72/100 | 94/100 | ⬆️ 22 points |
|
|
262
233
|
|
|
263
|
-
|
|
264
|
-
<OptimizedImage
|
|
265
|
-
src="https://res.cloudinary.com/demo/image/upload/sample.jpg"
|
|
266
|
-
alt="Sample"
|
|
267
|
-
width={800}
|
|
268
|
-
height={600}
|
|
269
|
-
cdnTransformations={{
|
|
270
|
-
quality: 'auto',
|
|
271
|
-
format: 'auto',
|
|
272
|
-
fetch_format: 'auto',
|
|
273
|
-
}}
|
|
274
|
-
/>
|
|
234
|
+
*Based on average e-commerce site with 50 images*
|
|
275
235
|
|
|
276
|
-
🏗️ Integration
|
|
277
|
-
Next.js Integration
|
|
278
|
-
jsx
|
|
236
|
+
## 🏗️ Framework Integration
|
|
279
237
|
|
|
280
|
-
|
|
281
|
-
module.exports = {
|
|
282
|
-
images: {
|
|
283
|
-
domains: ['your-cdn-domain.com'],
|
|
284
|
-
},
|
|
285
|
-
};
|
|
238
|
+
### Next.js
|
|
286
239
|
|
|
287
|
-
|
|
240
|
+
```jsx
|
|
241
|
+
// app/page.js
|
|
288
242
|
import { OptimizedImage } from 'react-media-optimizer';
|
|
289
243
|
|
|
290
244
|
export default function HomePage() {
|
|
291
245
|
return (
|
|
292
246
|
<OptimizedImage
|
|
293
|
-
src="/nextjs-
|
|
247
|
+
src="/nextjs-optimized.jpg"
|
|
294
248
|
alt="Next.js optimized"
|
|
295
|
-
width={
|
|
296
|
-
height={
|
|
297
|
-
priority //
|
|
249
|
+
width={1200}
|
|
250
|
+
height={630}
|
|
251
|
+
priority={true} // Load immediately for LCP
|
|
298
252
|
/>
|
|
299
253
|
);
|
|
300
254
|
}
|
|
301
255
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
// Install with Gatsby
|
|
306
|
-
npm install react-media-optimizer gatsby-plugin-image
|
|
256
|
+
```
|
|
257
|
+
### Gatsby
|
|
307
258
|
|
|
308
|
-
|
|
259
|
+
```jsx
|
|
260
|
+
// src/pages/index.js
|
|
309
261
|
import { OptimizedImage } from 'react-media-optimizer';
|
|
310
262
|
|
|
311
|
-
const
|
|
263
|
+
const IndexPage = () => (
|
|
312
264
|
<OptimizedImage
|
|
313
265
|
src={data.file.publicURL}
|
|
314
|
-
alt="Gatsby
|
|
266
|
+
alt="Gatsby site"
|
|
315
267
|
width={800}
|
|
316
268
|
height={600}
|
|
269
|
+
lazy={false} // Critical image
|
|
317
270
|
/>
|
|
318
271
|
);
|
|
319
272
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
Use placeholders: Implement blur-up or color placeholders for better UX
|
|
325
|
-
|
|
326
|
-
Prioritize critical images: Set lazy={false} for above-the-fold images
|
|
273
|
+
export default IndexPage;
|
|
274
|
+
```
|
|
275
|
+
### Remix
|
|
327
276
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
🔄 Migration Guide
|
|
333
|
-
From standard <img> tags
|
|
334
|
-
diff
|
|
335
|
-
|
|
336
|
-
- <img src="/image.jpg" alt="Example" />
|
|
337
|
-
+ <OptimizedImage src="/image.jpg" alt="Example" width={800} height={600} />
|
|
338
|
-
|
|
339
|
-
From Next.js Image
|
|
340
|
-
diff
|
|
341
|
-
|
|
342
|
-
- import Image from 'next/image';
|
|
343
|
-
+ import { OptimizedImage } from 'react-media-optimizer';
|
|
277
|
+
```jsx
|
|
278
|
+
// app/routes/_index.tsx
|
|
279
|
+
import { OptimizedImage } from 'react-media-optimizer';
|
|
344
280
|
|
|
345
|
-
|
|
346
|
-
|
|
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
|
+
);
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
---
|
|
294
|
+
## ⚡ Best Practices
|
|
295
|
+
### 1. Prioritize Critical Images
|
|
347
296
|
|
|
348
|
-
|
|
297
|
+
```jsx
|
|
298
|
+
// Above-the-fold hero image
|
|
299
|
+
<OptimizedImage
|
|
300
|
+
src="/hero.jpg"
|
|
301
|
+
alt="Hero"
|
|
302
|
+
lazy={false} // Load immediately
|
|
303
|
+
priority={true}
|
|
304
|
+
/>
|
|
349
305
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
306
|
+
// Below-the-fold gallery images
|
|
307
|
+
<OptimizedImage
|
|
308
|
+
src="/gallery-1.jpg"
|
|
309
|
+
alt="Gallery item"
|
|
310
|
+
lazy={true} // Lazy load
|
|
311
|
+
/>
|
|
312
|
+
```
|
|
357
313
|
|
|
358
|
-
|
|
314
|
+
### 2. Use Placeholders for Better UX
|
|
359
315
|
|
|
360
|
-
|
|
316
|
+
```jsx
|
|
317
|
+
<OptimizedImage
|
|
318
|
+
src="/product.jpg"
|
|
319
|
+
alt="Product"
|
|
320
|
+
placeholderSrc="/blur-placeholder.jpg"
|
|
321
|
+
showLoadingIndicator={true}
|
|
322
|
+
/>
|
|
323
|
+
```
|
|
324
|
+
## 3. Set Appropriate Quality
|
|
361
325
|
|
|
362
|
-
|
|
326
|
+
```jsx
|
|
327
|
+
// High quality for product photos
|
|
328
|
+
<OptimizedImage quality={90} />
|
|
363
329
|
|
|
364
|
-
|
|
330
|
+
// Medium quality for thumbnails
|
|
331
|
+
<OptimizedImage quality={70} />
|
|
365
332
|
|
|
366
|
-
|
|
333
|
+
// Low quality for background images
|
|
334
|
+
<OptimizedImage quality={50} />
|
|
335
|
+
```
|
|
367
336
|
|
|
368
|
-
|
|
337
|
+
## 🐛 Troubleshooting
|
|
338
|
+
| Issue | Solution |
|
|
339
|
+
|-------|---------|
|
|
340
|
+
| Images not lazy loading | Ensure parent container has `overflow: auto` or `overflow: scroll` |
|
|
341
|
+
| WebP not working | Check if your CDN supports auto-format conversion |
|
|
342
|
+
| Compression fails | Verify file is an image and Canvas API is supported |
|
|
343
|
+
| TypeScript errors | Update to latest version or check peer dependencies |
|
|
369
344
|
|
|
370
|
-
|
|
345
|
+
### Debug Mode:
|
|
371
346
|
|
|
372
|
-
|
|
347
|
+
```jsx
|
|
348
|
+
<OptimizedImage
|
|
349
|
+
src="/image.jpg"
|
|
350
|
+
alt="Debug"
|
|
351
|
+
debug={true} // Logs optimization steps
|
|
352
|
+
/>
|
|
353
|
+
```
|
|
354
|
+
## 🔄 Migration Guide
|
|
355
|
+
### From standard <img> tags
|
|
356
|
+
```diff
|
|
357
|
+
- <img src="/image.jpg" alt="Example" />
|
|
358
|
+
+ <OptimizedImage
|
|
359
|
+
+ src="/image.jpg"
|
|
360
|
+
+ alt="Example"
|
|
361
|
+
+ width={800}
|
|
362
|
+
+ height={600}
|
|
363
|
+
+ />
|
|
364
|
+
```
|
|
365
|
+
### From Next.js Image
|
|
366
|
+
```diff
|
|
367
|
+
- import Image from 'next/image';
|
|
368
|
+
- <Image src="/img.jpg" alt="Example" width={800} height={600} />
|
|
369
|
+
+ import { OptimizedImage } from 'react-media-optimizer';
|
|
370
|
+
+ <OptimizedImage src="/img.jpg" alt="Example" width={800} height={600} />
|
|
371
|
+
```
|
|
373
372
|
|
|
374
|
-
|
|
373
|
+
## ✨ Features
|
|
375
374
|
|
|
376
|
-
|
|
375
|
+
- ✅ Image & video lazy loading
|
|
376
|
+
- ✅ Client-side compression
|
|
377
|
+
- ✅ WebP/WebM detection
|
|
378
|
+
- ✅ SSR/SSG compatibility
|
|
379
|
+
- ✅ TypeScript support
|
|
377
380
|
|
|
378
|
-
|
|
381
|
+
## 📄 License
|
|
379
382
|
|
|
380
|
-
|
|
381
|
-
jsx
|
|
383
|
+
MIT © 2026 Yared Abebe
|
|
382
384
|
|
|
383
|
-
<OptimizedImage
|
|
384
|
-
src="/image.jpg"
|
|
385
|
-
alt="Debug"
|
|
386
|
-
debug={true} // Logs optimization process to console
|
|
387
|
-
/>
|
|
388
385
|
|
|
389
|
-
🤝 Contributing
|
|
390
386
|
|
|
391
|
-
We welcome contributions! Please see our Contributing Guide for details.
|
|
392
387
|
|
|
393
|
-
Fork the repository
|
|
394
388
|
|
|
395
|
-
Create a feature branch (git checkout -b feature/AmazingFeature)
|
|
396
389
|
|
|
397
|
-
Commit your changes (git commit -m 'Add some AmazingFeature')
|
|
398
390
|
|
|
399
|
-
Push to the branch (git push origin feature/AmazingFeature)
|
|
400
391
|
|
|
401
|
-
Open a Pull Request
|
|
402
392
|
|
|
403
|
-
📄 License
|
|
404
393
|
|
|
405
|
-
MIT © Yared Abebe.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-media-optimizer",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
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",
|