manifest.json Explained: Web App Manifest Fields, Install Prompts, and Icons
Last updated:
manifest.json (officially .webmanifest) is a JSON file defined by the W3C Web App Manifest specification that tells browsers how to install a website as a Progressive Web App. The spec marks only name and icons as required, but Chrome enforces four practical installability rules: HTTPS, a service worker with a fetch handler, a manifest, and icons that include both 192x192 and 512x512 PNGs. This guide covers every field grouped by purpose — required basics, display modes, icons, colors, scope, and advanced PWA features.
Need to validate a manifest file? Paste it into Jsonic's JSON Validator — it pinpoints syntax errors with line and column numbers before Chrome silently rejects the file.
Validate manifest.jsonRequired fields for an installable PWA
The W3C spec lists name and icons as the only required fields. In practice, Chromium-based browsers (Chrome, Edge, Brave, Samsung Internet, Opera) refuse to fire the beforeinstallprompt event unless the manifest also defines start_url, a display value other than browser, and icons that include both a 192x192 and a 512x512 PNG. The minimum useful manifest looks like this:
{
"name": "Jsonic JSON Tools",
"short_name": "Jsonic",
"start_url": "/",
"display": "standalone",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}name— full app name shown in install dialogs and the app drawer. No length limit, but ~30 characters fits most UIs.short_name— fallback used whennameis too long (home screen labels, taskbar tooltips). Aim for <12 characters.start_url— URL the installed app opens to. Must be same-origin and withinscope. Defaults to the manifest's URL if omitted.display— how the app launches. See section 2 for the four valid values.icons— array of icon objects withsrc,sizes, andtype. See section 3 for the icon contract.
Display modes: fullscreen, standalone, minimal-ui, browser
The display field decides how much browser chrome is visible when the installed app launches. There are four valid values and browsers fall back through the list in spec order if a mode is unsupported.
| Value | Browser chrome | OS chrome | When to use |
|---|---|---|---|
fullscreen | None | Hidden (status bar gone) | Games, video players, immersive media |
standalone | None | Visible | Most PWAs — looks like a native app |
minimal-ui | Back/forward + URL bar | Visible | Sites where users need light navigation |
browser | Full browser UI | Visible | Never for installable PWAs — disables install prompt |
A newer field display_override (Chrome 89+) takes an ordered array and overrides display when the browser supports an item in the list — useful for opting into window-controls-overlay (lets your app paint into the title bar) and tabbed (multi-tab installed apps). Example: "display_override": ["window-controls-overlay", "standalone"].
Icons: sizes, purpose (any / maskable), and the 512x512 + 192x192 requirement
icons is an array of icon objects. Chrome's installability check requires at least one 192x192 PNG and one 512x512 PNG. The browser uses 192x192 for the home-screen icon on most Android launchers and 512x512 for the splash screen image and high-DPI displays. Shipping additional sizes (48, 72, 96, 144) is harmless and improves rendering on older Android devices.
{
"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-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}The purpose field accepts three values: any (the default — icon is used as-is), maskable (icon includes a safe zone so launchers can crop it to any shape), and monochrome (single-color icon used for badging on Android). You typically ship at least one any icon and one maskable icon. The two can be different artwork — the maskable version usually has ~20% extra padding on each side.
An icon entry can declare multiple purpose values space-separated: "purpose": "any maskable" tells the browser the same image works for both roles, but in practice this rarely produces good results because a maskable-safe icon looks shrunken when rendered at the unmasked size. Ship two separate files.
Theme color, background color, and the splash screen
Two color fields control the visual identity of an installed PWA. They are both CSS color values (named, hex, rgb, hsl) but they fire at different times.
{
"theme_color": "#0a0a0a",
"background_color": "#ffffff"
}theme_color— the color of the OS toolbar (Android status bar, Chrome desktop title bar). Applies once the app is running. Also picked up by the HTML<meta name="theme-color">tag, which takes precedence per-page if set. Many sites use<meta name="theme-color" media="(prefers-color-scheme: dark)">to swap colors with system theme.background_color— the color shown on the splash screen during launch, before the first frame of the app paints. The browser uses this color, the 512x512 icon, andnameto auto-generate a splash screen on Android. Pick a color that matches the body background of your start_url to prevent a visible flash.
iOS Safari ignores both fields for installed apps and reads color hints from the meta tags <meta name="apple-mobile-web-app-status-bar-style"> and a separate apple-touch splash image set instead.
Scope, start_url, and id
Three fields together define the install identity and which URLs the installed app considers "inside" itself. Get these wrong and links inside your app open in the system browser instead of the installed window.
{
"id": "/?source=pwa",
"start_url": "/?source=pwa",
"scope": "/"
}scope— URL prefix that defines the navigation boundary of the app. Any in-app link to a URL outsidescopeopens in a regular browser tab. Defaults to the directory ofstart_url. Set to"/"to claim the entire origin.start_url— the entry URL the installed app opens to. Must be same-origin as the manifest and withinscope. A common pattern is/?source=pwaso you can attribute analytics traffic from installed apps separately.id— a stable identifier for the install. Two installs cannot coexist if they share the sameid. Defaults tostart_url, which means changingstart_urllater produces a "ghost install" — the same site installed twice. Set an explicitidfrom day one to avoid this.
The relationship between the three: id is the unique key, scope is the boundary, start_url is where the app opens. Cross-origin URLs are never allowed in any of them.
Advanced: shortcuts, share_target, file_handlers, protocol_handlers
The Manifest spec keeps adding fields for deeper OS integration. The four below are broadly supported in Chromium and turn a basic PWA into something that competes with a native app.
{
"shortcuts": [
{
"name": "New document",
"url": "/new",
"icons": [{ "src": "/icons/new.png", "sizes": "96x96" }]
},
{
"name": "Recent files",
"url": "/recent"
}
],
"share_target": {
"action": "/share",
"method": "POST",
"enctype": "multipart/form-data",
"params": {
"title": "title",
"text": "text",
"url": "url",
"files": [{ "name": "file", "accept": ["image/*"] }]
}
},
"file_handlers": [
{
"action": "/open",
"accept": { "application/json": [".json"] }
}
],
"protocol_handlers": [
{ "protocol": "web+jsonic", "url": "/open?url=%s" }
]
}shortcuts— right-click menu items on the taskbar/home screen icon. Limit to four; older Android only shows four. Each entry needsnameandurl;iconsare optional but improve the menu.share_target— registers the app as a target in the OS share sheet. When a user shares text or files from another app, the OS sends them to youractionURL using the declared HTTP method. Use POST for file shares.file_handlers— associates file types with the installed app. After install, the OS offers to open matching files (e.g., .json) directly in your PWA. Requires user permission on first use.protocol_handlers— registers custom URL protocols (e.g.,web+jsonic://) so clicking such links anywhere on the OS opens your installed app. The protocol prefixweb+is reserved for PWAs.
Linking the manifest from your HTML and serving the correct MIME type
The manifest file is inert until you reference it from an HTML page. Add one link tag in <head> on every page that should be installable (typically all pages):
<link rel="manifest" href="/site.webmanifest">
<!-- For cross-origin manifests, add crossorigin -->
<link rel="manifest" href="https://cdn.example.com/site.webmanifest" crossorigin="use-credentials">In Next.js App Router, set the manifest path in your root layout's metadata export:
export const metadata = {
manifest: '/site.webmanifest',
}The server must return the file with the correct MIME type. Browsers tolerate application/json but Lighthouse, audits, and some installability checks expect application/manifest+json. Configure your server:
# Nginx (in mime.types or a server block)
types {
application/manifest+json webmanifest;
}
# Apache (in .htaccess)
AddType application/manifest+json .webmanifest
# Vercel (in vercel.json)
{
"headers": [
{
"source": "/site.webmanifest",
"headers": [{ "key": "Content-Type", "value": "application/manifest+json" }]
}
]
}Verify everything by opening Chrome DevTools → Application → Manifest. The panel shows the resolved manifest, the parsed icons, and any errors in plain English. If Chrome reports no errors but the install prompt still does not appear, run a Lighthouse PWA audit — it lists each installability criterion and which one failed.
Key terms
- Progressive Web App (PWA)
- A website that, with a service worker and a Web App Manifest, can be installed to a device home screen or taskbar and launched in its own window. Browsers treat installed PWAs as first-class apps for share targets, file handlers, and protocol handlers.
- Web App Manifest
- A JSON file defined by the W3C Web App Manifest spec that describes how a site should be installed and presented as an app. Linked from HTML via
<link rel="manifest">. - Display mode
- One of
fullscreen,standalone,minimal-ui, orbrowser. Controls how much browser chrome is visible in the installed app window.browserdisables installability. - Maskable icon
- An icon designed with a safe zone (~80% center) so launchers can crop it to any shape (circle, squircle, rounded square) without clipping the logo. Declared with
"purpose": "maskable". - Scope
- A URL prefix that defines which pages are considered "inside" the installed app. Links to URLs outside
scopeopen in a regular browser tab. Defaults to the directory ofstart_url. - Service worker
- A script the browser runs in the background, registered via
navigator.serviceWorker.register(). Required for Chrome installability — must have afetchevent handler. Handles offline support, caching, and push notifications independently of the manifest.
Frequently asked questions
What is manifest.json used for?
manifest.json (also commonly named site.webmanifest) is a JSON file defined by the W3C Web App Manifest specification that tells browsers how to present a website as an installable Progressive Web App (PWA). It supplies the app name, icons, start URL, display mode, and theme color the browser uses when the user installs the site to their home screen, taskbar, or app launcher. Without a manifest, Chrome, Edge, and other Chromium browsers will not show the install prompt and the site cannot be added as a standalone app. The manifest is linked from HTML with <link rel="manifest" href="/site.webmanifest"> and is fetched by the browser when it evaluates installability, when generating the splash screen, and when launching the installed app.
What fields are required to make a PWA installable in Chrome?
Chrome considers a site installable when all of the following are true: the page is served over HTTPS (or localhost for testing), it has a registered service worker with a fetch handler, and it links a manifest containing at minimum name (or short_name), start_url, icons that include both a 192x192 and a 512x512 PNG, and a display value of standalone, fullscreen, or minimal-ui (not browser). The W3C spec itself only requires name and icons, but the four "installability criteria" above are what triggers the beforeinstallprompt event in Chromium. Edge, Brave, and Samsung Internet follow the same rules. Safari on iOS uses a different and looser path via apple-touch-icon meta tags and does not use the beforeinstallprompt event.
What's the difference between display: standalone and display: fullscreen?
display: standalone launches the installed app in its own window without the browser address bar, tabs, or bookmarks toolbar, but keeps the operating-system chrome — status bar, navigation gestures, and notification area. This is the most common choice for PWAs and is what most users expect from an installed app. display: fullscreen takes over the entire screen including the OS status bar and is intended for games, video players, and immersive media experiences. It is supported on Android and Chrome OS but on desktop platforms many browsers fall back to standalone. There are two more options: minimal-ui keeps a minimal browser UI (typically just back/forward and a URL display) and browser launches in a normal browser tab, which means the site is not actually treated as installed. Use standalone unless you have a specific reason for fullscreen.
What does "purpose": "maskable" do for icons?
A maskable icon is designed so its important visual content lives inside a guaranteed "safe zone" — roughly the center 80% of the canvas. Android and other platforms apply different mask shapes to home-screen icons (circle, squircle, rounded square, teardrop) depending on the launcher, and a regular icon often gets clipped or shrunk awkwardly. By declaring "purpose": "maskable" the browser knows it can crop the icon to any of these shapes without losing the logo. You typically ship two icon entries: one with "purpose": "any" (used as-is) and one with "purpose": "maskable" (designed with padding). The two can have different artwork. Test maskable icons at maskable.app, which previews every common mask shape side by side.
Should I use manifest.json or site.webmanifest?
Functionally identical — only the filename differs, and the browser does not care what you call the file. The W3C spec recommends the .webmanifest extension because the official MIME type is application/manifest+json and the extension makes server MIME configuration cleaner. Many tools (notably the RealFaviconGenerator and Next.js metadata API) default to site.webmanifest for that reason. Older tooling and most tutorials still use manifest.json, which works fine as long as your server returns application/manifest+json (or application/json — browsers accept both). What matters is the <link rel="manifest" href="..."> tag in HTML, which can point to any path. Pick one convention and keep it consistent across your project.
Why is my manifest.json not being recognized by Chrome?
Open Chrome DevTools → Application → Manifest. The panel shows the parsed manifest plus any errors. The five most common failures: (1) the <link rel="manifest"> tag is missing or has a wrong href — manifest must be reachable from the same origin; (2) the file returns 404 or HTML instead of JSON — check your server route; (3) the MIME type is wrong (e.g. text/plain) — set application/manifest+json; (4) the JSON is invalid — paste it into Jsonic's validator; (5) required fields are missing or icons don't include both 192x192 and 512x512 PNG, in which case Chrome parses the manifest but refuses to fire beforeinstallprompt. CORS can also block cross-origin manifests unless you set crossorigin="use-credentials" on the link tag.
What MIME type should manifest.json be served with?
The official MIME type registered with IANA is application/manifest+json. Browsers also accept application/json and even text/json without complaint, but Lighthouse and some accessibility audits flag anything other than application/manifest+json as a warning. Configure your server to map either .webmanifest or .json (in the manifest path) to that content type. In Nginx: add types { application/manifest+json webmanifest; }. In Apache: AddType application/manifest+json .webmanifest. In Next.js App Router or Vercel deployments, set the Content-Type header on the response or use a route handler that returns NextResponse.json() with the header overridden. Never serve the manifest as text/html — Chrome will silently ignore it.
Can I have multiple manifests on one site?
Only one manifest is active per page. A page can have exactly one <link rel="manifest"> tag — browsers use the first one and ignore the rest. However, different pages on the same domain can link to different manifests, and the browser treats each as a separate installable identity if the id and start_url combination differs. This is how some large products ship sub-apps (e.g., docs.example.com vs app.example.com each with its own manifest, or even /admin and /public with distinct manifests). Each installation is scoped by the manifest_id field (defaults to start_url): two installs cannot coexist if they share the same id. Set explicit id values when you want multiple installable surfaces on one origin.
Further reading and primary sources
- W3C Web App Manifest — The authoritative specification for every manifest field and value
- MDN — Web App Manifest — Browser-by-browser support tables and field reference
- Chrome — PWA install criteria — How Chrome decides whether to fire the install prompt
- Maskable.app — Preview maskable icons against every common launcher mask shape
- web.dev — Install criteria — Google’s walkthrough of the installability checklist with debugging tips