Web Manifest Generator
Fill in name, colors, display mode and icons and get a valid site.webmanifest plus the link tag — built entirely on this device.
{
"lang": "en",
"name": "Tidewater Notes",
"short_name": "Tidewater",
"scope": "/",
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"display": "standalone",
"start_url": "/",
"theme_color": "#0f766e",
"background_color": "#f8fafc",
"description": "Quick notes for small crews, saved on board and synced at the dock."
}<link rel="manifest" href="/site.webmanifest">Paste the link tag inside <head> and put the file next to your homepage, so it is served from /site.webmanifest.
The file holds the same text as Copy JSON, with a trailing newline.
Press ? for shortcuts
Do it offline:$ npx pwa-asset-generator logo.png ./icons --manifest site.webmanifest
How to use it
- Name the app, add a short name for launchers, and pick a display mode — standalone is the usual choice.
- Set the start URL and, for an app under a sub-path, a scope prefix that contains it; both must be relative.
- List icons by path, size and purpose, or press the preset button for the modern three-entry set with a maskable icon.
- Copy the JSON or download site.webmanifest, then paste the link tag into your page's head.
Fill in your app's name, colors, display mode and icon paths, and a valid site.webmanifest assembles itself beside the link tag that loads it — built entirely on this device, nothing uploaded. Empty members stay out of the file, and cross-origin paths, scope mismatches and unparsable icon sizes are refused before you copy. The icon images themselves are a separate job; this page writes the JSON that points at them.
How it works
When a page links a manifest, the browser reads that JSON at install time: name and icons feed the install prompt and launcher entry, start_url is where the installed app opens, scope bounds in-app navigation, and theme_color and background_color paint window chrome and splash screen. Omitted members fall back to spec defaults, which is why empty fields stay out instead of shipping as blank strings.
Display modes form a spec-defined fallback chain: a browser that cannot provide fullscreen falls back to standalone, then minimal-ui, then browser, which every user agent can supply. Declaring fullscreen is a request, not a command — the installed app gets the closest mode its platform supports.
An icon's purpose member tells launchers how it may be treated: "maskable" promises the image fills its whole canvas with the important content inside the safe zone, so an adaptive launcher can crop it into a circle or squircle without cutting the logo. Plain icons are masked conservatively — shrunk and padded — so the builder notes when no maskable entry exists.
Examples
The minimal valid manifest{
"name": "Daily Tides",
"display": "standalone",
"start_url": "/"
}Three members make a valid file: every empty field is omitted rather than emitted blank. One note remains — no icon carries the maskable purpose — because an installable app usually wants icons.
A full PWA with icons and a shortcut{
"lang": "en",
"name": "Tidewater Notes",
"short_name": "Tidewater",
"scope": "/",
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"display": "standalone",
"orientation": "portrait",
"start_url": "/",
"id": "tidewater-notes",
"theme_color": "#0f766e",
"background_color": "#f8fafc",
"shortcuts": [
{
"name": "New note",
"url": "/new"
}
],
"description": "Quick notes for small crews, saved on board and synced at the dock."
}Member order follows the spec's own listing, with description last — it comes from the Manifest App Info extension. The ordinary icons omit purpose — "any" is the default — while the third declares itself maskable.
Maskable-only, fullscreen kiosk-style app{
"name": "Harbor Radar",
"short_name": "Radar",
"icons": [
{
"src": "/maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"display": "fullscreen",
"start_url": "./index.html",
"theme_color": "#111827",
"background_color": "#0b1220"
}A single maskable 512 px entry clears the maskable note, and a page-relative start URL like ./index.html is accepted alongside root-relative paths. Fullscreen rides the fallback chain where a platform cannot honor it.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Short names get truncated, not wrapped
Launchers give the label under an icon one cramped line, so a long short_name is cut off rather than wrapped. The builder counts code points — an emoji is one character — and notes anything past the commonly cited twelve.
Scope is a string prefix, not a directory
The within-scope test compares serialized URLs character by character: a scope of /app also contains /application, and /app/ does not contain /app — the trailing slash participates. End a directory-style scope with a slash and keep the start URL inside it.
The id member survives a moved start URL
Browsers key an installed app on its id when one is declared, so routes can move later without the install becoming a different app. Set it once and keep it stable; this one field never travels in this page's shared links.
This file points at icons; it does not create them
Every src is a plain path to an image already on your site — nothing here uploads, resizes or encodes a picture. To make the actual icon files from one source image, use /favicon-generator and keep the paths in sync.
Common errors
start_url "https://cdn.example/app/" is an absolute URL — this generator only writes same-site relative paths such as / or ./index.html, so it never emits a cross-origin fetch target.- Every URL-shaped member is refused when absolute — the rule that keeps a generated manifest from naming another origin also catches a full address pasted where the spec resolves a relative one.
start_url "/" falls outside scope "/app/" — after resolution the start URL must begin with the scope string, character for character (a trailing slash counts).- A start URL outside the declared scope would open the app on a page its own boundary excludes; the spec's exact string rule is applied here, so the trailing-slash trap is caught before a device sees it.
The name member is empty — a manifest needs a name; browsers show it on the install prompt and under the installed icon.- Name is the one field this builder insists on: without it there is nothing to put on the install prompt, so no file is written and the last accepted one stays on screen.
FAQ
Where do I put the site.webmanifest file?
Serve it from your site's root, next to your homepage, and reference it with the link tag this page gives you. A sub-path works, but relative members then resolve against that location, so the root is the least surprising home.
Is manifest.json different from site.webmanifest?
Same format, different filename. The registered media type is application/manifest+json, and the .webmanifest extension maps to it — hence the download's name. Browsers read the JSON under either filename as long as the link tag points at it.
Is my app's information uploaded while I build the file?
No. The document is assembled by code running on this page, the download is created on this device, and using the tool sends nothing over the network — the network tab in your devtools will show exactly that.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Rebuild the manifest from the current fields.
- ⌘/Ctrl + S
- Download the file as site.webmanifest.
- ⌘/Ctrl + C
- Copy the JSON while nothing is selected.
- ⌘/Ctrl + Shift + C
- Copy the JSON even over a selection.
- t
- Cycle the color theme.
- ? or ⌘/Ctrl + /
- Open the shortcut help dialog.
- Esc
- Close the topmost dialog.