Manifest · Manifest member
Manifest icons and maskable icons
Published Updated
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.
Syntax
Section titled “Syntax”{ "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.
Where it is supported
Section titled “Where it is supported”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”.
The minimum set worth shipping
Section titled “The minimum set worth shipping”192x192and512x512— 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
maskablevariant, in addition to a plain one — see below.
Maskable icons
Section titled “Maskable icons”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”.
How to detect it at runtime
Section titled “How to detect it at runtime”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.
Practical checklist
Section titled “Practical checklist”- A 192 px and a 512 px icon are declared — without both, Chromium browsers do not promote the app for installation (MDN).
- A
maskablevariant is declared in addition to ananyicon, 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”.
-
typeis set on every entry so browsers can skip formats they cannot decode. -
srcvalues 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-srcdirective”, so a cross-origin icon outsideimg-srcis 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”.
Where to go next
Section titled “Where to go next”- Installability criteria — the rest of the bar your manifest clears before an install is offered.
- PWAs on Chrome and Android — where maskable icons and the splash-screen sizes actually show up.
- Manifest scope — the member that decides which URLs the installed app claims.
Specifications
| Specification | Status |
|---|---|
| Web App Manifest: icons | W3C draft |
- Legend
- Yes
- Partial
- Flag
- No
- Unknown
| Browser / Platform | Support | Versions | Confidence | Source | Notes |
|---|---|---|---|---|---|
| Chrome (Android) | Yes | 39 | medium | source | 1 |
| Chrome (Desktop) | Yes | 39 | low | source | — |
| Edge (Desktop) | Yes | 79 | low | source | — |
| Safari (iOS) | Yes | 15.4 | low | source | 2 |
| Safari (macOS) | Yes | 17 | low | source | — |
| Firefox (Desktop) | No | — | low | source | 3 |
| Firefox (Android) | Yes | 152 | medium | source | — |
| Samsung Internet | Yes | 4.0 | low | source | — |
- Maskable icons supported via purpose: maskable.
- Maskable icons not supported; iOS applies its own icon shape.
- No install flow consuming manifest icons on desktop.