Understanding the Web App Manifest
The Web App Manifest is a simple JSON file that provides essential metadata about a web application. It tells the browser how your app should behave when installed on a user's device, enabling features like a custom icon on the home screen, a full-screen immersive experience, and a defined splash screen. The manifest is a cornerstone of Progressive Web Apps (PWAs), bridging the gap between regular websites and native-like applications.
In practical terms, the manifest is a structured set of key-value pairs that define the app's name, icons, display mode, orientation, theme colors, and more. Browsers and operating systems consume this data to present your app as a standalone entity, separate from the browser chrome. Without a manifest, your web app remains just another browser tab, missing out on install prompts, offline capabilities integration, and a cohesive brand presence on the user's home screen.
The specification is maintained by the W3C and is widely supported across modern browsers, including Chrome, Edge, Firefox, and Samsung Internet. Safari on iOS has also improved its support, making the manifest increasingly critical for delivering a consistent cross-platform experience.
Why the Web App Manifest Matters
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →Implementing a manifest isn't just about getting an "add to home screen" prompt. It influences multiple aspects of the user experience:
- Installability: Browsers use specific manifest criteria (along with a service worker) to trigger an install prompt, allowing users to add the app to their device. This dramatically increases engagement and repeat visits.
- Appearance and Branding: The manifest defines the app's icon set, which appears on the home screen, task switcher, and launcher. It also sets the splash screen background color and theme color for the browser UI, creating a consistent brand identity.
- Display Mode Control: You can specify whether the app opens in full-screen mode, standalone mode (with no browser UI), minimal-ui, or as a standard browser tab. Standalone mode is essential for making a web app feel native.
- Orientation Locking: You can lock the app to a specific orientation (portrait or landscape) or let it adapt naturally, which is crucial for games, media apps, or productivity tools.
- Scope Enforcement: The manifest defines the navigational scope of the app. Links outside this scope open in a regular browser tab, preserving the standalone experience and preventing accidental navigation away from the app context.
Beyond the user-facing benefits, a well-crafted manifest signals to search engines and app stores (via PWA packaging) that your web application is ready for offline use and native integration. It's a key requirement for inclusion in technologies like Trusted Web Activities on Android and for meeting PWA installability criteria used by Lighthouse.
Creating and Linking the Manifest
The manifest file is typically named manifest.json (or app.webmanifest) and placed in the root directory of your web application. You then link to it from the <head> of every page using a <link> element with the rel="manifest" attribute.
Here's the minimal HTML snippet to include a manifest:
<head>
<link rel="manifest" href="/manifest.json">
<!-- Optionally specify a theme color for browsers that support it -->
<meta name="theme-color" content="#1e88e5">
</head>
The href can be an absolute or relative URL. For security and correct scope handling, it's strongly recommended to serve the manifest from the same origin as the app. The manifest must be served with the MIME type application/manifest+json. Most static servers handle .json files with application/json, which browsers also accept, but for correctness you should configure your server to send the proper content type.
Here's how to set the correct header in popular servers:
# Apache (.htaccess or server config)
AddType application/manifest+json .webmanifest
# Nginx
types {
application/manifest+json webmanifest;
}
# Express.js
app.use((req, res, next) => {
if (req.url.endsWith('.webmanifest')) {
res.set('Content-Type', 'application/manifest+json');
}
next();
});
Essential Manifest Properties
A valid manifest must contain at least a name or short_name property, along with an icon array. However, to fully leverage the platform, you should include several key fields. Below is a detailed breakdown of the most important properties.
name and short_name
name provides the full descriptive title of the app (used in the app launcher and splash screen). short_name is a condensed version (max 12 characters recommended) shown on the home screen under the icon when space is limited. If only name is provided, it will be truncated in tight spaces, so always supply both.
"name": "My Awesome Progressive Web App",
"short_name": "Awesome App"
start_url and scope
start_url specifies the entry point when the user launches the installed app. It can be a relative URL like "/" or a specific path. scope defines the set of URLs that are considered within the app. Navigation to URLs outside the scope opens in a regular browser tab. The default scope is the directory where the manifest resides. A common pattern is setting both explicitly:
"start_url": "/index.html?utm_source=pwa",
"scope": "/"
Adding a query parameter to start_url (like ?utm_source=pwa) helps you distinguish standalone app traffic in analytics. Ensure the start_url falls within the scope.
display
Controls the UI mode when the app is launched. Available values:
"fullscreen": No browser chrome, the app occupies the entire display area. Ideal for immersive experiences like games."standalone": Looks like a standalone app with no browser UI, but still shows system status bars and navigation controls on some platforms. This is the most common choice for PWAs."minimal-ui": Similar to standalone but shows a minimal set of UI controls (back, forward, reload). Often not available on modern platforms."browser": Opens as a normal browser tab with all UI elements. Rarely used for PWAs.
"display": "standalone"
orientation
Defines the default orientation and whether the app can rotate. Accepts "any", "natural", "portrait", "landscape", "portrait-primary", "landscape-primary", etc. Use with care; locking orientation can hinder usability on devices that naturally rotate (like tablets).
"orientation": "portrait-primary"
theme_color and background_color
theme_color tints the browser's toolbar and other UI elements (e.g., the address bar color on Android). background_color is the background color of the splash screen shown while the app loads. Both should match your app's branding to create a seamless experience.
"theme_color": "#1e88e5",
"background_color": "#e3f2fd"
icons
The icons array is arguably the most critical part. Each icon object must have at least src, sizes, and type. Browsers use these icons for the home screen, task switcher, splash screen, and app launcher. You should provide a range of sizes to cover different device densities and contexts.
"icons": [
{
"src": "/icons/icon-48x48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
The purpose property (optional) can be "any", "maskable", or "monochrome". "maskable" icons adapt to different shape masks (like circles on Android or rounded rectangles on iOS) by filling the entire icon area with a safe zone. Including both purposes ensures your icon looks great on all platforms.
Going Further with Advanced Properties
shortcuts
The shortcuts array lets you define quick actions that appear in the app's long-press context menu (on Android) or via jump list. Each shortcut needs a name, a url, and an optional description and icons.
"shortcuts": [
{
"name": "New Post",
"short_name": "Post",
"description": "Create a new blog post",
"url": "/new-post",
"icons": [
{
"src": "/icons/new-post-96.png",
"sizes": "96x96",
"type": "image/png"
}
]
},
{
"name": "View Stats",
"url": "/stats",
"description": "Check site analytics"
}
]
related_applications and prefer_related_applications
You can specify native app counterparts to prevent the browser from prompting installation of the web app if a native version is already installed or preferred.
"related_applications": [
{
"platform": "play",
"url": "https://play.google.com/store/apps/details?id=com.example.app",
"id": "com.example.app"
},
{
"platform": "itunes",
"url": "https://apps.apple.com/app/example/id123456789"
}
],
"prefer_related_applications": true
When prefer_related_applications is true, the browser suggests the native app instead of the PWA install prompt. Use this only if you have a mature native app and want to direct users there.
share_target (Web Share Target)
The manifest can register your app as a share target, allowing users to share content from other apps directly into your PWA. This requires careful handling of form data via GET or POST.
"share_target": {
"action": "/share-handler",
"method": "POST",
"enctype": "multipart/form-data",
"params": {
"title": "name",
"text": "description",
"url": "link",
"files": [
{
"name": "media",
"accept": ["image/*", "video/*"]
}
]
}
}
The action URL receives the shared data. On the service worker side, you can handle the incoming navigation to process the shared content. This is an advanced feature that significantly boosts the PWA's integration with the OS.
display_override
For cutting-edge display modes like "window-controls-overlay" on desktop platforms, use display_override to fall back gracefully if the preferred mode isn't supported.
"display_override": ["window-controls-overlay", "standalone"],
"display": "standalone"
This tells the browser to try window-controls-overlay first; if unsupported, fall back to standalone.
Putting It All Together: A Complete Manifest Example
Below is a robust, production-ready manifest.json that incorporates most of the properties discussed. Adapt it to your app's branding and requirements.
{
"name": "Daily Planner Pro",
"short_name": "Planner",
"description": "A powerful daily planner and task manager",
"start_url": "/index.html?utm_source=pwa",
"scope": "/",
"display": "standalone",
"display_override": ["window-controls-overlay", "standalone"],
"orientation": "any",
"theme_color": "#0d47a1",
"background_color": "#e8eaf6",
"icons": [
{
"src": "/icons/icon-48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "/icons/icon-72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/icons/icon-96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/icons/icon-144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"shortcuts": [
{
"name": "Add Task",
"short_name": "Add",
"description": "Quickly add a new task",
"url": "/add-task",
"icons": [
{
"src": "/icons/add-task-96.png",
"sizes": "96x96",
"type": "image/png"
}
]
},
{
"name": "View Today",
"url": "/today",
"description": "See today's schedule"
}
],
"share_target": {
"action": "/share-handler",
"method": "POST",
"enctype": "multipart/form-data",
"params": {
"title": "name",
"text": "description",
"url": "link"
}
},
"related_applications": [
{
"platform": "play",
"url": "https://play.google.com/store/apps/details?id=com.dailyplanner.pro",
"id": "com.dailyplanner.pro"
}
],
"prefer_related_applications": false,
"categories": ["productivity", "lifestyle"]
}
Best Practices for Production-Ready Manifests
Crafting a manifest is straightforward, but adhering to best practices ensures reliability, performance, and broad compatibility. Here are the key recommendations:
1. Provide a Full Range of Icon Sizes
Include at minimum 192x192 and 512x512 pixel icons, but also intermediate sizes (48, 72, 96, 144) to cover every platform. Use PNG or WebP formats with transparency for maskable icons. Always include at least one icon with "purpose": "maskable" to support Android's adaptive icons. Use tools like pwabuilder or a custom build script to generate these from a single high-resolution source image.
2. Validate Your Manifest
Use Lighthouse in Chrome DevTools, the manifest.json validator from the W3C, or online tools like manifest-validator.appspot.com. Check for correct MIME type, all required properties, and valid icon paths. A broken manifest silently fails, leaving the user with a generic browser icon.
3. Keep short_name Short
On many devices, the app label under the icon truncates beyond 12 characters. Use a concise, recognizable abbreviation. Test on real devices to ensure it doesn't get cut off awkwardly.
4. Set Proper Scope and start_url
Define scope explicitly to prevent users from navigating outside the app while in standalone mode. Use relative paths and keep the scope as broad as needed. If your app's root is /app/, set scope to /app/ and start_url to /app/. Avoid including external domains in scope; that would break the standalone boundary.
5. Match Theme and Background Colors
theme_color and background_color should reflect your brand palette. The splash screen background color is especially important for perceived loading speed. Use the same color as your app's initial loading screen background to avoid a jarring flash.
6. Use Analytics Tracking on start_url
Append a query parameter like ?utm_source=pwa to differentiate standalone sessions. This helps measure the PWA's impact on engagement and conversion rates.
7. Integrate with a Service Worker
A manifest alone doesn't enable offline caching or background sync. For full PWA installability, you must register a service worker that fetches at least a basic offline fallback page. The manifest and service worker together unlock the install prompt. Ensure the service worker scope matches or is broader than the manifest scope.
// Register service worker in your main JavaScript file
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js', { scope: '/' })
.then(reg => console.log('SW registered', reg))
.catch(err => console.error('SW registration failed', err));
}
8. Test Across Platforms and Devices
Manifest behavior varies slightly between Android (Chrome, Samsung Internet), iOS (Safari), and desktop PWAs. Test installation flow, icon rendering, splash screen, and scope navigation on real devices. Use remote debugging or tools like BrowserStack if needed.
9. Keep the Manifest Lightweight and Static
The manifest should be a static JSON file, not dynamically generated per request. Caching it aggressively (with a long max-age) is fine, but ensure changes propagate when you update icons or properties by using cache-busting filenames or versioned URLs.
10. Leverage Shortcuts and Share Target Wisely
Only add shortcuts that provide genuine user value. Too many shortcuts clutter the menu. For share target, ensure you handle both GET and POST requests appropriately, validate incoming data, and provide clear feedback.
Conclusion
The Web App Manifest is a small file with immense power. It transforms a standard web experience into an app-like presence on the user's device, bridging the gap between the web and native platforms. By carefully defining your app's identity, icons, display mode, and advanced features like shortcuts and share targets, you create a polished, engaging, and trustworthy experience that users are more likely to install and revisit. Pair it with a solid service worker, follow the best practices outlined here, and test across environments to ensure your PWA truly shines. As the web platform continues to evolve, the manifest will remain a foundational piece of the progressive web ecosystem, unlocking new capabilities and deeper OS integration.