Blog Mobile UX 8 min read

Ionic and Capacitor WebView Image Fallbacks

Fix capacitor image loading failures in Ionic apps by combining ion-img error events, onerror handlers, and deterministic fallback.pics URLs across WebView environments.

Capacitor image loadingIonic image fallbackion-img errorWebView imagesHybrid app images
Ionic and Capacitor WebView Image Fallbacks

Ionic apps run inside a Capacitor WebView, which means image loading behaves like a browser but with additional constraints around CORS, local file access, and mixed content policies. Capacitor image loading failures are common when remote URLs return 404s, CORS blocks requests, or the device is offline.

This guide covers the ion-img ionError event, vanilla onerror fallbacks for non-Ionic frameworks, CORS and mixed-content solutions, and pairing every failure case with deterministic fallback.pics URLs.

Problem

WebView image failures differ from browser image failures

Capacitor's WebView enforces the same CORS rules as a browser, but the origin is typically capacitor://localhost or http://localhost. Remote image hosts that do not include localhost in their Access-Control-Allow-Origin header block image loads even when the same URL works in a desktop browser.

Mixed content is another failure mode: an HTTPS Capacitor app cannot load HTTP image URLs without explicit allowMixedContent configuration in AndroidManifest.xml. These failures are silent — the image element fires an error event, but no console warning appears in the device log by default.

Using deterministic fallback.pics HTTPS URLs sidesteps both problems. The API always returns HTTPS, includes permissive CORS headers, and never blocks requests from localhost origins.

ion-img

Handling ion-img ionError for failed image loads

ion-img is Ionic's lazy-loading image component. It fires an ionError event when the src fails to load. Bind a handler to swap in a fallback URL. Avoid setting src directly inside the handler without a guard — it can trigger another error event if the fallback URL also fails.

For Angular Ionic, use event binding syntax. For React Ionic, use the onIonError prop. Both expose the same HTMLIonImgElement event object.

Implementation text
<!-- Angular Ionic -->
<ion-img
  [src]="product.imageUrl"
  [alt]="product.name"
  (ionError)="onImgError($event, 400, 300)"
></ion-img>

// In component class
onImgError(event: CustomEvent, w: number, h: number): void {
  const el = event.target as HTMLIonImgElement;
  el.src = `https://fallback.pics/api/v1/${w}x${h}/7C3AED/FFFFFF?text=No+Image`;
  el.onerror = null; // prevent loop
}

// React Ionic
<IonImg
  src={product.imageUrl}
  alt={product.name}
  onIonError={(e) => {
    const target = e.target as HTMLIonImgElement;
    target.src = `https://fallback.pics/api/v1/400x300/7C3AED/FFFFFF?text=No+Image`;
    (target as any).onerror = null;
  }}
/>

Vanilla

onerror fallback for img tags outside ion-img

Not all images in an Ionic app go through ion-img. Background images set via CSS, img tags in third-party plugins, or images inside iframes bypass the Ionic component system. Use standard onerror handlers on these elements.

A global onerror delegate registered on document during app initialization catches failures from any img tag regardless of how it was inserted into the DOM.

Implementation tsx
// app.component.ts — global image error handler
function registerGlobalImageFallback(w = 400, h = 300) {
  document.addEventListener('error', (event) => {
    const target = event.target as HTMLElement;
    if (target.tagName === 'IMG') {
      const img = target as HTMLImageElement;
      if (!img.dataset['fallbackApplied']) {
        img.dataset['fallbackApplied'] = '1';
        img.src = `https://fallback.pics/api/v1/${w}x${h}/7C3AED/FFFFFF?text=Unavailable`;
      }
    }
  }, true); // capture phase to catch all img errors
}

CORS

Fixing CORS and mixed content in Capacitor WebViews

For Android, add android:usesCleartextTraffic="false" and ensure your image CDN sends Access-Control-Allow-Origin: * or your app's Capacitor origin. Fallback.pics already sends permissive CORS headers so fallback images never block.

For iOS, configure NSAppTransportSecurity in Info.plist to allow only HTTPS connections. Fallback.pics serves exclusively over HTTPS, making it safe to whitelist without the NSAllowsArbitraryLoads flag.

Implementation text
// capacitor.config.ts — server config for image origins
{
  server: {
    allowNavigation: [
      "fallback.pics",
      "your-image-cdn.com"
    ]
  }
}

// AndroidManifest.xml (mixed content — do not set true in production)
// <application android:usesCleartextTraffic="false" ...>

Offline

Caching fallback images for offline use with Capacitor

Capacitor apps can use the Filesystem plugin to cache image blobs locally. For product catalogs with known images, pre-cache the set of fallback.pics URLs during the first launch. Ion-img will serve them from the local file system when the device is offline.

Alternatively, register a service worker that intercepts image requests and returns the cached fallback response. Service workers work in Capacitor WebViews on both iOS and Android with no additional plugin required.

Key takeaways

What to standardize before shipping

  • Use ion-img's ionError event to swap in a fallback.pics URL; set onerror to null immediately to prevent an error loop if the fallback URL also fails.
  • Register a document-level error listener in capture phase to catch failures from img tags inserted outside ion-img, including those from plugins and third-party libraries.
  • Fallback.pics serves HTTPS with permissive CORS headers, making it safe to use as a fallback source inside Capacitor WebViews without extra configuration.
  • Pre-cache fallback.pics responses with the Capacitor Filesystem plugin or a service worker to serve branded placeholders in offline mode.
  • Do not set android:usesCleartextTraffic=true in production; use HTTPS fallback URLs to avoid the configuration entirely.

Production fallback layer

Use fallback.pics anywhere an image URL is accepted.

Start with one deterministic URL and standardize fallback behavior across your design system.

Read the docs