npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vitepress-plugin-responsive-images

v0.3.3

Published

Improve PageSpeed and load times for VitePress docs by auto-generating modern, responsive picture elements from local Markdown images.

Readme

vitepress-plugin-responsive-images

vitepress-plugin-responsive-images social preview

Improve PageSpeed, Lighthouse, and real-world load times for VitePress documentation sites by automatically serving modern image formats and responsive sizes for local Markdown images.

Keep writing images the way you already do in Markdown:

<!-- Inline -->
![Dashboard screenshot](./images/dashboard.png)

<!-- Reference-style -->
![Architecture diagram][arch]
[arch]: ./images/architecture.png

<!-- Handwritten HTML -->
<img src="./images/hero.png" alt="Hero" class="rounded">

The plugin generates responsive variants at build time and rewrites local images into markup like this:

<picture>
  <source type="image/avif" srcset="... 480w, ... 720w, ... 1440w">
  <source type="image/webp" srcset="... 480w, ... 720w, ... 1440w">
  <img src="...png" srcset="... 480w, ... 720w, ... 1440w" sizes="(max-width: 768px) 100vw, 720px" alt="Dashboard screenshot">
</picture>

Why

This plugin exists because documentation sites often fail the same PageSpeed Insights and Lighthouse image audits:

  • Use modern image formats — WebP or AVIF can deliver the same visual quality at a smaller download size than PNG or JPEG alone.
  • Serve appropriately sized images — a 1920×1080 asset shown at 640×360 still downloads the full HD file unless you generate responsive variants.

Those warnings are not abstract. Oversized, legacy-format images slow down first paint, hurt mobile users on slow networks, and drag down performance scores that search engines and site owners care about. The goal is not compression for its own sake. It is faster page loads, better reader experience, and stronger SEO signals — outcomes every web admin wants.

VitePress makes writing docs easy, but it does not solve this by default. Authors still drop ![...](./image.png) into Markdown, and the build serves that single file as-is. This plugin closes that gap at build time:

  • generates AVIF and WebP <source> variants for modern browsers
  • emits srcset / sizes so each viewport downloads a fitting width
  • keeps JPG/PNG fallbacks for older browsers and enterprise environments

You keep the same authoring workflow. Visitors get smaller, appropriately sized images without manual export pipelines or hand-written <picture> markup.

Compatibility

| VitePress version | Status | | --- | --- | | 1.6.x | Stable supported | | 2.0.x | Supported and continuously tested; alpha caveats apply while VitePress 2 is not stable |

Install

npm install -D vitepress-plugin-responsive-images

Usage

// docs/.vitepress/config.ts
import { defineConfig } from 'vitepress'
import { withResponsiveImages } from 'vitepress-plugin-responsive-images'

export default withResponsiveImages(
  defineConfig({
    title: 'My Docs'
  })
)

Example page

A typical documentation page can mix all supported syntaxes in one file:

# Product overview

![Dashboard screenshot](./images/dashboard.png)

See the full architecture in the diagram below.

![Architecture diagram][arch]

For fine-grained layout control, you can still use HTML:

<img
  src="./images/hero.png"
  alt="Product hero"
  class="rounded border"
  loading="eager"
>

[arch]: ./images/architecture.png "System architecture"

After vitepress build, local images in that page are emitted as <picture> elements. Handwritten HTML keeps attributes such as class, style, loading, and decoding on the fallback <img>.

With options:

export default withResponsiveImages(
  defineConfig({
    title: 'My Docs'
  }),
  {
    widths: [480, 720, 960, 1440],
    formats: ['avif', 'webp'],
    sizes: '(max-width: 768px) 100vw, 720px'
  }
)

Defaults

  • Processes local images from Markdown inline images ![](), reference-style images ![alt][ref], and handwritten HTML <img src="..."> tags in Markdown files.
  • Processes Sharp-readable local images with file extensions.
  • Skips remote URLs, data URLs, SVG, GIF, Vue components, theme images, CSS backgrounds, and <img> tags already inside <picture>.
  • Generates AVIF + WebP modern sources, with JPG/PNG fallback.
  • Avoids upscaling images.
  • Adds loading="lazy" and decoding="async" by default.
  • Injects default .vp-doc layout styles for <picture> and <img> elements.
  • Invalidates cached variants automatically when widths, formats, or quality change.

Configuration

Responsive widths

Use widths to choose the generated responsive image widths. Use 0 when you want to keep the original image size and only convert formats.

| Value | Generates | When to use | | --- | --- | --- | | [480, 720, 960, 1440] (default) | Responsive resized variants | General documentation images | | [0] | Original-size variants only | Format conversion without resizing | | [480, 720, 0] | Resized variants plus original-size variant | Responsive images that can still use the original file width |

When using original-size-only output, pair it with defaultWidth: 0 if the rendered <img width> and <img height> should also use the source image dimensions:

export default withResponsiveImages(
  defineConfig({
    title: 'My Docs'
  }),
  {
    widths: [0],
    defaultWidth: 0,
    formats: ['avif', 'webp']
  }
)

Modern formats

Use formats to choose which modern image variants are generated for <picture><source> entries. JPG/PNG fallback is always kept for older browsers.

| Value | Generates | When to use | | --- | --- | --- | | ['avif', 'webp'] (default) | AVIF + WebP | Best balance of size and compatibility | | ['webp'] | WebP only | Faster builds; good for most documentation sites | | ['avif'] | AVIF only | Smallest files, but slower to encode |

Examples:

// Default: AVIF + WebP
formats: ['avif', 'webp']

// WebP only
formats: ['webp']

// AVIF only
formats: ['avif']

Quality

Use quality to tune encoder output. WebP and AVIF can be generated in Sharp's lossless mode by setting their quality value to -1.

| Value | Meaning | | --- | --- | | quality.webp: 80 (default) | Lossy WebP quality 80 | | quality.webp: 100 | Maximum lossy WebP quality | | quality.webp: -1 | Lossless WebP via webp({ lossless: true }) | | quality.avif: 50 (default) | Lossy AVIF quality 50 | | quality.avif: 100 | Maximum lossy AVIF quality | | quality.avif: -1 | Lossless AVIF via avif({ lossless: true }) |

export default withResponsiveImages(
  defineConfig({
    title: 'My Docs'
  }),
  {
    formats: ['avif', 'webp'],
    quality: {
      avif: -1,
      webp: -1
    }
  }
)

Fallback format

The fallback <img> always uses a broadly compatible JPG or PNG file:

| Source format | Fallback format | | --- | --- | | jpg, jpeg | jpg | | png | png | | Other Sharp-readable formats, such as webp, avif, bmp, or tiff | png |

Using PNG for non-JPG/PNG inputs preserves transparency and avoids unexpected lossy conversion. It can produce larger fallback files for photo-like images, but modern browsers will usually load the AVIF/WebP <source> first.

Layout styles

VitePress does not ship layout rules for <picture> elements. By default, this plugin adds them through VitePress head config as a small scoped stylesheet:

.vp-doc picture { display: block; }
.vp-doc picture > img,
.vp-doc img { max-width: 100%; height: auto; }

Disable automatic injection when your theme already provides equivalent rules:

export default withResponsiveImages(
  defineConfig({
    title: 'My Docs'
  }),
  {
    injectStyles: false
  }
)

You can also import the stylesheet manually:

import 'vitepress-plugin-responsive-images/vp-doc-picture.css'

Custom themes that do not use the .vp-doc wrapper should provide their own layout rules.

Cache behavior

Generated variants are stored in .vitepress/cache/responsive-images and copied into the build output on production builds.

The plugin tracks a fingerprint of generation options (widths, formats, and quality). When those options change, the cache directory is cleared automatically so old variants are not reused.

After each manifest rebuild, unreferenced cache files are pruned. Production builds also replace the output image directory with only the files referenced by the current manifest, so you do not need to manually delete .vitepress/dist or .vitepress/cache/responsive-images after changing image settings.

interface ResponsiveImagesOptions {
  widths?: number[]
  formats?: Array<'webp' | 'avif'>
  defaultWidth?: number
  sizes?: string
  outputDir?: string
  include?: string[]
  exclude?: string[]
  quality?: {
    webp?: number // -1 enables lossless WebP; 1..100 uses lossy quality
    avif?: number // -1 enables lossless AVIF; 1..100 uses lossy quality
    jpeg?: number
    png?: number
  }
  loading?: 'lazy' | 'eager' | false
  decoding?: 'async' | 'sync' | 'auto' | false
  failOnError?: boolean
  debug?: boolean
  injectStyles?: boolean
}

Supported Markdown image syntax

| Syntax | Example | Notes | | --- | --- | --- | | Inline Markdown | ![Alt](./images/a.png) | Default authoring style | | Reference-style | ![Alt][ref] + [ref]: ./images/a.png | Useful for repeated images or long URLs | | Shortcut reference | ![logo] + [logo]: ./images/logo.png | Same-file reference definitions only | | Handwritten HTML | <img src="./images/a.png" alt="Alt"> | Preserves existing HTML attributes |

Reference-style images must define the URL in the same Markdown file:

![Logo][logo]

[logo]: /logo.png

Handwritten HTML inside an existing <picture> element is left unchanged.

Not supported yet

  • Theme hero images, site logo, and other frontmatter-driven theme assets
  • Per-image opt-out (page-level responsiveImages: false is supported)
  • Vue image components and CSS background-image

Page opt-out

Disable the plugin for a single Markdown page with frontmatter:

---
responsiveImages: false
---

License

MIT