# App shortcuts after install

> Why the manifest shortcuts menu exists only on an installed app's icon, which launchers show it, how shortcut URLs resolve against scope and the manifest URL, and how the app tells which shortcut launched it.

App shortcuts are the entries of the manifest `shortcuts` member that the operating system lists when the user right-clicks or long-presses an installed web app's icon; because the menu belongs to the icon, no shortcut exists until the app is installed. Chrome 85 on Android, Chrome and Edge 96 on desktop, Samsung Internet 14, and Safari 17.4 on macOS read the member; Safari on iOS and Firefox ignore it (BCD `html.manifest.shortcuts`).

## How it works

At install time the browser copies `shortcuts` into the platform's own shortcut mechanism: a Windows jump list (right-click the taskbar or Start icon), the macOS Dock icon's context menu, the ChromeOS shelf menu, or Android's launcher shortcuts (long-press the home-screen or drawer icon). The entries are static copies, so a changed manifest reaches the menu only after the browser's next manifest update check, not on the next page load.

Each shortcut object carries `name` (required), `url` (required), and optional `short_name`, `description`, and `icons`. The `url` is resolved against the manifest's own URL, not against the page or `start_url`: a manifest at `/dashboard/manifest.json` with `"url": "../projects"` opens `/projects`. The resolved URL must lie within the manifest `scope`; an out-of-scope entry is dropped by the browser rather than reported. The specification caps nothing, but Windows jump lists and Android launchers show only the first few entries, so the most used actions belong at the top of the array.

Nothing about a shortcut is special once it opens: the app receives an ordinary navigation to the shortcut's URL in its standalone window. A shortcut therefore costs nothing where the member is unsupported (the same URLs still work as links inside the app), and gains a launcher-level entry point where it is.

:::observed
On Windows 11 (English UI), right-clicking the taskbar icon of a Chrome-installed PWA shows a jump list whose "Tasks" group lists the manifest shortcuts by `name`, each with its `icons` entry rendered at jump-list size, above the fixed "Pin to taskbar" and "Close window" items; a manifest without `shortcuts` shows only the fixed items. On Android with Chrome, a long-press on the WebAPK icon lists the same `name` values, and dragging one to the home screen creates a standalone pinned shortcut that opens the shortcut `url` directly.
:::

## Examples

The manifest excerpt declares two shortcuts; the script shows how the app learns which one launched it and what happens where the member is unsupported.

### Declaring shortcuts that resolve inside the app's scope

The manifest lives at `/app/manifest.webmanifest`, so the relative URLs resolve under `/app/`. Each entry carries a `?source=shortcut` query so the app can attribute the launch.

```json
{
  "name": "Calendar",
  "start_url": "/app/",
  "scope": "/app/",
  "display": "standalone",
  "shortcuts": [
    {
      "name": "New event",
      "short_name": "New",
      "description": "Create an event starting now",
      "url": "new-event?source=shortcut",
      "icons": [{ "src": "icons/new-event-96.png", "sizes": "96x96", "type": "image/png" }]
    },
    {
      "name": "Today",
      "url": "today?source=shortcut",
      "icons": [{ "src": "icons/today-96.png", "sizes": "96x96", "type": "image/png" }]
    }
  ]
}
```

Writing `"url": "/new-event"` instead would resolve outside `/app/` and the browser would silently drop that shortcut; keeping the icons at 96×96 px matches the size Chrome documents for shortcut icons on Android.

### Telling which shortcut launched the app, with a fallback for browsers without shortcuts

The app reads `location.pathname` and the `source` query on startup. Where `shortcuts` is unsupported (Safari on iOS, Firefox), the same URLs are still reachable as in-app links, so the app exposes them in its own menu instead.

```js
const url = new URL(location.href);
const fromShortcut = url.searchParams.get('source') === 'shortcut';

if (fromShortcut && url.pathname.endsWith('/new-event')) {
  openEventEditor({ startsAt: Date.now() });
  analytics.track('launch_shortcut', { shortcut: 'new-event' });
}

// Fallback: no launcher menu exists on iOS Safari or Firefox, so surface the same
// actions inside the app. `display-mode` tells whether the app runs installed at all.
const installed = matchMedia('(display-mode: standalone)').matches || navigator.standalone === true;
if (!installed || /iPhone|iPad|Firefox/.test(navigator.userAgent)) {
  renderQuickActions([
    { label: 'New event', href: '/app/new-event?source=menu' },
    { label: 'Today', href: '/app/today?source=menu' },
  ]);
}
```

The `source` query is the only reliable attribution: the referrer is empty for a launcher-initiated navigation, and `document.referrer` cannot distinguish a shortcut from a typed URL.

## See also

- [Web Application Manifest: shortcuts member](https://www.w3.org/TR/appmanifest/#shortcuts-member) (w3.org)
- [Expose common app actions as shortcuts](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/How_to/Expose_common_actions_as_shortcuts) (developer.mozilla.org)
- [`shortcuts` manifest member](/reference/manifest/shortcuts/)
- [`shortcuts` browser support](/compatibility/manifest-shortcuts/)
- [beforeinstallprompt event](/reference/installation/install-prompt/)
- [`scope` manifest member](/reference/manifest/scope/)