Skip to content

Web app manifest

the file that lets a site be installed

What it is

A web app manifest is a JSON document that describes a website as an application: what it is called, which address it opens at, which addresses belong to it, how its window should look and which pictures stand for it. A page points to the file with a link element whose rel is manifest, and a browser that offers installation reads it to decide what the installed copy will be. The W3C specification that defines it calls it just the manifest, and so does most everyday talk about it.

The file is plain JSON. Its registered media type is application/manifest+json and its registered extension is .webmanifest, although the specification accepts any JSON media type and plenty of sites serve a manifest.json. Every member is optional. A small one for an imaginary tide-table site looks like this:

{
    "name": "Harbour Tide Tables",
    "short_name": "Tides",
    "start_url": "/tides/",
    "display": "standalone",
    "background_color": "#ffffff",
    "icons": [
        {
            "src": "/icons/tides-512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ]
}

Three members decide what the app is, as distinct from how it looks. start_url is the page the installed app opens at. scope is the set of addresses that count as inside the app; when it is missing, the specification takes the start URL with its file name, query and fragment cut off, so the example above owns everything under /tides/. id is the app’s identity, and when it is missing it falls back to the start URL. Relative addresses in the file, for icons and the start URL alike, resolve against the manifest’s own URL rather than the page that linked to it.

The rest is presentation. display asks for fullscreen, standalone, minimal-ui or browser, and a browser that cannot honour the request steps down that list in order. Each icon may carry a purpose: any by default, maskable for a picture drawn to survive cropping, or monochrome for one whose colour is thrown away so that only its alpha channel is used. theme_color tints the window around the app. A companion W3C note adds description, categories and screenshots, which install dialogs and stores may show.

Why it matters

The manifest is the only part of an installation the site controls. The specification leaves the decision to install with the browser: it may offer a way to install, and each browser sets its own conditions, some of which require particular members. A team ships the input and the browser decides what to do with it, so the useful question is less whether to have a manifest than what it promises.

Identity is the promise that outlives the file. When a browser fetches the manifest again, the specification requires it to use the id to decide whether the new file applies to an app that is already installed. Because a missing id is the start URL, moving the start page quietly changes the identity, and the new file then describes a different application from the one on the reader’s device. Pinning id on the first release costs one line and removes that risk.

Scope decides where the app window ends. Navigation inside the scope stays in the installed window. A link that leaves it still opens, but the specification expects the browser to show the reader that they are outside the app, so a scope narrower than the site makes ordinary links between its sections look like exits.

Icons are where most of the visible damage happens. Launchers crop icons to circles, rounded squares and other shapes, and a logo that fills its whole square loses its edges. The specification defines the safe zone of a maskable icon as a circle in the middle with a radius of two fifths of the icon’s size, and anything outside it may be cut. A monochrome icon keeps only its alpha channel, so an icon drawn on an opaque background turns into a solid square.

The colour members are narrower than they look. background_color is meant only for the moment before the stylesheet arrives, and the specification forbids the browser from using it once the stylesheet is available. It is the colour of the first frame, and nothing after it.

What it looks like in practice

This site builds its manifest from a template rather than keeping a file by hand. Its Hugo configuration registers application/manifest+json with the suffix webmanifest and makes the manifest an output of each language’s home page, so every language gets its own file, and the head of each page links the file for its own language. The configuration records why: a single global copy can name only one start URL, and a Danish visitor who installed the site got an app that opened the English home.

The template assembles the manifest as a map and hands it to Hugo’s JSON encoder, so escaping and commas are the encoder’s problem and a missing comma cannot ship. Every icon address starts at the root of the host. Its comment spells out the reason: a relative address in /da/manifest.webmanifest would resolve under /da/, and every icon in Danish would 404. The icons themselves are read off disk from three folders, one per purpose, ten sizes each from 36 to 512 pixels, with the size taken from the file name, so a size added by the generator appears in the manifest with nothing to edit.

id is pinned to the language’s home address, and scope is the whole origin, so a link into another language stays inside the installed window. Screenshots are named <lang>-<form_factor>-<WxH>.webp, and the template reads all three facts off the file name: English and Danish each carry one wide shot at 1280 by 800 and one narrow shot at 780 by 1688. A language without screenshots gets no screenshots member at all, rather than an empty list.

The credits page lists the W3C specification among the standards the site follows, and PWABuilder among the outside checks anyone can run: a service that reads the manifest and the rest of what makes the site installable, from Microsoft’s machines.

The install page is the reader’s half of the same file. It walks through where each browser keeps the install command, notes that Apple’s browsers never offer it unprompted and that Firefox on the desktop installs no web apps at all, and sets expectations: the installed app is an icon and a window of its own, and it needs a connection, because working offline takes a service worker.

Where it goes wrong

Every icon 404s on a translated path. A relative src resolves against the manifest’s URL, so a manifest served from a language prefix asks for icons under that prefix. Write icon addresses from the root of the host, or as full URLs.

The installed app opens in the wrong language. One manifest can name one start URL, so every reader installs the same home page. Serve one manifest per language, each with its own start_url and id, and link the matching one from each page.

Installed copies ignore the new manifest. With no id, the identity is the start URL, so moving the start page gives the file a new identity, and the browser no longer applies it to the app readers already have. Set id before the first release and leave it alone afterwards.

The launcher crops the logo. An icon with purpose any is placed or cut as the platform sees fit. Add a maskable icon with everything that matters inside the central circle whose radius is 40% of the icon.

Edits to the manifest change nothing. The HTML standard uses only the first link element whose rel contains manifest, so when a theme or plugin has put its own link earlier in the page, the file being edited is never the one read. Keep exactly one manifest link per page.

Further reading

Glossary