UNPKG

@snapkit-studio/react

Version:

React components for Snapkit image optimization

484 lines (381 loc) 13.3 kB
# @snapkit-studio/react [![npm version](https://img.shields.io/npm/v/@snapkit-studio/react.svg)](https://www.npmjs.com/package/@snapkit-studio/react) [![npm downloads](https://img.shields.io/npm/dm/@snapkit-studio/react.svg)](https://www.npmjs.com/package/@snapkit-studio/react) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/) React components and hooks for Snapkit Studio image optimization. This package provides high-performance React image components with automatic format optimization, responsive loading, and Next.js-compatible APIs. ## Installation ```bash npm install @snapkit-studio/react # or yarn add @snapkit-studio/react # or pnpm add @snapkit-studio/react ``` ## Features - **React Image Component** - Drop-in replacement for HTML img with optimization - **Flexible CDN Configuration** - Use Snapkit CDN or integrate with your existing infrastructure (CloudFront, GCS, Cloudflare) - **Provider-less Architecture** - Direct component usage without wrappers - **Next.js Compatible** - Same API as Next.js Image component - **Environment Auto-Detection** - Automatically reads framework-specific environment variables - **Automatic Format Detection** - AVIF, WebP, JPEG fallback - **Responsive Images** - Automatic srcset generation - **DPR-based Optimization** - Crisp images on high-DPI displays - **Lazy Loading** - Intersection Observer based - **Network-aware Quality** - Automatic adjustment based on connection - **TypeScript Support** - Full type definitions included ## Quick Start ### 1. CDN Configuration Choose between Snapkit CDN for zero-config optimization or custom CDN integration: #### Option A: Snapkit CDN (Recommended) ```bash # .env (Vite/CRA) - Snapkit CDN VITE_IMAGE_CDN_PROVIDER=snapkit VITE_SNAPKIT_ORGANIZATION=your-organization-name ``` #### Option B: Custom CDN Integration ```bash # .env (Vite/CRA) - Custom CDN (Google Cloud Storage example) VITE_IMAGE_CDN_PROVIDER=custom VITE_IMAGE_CDN_URL=https://storage.googleapis.com/my-image-bucket # AWS CloudFront example # VITE_IMAGE_CDN_PROVIDER=custom # VITE_IMAGE_CDN_URL=https://d1234567890.cloudfront.net # Cloudflare or any custom domain # VITE_IMAGE_CDN_PROVIDER=custom # VITE_IMAGE_CDN_URL=https://images.example.com ``` ### 2. Basic Usage ```tsx import { Image } from '@snapkit-studio/react'; function MyComponent() { return ( <Image src="/project/hero.jpg" alt="Hero Image" width={800} height={600} priority transforms={{ format: 'auto' }} /> ); } ``` ## Import Options ### Selective Imports (Recommended) ```typescript // Image component only (~9 KB) // Specific hooks only (~8 KB) import { useImageOptimization } from '@snapkit-studio/react/hooks'; import { Image } from '@snapkit-studio/react/image'; // Utility functions only (~5 KB) import { createImageStyle, mergeConfiguration, } from '@snapkit-studio/react/utils'; ``` ### Full Bundle Import ```typescript // Full bundle (~22 KB) import { Image, useImageRefresh } from '@snapkit-studio/react'; ``` ## CDN Configuration The library supports flexible CDN configuration through environment variables. Configuration is automatically detected using `getCdnConfig()` from `@snapkit-studio/core`. ### Snapkit CDN Zero-configuration setup with automatic optimization, smart format delivery, and global edge caching: ```bash # .env (Vite/CRA) VITE_IMAGE_CDN_PROVIDER=snapkit VITE_SNAPKIT_ORGANIZATION=your-organization-name # .env.local (Next.js) - If using React package in Next.js NEXT_PUBLIC_IMAGE_CDN_PROVIDER=snapkit NEXT_PUBLIC_SNAPKIT_ORGANIZATION=your-organization-name ``` ### Custom CDN Integration Use your existing CDN infrastructure with Snapkit's optimization features: ```bash # .env (Vite/CRA) - Custom CDN examples VITE_IMAGE_CDN_PROVIDER=custom VITE_IMAGE_CDN_URL=https://your-cdn-domain.com # .env.local (Next.js) - Custom CDN examples NEXT_PUBLIC_IMAGE_CDN_PROVIDER=custom NEXT_PUBLIC_IMAGE_CDN_URL=https://your-cdn-domain.com ``` ### Environment Variables Reference #### Vite/CRA | Variable | Required For | Description | | --------------------------- | ------------ | ----------------------------------- | | `VITE_IMAGE_CDN_PROVIDER` | All setups | CDN provider: `snapkit` or `custom` | | `VITE_SNAPKIT_ORGANIZATION` | Snapkit CDN | Your Snapkit organization name | | `VITE_IMAGE_CDN_URL` | Custom CDN | Your custom CDN base URL | #### Next.js (when using React package) | Variable | Required For | Description | | --------------------------------------- | ------------ | ----------------------------------- | | `NEXT_PUBLIC_IMAGE_CDN_PROVIDER` | All setups | CDN provider: `snapkit` or `custom` | | `NEXT_PUBLIC_SNAPKIT_ORGANIZATION` | Snapkit CDN | Your Snapkit organization name | | `NEXT_PUBLIC_IMAGE_CDN_URL` | Custom CDN | Your custom CDN base URL | ## Image Component Props | Prop | Type | Default | Description | | ------------------ | ------------------------------------- | -------- | ---------------------------- | | `src` | `string` | - | Image path (required) | | `alt` | `string` | - | Alt text (required) | | `width` | `number` | - | Image width | | `height` | `number` | - | Image height | | `fill` | `boolean` | `false` | Fill parent container | | `sizes` | `string` | - | Responsive size settings | | `quality` | `number` | `85` | Image quality (1-100) | | `priority` | `boolean` | `false` | Priority loading | | `loading` | `'lazy' \| 'eager'` | `'lazy'` | Loading method | | `optimizeFormat` | `'auto' \| 'avif' \| 'webp' \| 'off'` | `'auto'` | Format optimization | | `transforms` | `ImageTransforms` | `{}` | Image transformation options | ## Key Features ### Automatic Format Detection The library uses Canvas-based detection to determine browser support for modern image formats: ```tsx // Automatically serves AVIF, WebP, or JPEG based on browser <Image src="/photo.jpg" alt="Photo" width={800} height={600} optimizeFormat="auto" /> // Force specific format <Image src="/logo.png" alt="Logo" width={200} height={100} optimizeFormat="webp" /> ``` **Format Selection Logic:** 1. **AVIF**: Chosen if browser supports it (Chrome 85+, Firefox 93+, Edge 91+, Safari 16+) 2. **WebP**: Chosen if AVIF not supported but WebP is (Chrome 23+, Firefox 65+, Edge 79+, Safari 14+) 3. **JPEG**: Final fallback for all other browsers **Detection Method:** - **Client-side**: Uses `Canvas.toDataURL()` to test format support - **Server-side**: Conservative fallback to basic formats for SSR consistency ### Responsive Images ```tsx // DPR-based srcset (1x, 2x, 3x) - default behavior <Image src="/image.jpg" alt="High-DPI optimized" width={800} height={600} /> // Width-based srcset for responsive layouts <Image src="/banner.jpg" alt="Responsive banner" width={1200} height={400} sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 25vw" /> ``` ### Image Transformations ```tsx // Basic transformations <Image src="/photo.jpg" alt="Transformed" width={400} height={400} transforms={{ fit: "cover", // contain | cover | fill | inside | outside quality: 90, // Override quality blur: 20, // Blur effect grayscale: true, // Convert to grayscale }} /> // Extract/crop region <Image src="/large-image.jpg" alt="Cropped section" width={300} height={200} transforms={{ extract: { x: 100, y: 100, width: 300, height: 200, }, }} /> ``` ### Loading Control ```tsx // Priority loading for above-the-fold images <Image src="/hero.jpg" alt="Hero image" width={1200} height={600} priority={true} /> // Lazy loading (default) <Image src="/content.jpg" alt="Content image" width={400} height={300} loading="lazy" /> ``` ### Event Handling ```tsx function ImageWithEvents() { const [isLoading, setIsLoading] = useState(true); const [hasError, setHasError] = useState(false); return ( <div> {isLoading && <div>Loading...</div>} {hasError && <div>Failed to load image</div>} <Image src="/photo.jpg" alt="Photo" width={600} height={400} onLoad={() => { setIsLoading(false); console.log('Image loaded successfully'); }} onError={() => { setIsLoading(false); setHasError(true); console.log('Failed to load image'); }} /> </div> ); } ``` ## Type Definitions ```typescript // Image component props interface SnapkitImageProps { src: string; alt: string; width?: number; height?: number; fill?: boolean; sizes?: string; quality?: number; priority?: boolean; loading?: 'lazy' | 'eager'; optimizeFormat?: 'auto' | 'avif' | 'webp' | 'off'; transforms?: ImageTransforms; organizationName?: string; className?: string; style?: React.CSSProperties; onLoad?: React.ReactEventHandler<HTMLImageElement>; onError?: React.ReactEventHandler<HTMLImageElement>; } // Image transformations interface ImageTransforms { width?: number; height?: number; quality?: number; format?: ImageFormat; fit?: 'contain' | 'cover' | 'fill' | 'inside' | 'outside'; blur?: number; grayscale?: boolean; flip?: boolean; flop?: boolean; extract?: { x: number; y: number; width: number; height: number; }; } // Image formats type ImageFormat = 'auto' | 'avif' | 'webp' | 'jpeg' | 'png' | 'gif'; ``` ## Migration from Next.js Image The Snapkit Image component is designed to be a drop-in replacement for Next.js Image: ```tsx // Before (Next.js Image) import Image from 'next/image'; <Image src="/photo.jpg" alt="Photo" width={800} height={600} quality={90} priority sizes="(max-width: 768px) 100vw, 50vw" /> // After (Snapkit Image) import { Image } from '@snapkit-studio/react'; <Image src="/photo.jpg" alt="Photo" width={800} height={600} quality={90} priority sizes="(max-width: 768px) 100vw, 50vw" // Additional Snapkit features transforms={{ fit: "cover" }} optimizeFormat="auto" /> ``` ## Live Demo Experience all features in action with our interactive demo: **[🚀 Live Demo →](https://react.snapkit.studio)** - Real-time examples with source code Explore features including: - Image transformations with live preview - Error boundary demonstrations - Network-aware quality adjustments - DPR optimization examples - All component props and configurations ## Browser Support - **AVIF**: Chrome 85+, Firefox 93+, Edge 91+ (Chromium), Safari 16+ - **WebP**: Chrome 23+, Firefox 65+, Edge 79+ (Chromium), Safari 14+ - **Lazy Loading**: Chrome 76+, Firefox 75+, Edge 79+, Safari 15.4+ - **Intersection Observer**: Chrome 58+, Firefox 55+, Edge 79+, Safari 12.1+ **Note**: Only Chromium-based Edge (79+) is supported. Legacy EdgeHTML Edge (18 and below) is not supported. **Format Selection**: Automatically selects the best supported format in order: AVIF → WebP → JPEG. Falls back to JPEG in browsers that don't support modern formats. ## Testing The package includes comprehensive test coverage with automatic coverage reporting: ```bash # Run tests npm test # Run with coverage report npm run test:coverage # Watch mode for development npm test -- --watch ``` ### Test Coverage The package maintains high test coverage standards: - **Coverage Threshold**: 80% minimum for branches, functions, lines, and statements - **Test Framework**: Vitest with jsdom environment and v8 coverage provider - **Coverage Reports**: Text (console), JSON, HTML, and LCOV formats - **Coverage Exclusions**: Test files, configuration files, type definitions, and test utilities Coverage reports are generated in multiple formats: - **Text**: Console output during test runs - **HTML**: Detailed coverage report in `coverage/` directory - **LCOV**: For CI/CD integration and coverage tools - **JSON**: Machine-readable coverage data ### Test Environment Tests run in a jsdom environment to simulate browser behavior for React components testing. ## Development ```bash # Install dependencies npm install # Build package npm run build # Watch mode for development npm run dev # Type checking npm run check-types # Linting npm run lint ``` ## Contributing See the main [repository README](../../README.md) for contribution guidelines. ## License MIT