How to create app icons for a PWA (without losing your mind)
Maskable, monochrome, light/dark — everything you need to ship a polished install experience.
Progressive Web Apps need icons. A lot of icons. The web manifest spec supports multiple sizes, multiple purposes, and multiple formats — and every platform treats them slightly differently.
Here's how to ship a polished install experience without going down the icon rabbit hole.
The minimum icon set
For a PWA in 2026, you need these in your manifest.json:
| Size | Purpose | Where it's used |
| ------- | ---------- | ------------------------------ |
| 192×192 | any | Android Chrome, install prompt |
| 512×512 | any | PWA splash screen, app drawer |
| 192×192 | maskable | Android adaptive icons |
| 512×512 | maskable | Splash screen on Android |
That's four images. Two sizes, two purposes.
Understanding maskable icons
Android uses "adaptive icons" — the OS crops your icon into whatever shape the device manufacturer chose (circle, squircle, rounded square, etc.). A maskable icon is designed with a safe zone: the important content stays within the inner 80% of the canvas.
If you don't provide a maskable icon, Android will use your regular icon and crop it — often cutting off edges of your logo.
The safe zone
The safe zone is a circle with a diameter equal to 80% of the icon size. For a 512×512 icon, keep all important content within a centered 410×410 circle.
Tip: Fill the entire 512×512 canvas with your brand colour, then center your logo within the safe zone. This ensures the icon looks clean regardless of how the OS crops it.
Monochrome icons (optional but nice)
Some Android devices show monochrome icons in certain contexts (notification area, themed home screens). A monochrome icon is a single-colour silhouette of your logo.
{
"src": "/icon-mono.svg",
"sizes": "any",
"type": "image/svg+xml",
"purpose": "monochrome"
}
This is optional — if you don't provide one, the OS uses the regular icon. But if your app supports it, it's a nice polish touch.
The manifest entry
Here's a complete icons array for a production PWA:
{
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/icons/icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
iOS considerations
iOS doesn't read the web manifest for home screen icons. It uses the apple-touch-icon link tag:
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
Size: 180×180px. iOS adds its own rounded corners and gloss — don't add them yourself.
Testing your icons
- Android: Install the PWA from Chrome, check the app drawer and splash screen
- iOS: "Add to Home Screen" from Safari, check the home screen icon
- Desktop: Install the PWA from Chrome, check the taskbar/dock icon
- maskable.app: Use maskable.app to preview how your maskable icon looks in different shapes
Generating everything at once
Instead of manually creating each size and purpose:
- Start with a square image, at least 512×512
- Create a maskable version with the safe zone filled to the edges
- Use our favicon generator to produce all required sizes
- Drop them into your
public/icons/directory - Update your manifest
The generator handles the resizing, format conversion, and manifest generation — you just need to provide the source image.
Common mistakes
- Using a non-square source: Icons must be square. If your logo is wider than it is tall, add padding
- Forgetting the maskable safe zone: Your logo gets cropped on Android
- Using transparency in maskable icons: The safe zone must have an opaque background
- Setting both
anyandmaskableon the same icon: Use separate entries for each purpose