Skip to content

Manifest · Manifest member

Manifest icons and maskable icons

Published Updated

Limited availabilityNot supported in Firefox (Desktop)W3C draft

In one line: Per MDN, the icons manifest member “is used to specify one or more image files that define the icons to represent your web application”, and per the Web Application Manifest specification those images “serve as iconic representations of the web application in various contexts” — for example to integrate the app with an OS’s task switcher or system preferences.

{
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/icons/icon-512-maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}

Per MDN, each entry is an object and only src is required:

Property Required What MDN says
src Yes The path to the icon image file; a relative src is resolved against the manifest file’s URL.
sizes No One or more <width>x<height> sizes, space-separated. For raster formats “specifying the exact available sizes is recommended”; for vector formats you “can use any to indicate scalability”.
type No The MIME type, e.g. image/png. If omitted, “browsers typically infer the image type from the file extension”.
purpose No One or more space-separated keywords — monochrome, maskable, any — used “as hints to determine where and how an icon is displayed”. any is the default.

Setting type “can significantly improve performance”, per MDN, “because it allows browsers to ignore images with unsupported formats more easily” instead of MIME-sniffing the file.

Two support facts decide which files you actually have to ship:

  • Chromium’s installability bar. Per MDN, Chromium-based browsers — including Chrome, Samsung Internet and Microsoft Edge — require that the manifest’s icons “must contain a 192px and a 512px icon” for the app to be promoted for installation.
  • Maskable icons. Per web.dev, maskable icons are “a new icon format for Chrome and Firefox” that lets a PWA use Android’s adaptive icons.

Which file a browser picks is not something you control: per MDN, “when multiple icons are available, browsers may select the most suitable icon for a particular display context”, and the context an icon is used in “is determined by the browser and the operating system, based on the specified sizes and formats”.

  • 192x192 and 512x512 — the two sizes Chromium requires for installability (MDN), and the two web.dev recommends for the Android splash screen: “We recommend you provide at least two icons sizes for the splash screen: 192px and 512px.”
  • A maskable variant, in addition to a plain one — see below.

Per web.dev, Android 8.0 introduced adaptive icons, “which display app icons in a variety of shapes across device models”, and “icons that don’t use this format have white backgrounds” — transparent PWA icons “appear inside white circles on Android”. Maskable icons “cover the entire circle instead”.

The safe zone is standardised. Per web.dev, “the important parts of your icon, such as your logo, must be within a circular area in the center of the icon with a radius equal to 40% of the icon width”, and “the outer 10% edge might be cropped on some platforms”. Check it in Chrome DevTools — the Application panel’s Icons section offers Show only the minimum safe area for maskable icons — or with Maskable.app, which web.dev recommends for trying “various shapes and sizes”.

Declare it with "purpose": "maskable":

{
"src": "/icons/icon-512-maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}

You can list several purposes in one entry ("any maskable"), but web.dev advises against it: “Using maskable icons as any icons adds unnecessary padding, making the core icon content smaller.” Ship a separate any icon designed “like your site’s favicons, with transparent regions and no extra padding”.

The runtime check that pays off is a self-check: read your own manifest and assert that the icons you believe you shipped are really declared — the sizes Chromium requires, and a maskable variant. The guard is the feature detection; the early returns are the fallback, so the page keeps working when there is no manifest link and when fetch is unavailable.

// null means "could not inspect the manifest", which is a different answer
// from [] ("the manifest declares no icons").
async function declaredIcons() {
const link = document.querySelector('link[rel="manifest"]');
if (!link || !('fetch' in window)) {
// Fallback: nothing to inspect — report "unknown" instead of throwing.
return null;
}
try {
const manifest = await fetch(link.href).then((response) => response.json());
return Array.isArray(manifest.icons) ? manifest.icons : [];
} catch {
return null; // Unreachable manifest or unparseable JSON.
}
}
async function auditIcons() {
const icons = await declaredIcons();
if (icons === null) {
console.info('Manifest not readable here — skipping the icon audit.');
return;
}
const hasSize = (size) => icons.some((i) => String(i.sizes ?? '').split(' ').includes(size));
const maskable = icons.some((i) => String(i.purpose ?? '').split(' ').includes('maskable'));
console.log({ has192: hasSize('192x192'), has512: hasSize('512x512'), maskable });
}

The fallback that matters inside the manifest is the plain icon. purpose is only a hint — the specification says user agents “SHOULD NOT use an icon other than for its stated icon purpose” — and per MDN, an icon whose purpose contains only unrecognized values “will be ignored”. A maskable-only icon set is therefore not a substitute for an any icon.

  • A 192 px and a 512 px icon are declared — without both, Chromium browsers do not promote the app for installation (MDN).
  • A maskable variant is declared in addition to an any icon, not instead of it.
  • The maskable icon’s logo sits inside the safe zone — a centre circle of radius 40% of the icon width; assume the outer 10% is cropped.
  • The maskable icon is opaque and padded — web.dev calls for “an opaque image with some padding that the browser can crop”.
  • type is set on every entry so browsers can skip formats they cannot decode.
  • src values resolve correctly — they are relative to the manifest’s URL, not the page’s.
  • Your CSP allows the icons: per MDN, fetching an icon “is governed by the Content Security Policy (CSP) of the manifest’s owner document, specifically by the img-src directive”, so a cross-origin icon outside img-src is simply not fetched.
  • You expect reshaping: the specification says user agents “MAY modify the images associated with a manifest image resource to better match the platform’s visual style”, “for example by rounding the corners or painting it in a specific color”.

Specifications

SpecificationStatus
Web App Manifest: iconsW3C draft
  • Legend
  • Yes
  • Partial
  • Flag
  • No
  • Unknown
Browser / PlatformSupportVersionsConfidenceSourceNotes
Chrome (Android)Yes39mediumsource1
Chrome (Desktop)Yes39lowsource—
Edge (Desktop)Yes79lowsource—
Safari (iOS)Yes15.4lowsource2
Safari (macOS)Yes17lowsource—
Firefox (Desktop)No—lowsource3
Firefox (Android)Yes152mediumsource—
Samsung InternetYes4.0lowsource—
  1. Maskable icons supported via purpose: maskable.
  2. Maskable icons not supported; iOS applies its own icon shape.
  3. No install flow consuming manifest icons on desktop.

Source data: /compatibility/manifest-icons.json · Global usage: 92 % (StatCounter 2026-05)

Source: spec · MDN · Last verified 2026-07-10 · Confidence: low (computed from sources)