JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 363
  • Score
    100M100P100Q85045F
  • License MIT

Content-aware, device-adaptive image optimizer for React. Works with Vite and Next.js. Auto-converts to AVIF/WebP/JPEG with SSIM auto-tune, face detection, SVGO, and device-tier delivery. Built by @ezitounioussama at gotodev.ma.

Package Exports

  • vite-image-react
  • vite-image-react/next-plugin
  • vite-image-react/vite-plugin

Readme

vite-image-react

Content-aware, device-adaptive image optimizer for React. Works with Vite and Next.js.

Built by @ezitounioussamagotodev.ma

Surpasses next/image in perceptual quality at equal or smaller file sizes. Framework-agnostic — use it with Vite, Next.js, or any React build pipeline.

npm version npm downloads npm bundle size License: MIT Node Vite compatibility React GitHub stars


How it works

Build time (Vite or Next.js)

Every image is divided into 64×64 tiles and analyzed:

  1. Analyze — per-tile Shannon entropy, Sobel edge density, and skin-tone ratio produce an importance map
  2. Weight — top 30% most-important tiles drive 70% of the quality decision; center-bias and edge-bonuses refine it
  3. Preprocess — high-importance tiles are selectively sharpened (with overlapped boundaries to prevent seams)
  4. Encode — each tier/variant is encoded at the perceptually-weighted quality; SSIM auto-tune finds the Pareto-optimal quality/size point
  5. Emit — manifest with variants, tiers, LQIP placeholders, and SRI hashes is embedded in the module

Runtime (GImage component)

  1. Fingerprint — reads effectiveType, deviceMemory, hardwareConcurrency, devicePixelRatio, saveData
  2. Tier — scoring algorithm selects ultra/high/medium/low per device capability
  3. Format<picture> with AVIF, WebP, and JPEG sources
  4. Load — IntersectionObserver with scroll-velocity-adaptive preload distance (600–3000px)
  5. Placeholder — blur-up CSS transition from 32×32 WebP base64 to full image

Features

  • Perceptually-optimized quality — 64×64 tile saliency drives quality per region. Faces, text, and detail get higher quality; backgrounds compress harder.
  • Saliency-driven preprocessing — important tiles are sharpened before encoding, preserving detail where it matters.
  • Skin-tone face detection — automatic quality boost around skin-colored regions. Zero extra dependencies.
  • SSIM auto-tune — finds the lowest quality where SSIM >= 0.97, saving 20–40% file size with no visible loss.
  • Device-adaptive delivery — runtime fingerprinting selects the optimal quality tier for each device.
  • Automatic format conversion — AVIF, WebP, and JPEG sources in a <picture> element.
  • Predictive lazy loading — scroll velocity sampling dynamically adjusts the preload distance.
  • Blur-up placeholders — 32×32 WebP base64 with CSS fade-in.
  • CLS prevention — fixed-aspect-ratio container from image metadata.
  • SVG optimization — automatic SVGO compression with security sanitization.

Install

npm install vite-image-react
# or
pnpm add vite-image-react
# or
bun add vite-image-react

Usage

Vite plugin

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import viteImageReact from 'vite-image-react/vite-plugin'

export default defineConfig({
  plugins: [
    react(),
    viteImageReact(),
  ],
})

Next.js plugin

// next.config.mjs
import { withViteImageReact } from 'vite-image-react/next-plugin'

export default withViteImageReact({
  // your Next.js config here
}, {
  // optional: plugin options
  remote: { domains: ['images.unsplash.com'] },
})

Then import GImage from 'vite-image-react' in your components. Works in both App Router and Pages Router.

'use client'
import GImage from 'vite-image-react'
import hero from './hero.jpg'

export default function Page() {
  return <GImage src={hero} alt="Hero" priority />
}

React component

import GImage from 'vite-image-react'
import hero from './hero.jpg'

function Page() {
  return (
    <GImage
      src={hero}
      alt="Hero banner"
      priority
      sizes="(max-width: 768px) 100vw, 50vw"
    />
  )
}

Remote images

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import viteImageReact from 'vite-image-react/vite-plugin'

export default defineConfig({
  plugins: [
    react(),
    viteImageReact({
      remote: {
        domains: ['images.unsplash.com', 'cdn.example.com'],
      },
    }),
  ],
})
import GImage from 'vite-image-react'
import hero from 'https://images.unsplash.com/photo-1234567890'

function Page() {
  return (
    <GImage src={hero} alt="Remote hero" />
  )
}

Public directory images

Images placed in your project's public/ directory are automatically scanned and optimized during build. No import needed:

public/
├── logo.png          ← optimized to dist/logo.png
├── og-image.jpg      ← optimized to dist/og-image.jpg
└── images/
    └── banner.webp   ← optimized to dist/images/banner.webp

Public images use the same compression pipeline (AVIF/WebP/JPEG encoding, SSIM auto-tune, saliency preprocessing) with the high quality tier defaults. Since they aren't directly imported, they get optimized on-disk in dist/ under their original paths.

All standard <img> props work: className, style, onLoad, onError, loading, etc.


Options

Plugin options

viteImageReact({
  tiers?: Partial<Record<QualityTier, TierConfig>>
  adaptive?: boolean          // default: true
  autoTune?: boolean          // default: true
  preprocess?: boolean        // default: true — sharpen important tiles before encoding
  faceDetection?: boolean     // default: true — boost quality around skin tones
  formats?: OutputFormat[]    // default: ['avif', 'webp', 'jpeg']
  maxFileSize?: number        // default: 52_428_800 (50MB)
  verbose?: boolean           // default: false
  remote?: {
    domains: string[]         // allowed remote image domains (e.g. ['images.unsplash.com'])
    cacheDir?: string         // cache directory (default: node_modules/.cache/vite-image-react)
  }
})

GImage props

interface GImageProps {
  src: string | ImageMetadata  // import result or metadata object
  alt: string
  priority?: boolean            // eager load + fetchPriority='high'
  sizes?: string                // default: '100vw'
  disableAdaptive?: boolean     // always deliver highest quality
  placeholder?: 'blur' | 'none' // default: 'blur'
  onLoad?: () => void
  onError?: () => void
  // + all standard img props (className, style, loading, etc.)
}

Comparison: Built-in next/image vs vite-image-react

Aspect next/image vite-image-react
Quality strategy Uniform (e.g. 75) Perceptually-weighted — important regions drive quality
Preprocessing None Saliency-guided sharpen — detail preserved where it matters
Face/subject detection None Skin-tone heuristic — quality boost on faces
SSIM auto-tune None Smallest file at SSIM >= 0.97
Device adaptation Responsive srcSet only Runtime tier switching — CPU/memory/connection-aware
Predictive loading Fixed threshold Velocity-adaptive — faster scroll = bigger preload zone
Format pipeline AVIF/WebP/JPEG Same + skin detection + selectable preprocessor
SVG optimization None SVGO integration with security sanitization

Requirements

  • Node.js >= 18.17
  • React >= 19
  • Vite >= 7 (optional — only for Vite plugin)
  • Next.js >= 13 (optional — only for Next.js plugin)

License

MIT — built with care by @ezitounioussama · gotodev.ma