react-use-optimized-image
Version:
A React hook for image optimization (format detection, lazy loading, caching, CDN support)
74 lines • 3.34 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { useState, useEffect, useRef } from "react";
// Supported image formats
const checkFormatSupport = (format) => {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => resolve(true);
img.onerror = () => resolve(false);
img.src = `data:image/${format};base64,`;
});
};
// Detects best image format
const getBestImageFormat = () => __awaiter(void 0, void 0, void 0, function* () {
if (yield checkFormatSupport("avif"))
return "avif";
if (yield checkFormatSupport("webp"))
return "webp";
return "jpeg"; // Fallback to jpeg
});
const getNetworkQuality = () => {
const connection = navigator.connection;
if (!connection)
return 80; // Default quality
if (connection.effectiveType.includes("4g"))
return 80;
if (connection.effectiveType.includes("3g"))
return 50;
return 30; // 2G or slow connections
};
export const useOptimizedImage = ({ src, quality, cdn, lazy = true, }) => {
const adjustedQuality = quality || getNetworkQuality();
const cachedSrc = localStorage.getItem(`optimized_${src}`);
const [optimizedSrc, setOptimizedSrc] = useState(cachedSrc || (lazy ? null : src));
const [isLoading, setIsLoading] = useState(lazy && !cachedSrc);
const imgRef = useRef(null);
useEffect(() => {
if (cachedSrc)
return;
if (!lazy || !imgRef.current)
return;
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => __awaiter(void 0, void 0, void 0, function* () {
if (entry.isIntersecting) {
setIsLoading(true);
const bestFormat = yield getBestImageFormat();
let newSrc = src;
if (cdn === "cloudinary") {
newSrc = src.replace("/upload/", `/upload/f_auto/q_${adjustedQuality}/`);
}
else if (cdn === "imgix") {
newSrc = `${src}?auto=format&q=${adjustedQuality}`;
}
newSrc = newSrc.replace(/\.(jpg|png|jpeg)/, `.${bestFormat}`);
localStorage.setItem(`optimized_${src}`, newSrc);
setOptimizedSrc(newSrc);
setIsLoading(false);
observer.disconnect();
}
}));
});
observer.observe(imgRef.current);
return () => observer.disconnect();
}, [src, adjustedQuality, cdn, lazy]);
return { optimizedSrc, isLoading, imgRef };
};
//# sourceMappingURL=useOptimizedImage.js.map