Skip to content

About

Stops one broken image import from taking down your whole build. Missing or mistyped image paths get swapped for a placeholder automatically, with a clear console warning, so your build finishes instead of crashing.

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

vite-plugin-image-fallback

npm version CI license

A Vite plugin that stops one broken image from taking down your whole build.

Documentation and live demo

Support me on Buy Me a Coffee Support me on Ko-fi More tools

What this does

If a page in your project imports an image that doesn't actually exist, maybe a typo in the filename, a file someone forgot to add, or an asset that got renamed, Vite normally stops the entire build with a Rollup "could not resolve" error. Nobody's build finishes until that one image gets fixed.

This plugin catches that case instead. When an image import doesn't resolve to a real file, it swaps in a placeholder image and prints a clear console warning naming exactly which import is missing. Your build finishes, your site still deploys, and you can go fix the actual image without everyone else being blocked on it.

Every image that does exist is left completely alone and passed straight through to Vite as usual. Only a genuinely missing one gets swapped.

Install

npm install --save-dev @binarynoir/vite-plugin-image-fallback

Usage

// vite.config.ts
import { defineConfig } from "vite";
import { imageFallbackPlugin } from "@binarynoir/vite-plugin-image-fallback";

export default defineConfig({
  plugins: [imageFallbackPlugin()],
});

Only relative (./foo.png) and root-relative (/foo.png) image imports are intercepted — bare package specifiers (some-icon-package/icon.png) are left alone so they still go through normal node_modules resolution. An existing file is untouched and passed straight through to Vite's own asset pipeline; only an import that doesn't resolve to a real file gets swapped. A root-relative import counts as existing if it is in Vite's root or in its publicDir, wherever that is (VitePress's default <srcDir>/public, or an absolute path outside the root).

Dev server

In vite dev (and vitepress dev), not every image is an import. VitePress compiles a Markdown image such as ![x](./assets/missing.png) to a plain URL, which the browser requests directly. Without this plugin, a missing image's request gets the page's HTML back, and the browser shows its broken-image icon.

The plugin answers those requests too. A request for an image that isn't in the root or publicDir gets the placeholder, with the missing path drawn into it, and logs the same warning, naming the page that requested it. A custom fallback data URI is served as is; a custom fallback URL is redirected to.

Every placeholder response carries an X-Image-Fallback header holding the missing path (URI-encoded), and a HEAD request for a missing image gets a 200 with that header (never a redirect). The #image-fallback= hint can't be added to these URLs, so on a dev-server page the client script (see Hints) asks the server about each image with a HEAD request instead, and shows the same outline and popover.

Options

Option Default Description
extensions ["jpg", "jpeg", "png", "gif", "svg", "webp", "avif"] File extensions (without the dot) treated as image imports.
fallback a plain gray placeholder SVG reading "Missing Image Asset" The asset returned in place of a missing image — a data URI, or any string Vite can serve as a URL.
silent false Suppress the console warning normally emitted for each missing asset.
hints true Record which import was missing, and where, in each placeholder's URL, for the on-page hints below. false uses fallback exactly as given.
imageFallbackPlugin({
  extensions: ["png", "jpg", "jpeg"],
  fallback: "/branding/placeholder.png",
  silent: false,
});

Hints

Each placeholder can explain itself on the page, the same way Broken Links and HTML Escape explain their fixes. Turn the hints on in the browser, in VitePress from your theme's enhanceApp:

// .vitepress/theme/index.ts
import DefaultTheme from "vitepress/theme";
import { enableImageFallbackHints } from "@binarynoir/vite-plugin-image-fallback/client";

export default {
  extends: DefaultTheme,
  enhanceApp() {
    enableImageFallbackHints();
  },
};

Every placeholder then gets a dashed outline. Hover it, or click or tap it, for a popover that names the missing import and the file and line that imported it:

plugins/image-fallback.md, line 19
./missing-image-demo.png could not be found, so a placeholder is shown
instead. Check the path and the file name, or add the image.

The build warning names the same file and line. The plugin records them in a #image-fallback=… fragment on the placeholder's URL, which the browser ignores when it loads the image, so the placeholder looks the same with or without the client script.

enableImageFallbackHints(options) takes:

Option Default Description
hover true Show the popover on mouse hover. false for click and tap only.
hoverDelay 300 Milliseconds the mouse rests on a placeholder before the popover shows.
injectStyles true Inject the default styles. false lets you style img[src*="#image-fallback="] and img[data-image-fallback] yourself.
devPlaceholders on a page the Vite dev server served Find the dev server's placeholders with one HEAD request per same-origin image URL, including images added or changed later.

On the dev server, each placeholder found gets a data-image-fallback attribute holding the missing path, and fires a bubbling image-fallback event whose detail.src is that path. To treat placeholders differently in your own code, for example to skip them in a click-to-zoom handler, use isImageFallback(img). It is true for built placeholders, and for dev ones once they have been found:

import { isImageFallback } from "@binarynoir/vite-plugin-image-fallback/client";

if (isImageFallback(img)) return; // nothing to zoom

The colors follow the VitePress theme and can be changed with the --image-fallback-hint-error CSS variable.

Why not just let the build fail?

For most application code, a missing asset failing the build is exactly the right behavior — you want to know immediately. This plugin is aimed at content-heavy sites (docs, blogs, CMS-driven pages) where hundreds of Markdown/MDX pages reference images added by many different contributors, and one bad reference in page 400 of 500 shouldn't block deploying the other 499. It trades a hard failure for a loud warning plus a visibly-broken (but visually obvious) placeholder.

Releasing

Releases are tag-triggered. To ship a new version, from a clean main that's in sync with origin/main:

npm run release:patch   # or release:minor / release:major

This runs typecheck/lint/test/build locally, then npm version <bump> (bumps package.json, commits, and creates a matching vX.Y.Z tag) and git push --follow-tags. Pushing that tag triggers .github/workflows/release.yml, which re-runs the checks, publishes to npm (with provenance), and creates a GitHub release with auto-generated notes.

For a prerelease or an explicit version, use npm run release -- <arg> (e.g. npm run release -- 1.2.3 or npm run release -- prerelease) — see npm version for the full list of accepted values.

This requires an NPM_TOKEN repository secret (an npm automation token with publish access) — set it under Settings → Secrets and variables → Actions. First time publishing this package? See PUBLISHING.md.

License

MIT


Feedback and support

Bug reports, feature requests and questions are all welcome. Please open them on this project's GitHub issue tracker:

Pull requests are welcome too.

More from BinaryNoir

BinaryNoir

Image Fallback is one of a family of open-source VitePress plugins, editor extensions and command-line tools from BinaryNoir. Browse them all, with documentation and live demos, at binarynoir.tech.

VitePress plugins

Vite plugins

Editor extensions

Command-line tools

  • NoirWatch: get notified when a web page changes
  • NoirCon: get notified when a site, server or port goes down
  • callrx: amateur radio callsign lookup

Website, docs and demos: binarynoir.tech

Author

John Smith III, BinaryNoir. Thanks to all contributors and users for their support and feedback.

About

Stops one broken image import from taking down your whole build. Missing or mistyped image paths get swapped for a placeholder automatically, with a clear console warning, so your build finishes instead of crashing.

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages