Skip to content

The Plugin

The oh-image Vite plugin optimizes images automatically for you.

To use the plugin, follow two steps:

1. Add the image plugin to the plugins array

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { ohImage } from "@lonik/oh-image/plugin";
export default defineConfig({
plugins: [ohImage(), react()],
});

2. Add the ?oh suffix to the image import path

import photo from "./assets/photo.jpg?oh";

The oh-image plugin can be configured at different levels.

You can pass a configuration object when you add the plugin:

import { ohImage } from "@lonik/oh-image/plugin";
ohImage({
format: "avif",
placeholder: false,
bps: [640, 828, 1200, 1920],
distDir: "optimized-images",
});

When you import an image with the ?oh suffix, you can add additional query parameters to configure that specific image:

import hero from "./assets/hero.jpg?oh&format=avif";
import thumbnail from "./assets/photo.jpg?oh&width=400&height=300";
import custom from "./assets/portrait.jpg?oh&bps=640,1080";

These are the default values used by the plugin when no configuration is provided:

{
format: "webp",
placeholder: true,
bps: [16, 48, 96, 128, 384, 640, 750, 828, 1080, 1200, 1920],
distDir: "oh-images",
}
OptionTypeDefaultDescription
formatstring"webp"Output format for the image (e.g., webp, avif, png)
placeholderbooleantrueWhether to generate a placeholder image for lazy loading
bpsnumber[][16, 48, 96, 128, 384, 640, 750, 828, 1080, 1200, 1920]Breakpoints — widths in pixels for responsive srcSet generation
distDirstring"oh-images"Directory name where processed images are output during build
OptionTypeDefaultDescription
formatstringInherits globalOutput format for this image (e.g., webp, avif, png)
widthnumberTarget width for the processed image in pixels
heightnumberTarget height for the processed image in pixels
placeholderbooleanInherits globalWhether to generate a placeholder image for lazy loading
bpsnumber[]Inherits globalBreakpoints — widths in pixels for responsive srcSet generation

Some import-level options (width, height, format) accept null to explicitly unset them — for example, to override a globally configured format and fall back to the original file’s format.

To pass null via a query parameter, include the key without a value:

// Set format to null — keeps the original file format instead of converting
import photo from "./assets/photo.jpg?oh&format";
// Set width to null — no width constraint applied
import photo from "./assets/photo.jpg?oh&width";
// Set height to null — no height constraint applied
import photo from "./assets/photo.jpg?oh&height";
// Set bps to null — disables responsive srcSet generation for this image
import photo from "./assets/photo.jpg?oh&bps";
// Null out all at once
import photo from "./assets/photo.jpg?oh&format&width&height&bps";

A key with no = value is parsed as null by the query string parser.

The plugin behaves differently depending on whether you’re in development or building for production.

Eager processing — All images are processed during the build step before deployment.

The process:

  1. Vite builds your application
  2. The plugin processes all registered images
  3. Optimized images are output to assets/oh-images/
  4. Files are ready to be served from your CDN

The plugin uses parallel processing (up to 30 images concurrently) to keep build times reasonable.

AspectDevBuild
When images are processedWhen first requestedDuring build
Where they’re storedVite cache directorydist/assets/oh-images/
Speed priorityFast iterationOptimized output

Images should not be placed in the public/ folder, as Vite does not process files from that directory.

Instead, place images in src/assets/ or any other directory within your source folder.

The plugin supports the following image formats as input:

  • .jpg / .jpeg
  • .png
  • .webp
  • .avif
  • .gif
  • .svg